From 099d25771e2bd1babc721385487380de106e53f4 Mon Sep 17 00:00:00 2001 From: Vinay Vishal Date: Wed, 20 Sep 2017 15:35:37 +0530 Subject: [PATCH 1/5] Add JSR375 Security API 1.0 Examples --- security/pom.xml | 1 + .../built-in-db-identity-store/README.md | 227 ++++++++++++++++++ .../built-in-db-identity-store/pom.xml | 29 +++ .../ApplicationConfig.java | 41 ++++ .../DatabaseSetup.java | 90 +++++++ .../built_in_db_identity_store/Servlet.java | 50 ++++ .../custom-identity-store/README.md | 143 +++++++++++ .../custom-identity-store/pom.xml | 29 +++ .../ApplicationConfig.java | 24 ++ .../custom_identity_store/Servlet.java | 50 ++++ .../TestIdentityStore.java | 41 ++++ security/security-api/pom.xml | 23 ++ 12 files changed, 748 insertions(+) create mode 100644 security/security-api/built-in-db-identity-store/README.md create mode 100644 security/security-api/built-in-db-identity-store/pom.xml create mode 100644 security/security-api/built-in-db-identity-store/src/main/java/javaeetutorial/built_in_db_identity_store/ApplicationConfig.java create mode 100644 security/security-api/built-in-db-identity-store/src/main/java/javaeetutorial/built_in_db_identity_store/DatabaseSetup.java create mode 100644 security/security-api/built-in-db-identity-store/src/main/java/javaeetutorial/built_in_db_identity_store/Servlet.java create mode 100644 security/security-api/custom-identity-store/README.md create mode 100644 security/security-api/custom-identity-store/pom.xml create mode 100644 security/security-api/custom-identity-store/src/main/java/javaeetutorial/custom_identity_store/ApplicationConfig.java create mode 100644 security/security-api/custom-identity-store/src/main/java/javaeetutorial/custom_identity_store/Servlet.java create mode 100644 security/security-api/custom-identity-store/src/main/java/javaeetutorial/custom_identity_store/TestIdentityStore.java create mode 100644 security/security-api/pom.xml diff --git a/security/pom.xml b/security/pom.xml index b6a63f0..69fa828 100644 --- a/security/pom.xml +++ b/security/pom.xml @@ -18,6 +18,7 @@ converter-secure hello1-formauth hello2-basicauth + security-api diff --git a/security/security-api/built-in-db-identity-store/README.md b/security/security-api/built-in-db-identity-store/README.md new file mode 100644 index 0000000..9f01bbf --- /dev/null +++ b/security/security-api/built-in-db-identity-store/README.md @@ -0,0 +1,227 @@ +# Built-in Database Identity Store +JSR 375 mandates that a Java EE container MUST support built-in `IdentityStore` backed by a database. + +To support this mandatory requirement, `DatabaseIdentityStore` comes bundled with GlassFish-RI. + +This example demonstrates how you can configure a `DatabaseIdentityStore` to point to a backend database and then use it as an `IdentityStore`. + +In this example, the following users are defined, along with the groups they are in. + +|User|Password|Group| +|----|--------|----| +|Joe|secret1|foo,bar| +|Sam|secret2|foo,bar| +|Tom|secret2|foo| +|Sue|secret2|foo| + +When a request is made to the application with certain credentials, the authentication mechanism bundled with this application comes into effect and authentication is performed against the `DatabaseIdentityStore` as defined in the application. + +Post authentication, the application also verifies the roles the caller is in and sends the details as part of the response. + +How to define credentials and the roles assigned to users is shown below: + +```java + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.annotation.Resource; +import javax.annotation.sql.DataSourceDefinition; +import javax.ejb.Singleton; +import javax.ejb.Startup; +import javax.sql.DataSource; + +@Singleton +@Startup +public class DatabaseSetup { + + // The default datasource that is bundled with GlassFish is used to store // credentials. + @Resource(lookup="java:comp/DefaultDataSource") + private DataSource dataSource; + + @PostConstruct + public void init() { + + // ... + executeUpdate(dataSource, "INSERT INTO caller VALUES('Joe', '" + passwordHash.generate("secret1".toCharArray()) + "')"); + // ... + executeUpdate(dataSource, "INSERT INTO caller_groups VALUES('Joe', 'foo')"); + executeUpdate(dataSource, "INSERT INTO caller_groups VALUES('Joe', 'bar')"); + // ... + } + + @PreDestroy + public void destroy() { + // ... + } + + private void executeUpdate(DataSource dataSource, String query) { + // ... + } +} + +``` +With `@Startup` annotation, this singleton enterprise bean is initialized during application startup and the credentials are set in the underlying database. + +Built-in `DatabaseIdentityStore` gets mapped with the `DefaultDataSource` by defining the `ApplicationConfig` with the help of `@DatabaseIdentityStoreDefinition`. + +```java +// Database Definition for built-in DatabaseIdentityStore +@DatabaseIdentityStoreDefinition( + callerQuery = "#{'select password from caller where name = ?'}", + groupsQuery = "select group_name from caller_groups where caller_name = ?", + hashAlgorithm = Pbkdf2PasswordHash.class, + priorityExpression = "#{100}", + hashAlgorithmParameters = { + "Pbkdf2PasswordHash.Iterations=3072", + "${applicationConfig.dyna}" + } +) + +@ApplicationScoped +@Named +public class ApplicationConfig { + + public String[] getDyna() { + return new String[]{"Pbkdf2PasswordHash.Algorithm=PBKDF2WithHmacSHA512", "Pbkdf2PasswordHash.SaltSizeBytes=64"}; + } + +} +``` +In this application, we are validating credentials using BASIC authentication mechanism. Following annotation in `ApplicationConfig` ensures that the `BasicAuthenticationMechanism` is used to perform credential validation. + +```java +@BasicAuthenticationMechanismDefinition( + realmName = "file" +) +``` + +Please note that in GlassFish, when BasicAuthenticationMechanism is used as the authentication mechanism, the `realmName` basically is presented to user ,as a hint, when wrong credentials are provided by the user. + + +```bash +curl -I -u Joe http://localhost:8080/built-in-db-identity-store/servlet +Enter host password for user 'Joe': +HTTP/1.1 401 Unauthorized +Server: GlassFish Server Open Source Edition 5.0 +X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition 5.0 Java/Oracle Corporation/1.8) +WWW-Authenticate: Basic realm="file" +Content-Length: 1090 +Content-Language: +Content-Type: text/html +``` + +When a request is made to the application, the roles the user is in get returned as part of the repsonse. + +```java +@WebServlet("/servlet") +@DeclareRoles({ "foo", "bar", "kaz" }) +@ServletSecurity(@HttpConstraint(rolesAllowed = "foo")) +public class Servlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + String webName = null; + if (request.getUserPrincipal() != null) { + webName = request.getUserPrincipal().getName(); + } + + response.getWriter().write("web username: " + webName + "\n"); + + response.getWriter().write("web user has role \"foo\": " + request.isUserInRole("foo") + "\n"); + response.getWriter().write("web user has role \"bar\": " + request.isUserInRole("bar") + "\n"); + response.getWriter().write("web user has role \"kaz\": " + request.isUserInRole("kaz") + "\n"); + } + +} +``` +Note that the container needs to be made aware of the supported roles, which is achieved with the help of `@DeclareRoles` annotation as shown above. + +```java +@DeclareRoles({ "foo", "bar", "kaz" }) +``` +In GlassFish 5.0, role to group mapping is enabled by default. Therefore, you do not need to bundle `web.xml` with the application to provide mapping between roles and groups. + +In this example, we are using the credentials of user `Joe` to make a request and validate the response according to the credentials/roles defined in `DatabaseSetup.java` above. + +**Steps:** + +* Since we are using the default datasource bundled with GlassFish for `DatabaseIdentityStore`, start the default database by running the following command: + +`asadmin start-database` + +* Start the domain + +`asadmin start-domain` + +* Deploy the application + +`asadmin deploy /target/built-in-db-identity-store.war` + +After the application is deployed, we can make a request to the application using the URL shown below: + +--- + +**Request URL:** + +```bash +http://localhost:8080/built-in-db-identity-store/servlet +``` + +Since BASIC authentication is being used here, the container responds back prompting for username and password. + +Once username and password are provided, the client presents the request to conainer with base64 encoded string and with `Authorization` header having value in the format expected for basic authentication. + +With username and password available to the container, the validation is then done against `DatabaseIdentityStore`. + +The corresponding `UsernamePasswordCredential` object is passed as parameter to `DatabaseIdentityStore#validate()` method. + +Password is then fetched from database for user `Joe`. The password stored in database is hashed using `PBKDF2` algorithm. The password is then verified by built-in `Pbkdf2PasswordHash` implementation. + +On successful verification, the request finally gets delegated to the servlet in question and response is returned to the end user. + +**Response:** + +```bash +web username: Joe +web user has role "foo": true +web user has role "bar": true +web user has role "kaz": false +``` +--- + +**If invalid credentials are used:** + +--- + +**Request URL:** + +```bash +http://localhost:8080/built-in-db-identity-store/servlet +``` + +**Response:** + + +**`HTTP Status 401 - Unauthorized`** + +**`type`** `Status report` + +**`message`** `Unauthorized` + +**`description`** `This request requires HTTP authentication.` + +**`GlassFish Server Open Source Edition 5`** + +--- + +In this application, we are using BasicAuthenticationMechanism. + +When a request is made to the servlet in question, +container delegates the request to `org.glassfish.soteria.mechanisms.jaspic.HttpBridgeServerAuthModule`, which then invokes BasicAuthenticationMechanism#validateRequest method, and gets the credential from the request. diff --git a/security/security-api/built-in-db-identity-store/pom.xml b/security/security-api/built-in-db-identity-store/pom.xml new file mode 100644 index 0000000..e040159 --- /dev/null +++ b/security/security-api/built-in-db-identity-store/pom.xml @@ -0,0 +1,29 @@ + + + + 4.0.0 + + + org.glassfish.javaeetutorial + security-api + 8.1-SNAPSHOT + + built-in-db-identity-store + war + + built-in-db-identity-store + + + false + + + + + javax.security.enterprise + javax.security.enterprise-api + 1.0 + provided + + + + diff --git a/security/security-api/built-in-db-identity-store/src/main/java/javaeetutorial/built_in_db_identity_store/ApplicationConfig.java b/security/security-api/built-in-db-identity-store/src/main/java/javaeetutorial/built_in_db_identity_store/ApplicationConfig.java new file mode 100644 index 0000000..d072c49 --- /dev/null +++ b/security/security-api/built-in-db-identity-store/src/main/java/javaeetutorial/built_in_db_identity_store/ApplicationConfig.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * You may not modify, use, reproduce, or distribute this software except in + * compliance with the terms of the License at: + * https://github.com/javaee/tutorial-examples/LICENSE.txt + */ + +package javaeetutorial.built_in_db_identity_store; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Named; +import javax.security.enterprise.authentication.mechanism.http.BasicAuthenticationMechanismDefinition; +import javax.security.enterprise.identitystore.DatabaseIdentityStoreDefinition; +import javax.security.enterprise.identitystore.Pbkdf2PasswordHash; + +// Database Definition for built-in DatabaseIdentityStore +@DatabaseIdentityStoreDefinition( + callerQuery = "#{'select password from caller where name = ?'}", + groupsQuery = "select group_name from caller_groups where caller_name = ?", + hashAlgorithm = Pbkdf2PasswordHash.class, + priorityExpression = "#{100}", + hashAlgorithmParameters = { + "Pbkdf2PasswordHash.Iterations=3072", + "${applicationConfig.dyna}" + } +) + +@BasicAuthenticationMechanismDefinition( + realmName = "file" +) + +@ApplicationScoped +@Named +public class ApplicationConfig { + + public String[] getDyna() { + return new String[]{"Pbkdf2PasswordHash.Algorithm=PBKDF2WithHmacSHA512", "Pbkdf2PasswordHash.SaltSizeBytes=64"}; + } + +} diff --git a/security/security-api/built-in-db-identity-store/src/main/java/javaeetutorial/built_in_db_identity_store/DatabaseSetup.java b/security/security-api/built-in-db-identity-store/src/main/java/javaeetutorial/built_in_db_identity_store/DatabaseSetup.java new file mode 100644 index 0000000..9e64371 --- /dev/null +++ b/security/security-api/built-in-db-identity-store/src/main/java/javaeetutorial/built_in_db_identity_store/DatabaseSetup.java @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * You may not modify, use, reproduce, or distribute this software except in + * compliance with the terms of the License at: + * https://github.com/javaee/tutorial-examples/LICENSE.txt + */ + +package javaeetutorial.built_in_db_identity_store; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.annotation.Resource; +import javax.annotation.sql.DataSourceDefinition; +import javax.ejb.Singleton; +import javax.ejb.Startup; +import javax.inject.Inject; +import javax.sql.DataSource; +import javax.security.enterprise.identitystore.Pbkdf2PasswordHash; + +@Singleton +@Startup +public class DatabaseSetup { + + @Resource(lookup="java:comp/DefaultDataSource") + private DataSource dataSource; + + @Inject + private Pbkdf2PasswordHash passwordHash; + + @PostConstruct + public void init() { + + Map parameters= new HashMap<>(); + parameters.put("Pbkdf2PasswordHash.Iterations", "3072"); + parameters.put("Pbkdf2PasswordHash.Algorithm", "PBKDF2WithHmacSHA512"); + parameters.put("Pbkdf2PasswordHash.SaltSizeBytes", "64"); + passwordHash.initialize(parameters); + + executeUpdate(dataSource, "CREATE TABLE caller(name VARCHAR(64) PRIMARY KEY, password VARCHAR(255))"); + executeUpdate(dataSource, "CREATE TABLE caller_groups(caller_name VARCHAR(64), group_name VARCHAR(64))"); + + executeUpdate(dataSource, "INSERT INTO caller VALUES('Joe', '" + passwordHash.generate("secret1".toCharArray()) + "')"); + executeUpdate(dataSource, "INSERT INTO caller VALUES('Sam', '" + passwordHash.generate("secret2".toCharArray()) + "')"); + executeUpdate(dataSource, "INSERT INTO caller VALUES('Tom', '" + passwordHash.generate("secret2".toCharArray()) + "')"); + executeUpdate(dataSource, "INSERT INTO caller VALUES('Sue', '" + passwordHash.generate("secret2".toCharArray()) + "')"); + + executeUpdate(dataSource, "INSERT INTO caller_groups VALUES('Joe', 'foo')"); + executeUpdate(dataSource, "INSERT INTO caller_groups VALUES('Joe', 'bar')"); + + executeUpdate(dataSource, "INSERT INTO caller_groups VALUES('Sam', 'foo')"); + executeUpdate(dataSource, "INSERT INTO caller_groups VALUES('Sam', 'bar')"); + + executeUpdate(dataSource, "INSERT INTO caller_groups VALUES('Tom', 'foo')"); + executeUpdate(dataSource, "INSERT INTO caller_groups VALUES('Sue', 'foo')"); + } + + /** + * Drops the tables before instance is removed by the container. + */ + @PreDestroy + public void destroy() { + try { + executeUpdate(dataSource, "DROP TABLE caller"); + executeUpdate(dataSource, "DROP TABLE caller_groups"); + } catch (Exception e) { + // silently ignore, concerns in-memory database + } + } + + /* + Executes the SQL statement in this PreparedStatement object against the database it is pointing to. + */ + private void executeUpdate(DataSource dataSource, String query) { + try (Connection connection = dataSource.getConnection()) { + try (PreparedStatement statement = connection.prepareStatement(query)) { + statement.executeUpdate(); + } + } catch (SQLException e) { + throw new IllegalStateException(e); + } + } + +} diff --git a/security/security-api/built-in-db-identity-store/src/main/java/javaeetutorial/built_in_db_identity_store/Servlet.java b/security/security-api/built-in-db-identity-store/src/main/java/javaeetutorial/built_in_db_identity_store/Servlet.java new file mode 100644 index 0000000..180eec9 --- /dev/null +++ b/security/security-api/built-in-db-identity-store/src/main/java/javaeetutorial/built_in_db_identity_store/Servlet.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * You may not modify, use, reproduce, or distribute this software except in + * compliance with the terms of the License at: + * https://github.com/javaee/tutorial-examples/LICENSE.txt + */ + +package javaeetutorial.built_in_db_identity_store; + +import java.io.IOException; + +import javax.annotation.security.DeclareRoles; +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; +import javax.servlet.http.HttpServletResponse; + +/** + * Test Servlet that prints out the name of the authenticated caller and whether + * this caller is in any of the roles {foo, bar, kaz} + * + */ + +@WebServlet("/servlet") +@DeclareRoles({ "foo", "bar", "kaz" }) +@ServletSecurity(@HttpConstraint(rolesAllowed = "foo")) +public class Servlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + String webName = null; + if (request.getUserPrincipal() != null) { + webName = request.getUserPrincipal().getName(); + } + + response.getWriter().write("web username: " + webName + "\n"); + + response.getWriter().write("web user has role \"foo\": " + request.isUserInRole("foo") + "\n"); + response.getWriter().write("web user has role \"bar\": " + request.isUserInRole("bar") + "\n"); + response.getWriter().write("web user has role \"kaz\": " + request.isUserInRole("kaz") + "\n"); + } + +} diff --git a/security/security-api/custom-identity-store/README.md b/security/security-api/custom-identity-store/README.md new file mode 100644 index 0000000..1e4f7ee --- /dev/null +++ b/security/security-api/custom-identity-store/README.md @@ -0,0 +1,143 @@ +# Custom Identity Store + +An application can also provide its own IdentityStore. Bundled with the application, this custom identity store can then be used for authentication and authorization. + +This example demonstrates how a custom identity store `TestIdentityStore` can be defined and provided as part of the application being deployed. + +In this example, the following user is defined along with the group he is in. + +|User|Password|Group| +|----|--------|----| +|Joe|secret1|foo,bar| + +When a request is made to the application with certain credentials, the configured authentication mechanism comes into effect and authentication is performed against the `TestIdentityStore` as defined in the application. + +Post authentication, the application also verifies the roles the caller is in and sends the details as part of the response. + +How to define credentials and the roles assigned to users is shown below: + +```java +if (usernamePasswordCredential.compareTo("Joe", "secret1")) { + return new CredentialValidationResult("Joe", new HashSet<>(asList("foo", "bar"))); +} + +``` + +Authentication mechanism, which gets invoked when a request is made, is defined in `ApplicationConfig` as shown below: + +```java +@BasicAuthenticationMechanismDefinition( + realmName = "file" +) + +@ApplicationScoped +@Named +public class ApplicationConfig { + +} +``` +Please note that in GlassFish, when BasicAuthenticationMechanism is used as the authentication mechanism, the `realmName` basically is presented to user ,as a hint, when wrong credentials are provided by the user. + + +```bash +curl -I -u Joe http://localhost:8080/custom-identity-store/servlet +Enter host password for user 'Joe': +HTTP/1.1 401 Unauthorized +Server: GlassFish Server Open Source Edition 5.0 +X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition 5.0 Java/Oracle Corporation/1.8) +WWW-Authenticate: Basic realm="file" +Content-Length: 1090 +Content-Language: +Content-Type: text/html +``` +In this example, the authentication mechanism is `BasicAuthenticationMechanism`, as is evident from the snippet above. + + +When a request is made to the application, the roles the user is in, gets returned as part of the repsonse. + +```java +@DeclareRoles({ "foo", "bar", "kaz" }) +@WebServlet("/servlet") +public class Servlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + String webName = null; + if (request.getUserPrincipal() != null) { + webName = request.getUserPrincipal().getName(); + } + + response.getWriter().write("web username: " + webName + "\n"); + + response.getWriter().write("web user has role \"foo\": " + request.isUserInRole("foo") + "\n"); + response.getWriter().write("web user has role \"bar\": " + request.isUserInRole("bar") + "\n"); + response.getWriter().write("web user has role \"kaz\": " + request.isUserInRole("kaz") + "\n"); + } + +} +``` +Note that the container needs to be made aware of the supported roles, which is achieved with the help of the `@Declareroles` annotation as shown above. + +```java +@DeclareRoles({ "foo", "bar", "kaz" }) +``` +In GlassFish 5.0, role to group mapping is enabled by default. Therefore, you do not need to bundle `web.xml` with the application to provide mapping between roles and groups. + +In this example, we are using the credentials of user `Joe` to make a request and to validate the response according to the credentials defined in `TestIdentityStore` above. + +**Steps:** + +* Start the domain + +`asadmin start-domain` + +* Deploy the application + +`asadmin deploy /target/custom-identity-store.war` + +After the application is deployed, we can make a request to the application using the URL shown below: + +--- + +**Request URL:** + +```bash +http://localhost:8080/custom-identity-store/servlet?name=Joe&password=secret1 +``` +**Response:** + +```bash +web username: Joe +web user has role "foo": true +web user has role "bar": true +web user has role "kaz": false +``` +--- + +**If invalid credentials are used:** + +--- + +**Request URL:** + +```bash +http://localhost:8080/custom-identity-store/servlet?name=Joe&password=secret3 +``` + +**Response:** + + +**`HTTP Status 401 - Unauthorized`** + +**`type`** `Status report` + +**`message`** `Unauthorized` + +**`description`** `This request requires HTTP authentication.` + +**`GlassFish Server Open Source Edition 5`** + +--- diff --git a/security/security-api/custom-identity-store/pom.xml b/security/security-api/custom-identity-store/pom.xml new file mode 100644 index 0000000..c0ef196 --- /dev/null +++ b/security/security-api/custom-identity-store/pom.xml @@ -0,0 +1,29 @@ + + + + 4.0.0 + + + org.glassfish.javaeetutorial + security-api + 8.1-SNAPSHOT + + custom-identity-store + war + + custom-identity-store + + + false + + + + + javax.security.enterprise + javax.security.enterprise-api + 1.0 + provided + + + + diff --git a/security/security-api/custom-identity-store/src/main/java/javaeetutorial/custom_identity_store/ApplicationConfig.java b/security/security-api/custom-identity-store/src/main/java/javaeetutorial/custom_identity_store/ApplicationConfig.java new file mode 100644 index 0000000..3c53410 --- /dev/null +++ b/security/security-api/custom-identity-store/src/main/java/javaeetutorial/custom_identity_store/ApplicationConfig.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + *

