Custom Identity Manager
This section will provide detailed information on how to implement a custom IDM.
For further help, please refer to the RESTHeart support channels https://restheart.org/support.html
Introduction
The Identity Manager is responsible of authenticating the users verifying the credentials provided via the basic authentication mechanism.
Following the dependency injection approach, the actual IDM implementation to use is specified in the configuration file.
There are some ready-to-use IDM implementations and custom ones can be developed.
This section explains how to develop a custom IDM.
If you develop a general purpose IDM please consider contributing to RESTHeart project using the github pull request feature.
The steps required to develop and configure an IDM are:
- develop the IDM in Java
- configure RESTHeart to use the new IDM via its configuration file
- add the implementation class(es) to the java classpath
Develop
The IDM class
The IDM implementation class must implement the interface io.undertow.security.idm.IdentityManager available from the undertow-core package.
maven coordinates of undertow-core package
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-core</artifactId>
<version>1.2.12.Final</version>
</dependency>
The IDM is a singleton instance of this class.
Constructor
The constructor must have the following signature:
public MyIdentityManager(Map<String, Object> args)
The argument args maps the idm properties as specified in the configuration file.
Methods to implement
The interface io.undertow.security.idm.IdentityManager mandates to implement 3 verify methods:
Modifier and Type | Method and Description |
---|---|
Account |
verify(Account account)
Verify a previously authenticated account.
|
Account |
verify(Credential credential)
Perform verification when all we have is the Credential, in this case the IdentityManager is also responsible for mapping the Credential to an account.
|
Account |
verify(String id, Credential credential)
Verify a supplied Credential against a requested ID.
|
The actual check occurs in the third verify method. They can be implemented as follows.
@Override
public Account verify(Account account) {
// need to re-verify previously authenticated account? If not, leave as follows.
return account;
}
@Override
public Account verify(Credential credential) {
// always return null because we use basic authentication and the id is always provided
return null;
}
public Account verify(String id, Credential credential) {
if (credential instanceof PasswordCredential) {
char[] password = ((PasswordCredential) credential).getPassword();
// here check the id and password
....
} else {
return null;
}
}
The Credential argument is actually an instance of io.undertow.security.idm.PasswordCredential
The verify methods should return null for invalid credentials or an instance of org.restheart.security.impl.SimpleAccount whose constructor allows to instantiate an immutable Account object that includes roles as an Array<Strings> object.
Configuration
The IDM is configured in the idm section of the yaml configuration file.
Here you specify the actual IDM implementation to use and any parameters it needs (for instance, the path of the file where the passwords are defined or some parameters that control caching).
For example, if the idm configuration section is:
idm:
implementation-class: org.restheart.examples.security.MyIdentityManager
arg1: 5
arg2: hey man!
arg3:
arg31: 1
arg32: 2
Then:
- the IDM singleton will be of class MyIdentityManager
- its constructor will be invoked passing a Map argument with 4 keys
- implementation-class of class String
- arg1 of class Integer
- arg2 of class String
- arg3 of class Map<String, Object>, having in turn 2 keys (arg31 and arg32)
Add custom classes to the classpath
Using the java classpath option
The custom classes must be added to the java classpath.
In order to achieve this, start RESTHeart with the following command:
$ java -server -classpath restheart.jar:custom-classes.jar org.restheart.Bootstrapper restheart.yml
Using the Maven shade plugin
The maven share plugin provides the capability to package the artifact in an uber-jar, including its dependencies and to shade - i.e. rename - the packages of some of the dependencies.
It allows to create a single jar including any RESTHeart class and your custom ones. In this case you can start RESTHeart with
$ java -server -jar restheart_plus_custom.jar restheart.yml
Example
A project with RESTHeart customization examples is available on github; find it at restheart-customization-examples.
It includes the ExampleIdentityManager; this is a simple IDM that verifies the password to be the flipped userid string. In case the user id is ‘admin’, it also associate the user with the ADMIN role and, in all other cases, with the ‘USER’ role.
The implementation class follows:
package org.restheart.examples.security;
import com.google.common.collect.Sets;
import io.undertow.security.idm.Account;
import io.undertow.security.idm.Credential;
import io.undertow.security.idm.IdentityManager;
import io.undertow.security.idm.PasswordCredential;
import java.util.Map;
import java.util.Set;
import org.restheart.security.impl.SimpleAccount;
/**
* ExampleIdentityManager verifies the password to be the flipped id string,
* i.e. id="username" => pwd="emanresu"
*
* @author Andrea Di Cesare <andrea@softinstigate.com>
*/
public class ExampleIdentityManager implements IdentityManager {
public enum ROLE {
ADMIN, USER
};
public ExampleIdentityManager(Map<String, Object> args) {
// args are ignored
}
public static final String ADMIN_ID = "admin";
private static final Set<String> NORMAL_USER_ROLES;
private static final Set<String> ADMIN_ROLES;
static {
NORMAL_USER_ROLES = Sets.newHashSet(ROLE.USER.toString());
ADMIN_ROLES = Sets.newHashSet(ROLE.ADMIN.toString());
}
@Override
public Account verify(String id, Credential credential) {
if (credential instanceof PasswordCredential) {
char[] password = ((PasswordCredential) credential).getPassword();
String flippedPassword = new StringBuilder(new String(password))
.reverse()
.toString();
if (id.equals(flippedPassword)) {
if (id.equals(ADMIN_ID)) {
return new SimpleAccount(id, password, ADMIN_ROLES);
} else {
return new SimpleAccount(id, password, NORMAL_USER_ROLES);
}
}
}
return null;
}
@Override
public Account verify(Account account) {
return account;
}
@Override
public Account verify(Credential credential) {
// Auto-generated method stub
return null;
}
}