From c4ea40899a964aee7b29d9630c3ebdac18a0d060 Mon Sep 17 00:00:00 2001 From: karl Date: Tue, 10 Dec 2019 14:50:11 +0100 Subject: [PATCH] Password authentication --- .idea/artifacts/MoviesWebApp_war_exploded.xml | 1 + .idea/dataSources.xml | 11 ++++++ MoviesClient/pom.xml | 38 +++++++++++++++++++ MoviesClient/src/jaxws/authfile | 1 + .../MovieResourceClient.java | 4 +- .../MoviesWebServiceClient.java | 2 + .../PasswordAuthenticator.java | 19 ++++++++++ .../RequestFilter.java | 25 ++++++++++++ MoviesClient/web/index.html | 3 +- MoviesWebApp/MoviesWebApp.iml | 2 + MoviesWebApp/pom.xml | 6 +++ .../technikumwien/movies/MovieResource.java | 2 + .../movies/MovieSecurityConfig.java | 24 ++++++++++++ .../technikumwien/movies/MoviesService.java | 10 +++++ .../technikumwien/movies/MoviesServlet.java | 5 +++ .../movies/SH2HexPasswordHash.java | 18 +++++++++ .../main/resources/META-INF/persistence.xml | 7 +++- .../resources/META-INF/sql/security-data.sql | 31 +++++++++++++++ .../src/main/webapp/WEB-INF/jboss-web.xml | 7 ++++ MoviesWebApp/src/main/webapp/WEB-INF/web.xml | 26 +++++++++++++ 20 files changed, 238 insertions(+), 4 deletions(-) create mode 100644 .idea/dataSources.xml create mode 100644 MoviesClient/src/jaxws/authfile create mode 100644 MoviesClient/src/main/java/at.technikumwien.movies/PasswordAuthenticator.java create mode 100644 MoviesClient/src/main/java/at.technikumwien.movies/RequestFilter.java create mode 100644 MoviesWebApp/src/main/java/at/technikumwien/movies/MovieSecurityConfig.java create mode 100644 MoviesWebApp/src/main/java/at/technikumwien/movies/SH2HexPasswordHash.java create mode 100644 MoviesWebApp/src/main/resources/META-INF/sql/security-data.sql create mode 100644 MoviesWebApp/src/main/webapp/WEB-INF/jboss-web.xml diff --git a/.idea/artifacts/MoviesWebApp_war_exploded.xml b/.idea/artifacts/MoviesWebApp_war_exploded.xml index 4b2d59c..6d090c6 100644 --- a/.idea/artifacts/MoviesWebApp_war_exploded.xml +++ b/.idea/artifacts/MoviesWebApp_war_exploded.xml @@ -17,6 +17,7 @@ + diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..d8be893 --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,11 @@ + + + + + mysql.8 + true + com.mysql.cj.jdbc.Driver + jdbc:mysql://localhost:3306/db_movies + + + \ No newline at end of file diff --git a/MoviesClient/pom.xml b/MoviesClient/pom.xml index 7b3762e..96ad7ed 100644 --- a/MoviesClient/pom.xml +++ b/MoviesClient/pom.xml @@ -85,6 +85,38 @@ + + org.apache.maven.plugins + maven-resources-plugin + 3.1.0 + + ${project.build.directory} + + + src/main/resources + + **.* + + classes + + + src/jaxws + + authfile + + true + + + + + + initialize + + copy-resources + + + + com.helger.maven jaxws-maven-plugin @@ -95,6 +127,7 @@ at.technikumwien.movies.generated ${project.build.directory}/generated + ${project.build.directory}/authfile @@ -114,4 +147,9 @@ + + + moviesuser + topsecret + diff --git a/MoviesClient/src/jaxws/authfile b/MoviesClient/src/jaxws/authfile new file mode 100644 index 0000000..077fcf6 --- /dev/null +++ b/MoviesClient/src/jaxws/authfile @@ -0,0 +1 @@ +http://${movies.user}:${movies.password}@localhost:8080/movieservice/MoviesWebService?wsdl \ No newline at end of file diff --git a/MoviesClient/src/main/java/at.technikumwien.movies/MovieResourceClient.java b/MoviesClient/src/main/java/at.technikumwien.movies/MovieResourceClient.java index 85a6d0a..47a7595 100644 --- a/MoviesClient/src/main/java/at.technikumwien.movies/MovieResourceClient.java +++ b/MoviesClient/src/main/java/at.technikumwien.movies/MovieResourceClient.java @@ -7,7 +7,9 @@ import java.util.List; public class MovieResourceClient { public static void main(String[] args) { - var target = ClientBuilder.newClient().target("http://localhost:8080/movieservice/resources/movie"); + var target = ClientBuilder.newClient() + .register(new RequestFilter("moviesuser", "topsecret")) + .target("http://localhost:8080/movieservice/resources/movie"); List allMovies = target .request(MediaType.APPLICATION_XML) diff --git a/MoviesClient/src/main/java/at.technikumwien.movies/MoviesWebServiceClient.java b/MoviesClient/src/main/java/at.technikumwien.movies/MoviesWebServiceClient.java index 1351023..f1be56f 100644 --- a/MoviesClient/src/main/java/at.technikumwien.movies/MoviesWebServiceClient.java +++ b/MoviesClient/src/main/java/at.technikumwien.movies/MoviesWebServiceClient.java @@ -10,6 +10,8 @@ import java.util.List; public class MoviesWebServiceClient { public static void main(String[] args) throws Exception { + PasswordAuthenticator.install("moviesuser", "topsecret"); + MoviesWebService_Service service = new MoviesWebService_Service(); MoviesWebService port = service.getMoviesWebServicePort(); diff --git a/MoviesClient/src/main/java/at.technikumwien.movies/PasswordAuthenticator.java b/MoviesClient/src/main/java/at.technikumwien.movies/PasswordAuthenticator.java new file mode 100644 index 0000000..e714bc0 --- /dev/null +++ b/MoviesClient/src/main/java/at.technikumwien.movies/PasswordAuthenticator.java @@ -0,0 +1,19 @@ +package at.technikumwien.movies; + +import java.net.Authenticator; +import java.net.InetAddress; +import java.net.PasswordAuthentication; +import java.net.URL; + +public class PasswordAuthenticator { + private PasswordAuthenticator() {} + + public static void install(String username, String password) { + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password.toCharArray()); + } + }); + } +} diff --git a/MoviesClient/src/main/java/at.technikumwien.movies/RequestFilter.java b/MoviesClient/src/main/java/at.technikumwien.movies/RequestFilter.java new file mode 100644 index 0000000..a8ce548 --- /dev/null +++ b/MoviesClient/src/main/java/at.technikumwien.movies/RequestFilter.java @@ -0,0 +1,25 @@ +package at.technikumwien.movies; + +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientRequestFilter; +import javax.xml.bind.DatatypeConverter; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +public class RequestFilter implements ClientRequestFilter { + private String username; + private String password; + + public RequestFilter(String username, String password) { + this.username = username; + this.password = password; + } + + @Override + public void filter(ClientRequestContext request) throws IOException { + var token = username + ":" + password; + var basicAuthentication = "Basic " + DatatypeConverter.printBase64Binary(token.getBytes(StandardCharsets.UTF_8)); + + request.getHeaders().add("Authorization", basicAuthentication); + } +} diff --git a/MoviesClient/web/index.html b/MoviesClient/web/index.html index 53dcd2a..e37bde6 100644 --- a/MoviesClient/web/index.html +++ b/MoviesClient/web/index.html @@ -13,7 +13,8 @@ let options = { mode: "cors", headers: { - "Accept": "application/json" + "Accept": "application/json", + "Authorization": "Basic " + btoa("moviesuser:topsecret") } }; diff --git a/MoviesWebApp/MoviesWebApp.iml b/MoviesWebApp/MoviesWebApp.iml index 751ea1a..320a1a8 100644 --- a/MoviesWebApp/MoviesWebApp.iml +++ b/MoviesWebApp/MoviesWebApp.iml @@ -4,6 +4,7 @@ + @@ -75,5 +76,6 @@ + \ No newline at end of file diff --git a/MoviesWebApp/pom.xml b/MoviesWebApp/pom.xml index be015c1..bebc21d 100644 --- a/MoviesWebApp/pom.xml +++ b/MoviesWebApp/pom.xml @@ -45,6 +45,12 @@ MoviesCommon 1.0.0-SNAPSHOT + + + commons-codec + commons-codec + 1.13 + diff --git a/MoviesWebApp/src/main/java/at/technikumwien/movies/MovieResource.java b/MoviesWebApp/src/main/java/at/technikumwien/movies/MovieResource.java index 94d5ad0..da4b50d 100644 --- a/MoviesWebApp/src/main/java/at/technikumwien/movies/MovieResource.java +++ b/MoviesWebApp/src/main/java/at/technikumwien/movies/MovieResource.java @@ -1,5 +1,6 @@ package at.technikumwien.movies; +import javax.annotation.security.RolesAllowed; import javax.inject.Inject; import javax.ws.rs.*; import javax.ws.rs.core.Context; @@ -10,6 +11,7 @@ import java.net.URI; import java.util.List; @Path("/movie") +@RolesAllowed("MoviesUserRole") public class MovieResource { @Inject private MoviesService moviesService; diff --git a/MoviesWebApp/src/main/java/at/technikumwien/movies/MovieSecurityConfig.java b/MoviesWebApp/src/main/java/at/technikumwien/movies/MovieSecurityConfig.java new file mode 100644 index 0000000..7bca2c7 --- /dev/null +++ b/MoviesWebApp/src/main/java/at/technikumwien/movies/MovieSecurityConfig.java @@ -0,0 +1,24 @@ +package at.technikumwien.movies; + +import javax.annotation.security.DeclareRoles; +import javax.enterprise.context.ApplicationScoped; +import javax.security.enterprise.authentication.mechanism.http.BasicAuthenticationMechanismDefinition; +import javax.security.enterprise.identitystore.DatabaseIdentityStoreDefinition; + +@BasicAuthenticationMechanismDefinition(realmName = "MoviesWebApp") +@DeclareRoles({ + "MoviesAdminRole", + "MoviesUerRole" +}) +@DatabaseIdentityStoreDefinition( + dataSourceLookup = "java:jboss/datasources/MoviesDS", + callerQuery = "SELECT password FROM t_user WHERE username = ?", + groupsQuery = "SELECT r.rolename FROM t_user u " + + "INNER JOIN t_user_role ur ON u.id = ur.userid " + + "INNER JOIN t_role r ON ur.roleid = r.id " + + "WHERE u.username = ?", + hashAlgorithm = SH2HexPasswordHash.class +) +@ApplicationScoped +public class MovieSecurityConfig { +} diff --git a/MoviesWebApp/src/main/java/at/technikumwien/movies/MoviesService.java b/MoviesWebApp/src/main/java/at/technikumwien/movies/MoviesService.java index 11bca87..8637d60 100644 --- a/MoviesWebApp/src/main/java/at/technikumwien/movies/MoviesService.java +++ b/MoviesWebApp/src/main/java/at/technikumwien/movies/MoviesService.java @@ -1,16 +1,20 @@ package at.technikumwien.movies; import javax.annotation.Resource; +import javax.annotation.security.RolesAllowed; import javax.ejb.*; +import javax.inject.Inject; import javax.persistence.EntityManager; import javax.persistence.EntityNotFoundException; import javax.persistence.PersistenceContext; +import javax.security.enterprise.SecurityContext; import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; @Stateless @TransactionManagement(value=TransactionManagementType.CONTAINER) +@RolesAllowed("MoviesUserRole") public class MoviesService { private static final Logger LOGGER = Logger.getLogger(MoviesService.class.getName()); @@ -20,6 +24,9 @@ public class MoviesService { @PersistenceContext(unitName = "MoviesPU") private EntityManager em; + @Inject + private SecurityContext securityContext; + public Movie findById(long id) { LOGGER.info("findById() >> id=" + id); @@ -42,6 +49,9 @@ public class MoviesService { public List findAll() { LOGGER.info("findAll()"); + LOGGER.info("> principal: " + (securityContext.getCallerPrincipal() != null ? securityContext.getCallerPrincipal().getName() : null)); + LOGGER.info("> role('MoviesUserRole'): " + (securityContext.isCallerInRole("MoviesUserRole"))); + return em.createNamedQuery("Movies.selectAll", Movie.class) //JPQL -> java persistence query language .getResultList(); } diff --git a/MoviesWebApp/src/main/java/at/technikumwien/movies/MoviesServlet.java b/MoviesWebApp/src/main/java/at/technikumwien/movies/MoviesServlet.java index 7c72347..1614731 100644 --- a/MoviesWebApp/src/main/java/at/technikumwien/movies/MoviesServlet.java +++ b/MoviesWebApp/src/main/java/at/technikumwien/movies/MoviesServlet.java @@ -2,6 +2,8 @@ package at.technikumwien.movies; import javax.inject.Inject; import javax.servlet.ServletException; +import javax.servlet.annotation.HttpConstraint; +import javax.servlet.annotation.ServletSecurity; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -10,6 +12,9 @@ import java.io.IOException; import java.io.PrintWriter; @WebServlet(urlPatterns = "movieserviceservlet") +@ServletSecurity( + @HttpConstraint(rolesAllowed = "MoviesUserRole") +) public class MoviesServlet extends HttpServlet { @Inject private MoviesService moviesService; diff --git a/MoviesWebApp/src/main/java/at/technikumwien/movies/SH2HexPasswordHash.java b/MoviesWebApp/src/main/java/at/technikumwien/movies/SH2HexPasswordHash.java new file mode 100644 index 0000000..fe27e0a --- /dev/null +++ b/MoviesWebApp/src/main/java/at/technikumwien/movies/SH2HexPasswordHash.java @@ -0,0 +1,18 @@ +package at.technikumwien.movies; + +import org.apache.commons.codec.digest.DigestUtils; + +import javax.security.enterprise.identitystore.PasswordHash; +import java.util.Objects; + +public class SH2HexPasswordHash implements PasswordHash { + @Override + public String generate(char[] chars) { + return DigestUtils.sha512Hex(new String(chars)); + } + + @Override + public boolean verify(char[] chars, String s) { + return Objects.equals(generate(chars), s); + } +} diff --git a/MoviesWebApp/src/main/resources/META-INF/persistence.xml b/MoviesWebApp/src/main/resources/META-INF/persistence.xml index b6797e1..c517bb2 100644 --- a/MoviesWebApp/src/main/resources/META-INF/persistence.xml +++ b/MoviesWebApp/src/main/resources/META-INF/persistence.xml @@ -7,9 +7,12 @@ java:jboss/datasources/MoviesDS lib/MoviesCommon-1.0.0-SNAPSHOT.jar + - - + + + diff --git a/MoviesWebApp/src/main/resources/META-INF/sql/security-data.sql b/MoviesWebApp/src/main/resources/META-INF/sql/security-data.sql new file mode 100644 index 0000000..35d38a2 --- /dev/null +++ b/MoviesWebApp/src/main/resources/META-INF/sql/security-data.sql @@ -0,0 +1,31 @@ +DROP TABLE IF EXISTS t_user_role; +DROP TABLE IF EXISTS t_user; +DROP TABLE IF EXISTS t_role; + +CREATE TABLE t_user ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(25) NOT NULL, + password VARCHAR(128) NOT NULL +); + +CREATE TABLE t_role ( + id INT AUTO_INCREMENT PRIMARY KEY, + rolename VARCHAR(25) NOT NULL +); + +CREATE TABLE t_user_role ( + id INT AUTO_INCREMENT PRIMARY KEY, + userid INT NOT NULL, + roleid INT NOT NULL, + FOREIGN KEY (userid) REFERENCES t_user(id), + FOREIGN KEY (roleid) REFERENCES t_role(id) +); + +INSERT INTO t_user (id, username, password) VALUES (1, 'moviesadmin', SHA2('topsecret', 512)); +INSERT INTO t_user (id, username, password) VALUES (2, 'moviesuser', SHA2('topsecret', 512)); + +INSERT INTO t_role (id, rolename) VALUES (1, 'MoviesAdminRole'); +INSERT INTO t_role (id, rolename) VALUES (2, 'MoviesUserRole'); + +INSERT INTO t_user_role (id, userid, roleid) VALUES (1, 1, 1); +INSERT INTO t_user_role (id, userid, roleid) VALUES (2, 2, 2); \ No newline at end of file diff --git a/MoviesWebApp/src/main/webapp/WEB-INF/jboss-web.xml b/MoviesWebApp/src/main/webapp/WEB-INF/jboss-web.xml new file mode 100644 index 0000000..6756679 --- /dev/null +++ b/MoviesWebApp/src/main/webapp/WEB-INF/jboss-web.xml @@ -0,0 +1,7 @@ + + + jaspitest + \ No newline at end of file diff --git a/MoviesWebApp/src/main/webapp/WEB-INF/web.xml b/MoviesWebApp/src/main/webapp/WEB-INF/web.xml index e764259..56dc5d2 100644 --- a/MoviesWebApp/src/main/webapp/WEB-INF/web.xml +++ b/MoviesWebApp/src/main/webapp/WEB-INF/web.xml @@ -17,4 +17,30 @@ Faces Servlet *.xhtml + + + resteasy.role.based.security + true + + + + + MoviesWebApp + /* + OPTIONS + + + * + + + NONE + + + + + MoviesAdminRole + + + MoviesUserRole +