Blueriq provides AuthenticationMapper interface that should be implementation when a CustomBean authentication provider is used and the authentication provider returns a custom Authentication implementation. Default implementations are already provided by Blueriq for the following Authentication implementations:
To implementation a custom AuthenticationMapper the following has to be done:
- Add the
blueriq-component-api
to the project dependencies.
<project ...>
...
<dependencies>
...
<dependency>
<groupId>com.blueriq</groupId>
<artifactId>blueriq-component-api</artifactId>
<version>${blueriq.version}</version>
</dependency>
...
</dependencies>
...
</project>
- Define the custom implementation like below:
package ...;
import com.blueriq.component.api.externalflow.data.mapping.AuthenticationMapper;
// other imports ...
@Component
public class CustomAuthenticationMapper implements AuthenticationMapper {
@Override
public String authenticationType() {
// add implementation here
return null;
}
@Override
public boolean supportsContract(ContractVersion contractVersion) {
// add implementation here
return false;
}
@Override
public boolean canMap(Authentication authentication) {
// add implementation here
return false;
}
@Override
public void fillModel(ObjectModelCreation objectModel, Authentication authentication) {
// add implementation here
}
@Override
public Authentication toAuthentication(ObjectModelRetrieval objectModel) {
// add implementation here
return null;
}
}
Example CustomBlueriqAuthentication AuthenticationMapper
Adding the dependencies
The following dependencies are required in the pom.xml
<project ...>
...
<dependencies>
...
<dependency>
<groupId>com.blueriq</groupId>
<artifactId>blueriq-component-api</artifactId>
<version>${blueriq.version}</version>
</dependency>
...
<dependencies>
...
</project>
The CustomBlueriqAuthentication
Here is an example implementation of the authentication mapper for a custom BlueriqAuthentication implementation. This implementation has an additional token field.
public class CustomBlueriqAuthentication implements BlueriqAuthentication {
private final String username;
private final String token;
private final List<String> teams;
private final List<String> roles;
private final List<? extends GrantedAuthority> authorities;
private final Map<String, List<String>> claims;
private final boolean authenticated;
public CustomBlueriqAuthentication(String username, String token, List<String> teams, List<String> roles,
Map<String, List<String>> claims, boolean authenticated) {
this.username = username;
this.token = token;
this.teams = teams;
this.roles = roles;
this.authorities = roles.stream().map(SimpleGrantedAuthority::new).toList();
this.claims = claims;
this.authenticated = authenticated;
}
@Override
public List<String> getTeams() {
return teams;
}
@Override
public List<String> getRoles() {
return roles;
}
@Override
public boolean isAnonymous() {
return false;
}
@Override
public boolean isAutomatic() {
return false;
}
@Override
public Set<String> getClaimNames() {
return claims.keySet();
}
@Override
public List<String> getClaim(String name) {
return claims.get(name);
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getDetails() {
return null;
}
@Override
public Object getPrincipal() {
return username;
}
@Override
public boolean isAuthenticated() {
return authenticated;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
throw new IllegalArgumentException("Cannot set authenticated after construction");
}
@Override
public String getName() {
return username;
}
public String getToken() {
return token;
}
}
Implementing the AuthenticationMapper Interface
package ...;
import com.blueriq.component.api.externalflow.data.mapping.AuthenticationMapper;
// other imports ...
@Component
public class CustomAuthenticationMapper implements AuthenticationMapper {
@Override
public String authenticationType() {
// provide a unique authentication type identifier that will be used to identity which mapper should be used.
return "custom-blueriq-token-authentication";
}
@Override
public boolean supportsContract(ContractVersion contractVersion) {
// check if the AuthenticationMapper supports writing and reading for the incoming contract version.
return SupportedContractVersions.V_1_0.equals(contractVersion);
}
@Override
public boolean canMap(Authentication authentication) {
// check if the provided authentication is supported by the AuthenticationMapper
return authentication instanceof CustomBlueriqAuthentication;
}
@Override
public void fillModel(ObjectModelCreation objectModel, Authentication authentication) {
// verify that we still have the supported Authentication object.
if (!(authentication instanceof CustomBlueriqAuthentication)) {
throw new IllegalArgumentException(
"Unable to fill authentication model, unexpected authentication implementation: "
+ authentication.getClass().getSimpleName());
}
CustomBlueriqAuthentication customAuthentication = (CustomBlueriqAuthentication) authentication;
// We need to supply the objectModel with enough information that it is able to the reconstruct the
// CustomBlueriqAuthentication object in the toAuthentication method.
objectModel.put("token", customAuthentication.getToken());
objectModel.put("user", customAuthentication.getName());
objectModel.put("authenticated", customAuthentication.isAuthenticated());
ObjectModelCreation claims = objectModel.createObject("claims");
customAuthentication.getClaimNames().forEach(key -> {
ArrayModelCreation values = claims.createArray(key);
customAuthentication.getClaim(key).forEach(values::add);
});
ArrayModelCreation roles = objectModel.createArray("roles");
customAuthentication.getRoles().forEach(roles::add);
ArrayModelCreation teams = objectModel.createArray("teams");
customAuthentication.getTeams().forEach(teams::add);
}
@Override
public Authentication toAuthentication(ObjectModelRetrieval objectModel) {
// We retrieve all of the filled information from the objectModel, in order to recreate the
// CustomBlueriqAuthentication
String token = objectModel.getString("token");
String user = objectModel.getString("user");
boolean isAuthenticated = objectModel.getBoolean("authenticated");
List<String> roles = objectModel.getStringList("roles");
List<String> teams = objectModel.getStringList("teams");
ObjectModelRetrieval claimsModel = objectModel.getObject("claims");
Map<String, List<String>> claims =
claimsModel.getProperties().collect(Collectors.toMap(Function.identity(), claimsModel::getStringList));
return new CustomBlueriqAuthentication(user, token, teams, roles, claims, isAuthenticated);
}
}
Testing the CustomerAuthenticationMapper
Here is an example on how to create a testcase for the AuthenticationMapper implementation. The AuthenticationMapper interface only works with interface object to mitigate implementation errors, for the testcases the ObjectModel implementation class should be used to fill the model and to create the authentication
package ...;
// imports ...
public class CustomAuthenticationMapperTest {
@Test
void roundTripAuthentication() {
// First we provide a CustomBlueriqAuthentication object filled with data.
String user = "user";
String token = "token";
List<String> roles = List.of("role");
List<String> teams = List.of("team");
boolean authenticated = true;
Map<String, List<String>> claims = Map.of("claim1", List.of("x", "y"), "claim2", List.of());
CustomBlueriqAuthentication originalAuthentication =
new CustomBlueriqAuthentication(user, token, roles, teams, claims, authenticated);
// Second we fill an ObjectModel with the CustomBlueriqAuthentication.
AuthenticationMapper authenticationMapper = new CustomAuthenticationMapper();
ObjectModel objectModel = new ObjectModel();
authenticationMapper.fillModel(objectModel, originalAuthentication);
// Third we transform the objectModel back into a Authentication implementation.
Authentication authentication = authenticationMapper.toAuthentication(objectModel);
// Fourth we verify that all of the field are set correctly.
assertThat(authentication, instanceOf(CustomBlueriqAuthentication.class));
CustomBlueriqAuthenticationresult = (CustomBlueriqAuthentication) authentication;
assertThat(result.getName(), equalTo(user));
assertThat(result.getToken(), equalTo(tplem));
assertThat(result.isAuthenticated(), equalTo(authenticated));
assertThat(result.getAuthorities(), equalTo(roles.stream().map(SimpleGrantedAuthority::new).toList()));
assertThat(result.getRoles(), equalTo(roles));
assertThat(result.getTeams(), equalTo(teams));
Set<String> claimNames = result.getClaimNames();
assertThat(claimNames, hasSize(2));
assertThat(claimNames, containsInAnyOrder("claim1", "claim2"));
assertThat(result.getClaim("claim1"), hasSize(2));
assertThat(result.getClaim("claim1"), containsInAnyOrder("x", "y"));
assertThat(result.getClaim("claim2"), hasSize(0));
}
}