Skip to content

Commit

Permalink
feat(jans-keycloak-integration): experimental protocol mapper #8614
Browse files Browse the repository at this point in the history
* added dependencies to protocol mapper
* added protocol mapper main class

Signed-off-by: Rolain Djeumen <uprightech@gmail.com>
  • Loading branch information
uprightech committed May 30, 2024
1 parent 0f1c5a4 commit a089c83
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 4 deletions.
50 changes: 50 additions & 0 deletions jans-keycloak-integration/protocol-mapper/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,56 @@
</dependency>
<!-- end keycloak dependencies-->

<!-- jans dependencies -->
<dependency>
<groupId>io.jans</groupId>
<artifactId>jans-core-standalone</artifactId>
</dependency>

<dependency>
<groupId>io.jans</groupId>
<artifactId>jans-core-service</artifactId>
</dependency>

<dependency>
<groupId>io.jans</groupId>
<artifactId>jans-orm-standalone</artifactId>
</dependency>

<dependency>
<groupId>io.jans</groupId>
<artifactId>jans-orm-couchbase</artifactId>
</dependency>

<dependency>
<groupId>io.jans</groupId>
<artifactId>jans-orm-hybrid</artifactId>
</dependency>

<dependency>
<groupId>io.jans</groupId>
<artifactId>jans-orm-ldap</artifactId>
</dependency>

<dependency>
<groupId>io.jans</groupId>
<artifactId>jans-orm-sql</artifactId>
</dependency>
<!-- end jans dependencies -->

<!-- log adapter -->
<dependency>
<groupId>org.jboss.slf4j</groupId>
<artifactId>slf4j-jboss-logmanager</artifactId>
<version>2.0.1.Final</version>
</dependency>
<!-- log adapter -->

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.12.0</version>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,84 @@
import java.util.ArrayList;
import java.util.List;

import io.jans.orm.search.filter.Filter;

import org.jboss.logging.Logger;

import org.keycloak.Config;

import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
import org.keycloak.dom.saml.v2.assertion.AttributeType;

import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;

import org.keycloak.protocol.saml.mappers.AttributeStatementHelper;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderConfigurationBuilder;

import io.jans.orm.model.base.CustomObjectAttribute;
import io.jans.kc.protocol.mapper.config.PersistenceConfigurationException;
import io.jans.kc.protocol.mapper.config.PersistenceConfigurationFactory;
import io.jans.kc.protocol.mapper.model.JansPerson;
import io.jans.model.JansAttribute;
import io.jans.model.GluuStatus;
import io.jans.orm.PersistenceEntryManager;

import org.keycloak.protocol.saml.mappers.AbstractSAMLProtocolMapper;
import org.keycloak.protocol.saml.mappers.SAMLAttributeStatementMapper;

import org.keycloak.saml.common.constants.JBossSAMLURIConstants;



