KEYCLOAK-886 User Federation Mappers - admin console
This commit is contained in:
parent
269f9fe860
commit
dfe232cf80
24 changed files with 996 additions and 162 deletions
|
@ -1,5 +1,6 @@
|
||||||
package org.keycloak.representations.idm;
|
package org.keycloak.representations.idm;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,7 +12,7 @@ public class UserFederationMapperTypeRepresentation {
|
||||||
protected String category;
|
protected String category;
|
||||||
protected String helpText;
|
protected String helpText;
|
||||||
|
|
||||||
protected List<ConfigPropertyRepresentation> properties;
|
protected List<ConfigPropertyRepresentation> properties = new LinkedList<>();
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
|
|
|
@ -24,7 +24,6 @@ import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserFederationEventAwareProviderFactory;
|
import org.keycloak.models.UserFederationEventAwareProviderFactory;
|
||||||
import org.keycloak.models.UserFederationMapperModel;
|
import org.keycloak.models.UserFederationMapperModel;
|
||||||
import org.keycloak.models.UserFederationProvider;
|
import org.keycloak.models.UserFederationProvider;
|
||||||
import org.keycloak.models.UserFederationProviderFactory;
|
|
||||||
import org.keycloak.models.UserFederationProviderModel;
|
import org.keycloak.models.UserFederationProviderModel;
|
||||||
import org.keycloak.models.UserFederationSyncResult;
|
import org.keycloak.models.UserFederationSyncResult;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
@ -89,7 +88,7 @@ public class LDAPFederationProviderFactory extends UserFederationEventAwareProvi
|
||||||
String usernameLdapAttribute = ldapConfig.getUsernameLdapAttribute();
|
String usernameLdapAttribute = ldapConfig.getUsernameLdapAttribute();
|
||||||
|
|
||||||
UserFederationMapperModel mapperModel;
|
UserFederationMapperModel mapperModel;
|
||||||
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("usernameMapper", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.ID,
|
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("username", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
|
||||||
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.USERNAME,
|
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.USERNAME,
|
||||||
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, usernameLdapAttribute,
|
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, usernameLdapAttribute,
|
||||||
UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
|
UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
|
||||||
|
@ -97,25 +96,25 @@ public class LDAPFederationProviderFactory extends UserFederationEventAwareProvi
|
||||||
|
|
||||||
// For AD deployments with sAMAccountName is probably more common to map "cn" to full name of user
|
// For AD deployments with sAMAccountName is probably more common to map "cn" to full name of user
|
||||||
if (activeDirectory && usernameLdapAttribute.equalsIgnoreCase(LDAPConstants.SAM_ACCOUNT_NAME)) {
|
if (activeDirectory && usernameLdapAttribute.equalsIgnoreCase(LDAPConstants.SAM_ACCOUNT_NAME)) {
|
||||||
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("fullNameMapper", newProviderModel.getId(), FullNameLDAPFederationMapperFactory.ID,
|
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("full name", newProviderModel.getId(), FullNameLDAPFederationMapperFactory.PROVIDER_ID,
|
||||||
FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, LDAPConstants.CN,
|
FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, LDAPConstants.CN,
|
||||||
UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
|
UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
|
||||||
realm.addUserFederationMapper(mapperModel);
|
realm.addUserFederationMapper(mapperModel);
|
||||||
} else {
|
} else {
|
||||||
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("firstNameMapper", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.ID,
|
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("first name", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
|
||||||
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.FIRST_NAME,
|
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.FIRST_NAME,
|
||||||
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.CN,
|
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.CN,
|
||||||
UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
|
UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
|
||||||
realm.addUserFederationMapper(mapperModel);
|
realm.addUserFederationMapper(mapperModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("lastNameMapper", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.ID,
|
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("last name", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
|
||||||
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.LAST_NAME,
|
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.LAST_NAME,
|
||||||
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.SN,
|
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.SN,
|
||||||
UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
|
UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
|
||||||
realm.addUserFederationMapper(mapperModel);
|
realm.addUserFederationMapper(mapperModel);
|
||||||
|
|
||||||
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("emailMapper", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.ID,
|
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("email", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
|
||||||
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.EMAIL,
|
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.EMAIL,
|
||||||
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.EMAIL,
|
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.EMAIL,
|
||||||
UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
|
UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
|
||||||
|
@ -125,14 +124,14 @@ public class LDAPFederationProviderFactory extends UserFederationEventAwareProvi
|
||||||
String modifyTimestampLdapAttrName = activeDirectory ? "whenChanged" : LDAPConstants.MODIFY_TIMESTAMP;
|
String modifyTimestampLdapAttrName = activeDirectory ? "whenChanged" : LDAPConstants.MODIFY_TIMESTAMP;
|
||||||
|
|
||||||
// map createTimeStamp as read-only
|
// map createTimeStamp as read-only
|
||||||
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("creationDateMapper", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.ID,
|
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("creation date", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
|
||||||
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, LDAPConstants.CREATE_TIMESTAMP,
|
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, LDAPConstants.CREATE_TIMESTAMP,
|
||||||
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, createTimestampLdapAttrName,
|
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, createTimestampLdapAttrName,
|
||||||
UserAttributeLDAPFederationMapper.READ_ONLY, "true");
|
UserAttributeLDAPFederationMapper.READ_ONLY, "true");
|
||||||
realm.addUserFederationMapper(mapperModel);
|
realm.addUserFederationMapper(mapperModel);
|
||||||
|
|
||||||
// map modifyTimeStamp as read-only
|
// map modifyTimeStamp as read-only
|
||||||
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("modifyDateMapper", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.ID,
|
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("modify date", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
|
||||||
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, LDAPConstants.MODIFY_TIMESTAMP,
|
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, LDAPConstants.MODIFY_TIMESTAMP,
|
||||||
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, modifyTimestampLdapAttrName,
|
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, modifyTimestampLdapAttrName,
|
||||||
UserAttributeLDAPFederationMapper.READ_ONLY, "true");
|
UserAttributeLDAPFederationMapper.READ_ONLY, "true");
|
||||||
|
|
|
@ -1,25 +1,65 @@
|
||||||
package org.keycloak.federation.ldap.mappers;
|
package org.keycloak.federation.ldap.mappers;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
|
||||||
|
import org.keycloak.mappers.MapperConfigValidationException;
|
||||||
import org.keycloak.mappers.UserFederationMapperFactory;
|
import org.keycloak.mappers.UserFederationMapperFactory;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
import org.keycloak.models.UserFederationMapperModel;
|
||||||
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractLDAPFederationMapperFactory implements UserFederationMapperFactory {
|
public abstract class AbstractLDAPFederationMapperFactory implements UserFederationMapperFactory {
|
||||||
|
|
||||||
|
// Used to map attributes from LDAP to UserModel attributes
|
||||||
|
public static final String ATTRIBUTE_MAPPER_CATEGORY = "Attribute Mapper";
|
||||||
|
|
||||||
|
// Used to map roles from LDAP to UserModel users
|
||||||
|
public static final String ROLE_MAPPER_CATEGORY = "Role Mapper";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Config.Scope config) {
|
public void init(Config.Scope config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFederationProviderType() {
|
||||||
|
return LDAPFederationProviderFactory.PROVIDER_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postInit(KeycloakSessionFactory factory) {
|
public void postInit(KeycloakSessionFactory factory) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ProviderConfigProperty> getConfigProperties() {
|
||||||
|
throw new IllegalStateException("Method not supported for this implementation");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ProviderConfigProperty createConfigProperty(String name, String label, String helpText, String type, Object defaultValue) {
|
||||||
|
ProviderConfigProperty configProperty = new ProviderConfigProperty();
|
||||||
|
configProperty.setName(name);
|
||||||
|
configProperty.setLabel(label);
|
||||||
|
configProperty.setHelpText(helpText);
|
||||||
|
configProperty.setType(type);
|
||||||
|
configProperty.setDefaultValue(defaultValue);
|
||||||
|
return configProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkMandatoryConfigAttribute(String name, String displayName, UserFederationMapperModel mapperModel) throws MapperConfigValidationException {
|
||||||
|
String attrConfigValue = mapperModel.getConfig().get(name);
|
||||||
|
if (attrConfigValue == null || attrConfigValue.trim().isEmpty()) {
|
||||||
|
throw new MapperConfigValidationException("Missing configuration for '" + displayName + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
package org.keycloak.federation.ldap.mappers;
|
package org.keycloak.federation.ldap.mappers;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.keycloak.mappers.MapperConfigValidationException;
|
||||||
import org.keycloak.mappers.UserFederationMapper;
|
import org.keycloak.mappers.UserFederationMapper;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.LDAPConstants;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserFederationMapperModel;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,21 +16,48 @@ import org.keycloak.provider.ProviderConfigProperty;
|
||||||
*/
|
*/
|
||||||
public class FullNameLDAPFederationMapperFactory extends AbstractLDAPFederationMapperFactory {
|
public class FullNameLDAPFederationMapperFactory extends AbstractLDAPFederationMapperFactory {
|
||||||
|
|
||||||
public static final String ID = "full-name-ldap-mapper";
|
public static final String PROVIDER_ID = "full-name-ldap-mapper";
|
||||||
|
|
||||||
@Override
|
protected static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||||
public String getHelpText() {
|
|
||||||
return "Some help text - full name mapper - TODO";
|
static {
|
||||||
|
ProviderConfigProperty userModelAttribute = createConfigProperty(FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, "LDAP Full Name Attribute",
|
||||||
|
"Name of LDAP attribute, which contains fullName of user. In most cases it will be 'cn' ", ProviderConfigProperty.STRING_TYPE, LDAPConstants.CN);
|
||||||
|
configProperties.add(userModelAttribute);
|
||||||
|
|
||||||
|
ProviderConfigProperty readOnly = createConfigProperty(UserAttributeLDAPFederationMapper.READ_ONLY, "Read Only",
|
||||||
|
"For Read-only is data imported from LDAP to Keycloak DB, but it's not saved back to LDAP when user is updated in Keycloak.", ProviderConfigProperty.BOOLEAN_TYPE, "false");
|
||||||
|
configProperties.add(readOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ProviderConfigProperty> getConfigProperties() {
|
public String getHelpText() {
|
||||||
return null;
|
return "Used to map full-name of user from single attribute in LDAP (usually 'cn' attribute) to firstName and lastName attributes of UserModel in Keycloak DB";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayCategory() {
|
||||||
|
return ATTRIBUTE_MAPPER_CATEGORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayType() {
|
||||||
|
return "Full Name";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ProviderConfigProperty> getConfigProperties(RealmModel realm) {
|
||||||
|
return configProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return ID;
|
return PROVIDER_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validateConfig(UserFederationMapperModel mapperModel) throws MapperConfigValidationException {
|
||||||
|
checkMandatoryConfigAttribute(FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, "LDAP Full Name Attribute", mapperModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -178,7 +178,6 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
|
||||||
}
|
}
|
||||||
String[] objClasses = objectClasses.split(",");
|
String[] objClasses = objectClasses.split(",");
|
||||||
|
|
||||||
// TODO: util method for trim and convert array to collection?
|
|
||||||
Set<String> trimmed = new HashSet<String>();
|
Set<String> trimmed = new HashSet<String>();
|
||||||
for (String objectClass : objClasses) {
|
for (String objectClass : objClasses) {
|
||||||
objectClass = objectClass.trim();
|
objectClass = objectClass.trim();
|
||||||
|
|
|
@ -1,9 +1,18 @@
|
||||||
package org.keycloak.federation.ldap.mappers;
|
package org.keycloak.federation.ldap.mappers;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.keycloak.mappers.MapperConfigValidationException;
|
||||||
import org.keycloak.mappers.UserFederationMapper;
|
import org.keycloak.mappers.UserFederationMapper;
|
||||||
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.LDAPConstants;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserFederationMapperModel;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,21 +20,95 @@ import org.keycloak.provider.ProviderConfigProperty;
|
||||||
*/
|
*/
|
||||||
public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMapperFactory {
|
public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMapperFactory {
|
||||||
|
|
||||||
public static final String ID = "role-ldap-mapper";
|
public static final String PROVIDER_ID = "role-ldap-mapper";
|
||||||
|
|
||||||
@Override
|
protected static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||||
public String getHelpText() {
|
|
||||||
return "Some help text - role mapper - TODO";
|
static {
|
||||||
|
ProviderConfigProperty rolesDn = createConfigProperty(RoleLDAPFederationMapper.ROLES_DN, "LDAP Roles DN",
|
||||||
|
"LDAP DN where are roles of this tree saved. For example 'ou=finance,dc=example,dc=org' ", ProviderConfigProperty.STRING_TYPE, null);
|
||||||
|
configProperties.add(rolesDn);
|
||||||
|
|
||||||
|
ProviderConfigProperty roleNameLDAPAttribute = createConfigProperty(RoleLDAPFederationMapper.ROLE_NAME_LDAP_ATTRIBUTE, "Role Name LDAP Attribute",
|
||||||
|
"Name of LDAP attribute, which is used in role objects for name and RDN of role. Usually it will be 'cn' . In this case typical group/role object may have DN like 'cn=role1,ou=finance,dc=example,dc=org' ",
|
||||||
|
ProviderConfigProperty.STRING_TYPE, LDAPConstants.CN);
|
||||||
|
configProperties.add(roleNameLDAPAttribute);
|
||||||
|
|
||||||
|
ProviderConfigProperty membershipLDAPAttribute = createConfigProperty(RoleLDAPFederationMapper.MEMBERSHIP_LDAP_ATTRIBUTE, "Membership LDAP Attribute",
|
||||||
|
"Name of LDAP attribute on role, which is used for membership mappings. Usually it will be 'member' ",
|
||||||
|
ProviderConfigProperty.STRING_TYPE, LDAPConstants.MEMBER);
|
||||||
|
configProperties.add(membershipLDAPAttribute);
|
||||||
|
|
||||||
|
ProviderConfigProperty roleObjectClasses = createConfigProperty(RoleLDAPFederationMapper.ROLE_OBJECT_CLASSES, "Role Object Classes",
|
||||||
|
"Object classes of the role object divided by comma (if more values needed). In typical LDAP deployment it could be 'groupOfNames' or 'groupOfEntries' ",
|
||||||
|
ProviderConfigProperty.STRING_TYPE, LDAPConstants.GROUP_OF_NAMES);
|
||||||
|
configProperties.add(roleObjectClasses);
|
||||||
|
|
||||||
|
List<String> modes = new LinkedList<String>();
|
||||||
|
for (RoleLDAPFederationMapper.Mode mode : RoleLDAPFederationMapper.Mode.values()) {
|
||||||
|
modes.add(mode.toString());
|
||||||
|
}
|
||||||
|
ProviderConfigProperty mode = createConfigProperty(RoleLDAPFederationMapper.MODE, "Mode",
|
||||||
|
"LDAP_ONLY means that all role mappings are retrieved from LDAP and saved into LDAP. READ_ONLY is Read-only LDAP mode where role mappings are " +
|
||||||
|
"retrieved from both LDAP and DB and merged together. New role grants are not saved to LDAP but to DB. IMPORT is Read-only LDAP mode where role mappings are retrieved from LDAP just at the time when user is imported from LDAP and then " +
|
||||||
|
"they are saved to local keycloak DB.",
|
||||||
|
ProviderConfigProperty.LIST_TYPE, modes);
|
||||||
|
configProperties.add(mode);
|
||||||
|
|
||||||
|
ProviderConfigProperty useRealmRolesMappings = createConfigProperty(RoleLDAPFederationMapper.USE_REALM_ROLES_MAPPING, "Use Realm Roles Mapping",
|
||||||
|
"If true, then LDAP role mappings will be mapped to realm role mappings in Keycloak. Otherwise it will be mapped to client role mappings", ProviderConfigProperty.BOOLEAN_TYPE, "true");
|
||||||
|
configProperties.add(useRealmRolesMappings);
|
||||||
|
|
||||||
|
// NOTE: ClientID will be computed dynamically from available clients
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ProviderConfigProperty> getConfigProperties() {
|
public String getHelpText() {
|
||||||
return null;
|
return "Used to map role mappings of roles from some LDAP DN to Keycloak role mappings of either realm roles or client roles of particular client";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayCategory() {
|
||||||
|
return ROLE_MAPPER_CATEGORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayType() {
|
||||||
|
return "Role mappings";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ProviderConfigProperty> getConfigProperties(RealmModel realm) {
|
||||||
|
List<ProviderConfigProperty> props = new ArrayList<ProviderConfigProperty>(configProperties);
|
||||||
|
|
||||||
|
Map<String, ClientModel> clients = realm.getClientNameMap();
|
||||||
|
List<String> clientIds = new ArrayList<String>(clients.keySet());
|
||||||
|
|
||||||
|
ProviderConfigProperty clientIdProperty = createConfigProperty(RoleLDAPFederationMapper.CLIENT_ID, "Client ID",
|
||||||
|
"Client ID of client to which LDAP role mappings will be mapped. Applicable just if 'Use Realm Roles Mapping' is false",
|
||||||
|
ProviderConfigProperty.LIST_TYPE, clientIds);
|
||||||
|
props.add(clientIdProperty);
|
||||||
|
|
||||||
|
return props;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return ID ;
|
return PROVIDER_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validateConfig(UserFederationMapperModel mapperModel) throws MapperConfigValidationException {
|
||||||
|
checkMandatoryConfigAttribute(RoleLDAPFederationMapper.ROLES_DN, "LDAP Roles DN", mapperModel);
|
||||||
|
|
||||||
|
String realmMappings = mapperModel.getConfig().get(RoleLDAPFederationMapper.USE_REALM_ROLES_MAPPING);
|
||||||
|
boolean useRealmMappings = Boolean.parseBoolean(realmMappings);
|
||||||
|
if (!useRealmMappings) {
|
||||||
|
String clientId = mapperModel.getConfig().get(RoleLDAPFederationMapper.CLIENT_ID);
|
||||||
|
if (clientId == null || clientId.trim().isEmpty()) {
|
||||||
|
throw new MapperConfigValidationException("Client ID needs to be provided in config when Realm Roles Mapping is not used");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
package org.keycloak.federation.ldap.mappers;
|
package org.keycloak.federation.ldap.mappers;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.keycloak.mappers.MapperConfigValidationException;
|
||||||
import org.keycloak.mappers.UserFederationMapper;
|
import org.keycloak.mappers.UserFederationMapper;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserFederationMapperModel;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,21 +15,52 @@ import org.keycloak.provider.ProviderConfigProperty;
|
||||||
*/
|
*/
|
||||||
public class UserAttributeLDAPFederationMapperFactory extends AbstractLDAPFederationMapperFactory {
|
public class UserAttributeLDAPFederationMapperFactory extends AbstractLDAPFederationMapperFactory {
|
||||||
|
|
||||||
public static final String ID = "user-attribute-ldap-mapper";
|
public static final String PROVIDER_ID = "user-attribute-ldap-mapper";
|
||||||
|
protected static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||||
|
|
||||||
@Override
|
static {
|
||||||
public String getHelpText() {
|
ProviderConfigProperty userModelAttribute = createConfigProperty(UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "User Model Attribute",
|
||||||
return "Some help text TODO";
|
"Name of mapped UserModel property or UserModel attribute in Keycloak DB. For example 'firstName', 'lastName, 'email', 'street' etc.", ProviderConfigProperty.STRING_TYPE, null);
|
||||||
|
configProperties.add(userModelAttribute);
|
||||||
|
|
||||||
|
ProviderConfigProperty ldapAttribute = createConfigProperty(UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, "LDAP Attribute",
|
||||||
|
"Name of mapped attribute on LDAP object. For example 'cn', 'sn, 'mail', 'street' etc.", ProviderConfigProperty.STRING_TYPE, null);
|
||||||
|
configProperties.add(ldapAttribute);
|
||||||
|
|
||||||
|
ProviderConfigProperty readOnly = createConfigProperty(UserAttributeLDAPFederationMapper.READ_ONLY, "Read Only",
|
||||||
|
"Read-only attribute is imported from LDAP to Keycloak DB, but it's not saved back to LDAP when user is updated in Keycloak.", ProviderConfigProperty.BOOLEAN_TYPE, "false");
|
||||||
|
configProperties.add(readOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ProviderConfigProperty> getConfigProperties() {
|
public String getHelpText() {
|
||||||
return null;
|
return "Used to map single attribute from LDAP user to attribute of UserModel in Keycloak DB";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayCategory() {
|
||||||
|
return ATTRIBUTE_MAPPER_CATEGORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayType() {
|
||||||
|
return "User Attribute";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ProviderConfigProperty> getConfigProperties(RealmModel realm) {
|
||||||
|
return configProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return ID;
|
return PROVIDER_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validateConfig(UserFederationMapperModel mapperModel) throws MapperConfigValidationException {
|
||||||
|
checkMandatoryConfigAttribute(UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "User Model Attribute", mapperModel);
|
||||||
|
checkMandatoryConfigAttribute(UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, "LDAP Attribute", mapperModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -952,6 +952,58 @@ module.config([ '$routeProvider', function($routeProvider) {
|
||||||
},
|
},
|
||||||
controller : 'GenericUserFederationCtrl'
|
controller : 'GenericUserFederationCtrl'
|
||||||
})
|
})
|
||||||
|
.when('/realms/:realm/user-federation/providers/:provider/:instance/mappers', {
|
||||||
|
templateUrl : function(params){ return resourceUrl + '/partials/federated-mappers.html'; },
|
||||||
|
resolve : {
|
||||||
|
realm : function(RealmLoader) {
|
||||||
|
return RealmLoader();
|
||||||
|
},
|
||||||
|
provider : function(UserFederationInstanceLoader) {
|
||||||
|
return UserFederationInstanceLoader();
|
||||||
|
},
|
||||||
|
mapperTypes : function(UserFederationMapperTypesLoader) {
|
||||||
|
return UserFederationMapperTypesLoader();
|
||||||
|
},
|
||||||
|
mappers : function(UserFederationMappersLoader) {
|
||||||
|
return UserFederationMappersLoader();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
controller : 'UserFederationMapperListCtrl'
|
||||||
|
})
|
||||||
|
.when('/realms/:realm/user-federation/providers/:provider/:instance/mappers/:mapperId', {
|
||||||
|
templateUrl : function(params){ return resourceUrl + '/partials/federated-mapper-detail.html'; },
|
||||||
|
resolve : {
|
||||||
|
realm : function(RealmLoader) {
|
||||||
|
return RealmLoader();
|
||||||
|
},
|
||||||
|
provider : function(UserFederationInstanceLoader) {
|
||||||
|
return UserFederationInstanceLoader();
|
||||||
|
},
|
||||||
|
mapperTypes : function(UserFederationMapperTypesLoader) {
|
||||||
|
return UserFederationMapperTypesLoader();
|
||||||
|
},
|
||||||
|
mapper : function(UserFederationMapperLoader) {
|
||||||
|
return UserFederationMapperLoader();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
controller : 'UserFederationMapperCtrl'
|
||||||
|
})
|
||||||
|
.when('/create/user-federation-mappers/:realm/:provider/:instance', {
|
||||||
|
templateUrl : function(params){ return resourceUrl + '/partials/federated-mapper-detail.html'; },
|
||||||
|
resolve : {
|
||||||
|
realm : function(RealmLoader) {
|
||||||
|
return RealmLoader();
|
||||||
|
},
|
||||||
|
provider : function(UserFederationInstanceLoader) {
|
||||||
|
return UserFederationInstanceLoader();
|
||||||
|
},
|
||||||
|
mapperTypes : function(UserFederationMapperTypesLoader) {
|
||||||
|
return UserFederationMapperTypesLoader();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
controller : 'UserFederationMapperCreateCtrl'
|
||||||
|
})
|
||||||
|
|
||||||
.when('/realms/:realm/defense/headers', {
|
.when('/realms/:realm/defense/headers', {
|
||||||
templateUrl : resourceUrl + '/partials/defense-headers.html',
|
templateUrl : resourceUrl + '/partials/defense-headers.html',
|
||||||
resolve : {
|
resolve : {
|
||||||
|
|
|
@ -511,8 +511,8 @@ module.controller('GenericUserFederationCtrl', function($scope, $location, Notif
|
||||||
}
|
}
|
||||||
|
|
||||||
function triggerSync(action) {
|
function triggerSync(action) {
|
||||||
UserFederationSync.get({ action: action, realm: $scope.realm.realm, provider: $scope.instance.id }, function() {
|
UserFederationSync.save({ action: action, realm: $scope.realm.realm, provider: $scope.instance.id }, {}, function(syncResult) {
|
||||||
Notifications.success("Sync of users finished successfully");
|
Notifications.success("Sync of users finished successfully. " + syncResult.status);
|
||||||
}, function() {
|
}, function() {
|
||||||
Notifications.error("Error during sync of users");
|
Notifications.error("Error during sync of users");
|
||||||
});
|
});
|
||||||
|
@ -734,3 +734,128 @@ module.controller('LDAPCtrl', function($scope, $location, $route, Notifications,
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
module.controller('UserFederationMapperListCtrl', function($scope, $location, Notifications, $route, Dialog, realm, provider, mapperTypes, mappers) {
|
||||||
|
console.log('UserFederationMapperListCtrl');
|
||||||
|
|
||||||
|
$scope.realm = realm;
|
||||||
|
$scope.provider = provider;
|
||||||
|
|
||||||
|
$scope.mapperTypes = mapperTypes;
|
||||||
|
$scope.mappers = mappers;
|
||||||
|
|
||||||
|
$scope.hasAnyMapperTypes = false;
|
||||||
|
for (var property in mapperTypes) {
|
||||||
|
if (!(property.startsWith('$'))) {
|
||||||
|
$scope.hasAnyMapperTypes = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
module.controller('UserFederationMapperCtrl', function($scope, realm, provider, mapperTypes, mapper, UserFederationMapper, Notifications, Dialog, $location) {
|
||||||
|
console.log('UserFederationMapperCtrl');
|
||||||
|
$scope.realm = realm;
|
||||||
|
$scope.provider = provider;
|
||||||
|
$scope.create = false;
|
||||||
|
$scope.mapper = angular.copy(mapper);
|
||||||
|
$scope.changed = false;
|
||||||
|
$scope.mapperType = mapperTypes[mapper.federationMapperType];
|
||||||
|
|
||||||
|
$scope.$watch('mapper', function() {
|
||||||
|
if (!angular.equals($scope.mapper, mapper)) {
|
||||||
|
$scope.changed = true;
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
$scope.save = function() {
|
||||||
|
UserFederationMapper.update({
|
||||||
|
realm : realm.realm,
|
||||||
|
provider: provider.id,
|
||||||
|
mapperId : mapper.id
|
||||||
|
}, $scope.mapper, function() {
|
||||||
|
$scope.changed = false;
|
||||||
|
mapper = angular.copy($scope.mapper);
|
||||||
|
$location.url("/realms/" + realm.realm + '/user-federation/providers/' + provider.providerName + '/' + provider.id + '/mappers/' + mapper.id);
|
||||||
|
Notifications.success("Your changes have been saved.");
|
||||||
|
}, function(error) {
|
||||||
|
if (error.status == 400) {
|
||||||
|
Notifications.error('Error in configuration of mapper: ' + error.data.error_description);
|
||||||
|
} else {
|
||||||
|
Notification.error('Unexpected error when creating mapper');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.reset = function() {
|
||||||
|
$scope.mapper = angular.copy(mapper);
|
||||||
|
$scope.changed = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.cancel = function() {
|
||||||
|
window.history.back();
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.remove = function() {
|
||||||
|
Dialog.confirmDelete($scope.mapper.name, 'mapper', function() {
|
||||||
|
UserFederationMapper.remove({ realm: realm.realm, provider: provider.id, mapperId : $scope.mapper.id }, function() {
|
||||||
|
Notifications.success("The mapper has been deleted.");
|
||||||
|
$location.url("/realms/" + realm.realm + '/user-federation/providers/' + provider.providerName + '/' + provider.id + '/mappers');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
module.controller('UserFederationMapperCreateCtrl', function($scope, realm, provider, mapperTypes, UserFederationMapper, Notifications, Dialog, $location) {
|
||||||
|
console.log('UserFederationMapperCreateCtrl');
|
||||||
|
$scope.realm = realm;
|
||||||
|
$scope.provider = provider;
|
||||||
|
$scope.create = true;
|
||||||
|
$scope.mapper = { federationProviderDisplayName: provider.displayName, config: {}};
|
||||||
|
$scope.mapperTypes = mapperTypes;
|
||||||
|
$scope.mapperType = null;
|
||||||
|
|
||||||
|
$scope.$watch('mapperType', function() {
|
||||||
|
if ($scope.mapperType != null) {
|
||||||
|
$scope.mapper.config = {};
|
||||||
|
for ( var i = 0; i < $scope.mapperType.properties.length; i++) {
|
||||||
|
var property = $scope.mapperType.properties[i];
|
||||||
|
if (property.type === 'String' || property.type === 'boolean') {
|
||||||
|
$scope.mapper.config[ property.name ] = property.defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
$scope.save = function() {
|
||||||
|
if ($scope.mapperType == null) {
|
||||||
|
Notifications.error("You need to select mapper type!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.mapper.federationMapperType = $scope.mapperType.id;
|
||||||
|
UserFederationMapper.save({
|
||||||
|
realm : realm.realm, provider: provider.id
|
||||||
|
}, $scope.mapper, function(data, headers) {
|
||||||
|
var l = headers().location;
|
||||||
|
var id = l.substring(l.lastIndexOf("/") + 1);
|
||||||
|
$location.url('/realms/' + realm.realm +'/user-federation/providers/' + provider.providerName + '/' + provider.id + '/mappers/' + id);
|
||||||
|
Notifications.success("Mapper has been created.");
|
||||||
|
}, function(error) {
|
||||||
|
if (error.status == 400) {
|
||||||
|
Notifications.error('Error in configuration of mapper: ' + error.data.error_description);
|
||||||
|
} else {
|
||||||
|
Notification.error('Unexpected error when creating mapper');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.cancel = function() {
|
||||||
|
window.history.back();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
|
@ -116,6 +116,34 @@ module.factory('UserFederationFactoryLoader', function(Loader, UserFederationPro
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.factory('UserFederationMapperTypesLoader', function(Loader, UserFederationMapperTypes, $route, $q) {
|
||||||
|
return Loader.get(UserFederationMapperTypes, function () {
|
||||||
|
return {
|
||||||
|
realm: $route.current.params.realm,
|
||||||
|
provider: $route.current.params.instance
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.factory('UserFederationMappersLoader', function(Loader, UserFederationMappers, $route, $q) {
|
||||||
|
return Loader.query(UserFederationMappers, function () {
|
||||||
|
return {
|
||||||
|
realm: $route.current.params.realm,
|
||||||
|
provider: $route.current.params.instance
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.factory('UserFederationMapperLoader', function(Loader, UserFederationMapper, $route, $q) {
|
||||||
|
return Loader.get(UserFederationMapper, function () {
|
||||||
|
return {
|
||||||
|
realm: $route.current.params.realm,
|
||||||
|
provider: $route.current.params.instance,
|
||||||
|
mapperId: $route.current.params.mapperId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
module.factory('UserSessionStatsLoader', function(Loader, UserSessionStats, $route, $q) {
|
module.factory('UserSessionStatsLoader', function(Loader, UserSessionStats, $route, $q) {
|
||||||
return Loader.get(UserSessionStats, function() {
|
return Loader.get(UserSessionStats, function() {
|
||||||
|
|
|
@ -238,7 +238,33 @@ module.factory('UserFederationProviders', function($resource) {
|
||||||
});
|
});
|
||||||
|
|
||||||
module.factory('UserFederationSync', function($resource) {
|
module.factory('UserFederationSync', function($resource) {
|
||||||
return $resource(authUrl + '/admin/realms/:realm/user-federation/sync/:provider');
|
return $resource(authUrl + '/admin/realms/:realm/user-federation/instances/:provider/sync');
|
||||||
|
});
|
||||||
|
|
||||||
|
module.factory('UserFederationMapperTypes', function($resource) {
|
||||||
|
return $resource(authUrl + '/admin/realms/:realm/user-federation/instances/:provider/mapper-types', {
|
||||||
|
realm : '@realm',
|
||||||
|
provider : '@provider'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.factory('UserFederationMappers', function($resource) {
|
||||||
|
return $resource(authUrl + '/admin/realms/:realm/user-federation/instances/:provider/mappers', {
|
||||||
|
realm : '@realm',
|
||||||
|
provider : '@provider'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.factory('UserFederationMapper', function($resource) {
|
||||||
|
return $resource(authUrl + '/admin/realms/:realm/user-federation/instances/:provider/mappers/:mapperId', {
|
||||||
|
realm : '@realm',
|
||||||
|
provider : '@provider',
|
||||||
|
mapperId: '@mapperId'
|
||||||
|
}, {
|
||||||
|
update: {
|
||||||
|
method : 'PUT'
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,13 @@
|
||||||
<li data-ng-show="create">Add User Federation Provider</li>
|
<li data-ng-show="create">Add User Federation Provider</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h1 data-ng-hide="create"><strong>User Federation Provider</strong> {{instance.displayName|capitalize}}</h1>
|
<h1 data-ng-hide="create"><strong>{{instance.providerName|capitalize}} User Federation Provider</strong> {{instance.displayName|capitalize}}</h1>
|
||||||
<h1 data-ng-show="create"><strong>Add User Federation Provider</strong></h1>
|
<h1 data-ng-show="create"><strong>Add {{instance.providerName|capitalize}} User Federation Provider</strong></h1>
|
||||||
|
|
||||||
|
<ul class="nav nav-tabs" data-ng-hide="create">
|
||||||
|
<li class="active"><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">Settings</a></li>
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}/mappers">Mappers</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
|
|
|
@ -8,6 +8,11 @@
|
||||||
<h1 data-ng-hide="create"><strong>Kerberos User Federation Provider</strong> {{instance.displayName|capitalize}}</h1>
|
<h1 data-ng-hide="create"><strong>Kerberos User Federation Provider</strong> {{instance.displayName|capitalize}}</h1>
|
||||||
<h1 data-ng-show="create"><strong>Add Kerberos User Federation Provider</strong></h1>
|
<h1 data-ng-show="create"><strong>Add Kerberos User Federation Provider</strong></h1>
|
||||||
|
|
||||||
|
<ul class="nav nav-tabs" data-ng-hide="create">
|
||||||
|
<li class="active"><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">Settings</a></li>
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}/mappers">Mappers</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend><span class="text">Required Settings</span></legend>
|
<legend><span class="text">Required Settings</span></legend>
|
||||||
|
|
|
@ -8,6 +8,11 @@
|
||||||
<h1 data-ng-hide="create"><strong>LDAP User Federation Provider</strong> {{instance.displayName|capitalize}}</h1>
|
<h1 data-ng-hide="create"><strong>LDAP User Federation Provider</strong> {{instance.displayName|capitalize}}</h1>
|
||||||
<h1 data-ng-show="create"><strong>Add LDAP User Federation Provider</strong></h1>
|
<h1 data-ng-show="create"><strong>Add LDAP User Federation Provider</strong></h1>
|
||||||
|
|
||||||
|
<ul class="nav nav-tabs" data-ng-hide="create">
|
||||||
|
<li class="active"><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">Settings</a></li>
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}/mappers">Mappers</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/user-federation">User Federation</a></li>
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{provider.providerName}}/{{provider.id}}">{{provider.displayName|capitalize}}</a></li>
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{provider.providerName}}/{{provider.id}}/mappers">User Federation Mappers</a></li>
|
||||||
|
<li class="active" data-ng-show="create">Create User Federation Mapper</li>
|
||||||
|
<li class="active" data-ng-hide="create">{{mapper.name}}</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h1 data-ng-hide="create"><strong>User Federation Mapper</strong> {{mapper.name}}</h1>
|
||||||
|
<h1 data-ng-show="create"><strong>Add User Federation Mapper</strong></h1>
|
||||||
|
|
||||||
|
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
||||||
|
<fieldset>
|
||||||
|
<div class="form-group clearfix" data-ng-show="!create">
|
||||||
|
<label class="col-md-2 control-label" for="mapperId">ID </label>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input class="form-control" id="mapperId" type="text" ng-model="mapper.id" readonly>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group clearfix">
|
||||||
|
<label class="col-md-2 control-label" for="name">Name <span class="required">*</span></label>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input class="form-control" id="name" type="text" ng-model="mapper.name" data-ng-readonly="!create" required>
|
||||||
|
</div>
|
||||||
|
<kc-tooltip>Name of the mapper.</kc-tooltip>
|
||||||
|
</div>
|
||||||
|
<div class="form-group" data-ng-show="create">
|
||||||
|
<label class="col-md-2 control-label" for="mapperTypeCreate">Mapper Type</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<div>
|
||||||
|
<select class="form-control" id="mapperTypeCreate"
|
||||||
|
ng-model="mapperType"
|
||||||
|
ng-options="mapperType.name for (mapperKey, mapperType) in mapperTypes">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<kc-tooltip>{{mapperType.helpText}}</kc-tooltip>
|
||||||
|
</div>
|
||||||
|
<div class="form-group clearfix" data-ng-hide="create">
|
||||||
|
<label class="col-md-2 control-label" for="mapperType">Mapper Type</label>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input class="form-control" id="mapperType" type="text" ng-model="mapperType.name" data-ng-readonly="true">
|
||||||
|
</div>
|
||||||
|
<kc-tooltip>{{mapperType.helpText}}</kc-tooltip>
|
||||||
|
</div>
|
||||||
|
<div data-ng-repeat="option in mapperType.properties" class="form-group">
|
||||||
|
<label class="col-md-2 control-label">{{option.label}}</label>
|
||||||
|
|
||||||
|
<div class="col-sm-4" data-ng-hide="option.type == 'boolean' || option.type == 'List'">
|
||||||
|
<input class="form-control" type="text" data-ng-model="mapper.config[ option.name ]">
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4" data-ng-show="option.type == 'boolean'">
|
||||||
|
<input ng-model="mapper.config[ option.name ]" value="'true'" name="option.name" id="option.name" onoffswitchmodel />
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4" data-ng-show="option.type == 'List'">
|
||||||
|
<select ng-model="mapper.config[ option.name ]" ng-options="data for data in option.defaultValue">
|
||||||
|
<option value="" selected> Select one... </option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<kc-tooltip>{{option.helpText}}</kc-tooltip>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
<div class="pull-right form-actions" data-ng-show="create && access.manageRealm">
|
||||||
|
<button kc-cancel data-ng-click="cancel()">Cancel</button>
|
||||||
|
<button kc-save>Save</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pull-right form-actions" data-ng-show="!create && access.manageRealm">
|
||||||
|
<button kc-reset data-ng-show="changed">Clear changes</button>
|
||||||
|
<button kc-save data-ng-show="changed">Save</button>
|
||||||
|
<button kc-delete data-ng-click="remove()" data-ng-hide="changed">Delete</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<kc-menu></kc-menu>
|
|
@ -0,0 +1,53 @@
|
||||||
|
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/user-federation">User Federation</a></li>
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{provider.providerName}}/{{provider.id}}">{{provider.displayName|capitalize}}</a></li>
|
||||||
|
<li>User Federation Mappers</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h1><strong>{{provider.providerName === 'ldap' ? 'LDAP' : (provider.providerName|capitalize)}} User Federation Provider</strong> {{provider.displayName|capitalize}}</h1>
|
||||||
|
|
||||||
|
<ul class="nav nav-tabs" data-ng-hide="create">
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{provider.providerName}}/{{provider.id}}">Settings</a></li>
|
||||||
|
<li class="active"><a href="#/realms/{{realm.realm}}/user-federation/providers/{{provider.providerName}}/{{provider.id}}/mappers">Mappers</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="kc-table-actions" colspan="4">
|
||||||
|
<div class="form-inline">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" placeholder="Search..." data-ng-model="search.name" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
|
||||||
|
<div class="input-group-addon">
|
||||||
|
<i class="fa fa-search" type="submit"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pull-right" data-ng-show="hasAnyMapperTypes">
|
||||||
|
<a class="btn btn-primary" href="#/create/user-federation-mappers/{{realm.realm}}/{{provider.providerName}}/{{provider.id}}">Create</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
<tr data-ng-hide="mappers.length == 0">
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Category</th>
|
||||||
|
<th>Type</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="mapper in mappers | filter:search">
|
||||||
|
<td><a href="#/realms/{{realm.realm}}/user-federation/providers/{{provider.providerName}}/{{provider.id}}/mappers/{{mapper.id}}">{{mapper.name}}</a></td>
|
||||||
|
<td>{{mapperTypes[mapper.federationMapperType].category}}</td>
|
||||||
|
<td>{{mapperTypes[mapper.federationMapperType].name}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr data-ng-show="mappers.length == 0">
|
||||||
|
<td>No mappers available</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<kc-menu></kc-menu>
|
|
@ -0,0 +1,15 @@
|
||||||
|
package org.keycloak.mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
public class MapperConfigValidationException extends Exception {
|
||||||
|
|
||||||
|
public MapperConfigValidationException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapperConfigValidationException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,36 @@
|
||||||
package org.keycloak.mappers;
|
package org.keycloak.mappers;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserFederationMapperModel;
|
||||||
import org.keycloak.provider.ConfiguredProvider;
|
import org.keycloak.provider.ConfiguredProvider;
|
||||||
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
import org.keycloak.provider.ProviderFactory;
|
import org.keycloak.provider.ProviderFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
public interface UserFederationMapperFactory extends ProviderFactory<UserFederationMapper>, ConfiguredProvider {
|
public interface UserFederationMapperFactory extends ProviderFactory<UserFederationMapper>, ConfiguredProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refers to providerName (type) of the federation provider, which this mapper can be used for. For example "ldap" or "kerberos"
|
||||||
|
*
|
||||||
|
* @return providerName
|
||||||
|
*/
|
||||||
|
String getFederationProviderType();
|
||||||
|
|
||||||
|
String getDisplayCategory();
|
||||||
|
String getDisplayType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when instance of mapperModel is created for this factory through admin endpoint
|
||||||
|
*
|
||||||
|
* @param mapperModel
|
||||||
|
* @throws MapperConfigValidationException if configuration provided in mapperModel is not valid
|
||||||
|
*/
|
||||||
|
void validateConfig(UserFederationMapperModel mapperModel) throws MapperConfigValidationException;
|
||||||
|
|
||||||
|
// TODO: Remove this and add realm to the method on ConfiguredProvider?
|
||||||
|
List<ProviderConfigProperty> getConfigProperties(RealmModel realm);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.keycloak.models.utils;
|
package org.keycloak.models.utils;
|
||||||
|
|
||||||
import org.bouncycastle.openssl.PEMWriter;
|
import org.bouncycastle.openssl.PEMWriter;
|
||||||
|
import org.keycloak.constants.KerberosConstants;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
@ -8,6 +9,7 @@ import org.keycloak.models.KeycloakSessionTask;
|
||||||
import org.keycloak.models.KeycloakTransaction;
|
import org.keycloak.models.KeycloakTransaction;
|
||||||
import org.keycloak.models.ModelDuplicateException;
|
import org.keycloak.models.ModelDuplicateException;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.RequiredCredentialModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserFederationMapperModel;
|
import org.keycloak.models.UserFederationMapperModel;
|
||||||
|
@ -349,4 +351,31 @@ public final class KeycloakModelUtils {
|
||||||
|
|
||||||
return mapperModel;
|
return mapperModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatically add "kerberos" to required realm credentials if it's supported by saved provider
|
||||||
|
*
|
||||||
|
* @param realm
|
||||||
|
* @param model
|
||||||
|
* @return true if kerberos credentials were added
|
||||||
|
*/
|
||||||
|
public static boolean checkKerberosCredential(RealmModel realm, UserFederationProviderModel model) {
|
||||||
|
String allowKerberosCfg = model.getConfig().get(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION);
|
||||||
|
if (Boolean.valueOf(allowKerberosCfg)) {
|
||||||
|
boolean found = false;
|
||||||
|
List<RequiredCredentialModel> currentCreds = realm.getRequiredCredentials();
|
||||||
|
for (RequiredCredentialModel cred : currentCreds) {
|
||||||
|
if (cred.getType().equals(UserCredentialModel.KERBEROS)) {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
realm.addRequiredCredential(UserCredentialModel.KERBEROS);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -235,8 +235,8 @@ public class RealmAdminResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("user-federation")
|
@Path("user-federation")
|
||||||
public UserFederationResource userFederation() {
|
public UserFederationProvidersResource userFederation() {
|
||||||
UserFederationResource fed = new UserFederationResource(realm, auth, adminEvent);
|
UserFederationProvidersResource fed = new UserFederationProvidersResource(realm, auth, adminEvent);
|
||||||
ResteasyProviderFactory.getInstance().injectProperties(fed);
|
ResteasyProviderFactory.getInstance().injectProperties(fed);
|
||||||
//resourceContext.initResource(fed);
|
//resourceContext.initResource(fed);
|
||||||
return fed;
|
return fed;
|
||||||
|
|
|
@ -0,0 +1,298 @@
|
||||||
|
package org.keycloak.services.resources.admin;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.DELETE;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.PUT;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||||
|
import org.jboss.resteasy.spi.NotFoundException;
|
||||||
|
import org.keycloak.events.admin.OperationType;
|
||||||
|
import org.keycloak.mappers.MapperConfigValidationException;
|
||||||
|
import org.keycloak.mappers.UserFederationMapper;
|
||||||
|
import org.keycloak.mappers.UserFederationMapperFactory;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserFederationMapperModel;
|
||||||
|
import org.keycloak.models.UserFederationProviderModel;
|
||||||
|
import org.keycloak.models.UserFederationSyncResult;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
|
import org.keycloak.provider.ProviderFactory;
|
||||||
|
import org.keycloak.representations.idm.ConfigPropertyRepresentation;
|
||||||
|
import org.keycloak.representations.idm.UserFederationMapperRepresentation;
|
||||||
|
import org.keycloak.representations.idm.UserFederationMapperTypeRepresentation;
|
||||||
|
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
|
||||||
|
import org.keycloak.services.ErrorResponseException;
|
||||||
|
import org.keycloak.services.managers.UsersSyncManager;
|
||||||
|
import org.keycloak.timer.TimerProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
public class UserFederationProviderResource {
|
||||||
|
|
||||||
|
protected static final Logger logger = Logger.getLogger(UserFederationProviderResource.class);
|
||||||
|
|
||||||
|
private final KeycloakSession session;
|
||||||
|
private final RealmModel realm;
|
||||||
|
private final RealmAuth auth;
|
||||||
|
private final UserFederationProviderModel federationProviderModel;
|
||||||
|
private final AdminEventBuilder adminEvent;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
private UriInfo uriInfo;
|
||||||
|
|
||||||
|
public UserFederationProviderResource(KeycloakSession session, RealmModel realm, RealmAuth auth, UserFederationProviderModel federationProviderModel, AdminEventBuilder adminEvent) {
|
||||||
|
this.session = session;
|
||||||
|
this.realm = realm;
|
||||||
|
this.auth = auth;
|
||||||
|
this.federationProviderModel = federationProviderModel;
|
||||||
|
this.adminEvent = adminEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a provider
|
||||||
|
*
|
||||||
|
* @param rep
|
||||||
|
*/
|
||||||
|
@PUT
|
||||||
|
@NoCache
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
public void updateProviderInstance(UserFederationProviderRepresentation rep) {
|
||||||
|
auth.requireManage();
|
||||||
|
String displayName = rep.getDisplayName();
|
||||||
|
if (displayName != null && displayName.trim().equals("")) {
|
||||||
|
displayName = null;
|
||||||
|
}
|
||||||
|
UserFederationProviderModel model = new UserFederationProviderModel(rep.getId(), rep.getProviderName(), rep.getConfig(), rep.getPriority(), displayName,
|
||||||
|
rep.getFullSyncPeriod(), rep.getChangedSyncPeriod(), rep.getLastSync());
|
||||||
|
realm.updateUserFederationProvider(model);
|
||||||
|
new UsersSyncManager().refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), model, realm.getId());
|
||||||
|
boolean kerberosCredsAdded = KeycloakModelUtils.checkKerberosCredential(realm, model);
|
||||||
|
if (kerberosCredsAdded) {
|
||||||
|
logger.info("Added 'kerberos' to required realm credentials");
|
||||||
|
}
|
||||||
|
|
||||||
|
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(rep).success();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get a provider
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@NoCache
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public UserFederationProviderRepresentation getProviderInstance() {
|
||||||
|
auth.requireView();
|
||||||
|
return ModelToRepresentation.toRepresentation(this.federationProviderModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a provider
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@DELETE
|
||||||
|
@NoCache
|
||||||
|
public void deleteProviderInstance() {
|
||||||
|
auth.requireManage();
|
||||||
|
|
||||||
|
realm.removeUserFederationProvider(this.federationProviderModel);
|
||||||
|
new UsersSyncManager().removePeriodicSyncForProvider(session.getProvider(TimerProvider.class), this.federationProviderModel);
|
||||||
|
|
||||||
|
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* trigger sync of users
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("sync")
|
||||||
|
@NoCache
|
||||||
|
public UserFederationSyncResult syncUsers(@QueryParam("action") String action) {
|
||||||
|
logger.debug("Syncing users");
|
||||||
|
auth.requireManage();
|
||||||
|
|
||||||
|
UsersSyncManager syncManager = new UsersSyncManager();
|
||||||
|
UserFederationSyncResult syncResult = null;
|
||||||
|
if ("triggerFullSync".equals(action)) {
|
||||||
|
syncResult = syncManager.syncAllUsers(session.getKeycloakSessionFactory(), realm.getId(), this.federationProviderModel);
|
||||||
|
} else if ("triggerChangedUsersSync".equals(action)) {
|
||||||
|
syncResult = syncManager.syncChangedUsers(session.getKeycloakSessionFactory(), realm.getId(), this.federationProviderModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
|
||||||
|
return syncResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of available User Federation mapper types
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("mapper-types")
|
||||||
|
@NoCache
|
||||||
|
public Map<String, UserFederationMapperTypeRepresentation> getMapperTypes() {
|
||||||
|
this.auth.requireView();
|
||||||
|
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
|
||||||
|
Map<String, UserFederationMapperTypeRepresentation> types = new HashMap<>();
|
||||||
|
List<ProviderFactory> factories = sessionFactory.getProviderFactories(UserFederationMapper.class);
|
||||||
|
|
||||||
|
for (ProviderFactory factory : factories) {
|
||||||
|
UserFederationMapperFactory mapperFactory = (UserFederationMapperFactory)factory;
|
||||||
|
if (mapperFactory.getFederationProviderType().equals(this.federationProviderModel.getProviderName())) {
|
||||||
|
|
||||||
|
UserFederationMapperTypeRepresentation rep = new UserFederationMapperTypeRepresentation();
|
||||||
|
rep.setId(mapperFactory.getId());
|
||||||
|
rep.setCategory(mapperFactory.getDisplayCategory());
|
||||||
|
rep.setName(mapperFactory.getDisplayType());
|
||||||
|
rep.setHelpText(mapperFactory.getHelpText());
|
||||||
|
List<ProviderConfigProperty> configProperties = mapperFactory.getConfigProperties(realm);
|
||||||
|
for (ProviderConfigProperty prop : configProperties) {
|
||||||
|
ConfigPropertyRepresentation propRep = new ConfigPropertyRepresentation();
|
||||||
|
propRep.setName(prop.getName());
|
||||||
|
propRep.setLabel(prop.getLabel());
|
||||||
|
propRep.setType(prop.getType());
|
||||||
|
propRep.setDefaultValue(prop.getDefaultValue());
|
||||||
|
propRep.setHelpText(prop.getHelpText());
|
||||||
|
rep.getProperties().add(propRep);
|
||||||
|
}
|
||||||
|
types.put(rep.getId(), rep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get mappers configured for this provider
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("mappers")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@NoCache
|
||||||
|
public List<UserFederationMapperRepresentation> getMappers() {
|
||||||
|
this.auth.requireView();
|
||||||
|
List<UserFederationMapperRepresentation> mappers = new LinkedList<>();
|
||||||
|
for (UserFederationMapperModel model : realm.getUserFederationMappersByFederationProvider(this.federationProviderModel.getId())) {
|
||||||
|
mappers.add(ModelToRepresentation.toRepresentation(realm, model));
|
||||||
|
}
|
||||||
|
return mappers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create mapper
|
||||||
|
*
|
||||||
|
* @param mapper
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("mappers")
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
public Response addMapper(UserFederationMapperRepresentation mapper) {
|
||||||
|
auth.requireManage();
|
||||||
|
UserFederationMapperModel model = RepresentationToModel.toModel(realm, mapper);
|
||||||
|
|
||||||
|
validateModel(model);
|
||||||
|
|
||||||
|
model = realm.addUserFederationMapper(model);
|
||||||
|
|
||||||
|
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, model.getId())
|
||||||
|
.representation(mapper).success();
|
||||||
|
|
||||||
|
return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get mapper
|
||||||
|
*
|
||||||
|
* @param id mapperId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@NoCache
|
||||||
|
@Path("mappers/{id}")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public UserFederationMapperRepresentation getMapperById(@PathParam("id") String id) {
|
||||||
|
auth.requireView();
|
||||||
|
UserFederationMapperModel model = realm.getUserFederationMapperById(id);
|
||||||
|
if (model == null) throw new NotFoundException("Model not found");
|
||||||
|
return ModelToRepresentation.toRepresentation(realm, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update mapper
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @param rep
|
||||||
|
*/
|
||||||
|
@PUT
|
||||||
|
@NoCache
|
||||||
|
@Path("mappers/{id}")
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
public void update(@PathParam("id") String id, UserFederationMapperRepresentation rep) {
|
||||||
|
auth.requireManage();
|
||||||
|
UserFederationMapperModel model = realm.getUserFederationMapperById(id);
|
||||||
|
if (model == null) throw new NotFoundException("Model not found");
|
||||||
|
model = RepresentationToModel.toModel(realm, rep);
|
||||||
|
|
||||||
|
validateModel(model);
|
||||||
|
|
||||||
|
realm.updateUserFederationMapper(model);
|
||||||
|
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(rep).success();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete mapper with given ID
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
@DELETE
|
||||||
|
@NoCache
|
||||||
|
@Path("mappers/{id}")
|
||||||
|
public void delete(@PathParam("id") String id) {
|
||||||
|
auth.requireManage();
|
||||||
|
UserFederationMapperModel model = realm.getUserFederationMapperById(id);
|
||||||
|
if (model == null) throw new NotFoundException("Model not found");
|
||||||
|
realm.removeUserFederationMapper(model);
|
||||||
|
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateModel(UserFederationMapperModel model) {
|
||||||
|
try {
|
||||||
|
UserFederationMapperFactory mapperFactory = (UserFederationMapperFactory) session.getKeycloakSessionFactory().getProviderFactory(UserFederationMapper.class, model.getFederationMapperType());
|
||||||
|
mapperFactory.validateConfig(model);
|
||||||
|
} catch (MapperConfigValidationException ex) {
|
||||||
|
throw new ErrorResponseException("Validation error", ex.getMessage(), Response.Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,16 +3,14 @@ package org.keycloak.services.resources.admin;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||||
import org.jboss.resteasy.spi.NotFoundException;
|
import org.jboss.resteasy.spi.NotFoundException;
|
||||||
import org.keycloak.constants.KerberosConstants;
|
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||||
import org.keycloak.events.admin.OperationType;
|
import org.keycloak.events.admin.OperationType;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RequiredCredentialModel;
|
|
||||||
import org.keycloak.models.UserCredentialModel;
|
|
||||||
import org.keycloak.models.UserFederationProvider;
|
import org.keycloak.models.UserFederationProvider;
|
||||||
import org.keycloak.models.UserFederationProviderFactory;
|
import org.keycloak.models.UserFederationProviderFactory;
|
||||||
import org.keycloak.models.UserFederationProviderModel;
|
import org.keycloak.models.UserFederationProviderModel;
|
||||||
import org.keycloak.models.UserFederationSyncResult;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
import org.keycloak.provider.ProviderFactory;
|
import org.keycloak.provider.ProviderFactory;
|
||||||
import org.keycloak.representations.idm.UserFederationProviderFactoryRepresentation;
|
import org.keycloak.representations.idm.UserFederationProviderFactoryRepresentation;
|
||||||
|
@ -21,14 +19,11 @@ import org.keycloak.services.managers.UsersSyncManager;
|
||||||
import org.keycloak.timer.TimerProvider;
|
import org.keycloak.timer.TimerProvider;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.DELETE;
|
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.POST;
|
import javax.ws.rs.POST;
|
||||||
import javax.ws.rs.PUT;
|
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.QueryParam;
|
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
@ -43,8 +38,8 @@ import java.util.List;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class UserFederationResource {
|
public class UserFederationProvidersResource {
|
||||||
protected static final Logger logger = Logger.getLogger(UserFederationResource.class);
|
protected static final Logger logger = Logger.getLogger(UserFederationProvidersResource.class);
|
||||||
|
|
||||||
protected RealmModel realm;
|
protected RealmModel realm;
|
||||||
|
|
||||||
|
@ -58,7 +53,7 @@ public class UserFederationResource {
|
||||||
@Context
|
@Context
|
||||||
protected KeycloakSession session;
|
protected KeycloakSession session;
|
||||||
|
|
||||||
public UserFederationResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
|
public UserFederationProvidersResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
|
||||||
this.auth = auth;
|
this.auth = auth;
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
this.adminEvent = adminEvent;
|
this.adminEvent = adminEvent;
|
||||||
|
@ -88,7 +83,7 @@ public class UserFederationResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get List of available provider factories
|
* Get factory with given ID
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
@ -130,77 +125,17 @@ public class UserFederationResource {
|
||||||
UserFederationProviderModel model = realm.addUserFederationProvider(rep.getProviderName(), rep.getConfig(), rep.getPriority(), displayName,
|
UserFederationProviderModel model = realm.addUserFederationProvider(rep.getProviderName(), rep.getConfig(), rep.getPriority(), displayName,
|
||||||
rep.getFullSyncPeriod(), rep.getChangedSyncPeriod(), rep.getLastSync());
|
rep.getFullSyncPeriod(), rep.getChangedSyncPeriod(), rep.getLastSync());
|
||||||
new UsersSyncManager().refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), model, realm.getId());
|
new UsersSyncManager().refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), model, realm.getId());
|
||||||
checkKerberosCredential(model);
|
boolean kerberosCredsAdded = KeycloakModelUtils.checkKerberosCredential(realm, model);
|
||||||
|
if (kerberosCredsAdded) {
|
||||||
|
logger.info("Added 'kerberos' to required realm credentials");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo).representation(rep).success();
|
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo).representation(rep).success();
|
||||||
|
|
||||||
return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
|
return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update a provider
|
|
||||||
*
|
|
||||||
* @param id
|
|
||||||
* @param rep
|
|
||||||
*/
|
|
||||||
@PUT
|
|
||||||
@Path("instances/{id}")
|
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
|
||||||
public void updateProviderInstance(@PathParam("id") String id, UserFederationProviderRepresentation rep) {
|
|
||||||
auth.requireManage();
|
|
||||||
String displayName = rep.getDisplayName();
|
|
||||||
if (displayName != null && displayName.trim().equals("")) {
|
|
||||||
displayName = null;
|
|
||||||
}
|
|
||||||
UserFederationProviderModel model = new UserFederationProviderModel(id, rep.getProviderName(), rep.getConfig(), rep.getPriority(), displayName,
|
|
||||||
rep.getFullSyncPeriod(), rep.getChangedSyncPeriod(), rep.getLastSync());
|
|
||||||
realm.updateUserFederationProvider(model);
|
|
||||||
new UsersSyncManager().refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), model, realm.getId());
|
|
||||||
checkKerberosCredential(model);
|
|
||||||
|
|
||||||
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(rep).success();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get a provider
|
|
||||||
*
|
|
||||||
* @param id
|
|
||||||
*/
|
|
||||||
@GET
|
|
||||||
@NoCache
|
|
||||||
@Path("instances/{id}")
|
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
|
||||||
public UserFederationProviderRepresentation getProviderInstance(@PathParam("id") String id) {
|
|
||||||
auth.requireView();
|
|
||||||
for (UserFederationProviderModel model : realm.getUserFederationProviders()) {
|
|
||||||
if (model.getId().equals(id)) {
|
|
||||||
return ModelToRepresentation.toRepresentation(model);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new NotFoundException("could not find provider");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a provider
|
|
||||||
*
|
|
||||||
* @param id
|
|
||||||
*/
|
|
||||||
@DELETE
|
|
||||||
@Path("instances/{id}")
|
|
||||||
public void deleteProviderInstance(@PathParam("id") String id) {
|
|
||||||
auth.requireManage();
|
|
||||||
|
|
||||||
UserFederationProviderRepresentation rep = getProviderInstance(id);
|
|
||||||
UserFederationProviderModel model = new UserFederationProviderModel(id, null, null, -1, null, -1, -1, 0);
|
|
||||||
realm.removeUserFederationProvider(model);
|
|
||||||
new UsersSyncManager().removePeriodicSyncForProvider(session.getProvider(TimerProvider.class), model);
|
|
||||||
|
|
||||||
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* list configured providers
|
* list configured providers
|
||||||
*
|
*
|
||||||
|
@ -220,53 +155,18 @@ public class UserFederationResource {
|
||||||
return reps;
|
return reps;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Path("instances/{id}")
|
||||||
* trigger sync of users
|
public UserFederationProviderResource getUserFederationInstance(@PathParam("id") String id) {
|
||||||
*
|
this.auth.requireView();
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@POST
|
|
||||||
@Path("sync/{id}")
|
|
||||||
@NoCache
|
|
||||||
public UserFederationSyncResult syncUsers(@PathParam("id") String providerId, @QueryParam("action") String action) {
|
|
||||||
logger.debug("Syncing users");
|
|
||||||
auth.requireManage();
|
|
||||||
|
|
||||||
for (UserFederationProviderModel model : realm.getUserFederationProviders()) {
|
UserFederationProviderModel model = KeycloakModelUtils.findUserFederationProviderById(id, realm);
|
||||||
if (model.getId().equals(providerId)) {
|
if (model == null) {
|
||||||
UsersSyncManager syncManager = new UsersSyncManager();
|
throw new NotFoundException("Could not find federation provider with id: " + id);
|
||||||
UserFederationSyncResult syncResult = null;
|
|
||||||
if ("triggerFullSync".equals(action)) {
|
|
||||||
syncResult = syncManager.syncAllUsers(session.getKeycloakSessionFactory(), realm.getId(), model);
|
|
||||||
} else if ("triggerChangedUsersSync".equals(action)) {
|
|
||||||
syncResult = syncManager.syncChangedUsers(session.getKeycloakSessionFactory(), realm.getId(), model);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
|
UserFederationProviderResource instanceResource = new UserFederationProviderResource(session, realm, this.auth, model, adminEvent);
|
||||||
return syncResult;
|
ResteasyProviderFactory.getInstance().injectProperties(instanceResource);
|
||||||
}
|
return instanceResource;
|
||||||
}
|
|
||||||
|
|
||||||
throw new NotFoundException("could not find provider");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Automatically add "kerberos" to required realm credentials if it's supported by saved provider
|
|
||||||
private void checkKerberosCredential(UserFederationProviderModel model) {
|
|
||||||
String allowKerberosCfg = model.getConfig().get(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION);
|
|
||||||
if (Boolean.valueOf(allowKerberosCfg)) {
|
|
||||||
boolean found = false;
|
|
||||||
List<RequiredCredentialModel> currentCreds = realm.getRequiredCredentials();
|
|
||||||
for (RequiredCredentialModel cred : currentCreds) {
|
|
||||||
if (cred.getType().equals(UserCredentialModel.KERBEROS)) {
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
realm.addRequiredCredential(UserCredentialModel.KERBEROS);
|
|
||||||
logger.info("Added 'kerberos' to required realm credentials");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -91,7 +91,7 @@ class FederationTestUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addZipCodeLDAPMapper(RealmModel realm, UserFederationProviderModel providerModel) {
|
public static void addZipCodeLDAPMapper(RealmModel realm, UserFederationProviderModel providerModel) {
|
||||||
UserFederationMapperModel mapperModel = KeycloakModelUtils.createUserFederationMapperModel("zipCodeMapper", providerModel.getId(), UserAttributeLDAPFederationMapperFactory.ID,
|
UserFederationMapperModel mapperModel = KeycloakModelUtils.createUserFederationMapperModel("zipCodeMapper", providerModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
|
||||||
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "postal_code",
|
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "postal_code",
|
||||||
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.POSTAL_CODE,
|
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.POSTAL_CODE,
|
||||||
UserAttributeLDAPFederationMapper.READ_ONLY, "false");
|
UserAttributeLDAPFederationMapper.READ_ONLY, "false");
|
||||||
|
@ -104,7 +104,7 @@ class FederationTestUtils {
|
||||||
mapperModel.getConfig().put(RoleLDAPFederationMapper.MODE, mode.toString());
|
mapperModel.getConfig().put(RoleLDAPFederationMapper.MODE, mode.toString());
|
||||||
realm.updateUserFederationMapper(mapperModel);
|
realm.updateUserFederationMapper(mapperModel);
|
||||||
} else {
|
} else {
|
||||||
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("realmRolesMapper", providerModel.getId(), RoleLDAPFederationMapperFactory.ID,
|
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("realmRolesMapper", providerModel.getId(), RoleLDAPFederationMapperFactory.PROVIDER_ID,
|
||||||
RoleLDAPFederationMapper.ROLES_DN, "ou=RealmRoles,dc=keycloak,dc=org",
|
RoleLDAPFederationMapper.ROLES_DN, "ou=RealmRoles,dc=keycloak,dc=org",
|
||||||
RoleLDAPFederationMapper.USE_REALM_ROLES_MAPPING, "true",
|
RoleLDAPFederationMapper.USE_REALM_ROLES_MAPPING, "true",
|
||||||
RoleLDAPFederationMapper.MODE, mode.toString());
|
RoleLDAPFederationMapper.MODE, mode.toString());
|
||||||
|
@ -116,7 +116,7 @@ class FederationTestUtils {
|
||||||
mapperModel.getConfig().put(RoleLDAPFederationMapper.MODE, mode.toString());
|
mapperModel.getConfig().put(RoleLDAPFederationMapper.MODE, mode.toString());
|
||||||
realm.updateUserFederationMapper(mapperModel);
|
realm.updateUserFederationMapper(mapperModel);
|
||||||
} else {
|
} else {
|
||||||
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("financeRolesMapper", providerModel.getId(), RoleLDAPFederationMapperFactory.ID,
|
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("financeRolesMapper", providerModel.getId(), RoleLDAPFederationMapperFactory.PROVIDER_ID,
|
||||||
RoleLDAPFederationMapper.ROLES_DN, "ou=FinanceRoles,dc=keycloak,dc=org",
|
RoleLDAPFederationMapper.ROLES_DN, "ou=FinanceRoles,dc=keycloak,dc=org",
|
||||||
RoleLDAPFederationMapper.USE_REALM_ROLES_MAPPING, "false",
|
RoleLDAPFederationMapper.USE_REALM_ROLES_MAPPING, "false",
|
||||||
RoleLDAPFederationMapper.CLIENT_ID, "finance",
|
RoleLDAPFederationMapper.CLIENT_ID, "finance",
|
||||||
|
|
|
@ -233,7 +233,7 @@ public class ImportTest extends AbstractModelTest {
|
||||||
Assert.assertTrue(fedMappers1.size() == 1);
|
Assert.assertTrue(fedMappers1.size() == 1);
|
||||||
UserFederationMapperModel fullNameMapper = fedMappers1.iterator().next();
|
UserFederationMapperModel fullNameMapper = fedMappers1.iterator().next();
|
||||||
Assert.assertEquals("FullNameMapper", fullNameMapper.getName());
|
Assert.assertEquals("FullNameMapper", fullNameMapper.getName());
|
||||||
Assert.assertEquals(FullNameLDAPFederationMapperFactory.ID, fullNameMapper.getFederationMapperType());
|
Assert.assertEquals(FullNameLDAPFederationMapperFactory.PROVIDER_ID, fullNameMapper.getFederationMapperType());
|
||||||
Assert.assertEquals(ldap1.getId(), fullNameMapper.getFederationProviderId());
|
Assert.assertEquals(ldap1.getId(), fullNameMapper.getFederationProviderId());
|
||||||
Assert.assertEquals("cn", fullNameMapper.getConfig().get(FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE));
|
Assert.assertEquals("cn", fullNameMapper.getConfig().get(FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE));
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue