This repository was archived by the owner on Jan 25, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 256
Add Java EE Security 1.0 API examples #1
Closed
Closed
Changes from all commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
af3e2a4
Add Java EE Security 1.0 API examples
2298932
Add example demonstrating custom identity store
0db26a0
Add custom_identity_store module
b22fa1f
Incorporate review comments
0e67b0a
Update copyright
51485be
Incorporate review comments
aaf0623
add resource element
4a03b38
[maven-release-plugin] rollback the release of 8.0
28c7cc8
add empty
30bf5ce
add empty file to ear projects
2c21f33
Update security-api version
1f559d8
Merge remote-tracking branch 'upstream/master' into javaee-security-e…
dfa9c53
add empty file to simple-flow
f5eabe7
[maven-release-plugin] prepare release 8.0
84eae93
[maven-release-plugin] prepare for next development iteration
4383a56
[maven-release-plugin] rollback the release of 8.0
f0e034a
release script changes
bb5b63d
[maven-release-plugin] prepare release 8.0
9e9cf5e
[maven-release-plugin] rollback the release of 8.0
587ef36
[maven-release-plugin] prepare release 8.0
ae6c056
[maven-release-plugin] prepare for next development iteration
305f038
updated copyright year
jruzzi dcd25b5
change back to 8.0-SNAPSHOT
79018e1
Add Java EE Security 1.0 API examples
00b86c4
Add example demonstrating custom identity store
9bd790d
Add custom_identity_store module
4805606
Incorporate review comments
009a6a9
Update copyright
14b6e3d
Incorporate review comments
3a19bb0
add resource element
6a6f123
[maven-release-plugin] rollback the release of 8.0
0f5de90
add empty
284ff83
Update security-api version
865a525
Update version
0b9d287
Remove promoted repo details
44aac95
Update version
e4b47f0
Resolve merge conflicts
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
227 changes: 227 additions & 0 deletions
227
security/security-api/built-in-db-identity-store/README.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 <project>/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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
|
|
||
| <parent> | ||
| <groupId>org.glassfish.javaeetutorial</groupId> | ||
| <artifactId>security-api</artifactId> | ||
| <version>8.0-SNAPSHOT</version> | ||
| </parent> | ||
|
|
||
| <artifactId>built-in-db-identity-store</artifactId> | ||
| <packaging>war</packaging> | ||
|
|
||
| <build> | ||
| <finalName>built-in-db-identity-store</finalName> | ||
| </build> | ||
|
|
||
| <properties> | ||
| <failOnMissingWebXml>false</failOnMissingWebXml> | ||
| </properties> | ||
|
|
||
| <dependencies> | ||
| <dependency> | ||
| <groupId>javax.security.enterprise</groupId> | ||
| <artifactId>javax.security.enterprise-api</artifactId> | ||
| <version>1.0</version> | ||
| <scope>provided</scope> | ||
| </dependency> | ||
| </dependencies> | ||
|
|
||
| </project> |
41 changes: 41 additions & 0 deletions
41
...tity-store/src/main/java/javaeetutorial/built_in_db_identity_store/ApplicationConfig.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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"}; | ||
| } | ||
|
|
||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might want to use the
LdapIdentityStoreprovider instead of database, as this will be a more common case.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, may be I will try to include LdapIdentityStore one as a seperate example and retain this one as well.