public class SamlProtocolMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper {

private static final String DISPLAY_TYPE = "User Attribute";
private static final String DISPLAY_CATEGORY = "User Attribute Mapper";
private static final String HELP_TEXT = "Janssen User Attributes Protocol Mapper";
private static final String DISPLAY_TYPE = "Janssen User Attribute";
private static final String DISPLAY_CATEGORY = AttributeStatementHelper.ATTRIBUTE_STATEMENT_CATEGORY;
private static final String HELP_TEXT = "Maps a Janssen User's Attribute to a SAML Attribute";
private static final String PROVIDER_ID = "kc-jans-saml-protocol-mapper";
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();

//properties
private static final String JANS_ATTR_NAME_PROP_NAME = "jans.attribute.name";
private static final String JANS_ATTR_NAME_PROP_LABEL = "Jans Attribute";
private static final String JANS_ATTR_NAME_PROP_HELPTEXT = "Name of the Attribute in Janssen Auth Server";
private static final List<ProviderConfigProperty> configProperties;


private static final Logger log = Logger.getLogger(SamlProtocolMapper.class);

private final PersistenceConfigurationFactory persistenceConfigurationFactory;

static {


configProperties = ProviderConfigurationBuilder.create()
.property()
.name(JANS_ATTR_NAME_PROP_NAME)
.label(JANS_ATTR_NAME_PROP_LABEL)
.helpText(JANS_ATTR_NAME_PROP_HELPTEXT)
.type(ProviderConfigProperty.STRING_TYPE)
.defaultValue(null)
.required(true)
.add()
.build();
}

public SamlProtocolMapper() {

try {
persistenceConfigurationFactory = PersistenceConfigurationFactory.create();
PersistenceEntryManager persistenceEntryManager = persistenceConfigurationFactory.getPersistenceEntryManager();

}catch(PersistenceConfigurationException e) {
throw new RuntimeException("Could not instantiate protocol mapper",e);
}
}

@Override
public void close() {

Expand Down Expand Up @@ -80,6 +133,91 @@ public void postInit(KeycloakSessionFactory factory) {
public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {

final String attributename = mappingModel.getConfig().get(JANS_ATTR_NAME_PROP_NAME);
log.infov("Transform attribute statement. Attribute name: {0}",attributename);
JansAttribute attr = findJansAttributeByName(attributename);
if(attr == null) {
log.infov("No attribute found by name : {0}. No transformation to effect",attributename);
return;
}

if(attr.getStatus() != GluuStatus.ACTIVE) {
log.infov("Attribute {0} disabled. Skipping it for transformAttributeStatement()");
return;
}
JansPerson person = findJansPersonByUsername(userSession.getLoginUsername(),new String[] {attributename});
if(person == null) {
log.infov("No jans User associated with this keycloak session's user {0}",userSession.getLoginUsername());
return;
}
addJansAttributeValueFromPerson(attr,person,attributeStatement,mappingModel,userSession);
}

private PersistenceEntryManager getPersistenceEntryManager() {

return persistenceConfigurationFactory.getPersistenceEntryManager();
}

private void addJansAttributeValueFromPerson(JansAttribute jansAttribute, JansPerson jansPerson,
AttributeStatementType attributeStatement, ProtocolMapperModel protocolMapper,UserSessionModel userSession) {

if(!jansPerson.hasCustomAttributes()) {
log.infov("Jans User with keycloak login username {0} returned no custom attributes.",userSession.getLoginUsername());
return;
}

AttributeType attributeType = createAttributeType(protocolMapper, jansAttribute);
List<String> values = jansPerson.customAttributeValues(jansAttribute);
if(values == null) {
log.infov("Jans user with keycloak login username {0} returned no values for attribute {1}",
userSession.getLoginUsername(),jansAttribute.getName());
return;
}

values.forEach(attributeType::addAttributeValue);
attributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(attributeType));
}

private JansAttribute findJansAttributeByName(final String jansAttrName) {

final String [] attrs = new String [] {
"displayName",
"jansAttrTyp",
"jansClaimName",
"jansSAML1URI",
"jansSAML2URI",
"jansStatus",
"jansAttrName"
};

final Filter filter = Filter.createEqualityFilter("jansAttrName",jansAttrName);
return getPersistenceEntryManager().findEntries("ou=attributes,o=jans",JansAttribute.class,filter,attrs).get(0);
}

private JansPerson findJansPersonByUsername(final String username, final String [] returnattributes) {

final Filter uidsearchfilter = Filter.createEqualityFilter("uid",username);
final Filter mailsearchfilter = Filter.createEqualityFilter("mail",username);
final Filter usersearchfilter = Filter.createORFilter(uidsearchfilter,mailsearchfilter);

return getPersistenceEntryManager().findEntries("ou=people,o=jans",JansPerson.class,usersearchfilter,returnattributes).get(0);
}

private AttributeType createAttributeType(ProtocolMapperModel model, JansAttribute jansAttributeMeta) {

String attributeName = jansAttributeMeta.getSaml2Uri();
String attributeNameFormat = JBossSAMLURIConstants.ATTRIBUTE_FORMAT_URI.get();
if(jansAttributeMeta.getSaml2Uri() == null || jansAttributeMeta.getSaml1Uri().isEmpty()) {
attributeName = jansAttributeMeta.getName();
attributeNameFormat = JBossSAMLURIConstants.ATTRIBUTE_FORMAT_BASIC.get();
}

AttributeType ret = new AttributeType(attributeName);
ret.setNameFormat(attributeNameFormat);
if(jansAttributeMeta.getDisplayName() != null && !jansAttributeMeta.getDisplayName().trim().isEmpty()) {
ret.setFriendlyName(jansAttributeMeta.getDisplayName());
}
return ret;
}

}

0 comments on commit a089c83

Please sign in to comment.