In the previous post, we saw how to use the built-in ‘tomcat-users.xml’ identity store with Apache TomEE. While this identity store is inherited from Tomcat and integrated into Jakarta Security implementation in TomEE, this is usually good for development or simple deployments, but may appear too simple or restrictive for production environments.
This blog will focus on how to implement your own identity store. TomEE can use LDAP or JDBC identity stores out of the box. We will try them out next time.
Let’s say you have your own file store or your own data store like an in-memory data grid, then you will need to implement your own identity store.
What is an identity store?
An identity store is a database or a directory (store) of identity information about a population of users that includes an application’s callers.
In essence, an identity store contains all information such as caller name, groups or roles, and required information to validate a caller’s credentials.
How to implement my own identity store?
This is actually fairly simple with Jakarta Security. The only thing you need to do is create an implementation of `jakarta.security.enterprise.identitystore.IdentityStore`. All methods in the interface have default implementations. So you only have to implement what you need.
public interface IdentityStore {
Set DEFAULT_VALIDATION_TYPES = EnumSet.of(VALIDATE, PROVIDE_GROUPS);
default CredentialValidationResult validate(Credential credential) {
}
default Set getCallerGroups(CredentialValidationResult validationResult) {
}
default int priority() {
}
default Set validationTypes() {
}
enum ValidationType {
VALIDATE, PROVIDE_GROUPS
}
}
By default, an identity store is used for both validating user credentials and providing groups/roles for the authenticated user. Depending on what #validationTypes() will return, you will have to implement #validate(…) and/or #getCallerGroups(…)
#getCallerGroups(…) will receive the result of #valide(…). Let’s look at a very simple example:
@ApplicationScoped
public class TestIdentityStore implements IdentityStore {
public CredentialValidationResult validate(Credential credential) {
if (!(credential instanceof UsernamePasswordCredential)) {
return INVALID_RESULT;
}
final UsernamePasswordCredential usernamePasswordCredential = (UsernamePasswordCredential) credential;
if (usernamePasswordCredential.compareTo("jon", "doe")) {
return new CredentialValidationResult("jon", new HashSet<>(asList("foo", "bar")));
}
if (usernamePasswordCredential.compareTo("iron", "man")) {
return new CredentialValidationResult("iron", new HashSet<>(Collections.singletonList("avengers")));
}
return INVALID_RESULT;
}
}
In this simple example, the identity store is hardcoded. Basically, it knows only 2 users, one of them has some roles, while the other has another set of roles.
You can easily extend this example and query a local file, or an in-memory data grid if you need. Or use JPA to access your relational database.
IMPORTANT: for TomEE to pick it up and use it in your application, the identity store must be a CDI bean.
The complete and runnable example is available under https://github.com/apache/tomee/tree/master/examples/security-custom-identitystore