+ * You may not modify, use, reproduce, or distribute this software except in + * compliance with the terms of the License at: + * https://github.com/javaee/tutorial-examples/LICENSE.txt + */ + +package javaeetutorial.custom_identity_store; + +import javax.security.enterprise.authentication.mechanism.http.BasicAuthenticationMechanismDefinition; +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Named; +import javax.security.enterprise.identitystore.DatabaseIdentityStoreDefinition; + +@BasicAuthenticationMechanismDefinition( + realmName = "file" +) + +@ApplicationScoped +@Named +public class ApplicationConfig { + +} diff --git a/security/security-api/custom-identity-store/src/main/java/javaeetutorial/custom_identity_store/Servlet.java b/security/security-api/custom-identity-store/src/main/java/javaeetutorial/custom_identity_store/Servlet.java new file mode 100644 index 0000000..2df6eea --- /dev/null +++ b/security/security-api/custom-identity-store/src/main/java/javaeetutorial/custom_identity_store/Servlet.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * You may not modify, use, reproduce, or distribute this software except in + * compliance with the terms of the License at: + * https://github.com/javaee/tutorial-examples/LICENSE.txt + */ + +package javaeetutorial.custom_identity_store; + +import java.io.IOException; + +import javax.annotation.security.DeclareRoles; +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; +import javax.servlet.http.HttpServletResponse; + +/** + * Test Servlet that prints out the name of the authenticated caller and whether + * this caller is in any of the roles {foo, bar, kaz} + * + */ + +@WebServlet("/servlet") +@DeclareRoles({ "foo", "bar", "kaz" }) +@ServletSecurity(@HttpConstraint(rolesAllowed = "foo")) +public class Servlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + String webName = null; + if (request.getUserPrincipal() != null) { + webName = request.getUserPrincipal().getName(); + } + + response.getWriter().write("web username: " + webName + "\n"); + + response.getWriter().write("web user has role \"foo\": " + request.isUserInRole("foo") + "\n"); + response.getWriter().write("web user has role \"bar\": " + request.isUserInRole("bar") + "\n"); + response.getWriter().write("web user has role \"kaz\": " + request.isUserInRole("kaz") + "\n"); + } + +} diff --git a/security/security-api/custom-identity-store/src/main/java/javaeetutorial/custom_identity_store/TestIdentityStore.java b/security/security-api/custom-identity-store/src/main/java/javaeetutorial/custom_identity_store/TestIdentityStore.java new file mode 100644 index 0000000..9dfea1b --- /dev/null +++ b/security/security-api/custom-identity-store/src/main/java/javaeetutorial/custom_identity_store/TestIdentityStore.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * You may not modify, use, reproduce, or distribute this software except in + * compliance with the terms of the License at: + * https://github.com/javaee/tutorial-examples/LICENSE.txt + */ + +package javaeetutorial.custom_identity_store; + +import static java.util.Arrays.asList; +import static javax.security.enterprise.identitystore.CredentialValidationResult.INVALID_RESULT; + +import java.util.HashSet; + +import javax.enterprise.context.ApplicationScoped; +import javax.security.enterprise.credential.UsernamePasswordCredential; +import javax.security.enterprise.identitystore.CredentialValidationResult; +import javax.security.enterprise.identitystore.IdentityStore; +import javax.security.enterprise.credential.UsernamePasswordCredential; + +@ApplicationScoped +public class TestIdentityStore implements IdentityStore { + + public CredentialValidationResult validate(UsernamePasswordCredential usernamePasswordCredential) { + + // This is for illustrative purposes only, and a real IdentityStore should include secure storage + // and credential validation capabilities. + // This example is a trivial one and is not meant to be used in production setup at all. Use of + // hard-coded/in-memory stores or user databases trivially provided as unencrypted files etc is not + // encouraged and has been used here in this manner just to demonstrate how a custom identity + // store can be defined. + + if (usernamePasswordCredential.compareTo("Joe", "secret1")) { + return new CredentialValidationResult("Joe", new HashSet<>(asList("foo", "bar"))); + } + + return INVALID_RESULT; + } + +} diff --git a/security/security-api/pom.xml b/security/security-api/pom.xml new file mode 100644 index 0000000..1b14564 --- /dev/null +++ b/security/security-api/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + + + security + org.glassfish.javaeetutorial + 8.1-SNAPSHOT + + + org.glassfish.javaeetutorial + security-api + 8.1-SNAPSHOT + pom + + security-api + + + built-in-db-identity-store + custom-identity-store + + + From 55fa16c1331c7889df55c07c0b71dac86a149481 Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Tue, 15 May 2018 12:59:29 +1000 Subject: [PATCH 2/5] Modified the way the path to the persistence.xml file was being specified in the maven-processor-plugin configuration so that it is tolerant of paths containing spaces. --- persistence/address-book/pom.xml | 4 +++- persistence/roster/roster-ejb/pom.xml | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/persistence/address-book/pom.xml b/persistence/address-book/pom.xml index 550e8e0..e09dc63 100644 --- a/persistence/address-book/pom.xml +++ b/persistence/address-book/pom.xml @@ -48,9 +48,11 @@ generate-sources + + ${basedir}/src/main/resources/META-INF/persistence.xml + ${basedir}/src/main/java ${project.build.directory}/generated-sources/metamodel - -Aeclipselink.persistencexml=${basedir}/src/main/resources/META-INF/persistence.xml org.eclipse.persistence.internal.jpa.modelgen.CanonicalModelProcessor diff --git a/persistence/roster/roster-ejb/pom.xml b/persistence/roster/roster-ejb/pom.xml index a2994af..59056c3 100644 --- a/persistence/roster/roster-ejb/pom.xml +++ b/persistence/roster/roster-ejb/pom.xml @@ -60,9 +60,11 @@ generate-sources + + ${basedir}/src/main/resources/META-INF/persistence.xml + ${basedir}/src/main/java ${project.build.directory}/generated-sources/metamodel - -Aeclipselink.persistencexml=${basedir}/src/main/resources/META-INF/persistence.xml org.eclipse.persistence.internal.jpa.modelgen.CanonicalModelProcessor From 573bdaa203693399d82a06bf04b5cf5a3fca4ae0 Mon Sep 17 00:00:00 2001 From: Ankur Kathuria Date: Fri, 25 Jan 2019 15:44:51 +0530 Subject: [PATCH 3/5] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index c72a378..b3d03f9 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +#### :warning:This project is now part of the EE4J initiative. The activity on this project has been paused while it is being migrated to the Eclipse Foundation. See [here](https://www.eclipse.org/ee4j/status.php) for the overall EE4J transition status. + +--- # Java EE tutorial examples This repository contains the example source that is used in the From 96efe71b6b7005b874c3ff848fa84890e6ac5fd9 Mon Sep 17 00:00:00 2001 From: Ankur Kathuria Date: Fri, 25 Jan 2019 15:45:50 +0530 Subject: [PATCH 4/5] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b3d03f9..0d0d6ca 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -#### :warning:This project is now part of the EE4J initiative. The activity on this project has been paused while it is being migrated to the Eclipse Foundation. See [here](https://www.eclipse.org/ee4j/status.php) for the overall EE4J transition status. +#### :warning:This project is now part of the EE4J initiative. This repository has been archived as all activities are now happening in the [corresponding Eclipse repository](https://github.com/eclipse-ee4j/glassfish-tutorial-examples). See [here](https://www.eclipse.org/ee4j/status.php) for the overall EE4J transition status. --- # Java EE tutorial examples From 41db0d1b26ec94313df132b9d676f8042eeb5157 Mon Sep 17 00:00:00 2001 From: Ankur Kathuria Date: Fri, 25 Jan 2019 15:46:47 +0530 Subject: [PATCH 5/5] Update CONTRIBUTING.md --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7630913..f201308 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,5 @@ +#### :warning:This project is now part of the EE4J initiative. This repository has been archived as all activities are now happening in the [corresponding Eclipse repository](https://github.com/eclipse-ee4j/glassfish-tutorial-examples). See [here](https://www.eclipse.org/ee4j/status.php) for the overall EE4J transition status. + --- ---