Running the Built-In Database Identity Store Example (original) (raw)

The example described in this section demonstrates how to use the built-in database identity store for credential validation.

Overview of the Built-In Database Identity Store Example

JSR 375 mandates that a Java EE container MUST support a built-in IdentityStorebacked by a database. To support this mandatory requirement, DatabaseIdentityStoreis bundled with the GlassFish-RI.

This example demonstrates how you can configure a DatabaseIdentityStore to point to a backend database and then use it as an IdentityStore. The authentication mechanism used isBasicAuthenticationMechanism.

The source code for this example is in the_tut-install_/examples/security/security-api/built-in-db-identity-store directory.

The following sections describe the high-level process for configuring theDatabaseIdentityStore. Note that the configuration described in these sections has already been completed in the application files, but is provided here to illustrate what you need to do to use the built-in database identity store.

When a request that includes credentials is sent to the application, it triggers the configured authentication mechanism 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.

Note that in GlassFish, if the user provides the wrong credentials when usingBasicAuthenticationMechanism, then the realmNameis presented to user, as a hint.

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

Define the Users and Groups in the Identity Store

The following table shows the users, passwords, and groups used in this example.

User Password Group
Joe secret1 foo, bar
Sam secret2 foo, bar
Tom secret2 foo
Sue secret2 foo

The following code shows how to define credentials and the roles assigned to users in the DatabaseSetup.java file.

With @Startup annotation, this singleton enterprise bean is initialized during application startup and the credentials are set in the underlying database.

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) {
        // ...
    }
}

Map the DatabaseIdentityStore to the Default Data source

Use the @DatabaseIdentityStoreDefinition annotation to map the built-in DatabaseIdentityStoreto the DefaultDataSource in the ApplicationConfig.java file. This example also demonstrates the use of thePbkdf2PasswordHash interface.

// 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"};
   }

}

Specify the Authentication Mechanism

In this application, credentials are validated using the BASIC authentication mechanism. Specify the @BasicAuthenticationMechanismDefinition annotation in the ApplicationConfig.javato ensure that the BasicAuthenticationMechanismis used to perform credential validation.

When a request is made to the servlet in question, the container delegates the request to org.glassfish.soteria.mechanisms.jaspic.HttpBridgeServerAuthModule, which then invokes the BasicAuthenticationMechanism#validateRequest method, and gets the credential from the request.

@BasicAuthenticationMechanismDefinition(
        realmName = "file"
)

Declare Roles in the Servlet Container

When a request is made to the application, the roles the user is in are returned as part of the response. Note that the container needs to be made aware of the supported roles, which are defined using the @DeclareRoles({ "foo", "bar", "kaz" })annotation as shown below.

@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");
    }

}

In GlassFish 5.0, group to role mapping is enabled by default. Therefore, you do not need to bundle web.xml with the application to provide mapping between roles and groups.

Running the built-in-db-identity-store Example

You can use either NetBeans IDE or Maven to build, package, deploy, and run the built-in-db-identity-store application as described in the following topics:

To Build, Package, and Deploy the built-in-db-identity-store Example Using NetBeans IDE

  1. If you have not already done so, start the default database. This is necessary because we are using the DefaultDataSource bundled with GlassFish for DatabaseIdentityStore. See Starting and Stopping Apache Derby.
  2. If you have not already done so, start the GlassFish server. SeeStarting and Stopping GlassFish Server.
  3. From the File menu, choose Open Project.
  4. In the Open Project dialog box, navigate to:
tut-install/examples/security/security-api  
  1. Select the built-in-db-identity-store folder.
  2. Click Open Project.
  3. In the Projects tab, right-click the built-in-db-identity-store project and select Build.
    This command builds and deploys the example application to your GlassFish Server instance.

To Build, Package, and Deploy the built-in-db-identity-store Example Using Maven

  1. If you have not already done so, start the default database. This is necessary because we are using the DefaultDataSource bundled with GlassFish for DatabaseIdentityStore. See Starting and Stopping Apache Derby.
  2. If you have not already done so, start the GlassFish server. SeeStarting and Stopping GlassFish Server.
  3. In a terminal window, go to:
tut-install/examples/security/security-api/built-in-db-identity-store  
  1. Enter the following command:
    This command builds and packages the application into a WAR file,built-in-db-identity-store.war, that is located in the target directory, then deploys the WAR file.

To Run the built-in-db-identity-store Example

In this example, use the credentials of user Joe to make a request and to validate the response according to the credentials/roles defined inDatabaseSetup.java.

  1. Make a request to the deployed application by entering the following request URL in your web browser:
    Request URL:
http://localhost:8080/built-in-db-identity-store/servlet  

Because BASIC authentication is being used here, the container responds back prompting for username and password. 2. Enter the username Joe, and the password secret1 at the prompt.
Once you provide the credentials, the following process occurs:

web username: Joe  
web user has role "foo": true  
web user has role "bar": true  
web user has role "kaz": false  
  1. Test the authentication using invalid credentials. Make a request to the deployed application by entering the following request URL in your web browser:
    Request URL:
http://localhost:8080/built-in-db-identity-store/servlet  

Again, because BASIC authentication is being used here, the container responds back prompting for username and password. 4. Enter an invalid username and password. You are promted to enter the credentials again, but you are not authenticated.
When you click Cancel in the Authentication required window, the following response is returned:

HTTP Status 401 - Unauthorized  
type Status report  
message Unauthorized  
description This request requires HTTP authentication.  
GlassFish Server Open Source Edition 5