KEYCLOAK-11862 Add Sync mode option
- Store in config map in database and model - Expose the field in the OIDC-IDP - Write logic for import, force and legacy mode - Show how mappers can be updated keeping correct legacy mode - Show how mappers that work correctly don't have to be modified - Log an error if sync mode is not supported Fix updateBrokeredUser method for all mappers - Allow updating of username (UsernameTemplateMapper) - Delete UserAttributeStatementMapper: mapper isn't even registered Was actually rejected but never cleaned up: https://github.com/keycloak/keycloak/pull/4513 The mapper won't work as specified and it's not easy to tests here - Fixup json mapper - Fix ExternalKeycloakRoleToRoleMapper: Bug: delete cannot work - just delete it. Don't fix it in legacy mode Rework mapper tests - Fix old tests for Identity Broker: Old tests did not work at all: They tested that if you take a realm and assign the role, this role is then assigned to the user in that realm, which has nothing to do with identity brokering Simplify logic in OidcClaimToRoleMapperTests - Add SyncMode tests to most mappers Added tests for UsernameTemplateMapper Added tests to all RoleMappers Add test for json attribute mapper (Github as example) - Extract common test setup(s) - Extend admin console tests for sync mode Signed-off-by: Martin Idel <external.Martin.Idel@bosch.io>
This commit is contained in:
parent
8f5e58234e
commit
7e8018c7ca
83 changed files with 2279 additions and 573 deletions
|
@ -57,4 +57,14 @@ public abstract class AbstractIdentityProviderMapper implements IdentityProvider
|
|||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUserLegacy(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
updateBrokeredUser(session, realm, user, mapperModel, context);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,6 +161,11 @@ public class BrokeredIdentityContext {
|
|||
getContextData().put(Constants.USER_ATTRIBUTES_PREFIX + attributeName, list);
|
||||
}
|
||||
|
||||
// Remove an attribute attribute, which would otherwise be available on "Update profile" page and in authenticators
|
||||
public void removeUserAttribute(String attributeName) {
|
||||
getContextData().remove(Constants.USER_ATTRIBUTES_PREFIX + attributeName);
|
||||
}
|
||||
|
||||
public void setUserAttribute(String attributeName, List<String> attributeValues) {
|
||||
getContextData().put(Constants.USER_ATTRIBUTES_PREFIX + attributeName, attributeValues);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
@ -25,17 +26,26 @@ import org.keycloak.provider.ConfiguredProvider;
|
|||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface IdentityProviderMapper extends Provider, ProviderFactory<IdentityProviderMapper>,ConfiguredProvider {
|
||||
String ANY_PROVIDER = "*";
|
||||
Set<IdentityProviderSyncMode> DEFAULT_IDENTITY_PROVIDER_MAPPER_SYNC_MODES = new HashSet<>(Arrays.asList(IdentityProviderSyncMode.LEGACY, IdentityProviderSyncMode.IMPORT));
|
||||
|
||||
String[] getCompatibleProviders();
|
||||
String getDisplayCategory();
|
||||
String getDisplayType();
|
||||
|
||||
default boolean supportsSyncMode(IdentityProviderSyncMode syncMode) {
|
||||
return DEFAULT_IDENTITY_PROVIDER_MAPPER_SYNC_MODES.contains(syncMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to determine what keycloak username and email to use to process the login request from the external IDP.
|
||||
* It's called before "FirstBrokerLogin" flow, so can be used to map attributes to BrokeredIdentityContext ( BrokeredIdentityContext.setUserAttribute ),
|
||||
|
@ -60,6 +70,18 @@ public interface IdentityProviderMapper extends Provider, ProviderFactory<Identi
|
|||
*/
|
||||
void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
|
||||
|
||||
/**
|
||||
* Called when this user has logged in before and has already been imported. Legacy behaviour. When updating the mapper to correctly update brokered users
|
||||
* in all sync modes, move the old behavior into this method.
|
||||
*
|
||||
* @param session
|
||||
* @param realm
|
||||
* @param user
|
||||
* @param mapperModel
|
||||
* @param context
|
||||
*/
|
||||
void updateBrokeredUserLegacy(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
|
||||
|
||||
/**
|
||||
* Called when this user has logged in before and has already been imported.
|
||||
*
|
||||
|
@ -70,6 +92,4 @@ public interface IdentityProviderMapper extends Provider, ProviderFactory<Identi
|
|||
* @param context
|
||||
*/
|
||||
void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
||||
public final class IdentityProviderMapperSyncModeDelegate {
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(IdentityProviderMapperSyncModeDelegate.class);
|
||||
|
||||
public static void delegateUpdateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context, IdentityProviderMapper mapper) {
|
||||
IdentityProviderSyncMode effectiveSyncMode = combineIdpAndMapperSyncMode(context.getIdpConfig().getSyncMode(), mapperModel.getSyncMode());
|
||||
|
||||
if (!mapper.supportsSyncMode(effectiveSyncMode)) {
|
||||
logger.warnf("The mapper %s does not explicitly support sync mode %s. Please ensure that the SPI supports the sync mode correctly and update it to reflect this.", mapper.getDisplayType(), effectiveSyncMode);
|
||||
}
|
||||
|
||||
if (effectiveSyncMode == IdentityProviderSyncMode.LEGACY) {
|
||||
mapper.updateBrokeredUserLegacy(session, realm, user, mapperModel, context);
|
||||
} else if (effectiveSyncMode == IdentityProviderSyncMode.FORCE) {
|
||||
mapper.updateBrokeredUser(session, realm, user, mapperModel, context);
|
||||
}
|
||||
}
|
||||
|
||||
public static IdentityProviderSyncMode combineIdpAndMapperSyncMode(IdentityProviderSyncMode syncMode, IdentityProviderMapperSyncMode mapperSyncMode) {
|
||||
return IdentityProviderMapperSyncMode.INHERIT.equals(mapperSyncMode) ? syncMode : IdentityProviderSyncMode.valueOf(mapperSyncMode.toString());
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ import java.util.stream.Collectors;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class IdentityProviderMapperModel implements Serializable {
|
||||
public static final String SYNC_MODE = "syncMode";
|
||||
|
||||
private static final TypeReference<List<StringPair>> MAP_TYPE_REPRESENTATION = new TypeReference<List<StringPair>>() {
|
||||
};
|
||||
|
@ -76,6 +77,14 @@ public class IdentityProviderMapperModel implements Serializable {
|
|||
this.identityProviderMapper = identityProviderMapper;
|
||||
}
|
||||
|
||||
public IdentityProviderMapperSyncMode getSyncMode() {
|
||||
return IdentityProviderMapperSyncMode.valueOf(getConfig().getOrDefault(SYNC_MODE, "LEGACY"));
|
||||
}
|
||||
|
||||
public void setSyncMode(IdentityProviderMapperSyncMode syncMode) {
|
||||
getConfig().put(SYNC_MODE, syncMode.toString());
|
||||
}
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package org.keycloak.models;
|
||||
|
||||
public enum IdentityProviderMapperSyncMode {
|
||||
INHERIT, LEGACY, IMPORT, FORCE
|
||||
}
|
|
@ -30,6 +30,8 @@ public class IdentityProviderModel implements Serializable {
|
|||
|
||||
public static final String ALLOWED_CLOCK_SKEW = "allowedClockSkew";
|
||||
|
||||
public static final String SYNC_MODE = "syncMode";
|
||||
|
||||
private String internalId;
|
||||
|
||||
/**
|
||||
|
@ -64,6 +66,8 @@ public class IdentityProviderModel implements Serializable {
|
|||
|
||||
private String displayName;
|
||||
|
||||
private IdentityProviderSyncMode syncMode;
|
||||
|
||||
/**
|
||||
* <p>A map containing the configuration and properties for a specific identity provider instance and implementation. The items
|
||||
* in the map are understood by the identity provider implementation.</p>
|
||||
|
@ -205,6 +209,13 @@ public class IdentityProviderModel implements Serializable {
|
|||
* @param realm the realm
|
||||
*/
|
||||
public void validate(RealmModel realm) {
|
||||
}
|
||||
|
||||
public IdentityProviderSyncMode getSyncMode() {
|
||||
return IdentityProviderSyncMode.valueOf(getConfig().getOrDefault(SYNC_MODE, "LEGACY"));
|
||||
}
|
||||
|
||||
public void setSyncMode(IdentityProviderSyncMode syncMode) {
|
||||
getConfig().put(SYNC_MODE, syncMode.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package org.keycloak.models;
|
||||
|
||||
public enum IdentityProviderSyncMode {
|
||||
LEGACY, IMPORT, FORCE
|
||||
}
|
|
@ -23,6 +23,7 @@ import org.keycloak.broker.oidc.OIDCIdentityProvider;
|
|||
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
@ -30,7 +31,10 @@ import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
|
|||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Abstract class for Social Provider mappers which allow mapping of JSON user profile field into Keycloak user
|
||||
|
@ -41,6 +45,7 @@ import java.util.List;
|
|||
*/
|
||||
public abstract class AbstractJsonUserAttributeMapper extends AbstractIdentityProviderMapper {
|
||||
|
||||
private static final Set<IdentityProviderSyncMode> IDENTITY_PROVIDER_SYNC_MODES = new HashSet<>(Arrays.asList(IdentityProviderSyncMode.values()));
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(AbstractJsonUserAttributeMapper.class);
|
||||
|
||||
|
@ -97,6 +102,11 @@ public abstract class AbstractJsonUserAttributeMapper extends AbstractIdentityPr
|
|||
LOGGER_DUMP_USER_PROFILE.debug("User Profile JSON Data for provider "+provider+": " + profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSyncMode(IdentityProviderSyncMode syncMode) {
|
||||
return IDENTITY_PROVIDER_SYNC_MODES.contains(syncMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
|
@ -119,12 +129,10 @@ public abstract class AbstractJsonUserAttributeMapper extends AbstractIdentityPr
|
|||
|
||||
@Override
|
||||
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String attribute = mapperModel.getConfig().get(CONF_USER_ATTRIBUTE);
|
||||
if (attribute == null || attribute.trim().isEmpty()) {
|
||||
logger.warnf("Attribute is not configured for mapper %s", mapperModel.getName());
|
||||
String attribute = getAttribute(mapperModel);
|
||||
if (attribute == null) {
|
||||
return;
|
||||
}
|
||||
attribute = attribute.trim();
|
||||
|
||||
Object value = getJsonValue(mapperModel, context);
|
||||
if (value != null) {
|
||||
|
@ -137,10 +145,37 @@ public abstract class AbstractJsonUserAttributeMapper extends AbstractIdentityPr
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
public void updateBrokeredUserLegacy(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
// we do not update user profile from social provider
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String attribute = getAttribute(mapperModel);
|
||||
if (attribute == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object value = getJsonValue(mapperModel, context);
|
||||
if (value == null) {
|
||||
user.removeAttribute(attribute);
|
||||
} else if (value instanceof List) {
|
||||
user.setAttribute(attribute, (List<String>) value);
|
||||
} else {
|
||||
user.setSingleAttribute(attribute, value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private String getAttribute(IdentityProviderMapperModel mapperModel) {
|
||||
String attribute = mapperModel.getConfig().get(CONF_USER_ATTRIBUTE);
|
||||
if (attribute == null || attribute.trim().isEmpty()) {
|
||||
logger.warnf("Attribute is not configured for mapper %s", mapperModel.getName());
|
||||
return null;
|
||||
}
|
||||
attribute = attribute.trim();
|
||||
return attribute;
|
||||
}
|
||||
|
||||
protected static Object getJsonValue(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
|
||||
String jsonField = mapperModel.getConfig().get(CONF_JSON_FIELD);
|
||||
|
@ -223,7 +258,7 @@ public abstract class AbstractJsonUserAttributeMapper extends AbstractIdentityPr
|
|||
if (values.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return values ;
|
||||
return values ;
|
||||
} else if (currentNode.isNull()) {
|
||||
|
||||
logger.debug("JsonNode is null node for name " + currentFieldName);
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.keycloak.broker.provider.BrokeredIdentityContext;
|
|||
import org.keycloak.broker.provider.ConfigConstants;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
|
@ -31,8 +32,11 @@ import org.keycloak.models.utils.KeycloakModelUtils;
|
|||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke, Benjamin Weimer</a>
|
||||
|
@ -44,6 +48,7 @@ public class AdvancedClaimToRoleMapper extends AbstractClaimMapper {
|
|||
public static final String ARE_CLAIM_VALUES_REGEX_PROPERTY_NAME = "are.claim.values.regex";
|
||||
|
||||
public static final String[] COMPATIBLE_PROVIDERS = {KeycloakOIDCIdentityProviderFactory.PROVIDER_ID, OIDCIdentityProviderFactory.PROVIDER_ID};
|
||||
private static final Set<IdentityProviderSyncMode> IDENTITY_PROVIDER_SYNC_MODES = new HashSet<>(Arrays.asList(IdentityProviderSyncMode.values()));
|
||||
|
||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||
|
||||
|
@ -70,6 +75,10 @@ public class AdvancedClaimToRoleMapper extends AbstractClaimMapper {
|
|||
|
||||
public static final String PROVIDER_ID = "oidc-advanced-role-idp-mapper";
|
||||
|
||||
@Override
|
||||
public boolean supportsSyncMode(IdentityProviderSyncMode syncMode) {
|
||||
return IDENTITY_PROVIDER_SYNC_MODES.contains(syncMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
|
@ -107,7 +116,7 @@ public class AdvancedClaimToRoleMapper extends AbstractClaimMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
public void updateBrokeredUserLegacy(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String roleName = mapperModel.getConfig().get(ConfigConstants.ROLE);
|
||||
if (!hasAllClaimValues(mapperModel, context)) {
|
||||
RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName);
|
||||
|
@ -117,6 +126,21 @@ public class AdvancedClaimToRoleMapper extends AbstractClaimMapper {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String roleName = mapperModel.getConfig().get(ConfigConstants.ROLE);
|
||||
RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName);
|
||||
if (role == null) {
|
||||
throw new IdentityBrokerException("Unable to find role: " + roleName);
|
||||
}
|
||||
if (!hasAllClaimValues(mapperModel, context)) {
|
||||
user.deleteRoleMapping(role);
|
||||
} else {
|
||||
user.grantRole(role);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "If all claims exists, grant the user the specified realm or application role.";
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.keycloak.broker.provider.BrokeredIdentityContext;
|
|||
import org.keycloak.broker.provider.ConfigConstants;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
|
@ -31,7 +32,10 @@ import org.keycloak.models.utils.KeycloakModelUtils;
|
|||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -42,6 +46,7 @@ public class ClaimToRoleMapper extends AbstractClaimMapper {
|
|||
public static final String[] COMPATIBLE_PROVIDERS = {KeycloakOIDCIdentityProviderFactory.PROVIDER_ID, OIDCIdentityProviderFactory.PROVIDER_ID};
|
||||
|
||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||
private static final Set<IdentityProviderSyncMode> IDENTITY_PROVIDER_SYNC_MODES = new HashSet<>(Arrays.asList(IdentityProviderSyncMode.values()));
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
|
@ -68,6 +73,10 @@ public class ClaimToRoleMapper extends AbstractClaimMapper {
|
|||
|
||||
public static final String PROVIDER_ID = "oidc-role-idp-mapper";
|
||||
|
||||
@Override
|
||||
public boolean supportsSyncMode(IdentityProviderSyncMode syncMode) {
|
||||
return IDENTITY_PROVIDER_SYNC_MODES.contains(syncMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
|
@ -105,7 +114,7 @@ public class ClaimToRoleMapper extends AbstractClaimMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
public void updateBrokeredUserLegacy(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String roleName = mapperModel.getConfig().get(ConfigConstants.ROLE);
|
||||
if (!hasClaimValue(mapperModel, context)) {
|
||||
RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName);
|
||||
|
@ -115,6 +124,20 @@ public class ClaimToRoleMapper extends AbstractClaimMapper {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String roleName = mapperModel.getConfig().get(ConfigConstants.ROLE);
|
||||
RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName);
|
||||
if (role == null) {
|
||||
throw new IdentityBrokerException("Unable to find role: " + roleName);
|
||||
}
|
||||
if (!hasClaimValue(mapperModel, context)) {
|
||||
user.deleteRoleMapping(role);
|
||||
} else {
|
||||
user.grantRole(role);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "If a claim exists, grant the user the specified realm or application role.";
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.keycloak.broker.provider.BrokeredIdentityContext;
|
|||
import org.keycloak.broker.provider.ConfigConstants;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
|
@ -32,7 +33,10 @@ import org.keycloak.provider.ProviderConfigProperty;
|
|||
import org.keycloak.representations.JsonWebToken;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -44,6 +48,7 @@ public class ExternalKeycloakRoleToRoleMapper extends AbstractClaimMapper {
|
|||
|
||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||
private static final String EXTERNAL_ROLE = "external.role";
|
||||
private static final Set<IdentityProviderSyncMode> IDENTITY_PROVIDER_SYNC_MODES = new HashSet<>(Arrays.asList(IdentityProviderSyncMode.values()));
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
|
@ -64,6 +69,10 @@ public class ExternalKeycloakRoleToRoleMapper extends AbstractClaimMapper {
|
|||
|
||||
public static final String PROVIDER_ID = "keycloak-oidc-role-to-role-idp-mapper";
|
||||
|
||||
@Override
|
||||
public boolean supportsSyncMode(IdentityProviderSyncMode syncMode) {
|
||||
return IDENTITY_PROVIDER_SYNC_MODES.contains(syncMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
|
@ -92,16 +101,13 @@ public class ExternalKeycloakRoleToRoleMapper extends AbstractClaimMapper {
|
|||
|
||||
@Override
|
||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
RoleModel role = hasRole(realm, mapperModel, context);
|
||||
if (role != null) {
|
||||
user.grantRole(role);
|
||||
if (hasRole(realm, mapperModel, context)) {
|
||||
user.grantRole(searchRole(realm, mapperModel));
|
||||
}
|
||||
}
|
||||
|
||||
private RoleModel hasRole(RealmModel realm,IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
private boolean hasRole(RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
JsonWebToken token = (JsonWebToken)context.getContextData().get(KeycloakOIDCIdentityProvider.VALIDATED_ACCESS_TOKEN);
|
||||
//if (token == null) return;
|
||||
String roleName = mapperModel.getConfig().get(ConfigConstants.ROLE);
|
||||
String[] parseRole = KeycloakModelUtils.parseRole(mapperModel.getConfig().get(EXTERNAL_ROLE));
|
||||
String externalRoleName = parseRole[1];
|
||||
String claimName = null;
|
||||
|
@ -111,19 +117,28 @@ public class ExternalKeycloakRoleToRoleMapper extends AbstractClaimMapper {
|
|||
claimName = "resource_access." + parseRole[0] + ".roles";
|
||||
}
|
||||
Object claim = getClaimValue(token, claimName);
|
||||
if (valueEquals(externalRoleName, claim)) {
|
||||
RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName);
|
||||
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
||||
return role;
|
||||
}
|
||||
return null;
|
||||
return valueEquals(externalRoleName, claim);
|
||||
}
|
||||
|
||||
private RoleModel searchRole(RealmModel realm, IdentityProviderMapperModel mapperModel) {
|
||||
String roleName = mapperModel.getConfig().get(ConfigConstants.ROLE);
|
||||
RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName);
|
||||
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
||||
return role;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUserLegacy(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
// The legacy mapper actually did nothing although it pretended to do something
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
RoleModel role = hasRole(realm, mapperModel, context);
|
||||
if (role == null) {
|
||||
user.deleteRoleMapping(role);
|
||||
if (hasRole(realm, mapperModel, context)) {
|
||||
user.grantRole(searchRole(realm, mapperModel));
|
||||
} else {
|
||||
user.deleteRoleMapping(searchRole(realm, mapperModel));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
|
|||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.common.util.CollectionUtil;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
@ -29,9 +30,12 @@ import org.keycloak.provider.ProviderConfigProperty;
|
|||
import org.keycloak.saml.common.util.StringUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -49,6 +53,7 @@ public class UserAttributeMapper extends AbstractClaimMapper {
|
|||
public static final String EMAIL = "email";
|
||||
public static final String FIRST_NAME = "firstName";
|
||||
public static final String LAST_NAME = "lastName";
|
||||
private static final Set<IdentityProviderSyncMode> IDENTITY_PROVIDER_SYNC_MODES = new HashSet<>(Arrays.asList(IdentityProviderSyncMode.values()));
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
|
@ -69,6 +74,11 @@ public class UserAttributeMapper extends AbstractClaimMapper {
|
|||
|
||||
public static final String PROVIDER_ID = "oidc-user-attribute-idp-mapper";
|
||||
|
||||
@Override
|
||||
public boolean supportsSyncMode(IdentityProviderSyncMode syncMode) {
|
||||
return IDENTITY_PROVIDER_SYNC_MODES.contains(syncMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.keycloak.broker.oidc.KeycloakOIDCIdentityProviderFactory;
|
|||
import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
@ -41,7 +42,10 @@ import org.keycloak.social.stackoverflow.StackoverflowIdentityProviderFactory;
|
|||
import org.keycloak.social.twitter.TwitterIdentityProviderFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -70,6 +74,7 @@ public class UsernameTemplateMapper extends AbstractClaimMapper {
|
|||
};
|
||||
|
||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||
private static final Set<IdentityProviderSyncMode> IDENTITY_PROVIDER_SYNC_MODES = new HashSet<>(Arrays.asList(IdentityProviderSyncMode.values()));
|
||||
|
||||
public static final String TEMPLATE = "template";
|
||||
|
||||
|
@ -86,6 +91,11 @@ public class UsernameTemplateMapper extends AbstractClaimMapper {
|
|||
|
||||
public static final String PROVIDER_ID = "oidc-username-idp-mapper";
|
||||
|
||||
@Override
|
||||
public boolean supportsSyncMode(IdentityProviderSyncMode syncMode) {
|
||||
return IDENTITY_PROVIDER_SYNC_MODES.contains(syncMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
|
@ -111,14 +121,27 @@ public class UsernameTemplateMapper extends AbstractClaimMapper {
|
|||
return "Username Template Importer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUserLegacy(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
// preprocessFederatedIdentity gets called anyways, so we only need to set the username if necessary.
|
||||
// However, we don't want to set the username when the email is used as username
|
||||
if (!realm.isRegistrationEmailAsUsername()) {
|
||||
user.setUsername(context.getModelUsername());
|
||||
}
|
||||
}
|
||||
|
||||
static Pattern substitution = Pattern.compile("\\$\\{([^}]+)\\}");
|
||||
|
||||
@Override
|
||||
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
setUserNameFromTemplate(mapperModel, context);
|
||||
}
|
||||
|
||||
private void setUserNameFromTemplate(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String template = mapperModel.getConfig().get(TEMPLATE);
|
||||
Matcher m = substitution.matcher(template);
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
@ -141,7 +164,6 @@ public class UsernameTemplateMapper extends AbstractClaimMapper {
|
|||
m.appendTail(sb);
|
||||
String username = sb.toString();
|
||||
context.setModelUsername(username);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,13 +18,17 @@
|
|||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -34,6 +38,7 @@ public class HardcodedAttributeMapper extends AbstractIdentityProviderMapper {
|
|||
public static final String ATTRIBUTE = "attribute";
|
||||
public static final String ATTRIBUTE_VALUE = "attribute.value";
|
||||
protected static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||
private static final Set<IdentityProviderSyncMode> IDENTITY_PROVIDER_SYNC_MODES = new HashSet<>(Arrays.asList(IdentityProviderSyncMode.values()));
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
|
@ -51,7 +56,10 @@ public class HardcodedAttributeMapper extends AbstractIdentityProviderMapper {
|
|||
configProperties.add(property);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supportsSyncMode(IdentityProviderSyncMode syncMode) {
|
||||
return IDENTITY_PROVIDER_SYNC_MODES.contains(syncMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
|
@ -26,14 +27,18 @@ import org.keycloak.models.utils.KeycloakModelUtils;
|
|||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class HardcodedRoleMapper extends AbstractIdentityProviderMapper {
|
||||
protected static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
|
||||
protected static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||
private static final Set<IdentityProviderSyncMode> IDENTITY_PROVIDER_SYNC_MODES = new HashSet<>(Arrays.asList(IdentityProviderSyncMode.values()));
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
|
@ -65,6 +70,11 @@ public class HardcodedRoleMapper extends AbstractIdentityProviderMapper {
|
|||
|
||||
public static final String PROVIDER_ID = "oidc-hardcoded-role-idp-mapper";
|
||||
|
||||
@Override
|
||||
public boolean supportsSyncMode(IdentityProviderSyncMode syncMode) {
|
||||
return IDENTITY_PROVIDER_SYNC_MODES.contains(syncMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
|
@ -77,6 +87,10 @@ public class HardcodedRoleMapper extends AbstractIdentityProviderMapper {
|
|||
|
||||
@Override
|
||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
grantUserRole(realm, user, mapperModel);
|
||||
}
|
||||
|
||||
private void grantUserRole(RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel) {
|
||||
String roleName = mapperModel.getConfig().get(ConfigConstants.ROLE);
|
||||
RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName);
|
||||
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
||||
|
@ -85,7 +99,11 @@ public class HardcodedRoleMapper extends AbstractIdentityProviderMapper {
|
|||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
grantUserRole(realm, user, mapperModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUserLegacy(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,13 +18,17 @@
|
|||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -34,6 +38,7 @@ public class HardcodedUserSessionAttributeMapper extends AbstractIdentityProvide
|
|||
public static final String ATTRIBUTE = "attribute";
|
||||
public static final String ATTRIBUTE_VALUE = "attribute.value";
|
||||
protected static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||
private static final Set<IdentityProviderSyncMode> IDENTITY_PROVIDER_SYNC_MODES = new HashSet<>(Arrays.asList(IdentityProviderSyncMode.values()));
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
|
@ -51,7 +56,10 @@ public class HardcodedUserSessionAttributeMapper extends AbstractIdentityProvide
|
|||
configProperties.add(property);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supportsSyncMode(IdentityProviderSyncMode syncMode) {
|
||||
return IDENTITY_PROVIDER_SYNC_MODES.contains(syncMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
|||
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AttributeType;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
|
@ -35,8 +36,11 @@ import org.keycloak.models.utils.KeycloakModelUtils;
|
|||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -51,6 +55,7 @@ public class AttributeToRoleMapper extends AbstractIdentityProviderMapper {
|
|||
public static final String ATTRIBUTE_NAME = "attribute.name";
|
||||
public static final String ATTRIBUTE_FRIENDLY_NAME = "attribute.friendly.name";
|
||||
public static final String ATTRIBUTE_VALUE = "attribute.value";
|
||||
private static final Set<IdentityProviderSyncMode> IDENTITY_PROVIDER_SYNC_MODES = new HashSet<>(Arrays.asList(IdentityProviderSyncMode.values()));
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
|
@ -82,6 +87,11 @@ public class AttributeToRoleMapper extends AbstractIdentityProviderMapper {
|
|||
|
||||
public static final String PROVIDER_ID = "saml-role-idp-mapper";
|
||||
|
||||
@Override
|
||||
public boolean supportsSyncMode(IdentityProviderSyncMode syncMode) {
|
||||
return IDENTITY_PROVIDER_SYNC_MODES.contains(syncMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
|
|
|
@ -25,11 +25,20 @@ import org.keycloak.common.util.CollectionUtil;
|
|||
import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AttributeType;
|
||||
import org.keycloak.models.*;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.saml.common.util.StringUtil;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -50,6 +59,7 @@ public class UserAttributeMapper extends AbstractIdentityProviderMapper {
|
|||
private static final String EMAIL = "email";
|
||||
private static final String FIRST_NAME = "firstName";
|
||||
private static final String LAST_NAME = "lastName";
|
||||
private static final Set<IdentityProviderSyncMode> IDENTITY_PROVIDER_SYNC_MODES = new HashSet<>(Arrays.asList(IdentityProviderSyncMode.values()));
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
|
@ -75,6 +85,11 @@ public class UserAttributeMapper extends AbstractIdentityProviderMapper {
|
|||
|
||||
public static final String PROVIDER_ID = "saml-user-attribute-idp-mapper";
|
||||
|
||||
@Override
|
||||
public boolean supportsSyncMode(IdentityProviderSyncMode syncMode) {
|
||||
return IDENTITY_PROVIDER_SYNC_MODES.contains(syncMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
|
@ -183,7 +198,7 @@ public class UserAttributeMapper extends AbstractIdentityProviderMapper {
|
|||
// attribute sent by brokered idp has different values as before, update it
|
||||
user.setAttribute(attribute, attributeValuesInContext);
|
||||
}
|
||||
// attribute allready set
|
||||
// attribute already set
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,220 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) eHealth
|
||||
*/
|
||||
package org.keycloak.broker.saml.mappers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.saml.SAMLEndpoint;
|
||||
import org.keycloak.broker.saml.SAMLIdentityProviderFactory;
|
||||
import org.keycloak.common.util.CollectionUtil;
|
||||
import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType.ASTChoiceType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AttributeType;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:frelibert@yahoo.com">Frederik Libert</a>
|
||||
*
|
||||
*/
|
||||
public class UserAttributeStatementMapper extends AbstractIdentityProviderMapper {
|
||||
|
||||
private static final String USER_ATTR_LOCALE = "locale";
|
||||
|
||||
private static final String[] COMPATIBLE_PROVIDERS = {SAMLIdentityProviderFactory.PROVIDER_ID};
|
||||
|
||||
private static final List<ProviderConfigProperty> CONFIG_PROPERTIES = new ArrayList<>();
|
||||
|
||||
public static final String ATTRIBUTE_NAME_PATTERN = "attribute.name.pattern";
|
||||
|
||||
public static final String USER_ATTRIBUTE_FIRST_NAME = "user.attribute.firstName";
|
||||
|
||||
public static final String USER_ATTRIBUTE_LAST_NAME = "user.attribute.lastName";
|
||||
|
||||
public static final String USER_ATTRIBUTE_EMAIL = "user.attribute.email";
|
||||
|
||||
public static final String USER_ATTRIBUTE_LANGUAGE = "user.attribute.language";
|
||||
|
||||
private static final String USE_FRIENDLY_NAMES = "use.friendly.names";
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(ATTRIBUTE_NAME_PATTERN);
|
||||
property.setLabel("Attribute Name Pattern");
|
||||
property.setHelpText("Pattern of attribute names in assertion that must be mapped. Leave blank to map all attributes.");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
CONFIG_PROPERTIES.add(property);
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(USER_ATTRIBUTE_FIRST_NAME);
|
||||
property.setLabel("User Attribute FirstName");
|
||||
property.setHelpText("Define which saml Attribute must be mapped to the User property firstName.");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
CONFIG_PROPERTIES.add(property);
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(USER_ATTRIBUTE_LAST_NAME);
|
||||
property.setLabel("User Attribute LastName");
|
||||
property.setHelpText("Define which saml Attribute must be mapped to the User property lastName.");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
CONFIG_PROPERTIES.add(property);
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(USER_ATTRIBUTE_EMAIL);
|
||||
property.setLabel("User Attribute Email");
|
||||
property.setHelpText("Define which saml Attribute must be mapped to the User property email.");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
CONFIG_PROPERTIES.add(property);
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(USER_ATTRIBUTE_LANGUAGE);
|
||||
property.setLabel("User Attribute Language");
|
||||
property.setHelpText("Define which saml Attribute must be mapped to the User attribute locale.");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
CONFIG_PROPERTIES.add(property);
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(USE_FRIENDLY_NAMES);
|
||||
property.setLabel("Use Attribute Friendly Name");
|
||||
property.setHelpText("Define which name to give to each mapped user attribute: name or friendlyName.");
|
||||
property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
|
||||
CONFIG_PROPERTIES.add(property);
|
||||
}
|
||||
|
||||
public static final String PROVIDER_ID = "saml-user-attributestatement-idp-mapper";
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return CONFIG_PROPERTIES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getCompatibleProviders() {
|
||||
return COMPATIBLE_PROVIDERS.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayCategory() {
|
||||
return "AttributeStatement Importer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "AttributeStatement Importer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String firstNameAttribute = mapperModel.getConfig().get(USER_ATTRIBUTE_FIRST_NAME);
|
||||
String lastNameAttribute = mapperModel.getConfig().get(USER_ATTRIBUTE_LAST_NAME);
|
||||
String emailAttribute = mapperModel.getConfig().get(USER_ATTRIBUTE_EMAIL);
|
||||
String langAttribute = mapperModel.getConfig().get(USER_ATTRIBUTE_LANGUAGE);
|
||||
Boolean useFriendlyNames = Boolean.valueOf(mapperModel.getConfig().get(USE_FRIENDLY_NAMES));
|
||||
List<AttributeType> attributesInContext = findAttributesInContext(context, getAttributePattern(mapperModel));
|
||||
for (AttributeType a : attributesInContext) {
|
||||
String attribute = useFriendlyNames ? a.getFriendlyName() : a.getName();
|
||||
List<String> attributeValuesInContext = a.getAttributeValue().stream().filter(Objects::nonNull).map(Object::toString).collect(Collectors.toList());
|
||||
if (!attributeValuesInContext.isEmpty()) {
|
||||
// set as attribute anyway
|
||||
context.setUserAttribute(attribute, attributeValuesInContext);
|
||||
// set as special field ?
|
||||
if (Objects.equals(attribute, emailAttribute)) {
|
||||
setIfNotEmpty(context::setEmail, attributeValuesInContext);
|
||||
} else if (Objects.equals(attribute, firstNameAttribute)) {
|
||||
setIfNotEmpty(context::setFirstName, attributeValuesInContext);
|
||||
} else if (Objects.equals(attribute, lastNameAttribute)) {
|
||||
setIfNotEmpty(context::setLastName, attributeValuesInContext);
|
||||
} else if (Objects.equals(attribute, langAttribute)) {
|
||||
context.setUserAttribute(USER_ATTR_LOCALE, attributeValuesInContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String firstNameAttribute = mapperModel.getConfig().get(USER_ATTRIBUTE_FIRST_NAME);
|
||||
String lastNameAttribute = mapperModel.getConfig().get(USER_ATTRIBUTE_LAST_NAME);
|
||||
String emailAttribute = mapperModel.getConfig().get(USER_ATTRIBUTE_EMAIL);
|
||||
String langAttribute = mapperModel.getConfig().get(USER_ATTRIBUTE_LANGUAGE);
|
||||
Boolean useFriendlyNames = Boolean.valueOf(mapperModel.getConfig().get(USE_FRIENDLY_NAMES));
|
||||
List<AttributeType> attributesInContext = findAttributesInContext(context, getAttributePattern(mapperModel));
|
||||
|
||||
Set<String> assertedUserAttributes = new HashSet<String>();
|
||||
for (AttributeType a : attributesInContext) {
|
||||
String attribute = useFriendlyNames ? a.getFriendlyName() : a.getName();
|
||||
List<String> attributeValuesInContext = a.getAttributeValue().stream().filter(Objects::nonNull).map(Object::toString).collect(Collectors.toList());
|
||||
List<String> currentAttributeValues = user.getAttributes().get(attribute);
|
||||
if (attributeValuesInContext == null) {
|
||||
// attribute no longer sent by brokered idp, remove it
|
||||
user.removeAttribute(attribute);
|
||||
} else if (currentAttributeValues == null) {
|
||||
// new attribute sent by brokered idp, add it
|
||||
user.setAttribute(attribute, attributeValuesInContext);
|
||||
} else if (!CollectionUtil.collectionEquals(attributeValuesInContext, currentAttributeValues)) {
|
||||
// attribute sent by brokered idp has different values as before, update it
|
||||
user.setAttribute(attribute, attributeValuesInContext);
|
||||
}
|
||||
if (Objects.equals(attribute, emailAttribute)) {
|
||||
setIfNotEmpty(context::setEmail, attributeValuesInContext);
|
||||
} else if (Objects.equals(attribute, firstNameAttribute)) {
|
||||
setIfNotEmpty(context::setFirstName, attributeValuesInContext);
|
||||
} else if (Objects.equals(attribute, lastNameAttribute)) {
|
||||
setIfNotEmpty(context::setLastName, attributeValuesInContext);
|
||||
} else if (Objects.equals(attribute, langAttribute)) {
|
||||
if(attributeValuesInContext == null) {
|
||||
user.removeAttribute(USER_ATTR_LOCALE);
|
||||
} else {
|
||||
user.setAttribute(USER_ATTR_LOCALE, attributeValuesInContext);
|
||||
}
|
||||
assertedUserAttributes.add(USER_ATTR_LOCALE);
|
||||
}
|
||||
// Mark attribute as handled
|
||||
assertedUserAttributes.add(attribute);
|
||||
}
|
||||
// Remove user attributes that were not referenced in assertion.
|
||||
user.getAttributes().keySet().stream().filter(a -> !assertedUserAttributes.contains(a)).forEach(a -> user.removeAttribute(a));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Import all saml attributes found in attributestatements in assertion into user properties or attributes.";
|
||||
}
|
||||
|
||||
private Optional<Pattern> getAttributePattern(IdentityProviderMapperModel mapperModel) {
|
||||
String attributePatternConfig = mapperModel.getConfig().get(ATTRIBUTE_NAME_PATTERN);
|
||||
return Optional.ofNullable(attributePatternConfig != null ? Pattern.compile(attributePatternConfig) : null);
|
||||
}
|
||||
|
||||
private List<AttributeType> findAttributesInContext(BrokeredIdentityContext context, Optional<Pattern> attributePattern) {
|
||||
AssertionType assertion = (AssertionType) context.getContextData().get(SAMLEndpoint.SAML_ASSERTION);
|
||||
|
||||
return assertion.getAttributeStatements().stream()//
|
||||
.flatMap(statement -> statement.getAttributes().stream())//
|
||||
.filter(item -> !attributePattern.isPresent() || attributePattern.get().matcher(item.getAttribute().getName()).matches())//
|
||||
.map(ASTChoiceType::getAttribute)//
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void setIfNotEmpty(Consumer<String> consumer, List<String> values) {
|
||||
if (values != null && !values.isEmpty()) {
|
||||
consumer.accept(values.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -27,6 +27,7 @@ import org.keycloak.dom.saml.v2.assertion.AttributeType;
|
|||
import org.keycloak.dom.saml.v2.assertion.NameIDType;
|
||||
import org.keycloak.dom.saml.v2.assertion.SubjectType;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
@ -34,7 +35,10 @@ import org.keycloak.models.utils.KeycloakModelUtils;
|
|||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -50,6 +54,8 @@ public class UsernameTemplateMapper extends AbstractIdentityProviderMapper {
|
|||
|
||||
public static final String TEMPLATE = "template";
|
||||
|
||||
private static final Set<IdentityProviderSyncMode> IDENTITY_PROVIDER_SYNC_MODES = new HashSet<>(Arrays.asList(IdentityProviderSyncMode.values()));
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
property = new ProviderConfigProperty();
|
||||
|
@ -63,6 +69,11 @@ public class UsernameTemplateMapper extends AbstractIdentityProviderMapper {
|
|||
|
||||
public static final String PROVIDER_ID = "saml-username-idp-mapper";
|
||||
|
||||
@Override
|
||||
public boolean supportsSyncMode(IdentityProviderSyncMode syncMode) {
|
||||
return IDENTITY_PROVIDER_SYNC_MODES.contains(syncMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
|
@ -89,13 +100,26 @@ public class UsernameTemplateMapper extends AbstractIdentityProviderMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
|
||||
public void updateBrokeredUserLegacy(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
// preprocessFederatedIdentity gets called anyways, so we only need to set the username if necessary.
|
||||
// However, we don't want to set the username when the email is used as username
|
||||
if (!realm.isRegistrationEmailAsUsername()) {
|
||||
user.setUsername(context.getModelUsername());
|
||||
}
|
||||
}
|
||||
|
||||
static Pattern substitution = Pattern.compile("\\$\\{([^}]+)\\}");
|
||||
|
||||
@Override
|
||||
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
setUserNameFromTemplate(mapperModel, context);
|
||||
}
|
||||
|
||||
private void setUserNameFromTemplate(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
AssertionType assertion = (AssertionType)context.getContextData().get(SAMLEndpoint.SAML_ASSERTION);
|
||||
String template = mapperModel.getConfig().get(TEMPLATE);
|
||||
Matcher m = substitution.matcher(template);
|
||||
|
@ -134,7 +158,6 @@ public class UsernameTemplateMapper extends AbstractIdentityProviderMapper {
|
|||
}
|
||||
m.appendTail(sb);
|
||||
context.setModelUsername(sb.toString());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.keycloak.broker.provider.ExchangeTokenToIdentityProviderToken;
|
|||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.provider.IdentityProviderFactory;
|
||||
import org.keycloak.broker.provider.IdentityProviderMapper;
|
||||
import org.keycloak.broker.provider.IdentityProviderMapperSyncModeDelegate;
|
||||
import org.keycloak.common.ClientConnection;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.constants.ServiceAccountConstants;
|
||||
|
@ -1077,7 +1078,7 @@ public class TokenEndpoint {
|
|||
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
|
||||
for (IdentityProviderMapperModel mapper : mappers) {
|
||||
IdentityProviderMapper target = (IdentityProviderMapper)sessionFactory.getProviderFactory(IdentityProviderMapper.class, mapper.getIdentityProviderMapper());
|
||||
target.updateBrokeredUser(session, realm, user, mapper, context);
|
||||
IdentityProviderMapperSyncModeDelegate.delegateUpdateBrokeredUser(session, realm, user, mapper, context, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,15 +32,14 @@ import org.keycloak.broker.provider.IdentityBrokerException;
|
|||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.provider.IdentityProviderFactory;
|
||||
import org.keycloak.broker.provider.IdentityProviderMapper;
|
||||
import org.keycloak.broker.provider.IdentityProviderMapperSyncModeDelegate;
|
||||
import org.keycloak.broker.provider.util.IdentityBrokerState;
|
||||
import org.keycloak.broker.saml.SAMLEndpoint;
|
||||
import org.keycloak.broker.saml.SAMLIdentityProvider;
|
||||
import org.keycloak.broker.social.SocialIdentityProvider;
|
||||
import org.keycloak.common.ClientConnection;
|
||||
import org.keycloak.common.util.Base64Url;
|
||||
import org.keycloak.common.util.ObjectUtil;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.dom.saml.v2.SAML2Object;
|
||||
import org.keycloak.dom.saml.v2.protocol.StatusResponseType;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
|
@ -1001,7 +1000,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
|||
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
|
||||
for (IdentityProviderMapperModel mapper : mappers) {
|
||||
IdentityProviderMapper target = (IdentityProviderMapper)sessionFactory.getProviderFactory(IdentityProviderMapper.class, mapper.getIdentityProviderMapper());
|
||||
target.updateBrokeredUser(session, realmModel, federatedUser, mapper, context);
|
||||
IdentityProviderMapperSyncModeDelegate.delegateUpdateBrokeredUser(session, realmModel, federatedUser, mapper, context, target);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.keycloak.broker.oidc.mappers.AbstractJsonUserAttributeMapper;
|
|||
*/
|
||||
public class GitHubUserAttributeMapper extends AbstractJsonUserAttributeMapper {
|
||||
|
||||
public static final String PROVIDER_ID = "github-user-attribute-mapper";
|
||||
private static final String[] cp = new String[] { GitHubIdentityProviderFactory.PROVIDER_ID };
|
||||
|
||||
@Override
|
||||
|
@ -34,7 +35,7 @@ public class GitHubUserAttributeMapper extends AbstractJsonUserAttributeMapper {
|
|||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "github-user-attribute-mapper";
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,6 +32,10 @@ import org.keycloak.dom.saml.v2.metadata.KeyTypes;
|
|||
import org.keycloak.dom.saml.v2.metadata.SPSSODescriptorType;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.models.utils.StripSecretsUtils;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.representations.idm.AdminEventRepresentation;
|
||||
|
@ -140,6 +144,7 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
|||
public void testCreate() {
|
||||
IdentityProviderRepresentation newIdentityProvider = createRep("new-identity-provider", "oidc");
|
||||
|
||||
newIdentityProvider.getConfig().put(IdentityProviderModel.SYNC_MODE, "IMPORT");
|
||||
newIdentityProvider.getConfig().put("clientId", "clientId");
|
||||
newIdentityProvider.getConfig().put("clientSecret", "some secret value");
|
||||
|
||||
|
@ -156,6 +161,7 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
|||
assertNotNull(representation.getInternalId());
|
||||
assertEquals("new-identity-provider", representation.getAlias());
|
||||
assertEquals("oidc", representation.getProviderId());
|
||||
assertEquals("IMPORT", representation.getConfig().get(IdentityProviderMapperModel.SYNC_MODE));
|
||||
assertEquals("clientId", representation.getConfig().get("clientId"));
|
||||
assertEquals(ComponentRepresentation.SECRET_VALUE, representation.getConfig().get("clientSecret"));
|
||||
assertTrue(representation.isEnabled());
|
||||
|
@ -243,6 +249,7 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
|||
public void testCreateWithBasicAuth() {
|
||||
IdentityProviderRepresentation newIdentityProvider = createRep("new-identity-provider", "oidc");
|
||||
|
||||
newIdentityProvider.getConfig().put(IdentityProviderModel.SYNC_MODE, "IMPORT");
|
||||
newIdentityProvider.getConfig().put("clientId", "clientId");
|
||||
newIdentityProvider.getConfig().put("clientSecret", "some secret value");
|
||||
newIdentityProvider.getConfig().put("clientAuthMethod",OIDCLoginProtocol.CLIENT_SECRET_BASIC);
|
||||
|
@ -260,6 +267,7 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
|||
assertNotNull(representation.getInternalId());
|
||||
assertEquals("new-identity-provider", representation.getAlias());
|
||||
assertEquals("oidc", representation.getProviderId());
|
||||
assertEquals("IMPORT", representation.getConfig().get(IdentityProviderMapperModel.SYNC_MODE));
|
||||
assertEquals("clientId", representation.getConfig().get("clientId"));
|
||||
assertEquals(ComponentRepresentation.SECRET_VALUE, representation.getConfig().get("clientSecret"));
|
||||
assertEquals(OIDCLoginProtocol.CLIENT_SECRET_BASIC, representation.getConfig().get("clientAuthMethod"));
|
||||
|
@ -278,6 +286,7 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
|||
public void testCreateWithJWT() {
|
||||
IdentityProviderRepresentation newIdentityProvider = createRep("new-identity-provider", "oidc");
|
||||
|
||||
newIdentityProvider.getConfig().put(IdentityProviderModel.SYNC_MODE, "IMPORT");
|
||||
newIdentityProvider.getConfig().put("clientId", "clientId");
|
||||
newIdentityProvider.getConfig().put("clientAuthMethod", OIDCLoginProtocol.PRIVATE_KEY_JWT);
|
||||
|
||||
|
@ -294,6 +303,7 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
|||
assertNotNull(representation.getInternalId());
|
||||
assertEquals("new-identity-provider", representation.getAlias());
|
||||
assertEquals("oidc", representation.getProviderId());
|
||||
assertEquals("IMPORT", representation.getConfig().get(IdentityProviderMapperModel.SYNC_MODE));
|
||||
assertEquals("clientId", representation.getConfig().get("clientId"));
|
||||
assertNull(representation.getConfig().get("clientSecret"));
|
||||
assertEquals(OIDCLoginProtocol.PRIVATE_KEY_JWT, representation.getConfig().get("clientAuthMethod"));
|
||||
|
@ -306,6 +316,7 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
|||
public void testUpdate() {
|
||||
IdentityProviderRepresentation newIdentityProvider = createRep("update-identity-provider", "oidc");
|
||||
|
||||
newIdentityProvider.getConfig().put(IdentityProviderModel.SYNC_MODE, "IMPORT");
|
||||
newIdentityProvider.getConfig().put("clientId", "clientId");
|
||||
newIdentityProvider.getConfig().put("clientSecret", "some secret value");
|
||||
|
||||
|
@ -676,6 +687,7 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
|||
mapper.setIdentityProviderMapper("oidc-hardcoded-role-idp-mapper");
|
||||
Map<String, String> config = new HashMap<>();
|
||||
config.put("role", "offline_access");
|
||||
config.put(IdentityProviderMapperModel.SYNC_MODE, IdentityProviderMapperSyncMode.INHERIT.toString());
|
||||
mapper.setConfig(config);
|
||||
|
||||
// createRep and add mapper
|
||||
|
@ -692,6 +704,7 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
|||
|
||||
// get mapper
|
||||
mapper = provider.getMapperById(id);
|
||||
Assert.assertEquals("INHERIT", mappers.get(0).getConfig().get(IdentityProviderMapperModel.SYNC_MODE));
|
||||
Assert.assertNotNull("mapperById not null", mapper);
|
||||
Assert.assertEquals("mapper id", id, mapper.getId());
|
||||
Assert.assertNotNull("mapper.config exists", mapper.getConfig());
|
||||
|
@ -734,6 +747,7 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
|||
mapper.setName("my_mapper");
|
||||
mapper.setIdentityProviderMapper("oidc-hardcoded-role-idp-mapper");
|
||||
Map<String, String> config = new HashMap<>();
|
||||
config.put(IdentityProviderMapperModel.SYNC_MODE, IdentityProviderMapperSyncMode.INHERIT.toString());
|
||||
config.put("role", "");
|
||||
mapper.setConfig(config);
|
||||
|
||||
|
@ -743,7 +757,7 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
|||
|
||||
List<IdentityProviderMapperRepresentation> mappers = provider.getMappers();
|
||||
assertEquals(1, mappers.size());
|
||||
assertEquals(0, mappers.get(0).getConfig().size());
|
||||
assertEquals(1, mappers.get(0).getConfig().size());
|
||||
|
||||
mapper = provider.getMapperById(mapperId);
|
||||
mapper.getConfig().put("role", "offline_access");
|
||||
|
@ -751,8 +765,9 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
|||
provider.update(mapperId, mapper);
|
||||
|
||||
mappers = provider.getMappers();
|
||||
assertEquals("INHERIT", mappers.get(0).getConfig().get(IdentityProviderMapperModel.SYNC_MODE));
|
||||
assertEquals(1, mappers.size());
|
||||
assertEquals(1, mappers.get(0).getConfig().size());
|
||||
assertEquals(2, mappers.get(0).getConfig().size());
|
||||
assertEquals("offline_access", mappers.get(0).getConfig().get("role"));
|
||||
}
|
||||
|
||||
|
@ -768,6 +783,7 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
|||
mapper.setName("my_mapper");
|
||||
mapper.setIdentityProviderMapper("oidc-hardcoded-role-idp-mapper");
|
||||
Map<String, String> config = new HashMap<>();
|
||||
config.put(IdentityProviderMapperModel.SYNC_MODE, IdentityProviderMapperSyncMode.INHERIT.toString());
|
||||
config.put("role", "offline_access");
|
||||
mapper.setConfig(config);
|
||||
|
||||
|
|
|
@ -1,23 +1,13 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientRequestFilter;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
|
@ -35,6 +25,19 @@ import org.keycloak.testsuite.util.OAuthClient;
|
|||
import org.keycloak.testsuite.util.RealmBuilder;
|
||||
import org.openqa.selenium.TimeoutException;
|
||||
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientRequestFilter;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.Matchers.hasItems;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
@ -63,21 +66,25 @@ public abstract class AbstractAdvancedBrokerTest extends AbstractBrokerTest {
|
|||
|
||||
|
||||
protected void createRoleMappersForConsumerRealm() {
|
||||
createRoleMappersForConsumerRealm(IdentityProviderMapperSyncMode.FORCE);
|
||||
}
|
||||
|
||||
protected void createRoleMappersForConsumerRealm(IdentityProviderMapperSyncMode syncMode) {
|
||||
log.debug("adding mappers to identity provider in realm " + bc.consumerRealmName());
|
||||
|
||||
RealmResource realm = adminClient.realm(bc.consumerRealmName());
|
||||
|
||||
IdentityProviderResource idpResource = realm.identityProviders().get(bc.getIDPAlias());
|
||||
for (IdentityProviderMapperRepresentation mapper : createIdentityProviderMappers()) {
|
||||
for (IdentityProviderMapperRepresentation mapper : createIdentityProviderMappers(syncMode)) {
|
||||
mapper.setIdentityProviderAlias(bc.getIDPAlias());
|
||||
Response resp = idpResource.addMapper(mapper);
|
||||
resp.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Iterable<IdentityProviderMapperRepresentation> createIdentityProviderMappers();
|
||||
|
||||
protected abstract Iterable<IdentityProviderMapperRepresentation> createIdentityProviderMappers(IdentityProviderMapperSyncMode syncMode);
|
||||
|
||||
protected abstract void createAdditionalMapperWithCustomSyncMode(IdentityProviderMapperSyncMode syncMode);
|
||||
|
||||
/**
|
||||
* Refers to in old test suite: org.keycloak.testsuite.broker.AbstractKeycloakIdentityProviderTest#testAccountManagementLinkIdentity
|
||||
|
@ -315,18 +322,35 @@ public abstract class AbstractAdvancedBrokerTest extends AbstractBrokerTest {
|
|||
assertEquals("Account is disabled, contact your administrator.", errorPage.getError());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// KEYCLOAK-3987
|
||||
@Test
|
||||
public void grantNewRoleFromToken() {
|
||||
public void mapperDoesNotGrantNewRoleFromTokenWithSyncModeImport() {
|
||||
testMapperAssigningRoles(IdentityProviderMapperSyncMode.IMPORT, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapperGrantsNewRoleFromTokenWithInheritedSyncModeForce() {
|
||||
RealmResource realm = adminClient.realm(bc.consumerRealmName());
|
||||
realm.identityProviders().get(bc.getIDPAlias())
|
||||
.update(bc.setUpIdentityProvider(suiteContext, IdentityProviderSyncMode.FORCE));
|
||||
|
||||
testMapperAssigningRoles(IdentityProviderMapperSyncMode.INHERIT, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapperDoesNotGrantNewRoleFromTokenWithInheritedSyncModeImport() {
|
||||
RealmResource realm = adminClient.realm(bc.consumerRealmName());
|
||||
realm.identityProviders().get(bc.getIDPAlias())
|
||||
.update(bc.setUpIdentityProvider(suiteContext, IdentityProviderSyncMode.IMPORT));
|
||||
|
||||
testMapperAssigningRoles(IdentityProviderMapperSyncMode.INHERIT, false);
|
||||
}
|
||||
|
||||
private void testMapperAssigningRoles(IdentityProviderMapperSyncMode anImport, boolean isAssigned) {
|
||||
createRolesForRealm(bc.providerRealmName());
|
||||
createRolesForRealm(bc.consumerRealmName());
|
||||
|
||||
createRoleMappersForConsumerRealm();
|
||||
createRoleMappersForConsumerRealm(anImport);
|
||||
|
||||
RoleRepresentation managerRole = adminClient.realm(bc.providerRealmName()).roles().get(ROLE_MANAGER).toRepresentation();
|
||||
RoleRepresentation userRole = adminClient.realm(bc.providerRealmName()).roles().get(ROLE_USER).toRepresentation();
|
||||
|
@ -336,7 +360,9 @@ public abstract class AbstractAdvancedBrokerTest extends AbstractBrokerTest {
|
|||
|
||||
logInAsUserInIDPForFirstTime();
|
||||
|
||||
Set<String> currentRoles = userResource.roles().realmLevel().listAll().stream()
|
||||
UserResource consumerUserResource = adminClient.realm(bc.consumerRealmName()).users().get(
|
||||
adminClient.realm(bc.consumerRealmName()).users().search(bc.getUserLogin()).get(0).getId());
|
||||
Set<String> currentRoles = consumerUserResource.roles().realmLevel().listAll().stream()
|
||||
.map(RoleRepresentation::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
|
@ -350,15 +376,63 @@ public abstract class AbstractAdvancedBrokerTest extends AbstractBrokerTest {
|
|||
|
||||
logInAsUserInIDP();
|
||||
|
||||
currentRoles = userResource.roles().realmLevel().listAll().stream()
|
||||
currentRoles = consumerUserResource.roles().realmLevel().listAll().stream()
|
||||
.map(RoleRepresentation::getName)
|
||||
.collect(Collectors.toSet());
|
||||
assertThat(currentRoles, hasItems(ROLE_MANAGER, ROLE_USER));
|
||||
if (isAssigned) {
|
||||
assertThat(currentRoles, hasItems(ROLE_MANAGER, ROLE_USER));
|
||||
} else {
|
||||
assertThat(currentRoles, hasItems(ROLE_MANAGER));
|
||||
assertThat(currentRoles, not(hasItems(ROLE_USER)));
|
||||
}
|
||||
|
||||
logoutFromRealm(bc.providerRealmName());
|
||||
logoutFromRealm(bc.consumerRealmName());
|
||||
logoutFromRealm(bc.providerRealmName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void differentMappersCanHaveDifferentSyncModes() {
|
||||
createRolesForRealm(bc.providerRealmName());
|
||||
createRolesForRealm(bc.consumerRealmName());
|
||||
|
||||
createRoleMappersForConsumerRealm(IdentityProviderMapperSyncMode.INHERIT);
|
||||
createAdditionalMapperWithCustomSyncMode(IdentityProviderMapperSyncMode.FORCE);
|
||||
|
||||
|
||||
RoleRepresentation managerRole = adminClient.realm(bc.providerRealmName()).roles().get(ROLE_MANAGER).toRepresentation();
|
||||
RoleRepresentation userRole = adminClient.realm(bc.providerRealmName()).roles().get(ROLE_USER).toRepresentation();
|
||||
RoleRepresentation friendlyManagerRole = adminClient.realm(bc.providerRealmName()).roles().get(ROLE_FRIENDLY_MANAGER).toRepresentation();
|
||||
|
||||
UserResource userResource = adminClient.realm(bc.providerRealmName()).users().get(userId);
|
||||
userResource.roles().realmLevel().add(Collections.singletonList(managerRole));
|
||||
|
||||
logInAsUserInIDPForFirstTime();
|
||||
|
||||
UserResource consumerUserResource = adminClient.realm(bc.consumerRealmName()).users().get(
|
||||
adminClient.realm(bc.consumerRealmName()).users().search(bc.getUserLogin()).get(0).getId());
|
||||
Set<String> currentRoles = consumerUserResource.roles().realmLevel().listAll().stream()
|
||||
.map(RoleRepresentation::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
assertThat(currentRoles, hasItems(ROLE_MANAGER));
|
||||
assertThat(currentRoles, not(hasItems(ROLE_USER, ROLE_FRIENDLY_MANAGER)));
|
||||
|
||||
logoutFromRealm(bc.consumerRealmName());
|
||||
|
||||
|
||||
userResource.roles().realmLevel().add(Arrays.asList(userRole, friendlyManagerRole));
|
||||
|
||||
logInAsUserInIDP();
|
||||
|
||||
currentRoles = consumerUserResource.roles().realmLevel().listAll().stream()
|
||||
.map(RoleRepresentation::getName)
|
||||
.collect(Collectors.toSet());
|
||||
assertThat(currentRoles, hasItems(ROLE_MANAGER, ROLE_FRIENDLY_MANAGER));
|
||||
assertThat(currentRoles, not(hasItems(ROLE_USER)));
|
||||
|
||||
logoutFromRealm(bc.consumerRealmName());
|
||||
logoutFromRealm(bc.providerRealmName());
|
||||
}
|
||||
|
||||
// KEYCLOAK-4016
|
||||
@Test
|
||||
|
|
|
@ -14,6 +14,8 @@ import org.keycloak.admin.client.resource.IdentityProviderResource;
|
|||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.broker.provider.HardcodedUserSessionAttributeMapper;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
|
@ -582,6 +584,7 @@ public abstract class AbstractFirstBrokerLoginTest extends AbstractInitializedBa
|
|||
hardCodedSessionNoteMapper.setIdentityProviderAlias(bc.getIDPAlias());
|
||||
hardCodedSessionNoteMapper.setIdentityProviderMapper(HardcodedUserSessionAttributeMapper.PROVIDER_ID);
|
||||
hardCodedSessionNoteMapper.setConfig(ImmutableMap.<String, String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, IdentityProviderSyncMode.IMPORT.toString())
|
||||
.put(HardcodedUserSessionAttributeMapper.ATTRIBUTE_VALUE, "sessionvalue")
|
||||
.put(HardcodedUserSessionAttributeMapper.ATTRIBUTE, "user-session-attr")
|
||||
.build());
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.MappingsRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.keycloak.testsuite.admin.ApiUtil.createUserAndResetPasswordWithAdminClient;
|
||||
|
||||
/**
|
||||
* @author hmlnarik
|
||||
* <a href="mailto:external.benjamin.weimer@bosch-si.com">Benjamin Weimer</a>,
|
||||
* <a href="mailto:external.martin.idel@bosch.io">Martin Idel</a>,
|
||||
*/
|
||||
public abstract class AbstractIdentityProviderMapperTest extends AbstractBaseBrokerTest {
|
||||
|
||||
protected RealmResource realm;
|
||||
|
||||
@Before
|
||||
public void addClients() {
|
||||
addClientsToProviderAndConsumer();
|
||||
realm = adminClient.realm(bc.consumerRealmName());
|
||||
}
|
||||
|
||||
protected IdentityProviderRepresentation setupIdentityProvider() {
|
||||
log.debug("adding identity provider to realm " + bc.consumerRealmName());
|
||||
|
||||
final IdentityProviderRepresentation idp = bc.setUpIdentityProvider(suiteContext);
|
||||
realm.identityProviders().create(idp).close();
|
||||
return idp;
|
||||
}
|
||||
|
||||
protected void createUserInProviderRealm(Map<String, List<String>> attributes) {
|
||||
log.debug("Creating user in realm " + bc.providerRealmName());
|
||||
|
||||
UserRepresentation user = UserBuilder.create()
|
||||
.username(bc.getUserLogin())
|
||||
.email(bc.getUserEmail())
|
||||
.build();
|
||||
user.setEmailVerified(true);
|
||||
user.setAttributes(attributes);
|
||||
|
||||
this.userId = createUserAndResetPasswordWithAdminClient(adminClient.realm(bc.providerRealmName()), user, bc.getUserPassword());
|
||||
}
|
||||
|
||||
protected UserRepresentation findUser(String realm, String userName, String email) {
|
||||
UsersResource consumerUsers = adminClient.realm(realm).users();
|
||||
|
||||
List<UserRepresentation> users = consumerUsers.list();
|
||||
assertThat("There must be exactly one user", users, hasSize(1));
|
||||
UserRepresentation user = users.get(0);
|
||||
assertThat("Username has to match", user.getUsername(), equalTo(userName));
|
||||
assertThat("Email has to match", user.getEmail(), equalTo(email));
|
||||
|
||||
MappingsRepresentation roles = consumerUsers.get(user.getId()).roles().getAll();
|
||||
|
||||
List<String> realmRoles = roles.getRealmMappings().stream()
|
||||
.map(RoleRepresentation::getName)
|
||||
.collect(Collectors.toList());
|
||||
user.setRealmRoles(realmRoles);
|
||||
|
||||
Map<String, List<String>> clientRoles = new HashMap<>();
|
||||
roles.getClientMappings().forEach((key, value) -> clientRoles.put(key, value.getMappings().stream()
|
||||
.map(RoleRepresentation::getName)
|
||||
.collect(Collectors.toList())));
|
||||
user.setClientRoles(clientRoles);
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
||||
/**
|
||||
* @author hmlnarik,
|
||||
* <a href="mailto:external.benjamin.weimer@bosch-si.com">Benjamin Weimer</a>,
|
||||
* <a href="mailto:external.martin.idel@bosch.io">Martin Idel</a>,
|
||||
*/
|
||||
public abstract class AbstractRoleMapperTest extends AbstractIdentityProviderMapperTest {
|
||||
|
||||
private static final String CLIENT = "realm-management";
|
||||
private static final String CLIENT_ROLE = "view-realm";
|
||||
public static final String ROLE_USER = "user";
|
||||
public static final String CLIENT_ROLE_MAPPER_REPRESENTATION = CLIENT + "." + CLIENT_ROLE;
|
||||
|
||||
protected abstract void createMapperInIdp(IdentityProviderRepresentation idp, IdentityProviderMapperSyncMode syncMode);
|
||||
|
||||
protected void updateUser() {
|
||||
}
|
||||
|
||||
protected UserRepresentation loginAsUserTwiceWithMapper(
|
||||
IdentityProviderMapperSyncMode syncMode, boolean createAfterFirstLogin, Map<String, List<String>> userConfig) {
|
||||
final IdentityProviderRepresentation idp = setupIdentityProvider();
|
||||
if (!createAfterFirstLogin) {
|
||||
createMapperInIdp(idp, syncMode);
|
||||
}
|
||||
createUserInProviderRealm(userConfig);
|
||||
createUserRoleAndGrantToUserInProviderRealm();
|
||||
|
||||
logInAsUserInIDPForFirstTime();
|
||||
|
||||
UserRepresentation user = findUser(bc.consumerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
if (!createAfterFirstLogin) {
|
||||
assertThatRoleHasBeenAssignedInConsumerRealmTo(user);
|
||||
} else {
|
||||
assertThatRoleHasNotBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
if (createAfterFirstLogin) {
|
||||
createMapperInIdp(idp, syncMode);
|
||||
}
|
||||
logoutFromRealm(bc.consumerRealmName());
|
||||
|
||||
updateUser();
|
||||
|
||||
logInAsUserInIDP();
|
||||
user = findUser(bc.consumerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
return user;
|
||||
}
|
||||
|
||||
protected void createUserRoleAndGrantToUserInProviderRealm() {
|
||||
RoleRepresentation userRole = new RoleRepresentation(ROLE_USER,null, false);
|
||||
adminClient.realm(bc.providerRealmName()).roles().create(userRole);
|
||||
RoleRepresentation role = adminClient.realm(bc.providerRealmName()).roles().get(ROLE_USER).toRepresentation();
|
||||
UserResource userResource = adminClient.realm(bc.providerRealmName()).users().get(userId);
|
||||
userResource.roles().realmLevel().add(Collections.singletonList(role));
|
||||
}
|
||||
|
||||
protected void assertThatRoleHasBeenAssignedInConsumerRealmTo(UserRepresentation user) {
|
||||
assertThat(user.getClientRoles().get(CLIENT), contains(CLIENT_ROLE));
|
||||
}
|
||||
|
||||
protected void assertThatRoleHasNotBeenAssignedInConsumerRealmTo(UserRepresentation user) {
|
||||
assertThat(user.getClientRoles().get(CLIENT), not(contains(CLIENT_ROLE)));
|
||||
}
|
||||
}
|
|
@ -1,33 +1,32 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.ws.rs.core.Response;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.keycloak.testsuite.admin.ApiUtil.*;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public abstract class AbstractUserAttributeMapperTest extends AbstractBaseBrokerTest {
|
||||
public abstract class AbstractUserAttributeMapperTest extends AbstractIdentityProviderMapperTest {
|
||||
|
||||
protected static final String MAPPED_ATTRIBUTE_NAME = "mapped-user-attribute";
|
||||
protected static final String MAPPED_ATTRIBUTE_FRIENDLY_NAME = "mapped-user-attribute-friendly";
|
||||
|
@ -41,60 +40,18 @@ public abstract class AbstractUserAttributeMapperTest extends AbstractBaseBroker
|
|||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, MAPPED_ATTRIBUTE_NAME)
|
||||
.build();
|
||||
|
||||
protected abstract Iterable<IdentityProviderMapperRepresentation> createIdentityProviderMappers();
|
||||
protected abstract Iterable<IdentityProviderMapperRepresentation> createIdentityProviderMappers(IdentityProviderMapperSyncMode syncMode);
|
||||
|
||||
@Before
|
||||
public void addIdentityProviderToConsumerRealm() {
|
||||
log.debug("adding identity provider to realm " + bc.consumerRealmName());
|
||||
|
||||
RealmResource realm = adminClient.realm(bc.consumerRealmName());
|
||||
final IdentityProviderRepresentation idp = bc.setUpIdentityProvider(suiteContext);
|
||||
Response resp = realm.identityProviders().create(idp);
|
||||
resp.close();
|
||||
public void addIdentityProviderToConsumerRealm(IdentityProviderMapperSyncMode syncMode) {
|
||||
IdentityProviderRepresentation idp = setupIdentityProvider();
|
||||
|
||||
IdentityProviderResource idpResource = realm.identityProviders().get(idp.getAlias());
|
||||
for (IdentityProviderMapperRepresentation mapper : createIdentityProviderMappers()) {
|
||||
for (IdentityProviderMapperRepresentation mapper : createIdentityProviderMappers(syncMode)) {
|
||||
mapper.setIdentityProviderAlias(bc.getIDPAlias());
|
||||
resp = idpResource.addMapper(mapper);
|
||||
resp.close();
|
||||
idpResource.addMapper(mapper).close();
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void addClients() {
|
||||
addClientsToProviderAndConsumer();
|
||||
}
|
||||
|
||||
protected void createUserInProviderRealm(Map<String, List<String>> attributes) {
|
||||
log.debug("creating user in realm " + bc.providerRealmName());
|
||||
|
||||
UserRepresentation user = UserBuilder.create()
|
||||
.username(bc.getUserLogin())
|
||||
.email(bc.getUserEmail())
|
||||
.build();
|
||||
user.setEmailVerified(true);
|
||||
user.setAttributes(attributes);
|
||||
this.userId = createUserAndResetPasswordWithAdminClient(adminClient.realm(bc.providerRealmName()), user, bc.getUserPassword());
|
||||
}
|
||||
|
||||
private UserRepresentation findUser(String realm, String userName, String email) {
|
||||
UsersResource consumerUsers = adminClient.realm(realm).users();
|
||||
|
||||
int userCount = consumerUsers.count();
|
||||
assertThat("There must be at least one user", userCount, greaterThan(0));
|
||||
|
||||
List<UserRepresentation> users = consumerUsers.search("", 0, userCount);
|
||||
|
||||
for (UserRepresentation user : users) {
|
||||
if (user.getUsername().equals(userName) && user.getEmail().equals(email)) {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
fail("User " + userName + " not found in " + realm + " realm");
|
||||
return null;
|
||||
}
|
||||
|
||||
private void assertUserAttributes(Map<String, List<String>> attrs, UserRepresentation userRep) {
|
||||
Set<String> mappedAttrNames = attrs.entrySet().stream()
|
||||
.filter(me -> me.getValue() != null && ! me.getValue().isEmpty())
|
||||
|
@ -127,7 +84,22 @@ public abstract class AbstractUserAttributeMapperTest extends AbstractBaseBroker
|
|||
}
|
||||
}
|
||||
|
||||
protected void testValueMapping(Map<String, List<String>> initialUserAttributes, Map<String, List<String>> modifiedUserAttributes) {
|
||||
private void testValueMappingForImportSyncMode(Map<String, List<String>> initialUserAttributes, Map<String, List<String>> modifiedUserAttributes) {
|
||||
addIdentityProviderToConsumerRealm(IdentityProviderMapperSyncMode.IMPORT);
|
||||
testValueMapping(initialUserAttributes, modifiedUserAttributes, initialUserAttributes);
|
||||
}
|
||||
|
||||
private void testValueMappingForForceSyncMode(Map<String, List<String>> initialUserAttributes, Map<String, List<String>> modifiedUserAttributes) {
|
||||
addIdentityProviderToConsumerRealm(IdentityProviderMapperSyncMode.FORCE);
|
||||
testValueMapping(initialUserAttributes, modifiedUserAttributes, modifiedUserAttributes);
|
||||
}
|
||||
|
||||
private void testValueMappingForLegacySyncMode(Map<String, List<String>> initialUserAttributes, Map<String, List<String>> modifiedUserAttributes) {
|
||||
addIdentityProviderToConsumerRealm(IdentityProviderMapperSyncMode.LEGACY);
|
||||
testValueMapping(initialUserAttributes, modifiedUserAttributes, modifiedUserAttributes);
|
||||
}
|
||||
|
||||
private void testValueMapping(Map<String, List<String>> initialUserAttributes, Map<String, List<String>> modifiedUserAttributes, Map<String, List<String>> assertedModifiedAttributes) {
|
||||
String email = bc.getUserEmail();
|
||||
createUserInProviderRealm(initialUserAttributes);
|
||||
|
||||
|
@ -160,12 +132,23 @@ public abstract class AbstractUserAttributeMapperTest extends AbstractBaseBroker
|
|||
logInAsUserInIDP();
|
||||
userRep = findUser(bc.consumerRealmName(), bc.getUserLogin(), email);
|
||||
|
||||
assertUserAttributes(modifiedUserAttributes, userRep);
|
||||
assertUserAttributes(assertedModifiedAttributes, userRep);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicMappingSingleValue() {
|
||||
testValueMapping(ImmutableMap.<String, List<String>>builder()
|
||||
public void testBasicMappingSingleValueForce() {
|
||||
testValueMappingForForceSyncMode(ImmutableMap.<String, List<String>>builder()
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("value 1").build())
|
||||
.build(),
|
||||
ImmutableMap.<String, List<String>>builder()
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("second value").build())
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicMappingSingleValueImport() {
|
||||
testValueMappingForImportSyncMode(ImmutableMap.<String, List<String>>builder()
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("value 1").build())
|
||||
.build(),
|
||||
ImmutableMap.<String, List<String>>builder()
|
||||
|
@ -176,7 +159,7 @@ public abstract class AbstractUserAttributeMapperTest extends AbstractBaseBroker
|
|||
|
||||
@Test
|
||||
public void testBasicMappingEmail() {
|
||||
testValueMapping(ImmutableMap.<String, List<String>>builder()
|
||||
testValueMappingForForceSyncMode(ImmutableMap.<String, List<String>>builder()
|
||||
.put("email", ImmutableList.<String>builder().add(bc.getUserEmail()).build())
|
||||
.put("nested.email", ImmutableList.<String>builder().add(bc.getUserEmail()).build())
|
||||
.put("dotted.email", ImmutableList.<String>builder().add(bc.getUserEmail()).build())
|
||||
|
@ -190,8 +173,8 @@ public abstract class AbstractUserAttributeMapperTest extends AbstractBaseBroker
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testBasicMappingClearValue() {
|
||||
testValueMapping(ImmutableMap.<String, List<String>>builder()
|
||||
public void testBasicMappingAttributeGetsModifiedInSyncModeForce() {
|
||||
testValueMappingForForceSyncMode(ImmutableMap.<String, List<String>>builder()
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("value 1").build())
|
||||
.build(),
|
||||
ImmutableMap.<String, List<String>>builder()
|
||||
|
@ -201,8 +184,8 @@ public abstract class AbstractUserAttributeMapperTest extends AbstractBaseBroker
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testBasicMappingRemoveValue() {
|
||||
testValueMapping(ImmutableMap.<String, List<String>>builder()
|
||||
public void testBasicMappingAttributeGetsRemovedInSyncModeForce() {
|
||||
testValueMappingForForceSyncMode(ImmutableMap.<String, List<String>>builder()
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("value 1").build())
|
||||
.build(),
|
||||
ImmutableMap.<String, List<String>>builder()
|
||||
|
@ -211,8 +194,8 @@ public abstract class AbstractUserAttributeMapperTest extends AbstractBaseBroker
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testBasicMappingMultipleValues() {
|
||||
testValueMapping(ImmutableMap.<String, List<String>>builder()
|
||||
public void testBasicMappingAttributeWithMultipleValuesIsModifiedInSyncModeForce() {
|
||||
testValueMappingForForceSyncMode(ImmutableMap.<String, List<String>>builder()
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("value 1").add("value 2").build())
|
||||
.build(),
|
||||
ImmutableMap.<String, List<String>>builder()
|
||||
|
@ -222,8 +205,9 @@ public abstract class AbstractUserAttributeMapperTest extends AbstractBaseBroker
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testAddBasicMappingMultipleValues() {
|
||||
testValueMapping(ImmutableMap.<String, List<String>>builder()
|
||||
public void testBasicMappingAttributeWithMultipleValuesIsModifiedInSyncModeLegacy() {
|
||||
testValueMappingForLegacySyncMode(ImmutableMap.<String, List<String>>builder()
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("value 1").add("value 2").build())
|
||||
.build(),
|
||||
ImmutableMap.<String, List<String>>builder()
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("second value").add("second value 2").build())
|
||||
|
@ -232,11 +216,32 @@ public abstract class AbstractUserAttributeMapperTest extends AbstractBaseBroker
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteBasicMappingMultipleValues() {
|
||||
testValueMapping(ImmutableMap.<String, List<String>>builder()
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("second value").add("second value 2").build())
|
||||
public void testBasicMappingAttributeWithMultipleValuesDoesNotGetModifiedInSyncModeImport() {
|
||||
testValueMappingForImportSyncMode(ImmutableMap.<String, List<String>>builder()
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("value 1").add("value 2").build())
|
||||
.build(),
|
||||
ImmutableMap.<String, List<String>>builder()
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("second value").add("second value 2").build())
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicMappingAttributeWithMultipleValuesGetsAddedInSyncModeForce() {
|
||||
testValueMappingForForceSyncMode(ImmutableMap.<String, List<String>>builder()
|
||||
.build(),
|
||||
ImmutableMap.<String, List<String>>builder()
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("second value").add("second value 2").build())
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicMappingAttributeWithMultipleValuesDoesNotGetAddedInSyncModeImport() {
|
||||
testValueMappingForImportSyncMode(ImmutableMap.<String, List<String>>builder()
|
||||
.build(),
|
||||
ImmutableMap.<String, List<String>>builder()
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("second value").add("second value 2").build())
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
|
||||
import static org.keycloak.testsuite.broker.KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:external.martin.idel@bosch.io">Martin Idel</a>,
|
||||
*/
|
||||
public abstract class AbstractUsernameTemplateMapperTest extends AbstractIdentityProviderMapperTest {
|
||||
|
||||
protected abstract String getMapperTemplate();
|
||||
|
||||
protected abstract void createMapperInIdp(IdentityProviderRepresentation idp, IdentityProviderMapperSyncMode syncMode);
|
||||
|
||||
@Test
|
||||
public void testUsernameGetsInsertedFromClaim() {
|
||||
loginAsUserTwiceWithMapperWillNotUpdateUsername(IdentityProviderMapperSyncMode.IMPORT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsernameGetsUpdatedFromClaimInForceMode() {
|
||||
loginAsUserTwiceWithMapperUpdatesUsername(IdentityProviderMapperSyncMode.FORCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsernameDoesNotGetUpdatedInLegacyMode() {
|
||||
loginAsUserTwiceWithMapperWillNotUpdateUsername(IdentityProviderMapperSyncMode.LEGACY);
|
||||
}
|
||||
|
||||
private void loginAsUserTwiceWithMapperUpdatesUsername(IdentityProviderMapperSyncMode syncMode) {
|
||||
loginAsUserTwiceWithMapper(syncMode, "customusername", "newname", true);
|
||||
}
|
||||
|
||||
private void loginAsUserTwiceWithMapperWillNotUpdateUsername(IdentityProviderMapperSyncMode syncMode) {
|
||||
loginAsUserTwiceWithMapper(syncMode, "customusername", "newname", false);
|
||||
}
|
||||
|
||||
private void loginAsUserTwiceWithMapper(
|
||||
IdentityProviderMapperSyncMode syncMode, String userName, String updatedUserName, boolean updatingUserName) {
|
||||
final IdentityProviderRepresentation idp = setupIdentityProvider();
|
||||
createMapperInIdp(idp, syncMode);
|
||||
// The ATTRIBUTE_TO_MAP_NAME gets mapped to a claim by the setup. It's value will always be an array, therefore the [] around the value
|
||||
createUserInProviderRealm(ImmutableMap.<String, List<String>>builder()
|
||||
.put(ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add(userName).build())
|
||||
.build());
|
||||
|
||||
logInAsUserInIDPForFirstTime();
|
||||
|
||||
String mappedUserName = String.format(getMapperTemplate(), userName);
|
||||
findUser(bc.consumerRealmName(), mappedUserName, bc.getUserEmail());
|
||||
|
||||
logoutFromRealm(bc.consumerRealmName());
|
||||
|
||||
updateUser(updatedUserName);
|
||||
|
||||
logInAsUserInIDP();
|
||||
String updatedMappedUserName = String.format(getMapperTemplate(), updatedUserName);
|
||||
UserRepresentation user = findUser(bc.consumerRealmName(), updatingUserName ? updatedMappedUserName : mappedUserName, bc.getUserEmail());
|
||||
if (updatingUserName) {
|
||||
assertThat(user.getUsername(), is(updatedMappedUserName));
|
||||
} else {
|
||||
assertThat(user.getUsername(), is(mappedUserName));
|
||||
}
|
||||
}
|
||||
|
||||
// We don't want to update the username - that needs to be done by the mapper
|
||||
@Override
|
||||
protected void logInAsUserInIDPForFirstTime() {
|
||||
logInAsUserInIDP();
|
||||
|
||||
waitForPage(driver, "update account information", false);
|
||||
|
||||
Assert.assertTrue(updateAccountInformationPage.isCurrent());
|
||||
Assert.assertTrue("We must be on correct realm right now",
|
||||
driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"));
|
||||
|
||||
log.debug("Updating info on updateAccount page");
|
||||
updateAccountInformationPage.updateAccountInformation(bc.getUserEmail(), "FirstName", "LastName");
|
||||
}
|
||||
|
||||
private void updateUser(String updatedUserName) {
|
||||
UserRepresentation user = findUser(bc.providerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
ImmutableMap<String, List<String>> matchingAttributes = ImmutableMap.<String, List<String>>builder()
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add(updatedUserName).build())
|
||||
.build();
|
||||
user.setAttributes(matchingAttributes);
|
||||
adminClient.realm(bc.providerRealmName()).users().get(user.getId()).update(user);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE;
|
||||
import static org.keycloak.models.IdentityProviderMapperSyncMode.LEGACY;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.broker.saml.mappers.AttributeToRoleMapper;
|
||||
import org.keycloak.broker.saml.mappers.UserAttributeMapper;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:external.martin.idel@bosch.io">Martin Idel</a>
|
||||
*/
|
||||
public class AttributeToRoleMapperTest extends AbstractRoleMapperTest {
|
||||
|
||||
@Override
|
||||
protected BrokerConfiguration getBrokerConfiguration() {
|
||||
return new KcSamlBrokerConfiguration();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapperGrantsRoleOnFirstLogin() {
|
||||
UserRepresentation user = createMapperThenLoginAsUserTwiceWithAttributeToRoleMapper(FORCE);
|
||||
|
||||
assertThatRoleHasBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBrokeredUserGrantsRoleInLegacyMode() {
|
||||
UserRepresentation user = loginAsUserThenCreateMapperAndLoginAgainWithAttributeToRoleMapper(LEGACY);
|
||||
|
||||
assertThatRoleHasBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBrokeredUserGrantsRoleInForceMode() {
|
||||
UserRepresentation user = loginAsUserThenCreateMapperAndLoginAgainWithAttributeToRoleMapper(FORCE);
|
||||
|
||||
assertThatRoleHasBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
private UserRepresentation createMapperThenLoginAsUserTwiceWithAttributeToRoleMapper(IdentityProviderMapperSyncMode syncMode) {
|
||||
return loginAsUserTwiceWithMapper(syncMode, false,
|
||||
ImmutableMap.<String, List<String>>builder()
|
||||
.put("Role", ImmutableList.<String>builder().add(CLIENT_ROLE_MAPPER_REPRESENTATION).build())
|
||||
.build());
|
||||
}
|
||||
|
||||
private UserRepresentation loginAsUserThenCreateMapperAndLoginAgainWithAttributeToRoleMapper(IdentityProviderMapperSyncMode syncMode) {
|
||||
return loginAsUserTwiceWithMapper(syncMode, true,
|
||||
ImmutableMap.<String, List<String>>builder()
|
||||
.put("Role", ImmutableList.<String>builder().add(CLIENT_ROLE_MAPPER_REPRESENTATION).build())
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createMapperInIdp(IdentityProviderRepresentation idp, IdentityProviderMapperSyncMode syncMode) {
|
||||
IdentityProviderMapperRepresentation samlAttributeToRoleMapper = new IdentityProviderMapperRepresentation();
|
||||
samlAttributeToRoleMapper.setName("user-role-mapper");
|
||||
samlAttributeToRoleMapper.setIdentityProviderMapper(AttributeToRoleMapper.PROVIDER_ID);
|
||||
samlAttributeToRoleMapper.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(UserAttributeMapper.ATTRIBUTE_NAME, "Role")
|
||||
.put(ATTRIBUTE_VALUE, ROLE_USER)
|
||||
.put("role", CLIENT_ROLE_MAPPER_REPRESENTATION)
|
||||
.build());
|
||||
|
||||
IdentityProviderResource idpResource = realm.identityProviders().get(idp.getAlias());
|
||||
samlAttributeToRoleMapper.setIdentityProviderAlias(bc.getIDPAlias());
|
||||
idpResource.addMapper(samlAttributeToRoleMapper).close();
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
|
@ -30,7 +31,14 @@ public interface BrokerConfiguration {
|
|||
/**
|
||||
* @return Representation of the identity provider for declaration in the broker
|
||||
*/
|
||||
IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext);
|
||||
default IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
|
||||
return setUpIdentityProvider(suiteContext, IdentityProviderSyncMode.IMPORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Representation of the identity provider for declaration in the broker
|
||||
*/
|
||||
IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext, IdentityProviderSyncMode force);
|
||||
|
||||
/**
|
||||
* @return Name of realm containing identity provider. Must be consistent with {@link #createProviderRealm()}
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.broker.oidc.mappers.ExternalKeycloakRoleToRoleMapper;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import static org.keycloak.models.IdentityProviderMapperSyncMode.*;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:external.martin.idel@bosch.io">Martin Idel</a>
|
||||
*/
|
||||
public class ExternalKeycloakRoleToRoleMapperTest extends AbstractRoleMapperTest {
|
||||
private RealmResource realm;
|
||||
private boolean deleteRoleFromUser = true;
|
||||
|
||||
@Override
|
||||
protected BrokerConfiguration getBrokerConfiguration() {
|
||||
return new KcOidcBrokerConfiguration();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setupRealm() {
|
||||
super.addClients();
|
||||
realm = adminClient.realm(bc.consumerRealmName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapperGrantsRoleOnFirstLogin() {
|
||||
UserRepresentation user = createMapperThenLoginAsUserTwiceWithExternalKeycloakRoleToRoleMapper(IMPORT);
|
||||
|
||||
assertThatRoleHasBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBrokeredUserDoesNotGrantRoleInLegacyMode() {
|
||||
UserRepresentation user = loginAsUserThenCreateMapperAndLoginAgainWithExternalKeycloakRoleToRoleMapper(LEGACY);
|
||||
|
||||
assertThatRoleHasNotBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBrokeredUserGrantsRoleInForceMode() {
|
||||
UserRepresentation user = loginAsUserThenCreateMapperAndLoginAgainWithExternalKeycloakRoleToRoleMapper(FORCE);
|
||||
|
||||
assertThatRoleHasBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBrokeredUserMatchDeletesRoleInForceMode() {
|
||||
UserRepresentation user = createMapperThenLoginAsUserTwiceWithExternalKeycloakRoleToRoleMapper(FORCE);
|
||||
|
||||
assertThatRoleHasNotBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBrokeredUserMatchDoesNotDeleteRoleInLegacyMode() {
|
||||
UserRepresentation user = createMapperThenLoginAsUserTwiceWithExternalKeycloakRoleToRoleMapper(LEGACY);
|
||||
|
||||
assertThatRoleHasBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
private UserRepresentation createMapperThenLoginAsUserTwiceWithExternalKeycloakRoleToRoleMapper(IdentityProviderMapperSyncMode syncMode) {
|
||||
return loginAsUserTwiceWithMapper(syncMode, false, ImmutableMap.<String, List<String>>builder().build());
|
||||
}
|
||||
|
||||
private UserRepresentation loginAsUserThenCreateMapperAndLoginAgainWithExternalKeycloakRoleToRoleMapper(IdentityProviderMapperSyncMode syncMode) {
|
||||
deleteRoleFromUser = false;
|
||||
return loginAsUserTwiceWithMapper(syncMode, true, ImmutableMap.<String, List<String>>builder().build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createMapperInIdp(IdentityProviderRepresentation idp, IdentityProviderMapperSyncMode syncMode) {
|
||||
IdentityProviderMapperRepresentation externalRoleToRoleMapper = new IdentityProviderMapperRepresentation();
|
||||
externalRoleToRoleMapper.setName("external-keycloak-role-mapper");
|
||||
externalRoleToRoleMapper.setIdentityProviderMapper(ExternalKeycloakRoleToRoleMapper.PROVIDER_ID);
|
||||
externalRoleToRoleMapper.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put("external.role", ROLE_USER)
|
||||
.put("role", CLIENT_ROLE_MAPPER_REPRESENTATION)
|
||||
.build());
|
||||
|
||||
IdentityProviderResource idpResource = realm.identityProviders().get(idp.getAlias());
|
||||
externalRoleToRoleMapper.setIdentityProviderAlias(bc.getIDPAlias());
|
||||
idpResource.addMapper(externalRoleToRoleMapper).close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUser() {
|
||||
if (deleteRoleFromUser) {
|
||||
RoleRepresentation role = adminClient.realm(bc.providerRealmName()).roles().get(ROLE_USER).toRepresentation();
|
||||
UserResource userResource = adminClient.realm(bc.providerRealmName()).users().get(userId);
|
||||
userResource.roles().realmLevel().remove(Collections.singletonList(role));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE;
|
||||
import static org.keycloak.models.IdentityProviderMapperSyncMode.IMPORT;
|
||||
import static org.keycloak.models.IdentityProviderMapperSyncMode.LEGACY;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.broker.provider.ConfigConstants;
|
||||
import org.keycloak.broker.provider.HardcodedRoleMapper;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:external.martin.idel@bosch.io">Martin Idel</a>
|
||||
*/
|
||||
public class HardcodedRoleMapperTest extends AbstractRoleMapperTest {
|
||||
private RealmResource realm;
|
||||
|
||||
@Override
|
||||
protected BrokerConfiguration getBrokerConfiguration() {
|
||||
return new KcOidcBrokerConfiguration();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setupRealm() {
|
||||
super.addClients();
|
||||
realm = adminClient.realm(bc.consumerRealmName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapperGrantsRoleOnFirstLogin() {
|
||||
UserRepresentation user = createMapperThenLoginAsUserTwiceWithHardcodedRoleMapper(IMPORT);
|
||||
|
||||
assertThatRoleHasBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapperDoesNotGrantRoleInModeImportIfMapperIsAddedLater() {
|
||||
UserRepresentation user = loginAsUserThenCreateMapperAndLoginAgainWithHardcodedRoleMapper(IMPORT);
|
||||
|
||||
assertThatRoleHasNotBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBrokeredUserDoesNotGrantRoleInLegacyMode() {
|
||||
UserRepresentation user = loginAsUserThenCreateMapperAndLoginAgainWithHardcodedRoleMapper(LEGACY);
|
||||
|
||||
assertThatRoleHasNotBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBrokeredUserGrantsRoleInForceMode() {
|
||||
UserRepresentation user = loginAsUserThenCreateMapperAndLoginAgainWithHardcodedRoleMapper(FORCE);
|
||||
|
||||
assertThatRoleHasBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBrokeredUserMatchDoesntDeleteRole() {
|
||||
UserRepresentation user = createMapperThenLoginAsUserTwiceWithHardcodedRoleMapper(FORCE);
|
||||
|
||||
assertThatRoleHasBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
private UserRepresentation createMapperThenLoginAsUserTwiceWithHardcodedRoleMapper(IdentityProviderMapperSyncMode syncMode) {
|
||||
return loginAsUserTwiceWithMapper(syncMode, false, new HashMap<>());
|
||||
}
|
||||
|
||||
private UserRepresentation loginAsUserThenCreateMapperAndLoginAgainWithHardcodedRoleMapper(IdentityProviderMapperSyncMode syncMode) {
|
||||
return loginAsUserTwiceWithMapper(syncMode, true, new HashMap<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createMapperInIdp(IdentityProviderRepresentation idp, IdentityProviderMapperSyncMode syncMode) {
|
||||
IdentityProviderMapperRepresentation advancedClaimToRoleMapper = new IdentityProviderMapperRepresentation();
|
||||
advancedClaimToRoleMapper.setName("oidc-hardcoded-role-mapper");
|
||||
advancedClaimToRoleMapper.setIdentityProviderMapper(HardcodedRoleMapper.PROVIDER_ID);
|
||||
advancedClaimToRoleMapper.setConfig(ImmutableMap.<String, String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(ConfigConstants.ROLE, CLIENT_ROLE_MAPPER_REPRESENTATION)
|
||||
.build());
|
||||
|
||||
IdentityProviderResource idpResource = realm.identityProviders().get(idp.getAlias());
|
||||
advancedClaimToRoleMapper.setIdentityProviderAlias(bc.getIDPAlias());
|
||||
idpResource.addMapper(advancedClaimToRoleMapper).close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE;
|
||||
import static org.keycloak.models.IdentityProviderMapperSyncMode.IMPORT;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.broker.provider.HardcodedAttributeMapper;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
/**
|
||||
* <a href="mailto:external.martin.idel@bosch.io">Martin Idel</a>,
|
||||
*/
|
||||
public class HardcodedUserAttributeMapperTest extends AbstractIdentityProviderMapperTest {
|
||||
|
||||
private static final String USER_ATTRIBUTE = "user-attribute";
|
||||
private static final String USER_ATTRIBUTE_VALUE = "user-attribute";
|
||||
|
||||
@Test
|
||||
public void addHardcodedAttributeOnFirstLogin() {
|
||||
final IdentityProviderRepresentation idp = setupIdentityProvider();
|
||||
createMapperInIdp(idp, IMPORT);
|
||||
createUserInProviderRealm();
|
||||
|
||||
logInAsUserInIDPForFirstTime();
|
||||
|
||||
UserRepresentation user = findUser(bc.consumerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
assertThatAttributeHasBeenAssigned(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hardcodedAttributeGetsAddedEvenIfMapperIsAddedLaterInSyncModeForce() {
|
||||
UserRepresentation user = loginAsUserTwiceWithMapper(FORCE, true);
|
||||
|
||||
assertThatAttributeHasBeenAssigned(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hardcodedAttributeDoesNotGetAddedIfMapperIsAddedLaterInSyncModeImport() {
|
||||
UserRepresentation user = loginAsUserTwiceWithMapper(IMPORT, true);
|
||||
|
||||
assertThatAttributeHasNotBeenAssigned(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hardcodedAttributeDoesNotGetAddedAgainInSyncModeImport() {
|
||||
UserRepresentation user = loginAsUserTwiceWithMapper(IMPORT, false);
|
||||
|
||||
assertThatAttributeHasNotBeenAssigned(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hardcodedAttributeGetsUpdatedInSyncModeForce() {
|
||||
UserRepresentation user = loginAsUserTwiceWithMapper(FORCE, false);
|
||||
|
||||
assertThatAttributeHasBeenAssigned(user);
|
||||
}
|
||||
|
||||
protected UserRepresentation loginAsUserTwiceWithMapper(
|
||||
IdentityProviderMapperSyncMode syncMode, boolean createAfterFirstLogin) {
|
||||
final IdentityProviderRepresentation idp = setupIdentityProvider();
|
||||
if (!createAfterFirstLogin) {
|
||||
createMapperInIdp(idp, syncMode);
|
||||
}
|
||||
createUserInProviderRealm();
|
||||
|
||||
logInAsUserInIDPForFirstTime();
|
||||
|
||||
UserRepresentation user = findUser(bc.consumerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
if (!createAfterFirstLogin) {
|
||||
assertThatAttributeHasBeenAssigned(user);
|
||||
} else {
|
||||
assertThatAttributeHasNotBeenAssigned(user);
|
||||
}
|
||||
|
||||
if (createAfterFirstLogin) {
|
||||
createMapperInIdp(idp, syncMode);
|
||||
}
|
||||
logoutFromRealm(bc.consumerRealmName());
|
||||
|
||||
if (user.getAttributes() != null) {
|
||||
user.setAttributes(new HashMap<>());
|
||||
}
|
||||
adminClient.realm(bc.consumerRealmName()).users().get(user.getId()).update(user);
|
||||
|
||||
logInAsUserInIDP();
|
||||
return findUser(bc.consumerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
}
|
||||
|
||||
protected void createMapperInIdp(IdentityProviderRepresentation idp, IdentityProviderMapperSyncMode syncMode) {
|
||||
IdentityProviderMapperRepresentation advancedClaimToRoleMapper = new IdentityProviderMapperRepresentation();
|
||||
advancedClaimToRoleMapper.setName("hardcoded-attribute-mapper");
|
||||
advancedClaimToRoleMapper.setIdentityProviderMapper(HardcodedAttributeMapper.PROVIDER_ID);
|
||||
advancedClaimToRoleMapper.setConfig(ImmutableMap.<String, String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(HardcodedAttributeMapper.ATTRIBUTE, USER_ATTRIBUTE)
|
||||
.put(HardcodedAttributeMapper.ATTRIBUTE_VALUE, USER_ATTRIBUTE_VALUE)
|
||||
.build());
|
||||
|
||||
IdentityProviderResource idpResource = realm.identityProviders().get(idp.getAlias());
|
||||
advancedClaimToRoleMapper.setIdentityProviderAlias(bc.getIDPAlias());
|
||||
idpResource.addMapper(advancedClaimToRoleMapper).close();
|
||||
}
|
||||
|
||||
protected void createUserInProviderRealm() {
|
||||
createUserInProviderRealm(new HashMap<>());
|
||||
}
|
||||
|
||||
protected void assertThatAttributeHasBeenAssigned(UserRepresentation user) {
|
||||
assertThat(user.getAttributes().get(USER_ATTRIBUTE), contains(USER_ATTRIBUTE_VALUE));
|
||||
}
|
||||
|
||||
protected void assertThatAttributeHasNotBeenAssigned(UserRepresentation user) {
|
||||
if (user.getAttributes() != null) {
|
||||
assertThat(user.getAttributes().get(USER_ATTRIBUTE), not(contains(USER_ATTRIBUTE_VALUE)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BrokerConfiguration getBrokerConfiguration() {
|
||||
return new KcOidcBrokerConfiguration();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.admin.client.resource.ProtocolMappersResource;
|
||||
import org.keycloak.broker.oidc.mappers.AbstractJsonUserAttributeMapper;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.protocol.oidc.mappers.HardcodedClaim;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.social.github.GitHubUserAttributeMapper;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE;
|
||||
import static org.keycloak.models.IdentityProviderMapperSyncMode.IMPORT;
|
||||
import static org.keycloak.models.IdentityProviderMapperSyncMode.LEGACY;
|
||||
import static org.keycloak.testsuite.broker.KcOidcBrokerConfiguration.HARDOCDED_CLAIM;
|
||||
import static org.keycloak.testsuite.broker.KcOidcBrokerConfiguration.HARDOCDED_VALUE;
|
||||
import static org.keycloak.testsuite.broker.KcOidcBrokerConfiguration.USER_INFO_CLAIM;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:external.martin.idel@bosch.io">Martin Idel</a>
|
||||
*/
|
||||
public class JsonUserAttributeMapperTest extends AbstractIdentityProviderMapperTest {
|
||||
|
||||
public static final String USER_ATTRIBUTE = "user-attribute";
|
||||
|
||||
@Override
|
||||
protected BrokerConfiguration getBrokerConfiguration() {
|
||||
return new KcOidcBrokerConfiguration();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWithIdentityProviderMapsJsonAttributeToUserAttributeButDoesNotModify() {
|
||||
UserRepresentation user = createMapperThenModifyAttribute(IMPORT, "new-value");
|
||||
|
||||
assertUserAttribute(HARDOCDED_VALUE, user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWithIdentityProviderDeletesAttributeInForceMode() {
|
||||
UserRepresentation user = createMapperThenDeleteAttribute(FORCE);
|
||||
|
||||
assertAbsentUserAttribute(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWithIdentityProviderDoesNotDeleteAttributeInLegacyMode() {
|
||||
UserRepresentation user = createMapperThenDeleteAttribute(LEGACY);
|
||||
|
||||
assertUserAttribute(HARDOCDED_VALUE, user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWithIdentityProviderModifiesAttributeInForceMode() {
|
||||
UserRepresentation user = createMapperThenModifyAttribute(FORCE, "new-value");
|
||||
|
||||
assertUserAttribute("new-value", user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWithIdentityProviderAddsUserAttributeInForceNameWhenMapperIsCreatedLater() {
|
||||
UserRepresentation user = loginAndThenCreateMapperThenLoginAgain(FORCE);
|
||||
|
||||
assertUserAttribute(HARDOCDED_VALUE, user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWithIdentityProviderDoesNotAddUserAttributeInImportNameWhenMapperIsCreatedLater() {
|
||||
UserRepresentation user = loginAndThenCreateMapperThenLoginAgain(IMPORT);
|
||||
|
||||
assertAbsentUserAttribute(user);
|
||||
}
|
||||
|
||||
private UserRepresentation loginAndThenCreateMapperThenLoginAgain(IdentityProviderMapperSyncMode syncMode) {
|
||||
return loginAsUserTwiceWithMapper(syncMode, true, HARDOCDED_CLAIM, HARDOCDED_VALUE);
|
||||
}
|
||||
|
||||
private UserRepresentation createMapperThenDeleteAttribute(IdentityProviderMapperSyncMode syncMode) {
|
||||
return loginAsUserTwiceWithMapper(syncMode, false, "deleted", "deleted");
|
||||
}
|
||||
|
||||
private UserRepresentation createMapperThenModifyAttribute(IdentityProviderMapperSyncMode syncMode, String updatedValue) {
|
||||
return loginAsUserTwiceWithMapper(syncMode, false, HARDOCDED_CLAIM, updatedValue);
|
||||
}
|
||||
|
||||
private UserRepresentation loginAsUserTwiceWithMapper(
|
||||
IdentityProviderMapperSyncMode syncMode, boolean createAfterFirstLogin, String claim, String updatedValue) {
|
||||
final IdentityProviderRepresentation idp = setupIdentityProvider();
|
||||
if (!createAfterFirstLogin) {
|
||||
createGithubProviderMapper(idp, syncMode);
|
||||
}
|
||||
createUserInProviderRealm(new HashMap<>());
|
||||
|
||||
logInAsUserInIDPForFirstTime();
|
||||
|
||||
UserRepresentation user = findUser(bc.consumerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
if (!createAfterFirstLogin) {
|
||||
assertUserAttribute(HARDOCDED_VALUE, user);
|
||||
} else {
|
||||
assertAbsentUserAttribute(user);
|
||||
}
|
||||
|
||||
if (createAfterFirstLogin) {
|
||||
createGithubProviderMapper(idp, syncMode);
|
||||
}
|
||||
logoutFromRealm(bc.consumerRealmName());
|
||||
|
||||
if (!createAfterFirstLogin) {
|
||||
updateClaimSentToIDP(claim, updatedValue);
|
||||
}
|
||||
|
||||
logInAsUserInIDP();
|
||||
return findUser(bc.consumerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
}
|
||||
|
||||
private void updateClaimSentToIDP(String claim, String updatedValue) {
|
||||
ProtocolMapperRepresentation claimMapper = null;
|
||||
ProtocolMappersResource protocolMappers = adminClient.realm(bc.providerRealmName()).clients().get(BrokerTestConstants.CLIENT_ID).getProtocolMappers();
|
||||
for (ProtocolMapperRepresentation representation : protocolMappers.getMappers()) {
|
||||
if (representation.getProtocolMapper().equals(HardcodedClaim.PROVIDER_ID)) {
|
||||
claimMapper = representation;
|
||||
}
|
||||
}
|
||||
assertThat(claimMapper, notNullValue());
|
||||
claimMapper.getConfig().put(HardcodedClaim.CLAIM_VALUE, "{\"" + claim + "\": \"" + updatedValue + "\"}");
|
||||
adminClient.realm(bc.providerRealmName()).clients().get(BrokerTestConstants.CLIENT_ID).getProtocolMappers().update(claimMapper.getId(), claimMapper);
|
||||
}
|
||||
|
||||
private void assertUserAttribute(String value, UserRepresentation userRep) {
|
||||
assertThat(userRep.getAttributes(), notNullValue());
|
||||
assertThat(userRep.getAttributes().get(USER_ATTRIBUTE), containsInAnyOrder(value));
|
||||
}
|
||||
|
||||
private void assertAbsentUserAttribute(UserRepresentation userRep) {
|
||||
assertThat(userRep.getAttributes(), nullValue());
|
||||
}
|
||||
|
||||
private void createGithubProviderMapper(IdentityProviderRepresentation idp, IdentityProviderMapperSyncMode syncMode) {
|
||||
IdentityProviderMapperRepresentation githubProvider = new IdentityProviderMapperRepresentation();
|
||||
githubProvider.setName("json-attribute-mapper");
|
||||
githubProvider.setIdentityProviderMapper(GitHubUserAttributeMapper.PROVIDER_ID);
|
||||
githubProvider.setConfig(ImmutableMap.<String, String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(AbstractJsonUserAttributeMapper.CONF_JSON_FIELD, USER_INFO_CLAIM + "." + HARDOCDED_CLAIM)
|
||||
.put(AbstractJsonUserAttributeMapper.CONF_USER_ATTRIBUTE, USER_ATTRIBUTE)
|
||||
.build());
|
||||
|
||||
IdentityProviderResource idpResource = realm.identityProviders().get(idp.getAlias());
|
||||
githubProvider.setIdentityProviderAlias(bc.getIDPAlias());
|
||||
idpResource.addMapper(githubProvider).close();
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.testsuite.arquillian.SuiteContext;
|
||||
|
@ -21,10 +22,10 @@ public class KcOidcBrokerClientSecretBasicAuthTest extends AbstractBrokerTest {
|
|||
private class KcOidcBrokerConfigurationWithBasicAuthAuthentication extends KcOidcBrokerConfiguration {
|
||||
|
||||
@Override
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext, IdentityProviderSyncMode syncMode) {
|
||||
IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID);
|
||||
Map<String, String> config = idp.getConfig();
|
||||
applyDefaultConfiguration(suiteContext, config);
|
||||
applyDefaultConfiguration(suiteContext, config, syncMode);
|
||||
config.put("clientAuthMethod", OIDCLoginProtocol.CLIENT_SECRET_BASIC);
|
||||
return idp;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
|
@ -35,10 +36,10 @@ public class KcOidcBrokerClientSecretJwtTest extends AbstractBrokerTest {
|
|||
}
|
||||
|
||||
@Override
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext, IdentityProviderSyncMode syncMode) {
|
||||
IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID);
|
||||
Map<String, String> config = idp.getConfig();
|
||||
applyDefaultConfiguration(suiteContext, config);
|
||||
applyDefaultConfiguration(suiteContext, config, syncMode);
|
||||
config.put("clientAuthMethod", OIDCLoginProtocol.CLIENT_SECRET_JWT);
|
||||
return idp;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.protocol.ProtocolMapperUtils;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.mappers.HardcodedClaim;
|
||||
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
|
||||
import org.keycloak.protocol.oidc.mappers.UserAttributeMapper;
|
||||
import org.keycloak.protocol.oidc.mappers.UserPropertyMapper;
|
||||
|
@ -29,6 +32,9 @@ public class KcOidcBrokerConfiguration implements BrokerConfiguration {
|
|||
|
||||
protected static final String ATTRIBUTE_TO_MAP_NAME = "user-attribute";
|
||||
protected static final String ATTRIBUTE_TO_MAP_NAME_2 = "user-attribute-2";
|
||||
public static final String USER_INFO_CLAIM = "user-claim";
|
||||
public static final String HARDOCDED_CLAIM = "test";
|
||||
public static final String HARDOCDED_VALUE = "value";
|
||||
|
||||
@Override
|
||||
public RealmRepresentation createProviderRealm() {
|
||||
|
@ -131,7 +137,18 @@ public class KcOidcBrokerConfiguration implements BrokerConfiguration {
|
|||
userAttrMapperConfig2.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true");
|
||||
userAttrMapperConfig2.put(ProtocolMapperUtils.MULTIVALUED, "true");
|
||||
|
||||
client.setProtocolMappers(Arrays.asList(emailMapper, userAttrMapper, userAttrMapper2, nestedAttrMapper, dottedAttrMapper));
|
||||
ProtocolMapperRepresentation hardcodedJsonClaim = new ProtocolMapperRepresentation();
|
||||
hardcodedJsonClaim.setName("json-mapper");
|
||||
hardcodedJsonClaim.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
hardcodedJsonClaim.setProtocolMapper(HardcodedClaim.PROVIDER_ID);
|
||||
|
||||
Map<String, String> hardcodedJsonClaimMapperConfig = hardcodedJsonClaim.getConfig();
|
||||
hardcodedJsonClaimMapperConfig.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, KcOidcBrokerConfiguration.USER_INFO_CLAIM);
|
||||
hardcodedJsonClaimMapperConfig.put(OIDCAttributeMapperHelper.JSON_TYPE, "JSON");
|
||||
hardcodedJsonClaimMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
|
||||
hardcodedJsonClaimMapperConfig.put(HardcodedClaim.CLAIM_VALUE, "{\"" + HARDOCDED_CLAIM + "\": \"" + HARDOCDED_VALUE + "\"}");
|
||||
|
||||
client.setProtocolMappers(Arrays.asList(emailMapper, userAttrMapper, userAttrMapper2, nestedAttrMapper, dottedAttrMapper, hardcodedJsonClaim));
|
||||
|
||||
return Collections.singletonList(client);
|
||||
}
|
||||
|
@ -156,16 +173,17 @@ public class KcOidcBrokerConfiguration implements BrokerConfiguration {
|
|||
}
|
||||
|
||||
@Override
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext, IdentityProviderSyncMode syncMode) {
|
||||
IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID);
|
||||
|
||||
Map<String, String> config = idp.getConfig();
|
||||
applyDefaultConfiguration(suiteContext, config);
|
||||
applyDefaultConfiguration(suiteContext, config, syncMode);
|
||||
|
||||
return idp;
|
||||
}
|
||||
|
||||
protected void applyDefaultConfiguration(final SuiteContext suiteContext, final Map<String, String> config) {
|
||||
protected void applyDefaultConfiguration(final SuiteContext suiteContext, final Map<String, String> config, IdentityProviderSyncMode syncMode) {
|
||||
config.put(IdentityProviderModel.SYNC_MODE, syncMode.toString());
|
||||
config.put("clientId", CLIENT_ID);
|
||||
config.put("clientSecret", CLIENT_SECRET);
|
||||
config.put("prompt", "login");
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.keycloak.testsuite.broker;
|
|||
|
||||
import java.util.Map;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
|
||||
|
||||
|
@ -43,11 +44,11 @@ public class KcOidcBrokerHiddenIdpHintTest extends AbstractInitializedBaseBroker
|
|||
private class KcOidcHiddenBrokerConfiguration extends KcOidcBrokerConfiguration {
|
||||
|
||||
@Override
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext, IdentityProviderSyncMode syncMode) {
|
||||
IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID);
|
||||
|
||||
Map<String, String> config = idp.getConfig();
|
||||
applyDefaultConfiguration(suiteContext, config);
|
||||
applyDefaultConfiguration(suiteContext, config, syncMode);
|
||||
config.put("hideOnLoginPage", "true");
|
||||
return idp;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
|
|||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
|
@ -30,11 +31,11 @@ public class KcOidcBrokerLoginHintTest extends AbstractBrokerTest {
|
|||
private class KcOidcBrokerConfigurationWithLoginHint extends KcOidcBrokerConfiguration {
|
||||
|
||||
@Override
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext, IdentityProviderSyncMode syncMode) {
|
||||
IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID);
|
||||
|
||||
Map<String, String> config = idp.getConfig();
|
||||
applyDefaultConfiguration(suiteContext, config);
|
||||
applyDefaultConfiguration(suiteContext, config, syncMode);
|
||||
config.put("loginHint", "true");
|
||||
return idp;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvid
|
|||
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
|
@ -25,11 +26,11 @@ public class KcOidcBrokerNoLoginHintTest extends AbstractBrokerTest {
|
|||
private class KcOidcBrokerConfigurationWithNoLoginHint extends KcOidcBrokerConfiguration {
|
||||
|
||||
@Override
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext, IdentityProviderSyncMode syncMode) {
|
||||
IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID);
|
||||
|
||||
Map<String, String> config = idp.getConfig();
|
||||
applyDefaultConfiguration(suiteContext, config);
|
||||
applyDefaultConfiguration(suiteContext, config, syncMode);
|
||||
config.put("loginHint", "false");
|
||||
return idp;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
|
@ -31,10 +32,10 @@ public class KcOidcBrokerParameterForwardTest extends AbstractBrokerTest {
|
|||
private class KcOidcBrokerConfigurationWithParameterForward extends KcOidcBrokerConfiguration {
|
||||
|
||||
@Override
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext, IdentityProviderSyncMode syncMode) {
|
||||
IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID);
|
||||
Map<String, String> config = idp.getConfig();
|
||||
applyDefaultConfiguration(suiteContext, config);
|
||||
applyDefaultConfiguration(suiteContext, config, syncMode);
|
||||
config.put("forwardParameters", FORWARDED_PARAMETER +", " + PARAMETER_NOT_SET);
|
||||
return idp;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.keycloak.testsuite.broker;
|
|||
|
||||
import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
|
@ -58,10 +59,10 @@ public class KcOidcBrokerPrivateKeyJwtTest extends AbstractBrokerTest {
|
|||
}
|
||||
|
||||
@Override
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext, IdentityProviderSyncMode syncMode) {
|
||||
IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID);
|
||||
Map<String, String> config = idp.getConfig();
|
||||
applyDefaultConfiguration(suiteContext, config);
|
||||
applyDefaultConfiguration(suiteContext, config, syncMode);
|
||||
config.put("clientSecret", null);
|
||||
config.put("clientAuthMethod", OIDCLoginProtocol.PRIVATE_KEY_JWT);
|
||||
return idp;
|
||||
|
|
|
@ -21,6 +21,8 @@ import java.util.Map;
|
|||
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
|
@ -226,8 +228,9 @@ public class KcOidcBrokerPromptNoneRedirectTest extends AbstractInitializedBaseB
|
|||
* Override the default configuration to unset the {@code prompt} parameter and specify that the IDP accepts forwarded
|
||||
* auth requests with {@code prompt=none}.
|
||||
*/
|
||||
protected void applyDefaultConfiguration(final SuiteContext suiteContext, final Map<String, String> config) {
|
||||
super.applyDefaultConfiguration(suiteContext, config);
|
||||
@Override
|
||||
protected void applyDefaultConfiguration(final SuiteContext suiteContext, final Map<String, String> config, IdentityProviderSyncMode syncMode) {
|
||||
super.applyDefaultConfiguration(suiteContext, config, syncMode);
|
||||
config.remove("prompt");
|
||||
config.put("acceptsPromptNoneForwardFromClient", "true");
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
|
@ -75,8 +76,9 @@ public class KcOidcBrokerPromptParameterTest extends AbstractBrokerTest {
|
|||
}
|
||||
|
||||
private class KcOidcBrokerConfiguration2 extends KcOidcBrokerConfiguration {
|
||||
protected void applyDefaultConfiguration(final SuiteContext suiteContext, final Map<String, String> config) {
|
||||
super.applyDefaultConfiguration(suiteContext, config);
|
||||
@Override
|
||||
protected void applyDefaultConfiguration(final SuiteContext suiteContext, final Map<String, String> config, IdentityProviderSyncMode syncMode) {
|
||||
super.applyDefaultConfiguration(suiteContext, config, syncMode);
|
||||
config.remove("prompt");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,5 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import static org.keycloak.testsuite.admin.ApiUtil.removeUserByUsername;
|
||||
import static org.keycloak.testsuite.broker.BrokerRunOnServerUtil.configurePostBrokerLoginWithOTP;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_PROV_NAME;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestTools.getAuthRoot;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
|
||||
import static org.keycloak.testsuite.util.ProtocolMapperUtil.createHardcodedClaim;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.junit.Test;
|
||||
|
@ -16,20 +7,39 @@ import org.keycloak.admin.client.resource.ClientResource;
|
|||
import org.keycloak.admin.client.resource.ClientsResource;
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
|
||||
import org.keycloak.broker.oidc.mappers.ExternalKeycloakRoleToRoleMapper;
|
||||
import org.keycloak.broker.oidc.mappers.UserAttributeMapper;
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.Matchers.hasItems;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.keycloak.testsuite.admin.ApiUtil.removeUserByUsername;
|
||||
import static org.keycloak.testsuite.broker.BrokerRunOnServerUtil.configurePostBrokerLoginWithOTP;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_PROV_NAME;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestTools.getAuthRoot;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
|
||||
import static org.keycloak.testsuite.util.ProtocolMapperUtil.createHardcodedClaim;
|
||||
|
||||
/**
|
||||
* Final class as it's not intended to be overriden. Feel free to remove "final" if you really know what you are doing.
|
||||
*/
|
||||
|
@ -41,11 +51,12 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<IdentityProviderMapperRepresentation> createIdentityProviderMappers() {
|
||||
protected Iterable<IdentityProviderMapperRepresentation> createIdentityProviderMappers(IdentityProviderMapperSyncMode syncMode) {
|
||||
IdentityProviderMapperRepresentation attrMapper1 = new IdentityProviderMapperRepresentation();
|
||||
attrMapper1.setName("manager-role-mapper");
|
||||
attrMapper1.setIdentityProviderMapper(ExternalKeycloakRoleToRoleMapper.PROVIDER_ID);
|
||||
attrMapper1.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put("external.role", ROLE_MANAGER)
|
||||
.put("role", ROLE_MANAGER)
|
||||
.build());
|
||||
|
@ -54,6 +65,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
|
|||
attrMapper2.setName("user-role-mapper");
|
||||
attrMapper2.setIdentityProviderMapper(ExternalKeycloakRoleToRoleMapper.PROVIDER_ID);
|
||||
attrMapper2.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put("external.role", ROLE_USER)
|
||||
.put("role", ROLE_USER)
|
||||
.build());
|
||||
|
@ -61,6 +73,63 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
|
|||
return Lists.newArrayList(attrMapper1, attrMapper2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createAdditionalMapperWithCustomSyncMode(IdentityProviderMapperSyncMode syncMode) {
|
||||
IdentityProviderMapperRepresentation friendlyManagerMapper = new IdentityProviderMapperRepresentation();
|
||||
friendlyManagerMapper.setName("friendly-manager-role-mapper");
|
||||
friendlyManagerMapper.setIdentityProviderMapper(ExternalKeycloakRoleToRoleMapper.PROVIDER_ID);
|
||||
friendlyManagerMapper.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put("external.role", ROLE_FRIENDLY_MANAGER)
|
||||
.put("role", ROLE_FRIENDLY_MANAGER)
|
||||
.build());
|
||||
friendlyManagerMapper.setIdentityProviderAlias(bc.getIDPAlias());
|
||||
RealmResource realm = adminClient.realm(bc.consumerRealmName());
|
||||
IdentityProviderResource idpResource = realm.identityProviders().get(bc.getIDPAlias());
|
||||
idpResource.addMapper(friendlyManagerMapper).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapperDoesNothingForLegacyMode() {
|
||||
createRolesForRealm(bc.providerRealmName());
|
||||
createRolesForRealm(bc.consumerRealmName());
|
||||
|
||||
createRoleMappersForConsumerRealm(IdentityProviderMapperSyncMode.LEGACY);
|
||||
|
||||
RoleRepresentation managerRole = adminClient.realm(bc.providerRealmName()).roles().get(ROLE_MANAGER).toRepresentation();
|
||||
RoleRepresentation userRole = adminClient.realm(bc.providerRealmName()).roles().get(ROLE_USER).toRepresentation();
|
||||
|
||||
UserResource userResource = adminClient.realm(bc.providerRealmName()).users().get(userId);
|
||||
userResource.roles().realmLevel().add(Collections.singletonList(managerRole));
|
||||
|
||||
logInAsUserInIDPForFirstTime();
|
||||
|
||||
UserResource consumerUserResource = adminClient.realm(bc.consumerRealmName()).users().get(
|
||||
adminClient.realm(bc.consumerRealmName()).users().search(bc.getUserLogin()).get(0).getId());
|
||||
Set<String> currentRoles = consumerUserResource.roles().realmLevel().listAll().stream()
|
||||
.map(RoleRepresentation::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
assertThat(currentRoles, hasItems(ROLE_MANAGER));
|
||||
assertThat(currentRoles, not(hasItems(ROLE_USER)));
|
||||
|
||||
logoutFromRealm(bc.consumerRealmName());
|
||||
|
||||
|
||||
userResource.roles().realmLevel().add(Collections.singletonList(userRole));
|
||||
|
||||
logInAsUserInIDP();
|
||||
|
||||
currentRoles = consumerUserResource.roles().realmLevel().listAll().stream()
|
||||
.map(RoleRepresentation::getName)
|
||||
.collect(Collectors.toSet());
|
||||
assertThat(currentRoles, hasItems(ROLE_MANAGER));
|
||||
assertThat(currentRoles, not(hasItems(ROLE_USER)));
|
||||
|
||||
logoutFromRealm(bc.consumerRealmName());
|
||||
logoutFromRealm(bc.providerRealmName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginFetchingUserFromUserEndpoint() {
|
||||
RealmResource realm = realmsResouce().realm(bc.providerRealmName());
|
||||
|
@ -131,6 +200,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
|
|||
hardCodedSessionNoteMapper.setIdentityProviderAlias(bc.getIDPAlias());
|
||||
hardCodedSessionNoteMapper.setIdentityProviderMapper(UserAttributeMapper.PROVIDER_ID);
|
||||
hardCodedSessionNoteMapper.setConfig(ImmutableMap.<String, String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, IdentityProviderMapperSyncMode.INHERIT.toString())
|
||||
.put(UserAttributeMapper.USER_ATTRIBUTE, "hard-coded")
|
||||
.put(UserAttributeMapper.CLAIM, "hard-coded")
|
||||
.build());
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
|
@ -28,10 +29,10 @@ public class KcOidcBrokerUiLocalesDisabledTest extends AbstractBrokerTest {
|
|||
private class KcOidcBrokerConfigurationWithUiLocalesDisabled extends KcOidcBrokerConfiguration {
|
||||
|
||||
@Override
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext, IdentityProviderSyncMode syncMode) {
|
||||
IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID);
|
||||
Map<String, String> config = idp.getConfig();
|
||||
applyDefaultConfiguration(suiteContext, config);
|
||||
applyDefaultConfiguration(suiteContext, config, syncMode);
|
||||
config.put("uiLocales", "false");
|
||||
return idp;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
|
@ -27,10 +28,10 @@ public class KcOidcBrokerUiLocalesEnabledTest extends AbstractBrokerTest {
|
|||
private class KcOidcBrokerConfigurationWithUiLocalesEnabled extends KcOidcBrokerConfiguration {
|
||||
|
||||
@Override
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext, IdentityProviderSyncMode syncMode) {
|
||||
IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID);
|
||||
Map<String, String> config = idp.getConfig();
|
||||
applyDefaultConfiguration(suiteContext, config);
|
||||
applyDefaultConfiguration(suiteContext, config, syncMode);
|
||||
config.put("uiLocales", "true");
|
||||
return idp;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.testsuite.arquillian.SuiteContext;
|
||||
|
||||
|
@ -13,8 +14,8 @@ public class KcOidcBrokerVaultConfiguration extends KcOidcBrokerConfiguration {
|
|||
public static final KcOidcBrokerVaultConfiguration INSTANCE = new KcOidcBrokerVaultConfiguration();
|
||||
|
||||
@Override
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
|
||||
IdentityProviderRepresentation idpRep = super.setUpIdentityProvider(suiteContext);
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext, IdentityProviderSyncMode syncMode) {
|
||||
IdentityProviderRepresentation idpRep = super.setUpIdentityProvider(suiteContext, syncMode);
|
||||
|
||||
idpRep.getConfig().put("clientSecret", VAULT_CLIENT_SECRET);
|
||||
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.broker.oidc.mappers.UsernameTemplateMapper;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:external.martin.idel@bosch.io">Martin Idel</a>
|
||||
*/
|
||||
public class KcOidcUsernameTemplateMapperTest extends AbstractUsernameTemplateMapperTest {
|
||||
@Override
|
||||
protected void createMapperInIdp(IdentityProviderRepresentation idp, IdentityProviderMapperSyncMode syncMode) {
|
||||
IdentityProviderMapperRepresentation usernameTemplateMapper = new IdentityProviderMapperRepresentation();
|
||||
usernameTemplateMapper.setName("oidc-username-template-mapper");
|
||||
usernameTemplateMapper.setIdentityProviderMapper(UsernameTemplateMapper.PROVIDER_ID);
|
||||
usernameTemplateMapper.setConfig(ImmutableMap.<String, String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put("template", "${ALIAS}-${CLAIM.user-attribute}")
|
||||
.build());
|
||||
|
||||
IdentityProviderResource idpResource = realm.identityProviders().get(idp.getAlias());
|
||||
usernameTemplateMapper.setIdentityProviderAlias(bc.getIDPAlias());
|
||||
idpResource.addMapper(usernameTemplateMapper).close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMapperTemplate() {
|
||||
return "kc-oidc-idp-[%s]";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BrokerConfiguration getBrokerConfiguration() {
|
||||
return new KcOidcBrokerConfiguration();
|
||||
}
|
||||
}
|
|
@ -5,6 +5,8 @@
|
|||
*/
|
||||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.protocol.ProtocolMapperUtils;
|
||||
import org.keycloak.protocol.saml.SamlConfigAttributes;
|
||||
import org.keycloak.protocol.saml.SamlProtocol;
|
||||
|
@ -187,7 +189,7 @@ public class KcSamlBrokerConfiguration implements BrokerConfiguration {
|
|||
}
|
||||
|
||||
@Override
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext, IdentityProviderSyncMode syncMode) {
|
||||
IdentityProviderRepresentation idp = createIdentityProvider(IDP_SAML_ALIAS, IDP_SAML_PROVIDER_ID);
|
||||
|
||||
idp.setTrustEmail(true);
|
||||
|
@ -196,6 +198,7 @@ public class KcSamlBrokerConfiguration implements BrokerConfiguration {
|
|||
|
||||
Map<String, String> config = idp.getConfig();
|
||||
|
||||
config.put(IdentityProviderModel.SYNC_MODE, syncMode.toString());
|
||||
config.put(SINGLE_SIGN_ON_SERVICE_URL, getAuthRoot(suiteContext) + "/auth/realms/" + REALM_PROV_NAME + "/protocol/saml");
|
||||
config.put(SINGLE_LOGOUT_SERVICE_URL, getAuthRoot(suiteContext) + "/auth/realms/" + REALM_PROV_NAME + "/protocol/saml");
|
||||
config.put(NAME_ID_POLICY_FORMAT, "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress");
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.keycloak.broker.oidc.mappers.ExternalKeycloakRoleToRoleMapper;
|
||||
import org.keycloak.broker.saml.mappers.AttributeToRoleMapper;
|
||||
import org.keycloak.broker.saml.mappers.UserAttributeMapper;
|
||||
import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
||||
|
@ -10,6 +13,9 @@ import org.keycloak.dom.saml.v2.assertion.AttributeType;
|
|||
import org.keycloak.dom.saml.v2.assertion.StatementAbstractType;
|
||||
import org.keycloak.dom.saml.v2.protocol.AuthnRequestType;
|
||||
import org.keycloak.dom.saml.v2.protocol.ResponseType;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
@ -59,11 +65,12 @@ public final class KcSamlBrokerTest extends AbstractAdvancedBrokerTest {
|
|||
private static final String EMPTY_ATTRIBUTE_NAME = "empty.attribute.name";
|
||||
|
||||
@Override
|
||||
protected Iterable<IdentityProviderMapperRepresentation> createIdentityProviderMappers() {
|
||||
protected Iterable<IdentityProviderMapperRepresentation> createIdentityProviderMappers(IdentityProviderMapperSyncMode syncMode) {
|
||||
IdentityProviderMapperRepresentation attrMapper1 = new IdentityProviderMapperRepresentation();
|
||||
attrMapper1.setName("manager-role-mapper");
|
||||
attrMapper1.setIdentityProviderMapper(AttributeToRoleMapper.PROVIDER_ID);
|
||||
attrMapper1.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(UserAttributeMapper.ATTRIBUTE_NAME, "Role")
|
||||
.put(ATTRIBUTE_VALUE, ROLE_MANAGER)
|
||||
.put("role", ROLE_MANAGER)
|
||||
|
@ -73,6 +80,7 @@ public final class KcSamlBrokerTest extends AbstractAdvancedBrokerTest {
|
|||
attrMapper2.setName("user-role-mapper");
|
||||
attrMapper2.setIdentityProviderMapper(AttributeToRoleMapper.PROVIDER_ID);
|
||||
attrMapper2.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(UserAttributeMapper.ATTRIBUTE_NAME, "Role")
|
||||
.put(ATTRIBUTE_VALUE, ROLE_USER)
|
||||
.put("role", ROLE_USER)
|
||||
|
@ -82,6 +90,7 @@ public final class KcSamlBrokerTest extends AbstractAdvancedBrokerTest {
|
|||
attrMapper3.setName("friendly-mapper");
|
||||
attrMapper3.setIdentityProviderMapper(AttributeToRoleMapper.PROVIDER_ID);
|
||||
attrMapper3.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(UserAttributeMapper.ATTRIBUTE_FRIENDLY_NAME, AbstractUserAttributeMapperTest.ATTRIBUTE_TO_MAP_FRIENDLY_NAME)
|
||||
.put(ATTRIBUTE_VALUE, ROLE_FRIENDLY_MANAGER)
|
||||
.put("role", ROLE_FRIENDLY_MANAGER)
|
||||
|
@ -91,6 +100,7 @@ public final class KcSamlBrokerTest extends AbstractAdvancedBrokerTest {
|
|||
attrMapper4.setName("user-role-dot-guide-mapper");
|
||||
attrMapper4.setIdentityProviderMapper(AttributeToRoleMapper.PROVIDER_ID);
|
||||
attrMapper4.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(UserAttributeMapper.ATTRIBUTE_NAME, "Role")
|
||||
.put(ATTRIBUTE_VALUE, ROLE_USER_DOT_GUIDE)
|
||||
.put("role", ROLE_USER_DOT_GUIDE)
|
||||
|
@ -100,22 +110,37 @@ public final class KcSamlBrokerTest extends AbstractAdvancedBrokerTest {
|
|||
attrMapper5.setName("empty-attribute-to-role-mapper");
|
||||
attrMapper5.setIdentityProviderMapper(AttributeToRoleMapper.PROVIDER_ID);
|
||||
attrMapper5.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(UserAttributeMapper.ATTRIBUTE_NAME, EMPTY_ATTRIBUTE_NAME)
|
||||
.put(ATTRIBUTE_VALUE, "")
|
||||
.put("role", EMPTY_ATTRIBUTE_ROLE)
|
||||
.build());
|
||||
|
||||
return Arrays.asList(new IdentityProviderMapperRepresentation[] { attrMapper1, attrMapper2, attrMapper3, attrMapper4, attrMapper5 });
|
||||
return Arrays.asList(attrMapper1, attrMapper2, attrMapper3, attrMapper4, attrMapper5 );
|
||||
}
|
||||
|
||||
protected void createAdditionalMapperWithCustomSyncMode(IdentityProviderMapperSyncMode syncMode) {
|
||||
IdentityProviderMapperRepresentation friendlyManagerMapper = new IdentityProviderMapperRepresentation();
|
||||
friendlyManagerMapper.setName("friendly-manager-role-mapper");
|
||||
friendlyManagerMapper.setIdentityProviderMapper(AttributeToRoleMapper.PROVIDER_ID);
|
||||
friendlyManagerMapper.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(UserAttributeMapper.ATTRIBUTE_NAME, "Role")
|
||||
.put(ATTRIBUTE_VALUE, ROLE_FRIENDLY_MANAGER)
|
||||
.put("role", ROLE_FRIENDLY_MANAGER)
|
||||
.build());
|
||||
friendlyManagerMapper.setIdentityProviderAlias(bc.getIDPAlias());
|
||||
RealmResource realm = adminClient.realm(bc.consumerRealmName());
|
||||
IdentityProviderResource idpResource = realm.identityProviders().get(bc.getIDPAlias());
|
||||
idpResource.addMapper(friendlyManagerMapper).close();
|
||||
}
|
||||
|
||||
// KEYCLOAK-3987
|
||||
@Test
|
||||
@Override
|
||||
public void grantNewRoleFromToken() {
|
||||
public void mapperUpdatesRolesOnEveryLogInForLegacyMode() {
|
||||
createRolesForRealm(bc.providerRealmName());
|
||||
createRolesForRealm(bc.consumerRealmName());
|
||||
|
||||
createRoleMappersForConsumerRealm();
|
||||
createRoleMappersForConsumerRealm(IdentityProviderMapperSyncMode.FORCE);
|
||||
|
||||
RoleRepresentation managerRole = adminClient.realm(bc.providerRealmName()).roles().get(ROLE_MANAGER).toRepresentation();
|
||||
RoleRepresentation friendlyManagerRole = adminClient.realm(bc.providerRealmName()).roles().get(ROLE_FRIENDLY_MANAGER).toRepresentation();
|
||||
|
@ -171,7 +196,6 @@ public final class KcSamlBrokerTest extends AbstractAdvancedBrokerTest {
|
|||
createRoleMappersForConsumerRealm();
|
||||
|
||||
RoleRepresentation managerRole = adminClient.realm(bc.providerRealmName()).roles().get(ROLE_MANAGER).toRepresentation();
|
||||
RoleRepresentation friendlyManagerRole = adminClient.realm(bc.providerRealmName()).roles().get(ROLE_FRIENDLY_MANAGER).toRepresentation();
|
||||
RoleRepresentation userRole = adminClient.realm(bc.providerRealmName()).roles().get(ROLE_USER).toRepresentation();
|
||||
RoleRepresentation userRoleDotGuide = adminClient.realm(bc.providerRealmName()).roles().get(ROLE_USER_DOT_GUIDE).toRepresentation();
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.keycloak.testsuite.broker;
|
|||
import org.keycloak.broker.saml.SAMLIdentityProviderConfig;
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
import org.keycloak.dom.saml.v2.protocol.AuthnRequestType;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.protocol.saml.SamlConfigAttributes;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
|
@ -112,8 +113,8 @@ public class KcSamlSignedBrokerTest extends AbstractBrokerTest {
|
|||
}
|
||||
|
||||
@Override
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
|
||||
IdentityProviderRepresentation result = super.setUpIdentityProvider(suiteContext);
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext, IdentityProviderSyncMode syncMode) {
|
||||
IdentityProviderRepresentation result = super.setUpIdentityProvider(suiteContext, syncMode);
|
||||
|
||||
String providerCert = KeyUtils.getActiveKey(adminClient.realm(providerRealmName()).keys().getKeyMetadata(), Algorithm.RS256).getCertificate();
|
||||
Assert.assertThat(providerCert, Matchers.notNullValue());
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
|
@ -61,8 +62,8 @@ public class KcSamlSignedDocumentOnlyBrokerTest extends AbstractBrokerTest {
|
|||
}
|
||||
|
||||
@Override
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
|
||||
IdentityProviderRepresentation result = super.setUpIdentityProvider(suiteContext);
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext, IdentityProviderSyncMode syncMode) {
|
||||
IdentityProviderRepresentation result = super.setUpIdentityProvider(suiteContext, syncMode);
|
||||
|
||||
Map<String, String> config = result.getConfig();
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import static org.keycloak.broker.saml.mappers.UsernameTemplateMapper.PROVIDER_ID;
|
||||
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:external.martin.idel@bosch.io">Martin Idel</a>
|
||||
*/
|
||||
public class KcSamlUsernameTemplateMapperTest extends AbstractUsernameTemplateMapperTest {
|
||||
|
||||
@Override
|
||||
protected void createMapperInIdp(IdentityProviderRepresentation idp, IdentityProviderMapperSyncMode syncMode) {
|
||||
IdentityProviderMapperRepresentation usernameTemplateMapper = new IdentityProviderMapperRepresentation();
|
||||
usernameTemplateMapper.setName("saml-username-template-mapper");
|
||||
usernameTemplateMapper.setIdentityProviderMapper(PROVIDER_ID);
|
||||
usernameTemplateMapper.setConfig(ImmutableMap.<String, String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put("template", "${ALIAS}-${ATTRIBUTE.user-attribute}")
|
||||
.build());
|
||||
|
||||
IdentityProviderResource idpResource = realm.identityProviders().get(idp.getAlias());
|
||||
usernameTemplateMapper.setIdentityProviderAlias(bc.getIDPAlias());
|
||||
idpResource.addMapper(usernameTemplateMapper).close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMapperTemplate() {
|
||||
return "kc-saml-idp-%s";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BrokerConfiguration getBrokerConfiguration() {
|
||||
return new KcSamlBrokerConfiguration();
|
||||
}
|
||||
}
|
|
@ -1,45 +1,32 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE;
|
||||
import static org.keycloak.models.IdentityProviderMapperSyncMode.IMPORT;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.broker.oidc.mappers.AdvancedClaimToRoleMapper;
|
||||
import org.keycloak.broker.provider.ConfigConstants;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.broker.oidc.mappers.AdvancedClaimToRoleMapper;
|
||||
import org.keycloak.broker.provider.ConfigConstants;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.MappingsRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.keycloak.testsuite.admin.ApiUtil.createUserAndResetPasswordWithAdminClient;
|
||||
|
||||
/**
|
||||
* @author hmlnarik, <a href="mailto:external.benjamin.weimer@bosch-si.com">Benjamin Weimer</a>
|
||||
* @author hmlnarik,
|
||||
* <a href="mailto:external.benjamin.weimer@bosch-si.com">Benjamin Weimer</a>,
|
||||
* <a href="mailto:external.martin.idel@bosch.io">Martin Idel</a>
|
||||
*/
|
||||
public class OidcAdvancedClaimToRoleMapperTest extends AbstractBaseBrokerTest {
|
||||
|
||||
private static final String CLIENT = "realm-management";
|
||||
private static final String CLIENT_ROLE = "view-realm";
|
||||
private static final String CLIENT_ROLE_MAPPER_REPRESENTATION = CLIENT + "." + CLIENT_ROLE;
|
||||
public class OidcAdvancedClaimToRoleMapperTest extends AbstractRoleMapperTest {
|
||||
|
||||
private static final String CLAIMS = "[\n" +
|
||||
" {\n" +
|
||||
|
@ -63,17 +50,13 @@ public class OidcAdvancedClaimToRoleMapperTest extends AbstractBaseBrokerTest {
|
|||
" }\n" +
|
||||
"]";
|
||||
|
||||
private String newValueForAttribute2 = "";
|
||||
|
||||
@Override
|
||||
protected BrokerConfiguration getBrokerConfiguration() {
|
||||
return new KcOidcBrokerConfiguration();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void addClients() {
|
||||
addClientsToProviderAndConsumer();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void valueMatchesRegexTest() {
|
||||
AdvancedClaimToRoleMapper advancedClaimToRoleMapper = new AdvancedClaimToRoleMapper();
|
||||
|
@ -98,9 +81,7 @@ public class OidcAdvancedClaimToRoleMapperTest extends AbstractBaseBrokerTest {
|
|||
logInAsUserInIDPForFirstTime();
|
||||
|
||||
UserRepresentation user = findUser(bc.consumerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
assertThatRoleHasBeenAssigned(user);
|
||||
|
||||
logoutFromRealm(bc.consumerRealmName());
|
||||
assertThatRoleHasBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -114,9 +95,7 @@ public class OidcAdvancedClaimToRoleMapperTest extends AbstractBaseBrokerTest {
|
|||
logInAsUserInIDPForFirstTime();
|
||||
|
||||
UserRepresentation user = findUser(bc.consumerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
assertThatRoleHasNotBeenAssigned(user);
|
||||
|
||||
logoutFromRealm(bc.consumerRealmName());
|
||||
assertThatRoleHasNotBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -130,9 +109,7 @@ public class OidcAdvancedClaimToRoleMapperTest extends AbstractBaseBrokerTest {
|
|||
logInAsUserInIDPForFirstTime();
|
||||
|
||||
UserRepresentation user = findUser(bc.consumerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
assertThatRoleHasBeenAssigned(user);
|
||||
|
||||
logoutFromRealm(bc.consumerRealmName());
|
||||
assertThatRoleHasBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -146,9 +123,7 @@ public class OidcAdvancedClaimToRoleMapperTest extends AbstractBaseBrokerTest {
|
|||
logInAsUserInIDPForFirstTime();
|
||||
|
||||
UserRepresentation user = findUser(bc.consumerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
assertThatRoleHasBeenAssigned(user);
|
||||
|
||||
logoutFromRealm(bc.consumerRealmName());
|
||||
assertThatRoleHasBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
|
||||
|
@ -163,84 +138,76 @@ public class OidcAdvancedClaimToRoleMapperTest extends AbstractBaseBrokerTest {
|
|||
logInAsUserInIDPForFirstTime();
|
||||
|
||||
UserRepresentation user = findUser(bc.consumerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
assertThatRoleHasNotBeenAssigned(user);
|
||||
|
||||
logoutFromRealm(bc.consumerRealmName());
|
||||
assertThatRoleHasNotBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBrokeredUserMismatchDeletesRole() {
|
||||
createAdvancedClaimToRoleMapper(CLAIMS, false);
|
||||
createUserInProviderRealm(ImmutableMap.<String, List<String>>builder()
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("value 1").build())
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME_2, ImmutableList.<String>builder().add("value 2").build())
|
||||
.build());
|
||||
newValueForAttribute2 = "value mismatch";
|
||||
UserRepresentation user = createMapperAndLoginAsUserTwiceWithMapper(FORCE, false);
|
||||
|
||||
logInAsUserInIDPForFirstTime();
|
||||
assertThatRoleHasNotBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
UserRepresentation user = findUser(bc.consumerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
assertThatRoleHasBeenAssigned(user);
|
||||
@Test
|
||||
public void updateBrokeredUserMismatchDoesNotDeleteRoleInImportMode() {
|
||||
newValueForAttribute2 = "value mismatch";
|
||||
UserRepresentation user = createMapperAndLoginAsUserTwiceWithMapper(IMPORT, false);
|
||||
|
||||
logoutFromRealm(bc.consumerRealmName());
|
||||
|
||||
// update
|
||||
user = findUser(bc.providerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
ImmutableMap<String, List<String>> mismatchingAttributes = ImmutableMap.<String, List<String>>builder()
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("value 1").build())
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME_2, ImmutableList.<String>builder().add("value mismatch").build())
|
||||
.build();
|
||||
user.setAttributes(mismatchingAttributes);
|
||||
adminClient.realm(bc.providerRealmName()).users().get(user.getId()).update(user);
|
||||
|
||||
logInAsUserInIDP();
|
||||
user = findUser(bc.consumerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
|
||||
assertThatRoleHasNotBeenAssigned(user);
|
||||
assertThatRoleHasBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBrokeredUserMatchDoesntDeleteRole() {
|
||||
createAdvancedClaimToRoleMapper(CLAIMS, false);
|
||||
createUserInProviderRealm(ImmutableMap.<String, List<String>>builder()
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("value 1").build())
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME_2, ImmutableList.<String>builder().add("value 2").build())
|
||||
.build());
|
||||
newValueForAttribute2 = "value 2";
|
||||
UserRepresentation user = createMapperAndLoginAsUserTwiceWithMapper(FORCE, false);
|
||||
|
||||
logInAsUserInIDPForFirstTime();
|
||||
assertThatRoleHasBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
UserRepresentation user = findUser(bc.consumerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
assertThatRoleHasBeenAssigned(user);
|
||||
@Test
|
||||
public void updateBrokeredUserAssignsRoleInForceModeWhenCreatingTheMapperAfterFirstLogin() {
|
||||
newValueForAttribute2 = "value 2";
|
||||
UserRepresentation user = createMapperAndLoginAsUserTwiceWithMapper(FORCE, true);
|
||||
|
||||
logoutFromRealm(bc.consumerRealmName());
|
||||
assertThatRoleHasBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
// update
|
||||
user = findUser(bc.providerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
public UserRepresentation createMapperAndLoginAsUserTwiceWithMapper(IdentityProviderMapperSyncMode syncMode, boolean createAfterFirstLogin) {
|
||||
return loginAsUserTwiceWithMapper(syncMode, createAfterFirstLogin, ImmutableMap.<String, List<String>>builder()
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("value 1").build())
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME_2, ImmutableList.<String>builder().add("value 2").build())
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateUser() {
|
||||
UserRepresentation user = findUser(bc.providerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
ImmutableMap<String, List<String>> matchingAttributes = ImmutableMap.<String, List<String>>builder()
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("value 1").build())
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME_2, ImmutableList.<String>builder().add("value 2").build())
|
||||
.put("some.other.attribute", ImmutableList.<String>builder().add("some value").build())
|
||||
.build();
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("value 1").build())
|
||||
.put(KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME_2, ImmutableList.<String>builder().add(newValueForAttribute2).build())
|
||||
.put("some.other.attribute", ImmutableList.<String>builder().add("some value").build())
|
||||
.build();
|
||||
user.setAttributes(matchingAttributes);
|
||||
adminClient.realm(bc.providerRealmName()).users().get(user.getId()).update(user);
|
||||
}
|
||||
|
||||
logInAsUserInIDP();
|
||||
user = findUser(bc.consumerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
|
||||
assertThatRoleHasBeenAssigned(user);
|
||||
@Override
|
||||
protected void createMapperInIdp(IdentityProviderRepresentation idp, IdentityProviderMapperSyncMode syncMode) {
|
||||
createAdvancedClaimToRoleMapperInIdp(idp, CLAIMS, false, syncMode);
|
||||
}
|
||||
|
||||
private void createAdvancedClaimToRoleMapper(String claimsRepresentation, boolean areClaimValuesRegex) {
|
||||
log.debug("adding identity provider to realm " + bc.consumerRealmName());
|
||||
|
||||
RealmResource realm = adminClient.realm(bc.consumerRealmName());
|
||||
final IdentityProviderRepresentation idp = bc.setUpIdentityProvider(suiteContext);
|
||||
Response resp = realm.identityProviders().create(idp);
|
||||
resp.close();
|
||||
IdentityProviderRepresentation idp = setupIdentityProvider();
|
||||
createAdvancedClaimToRoleMapperInIdp(idp, claimsRepresentation, areClaimValuesRegex, IMPORT);
|
||||
}
|
||||
|
||||
protected void createAdvancedClaimToRoleMapperInIdp(IdentityProviderRepresentation idp , String claimsRepresentation, boolean areClaimValuesRegex, IdentityProviderMapperSyncMode syncMode) {
|
||||
IdentityProviderMapperRepresentation advancedClaimToRoleMapper = new IdentityProviderMapperRepresentation();
|
||||
advancedClaimToRoleMapper.setName("advanced-claim-to-role-mapper");
|
||||
advancedClaimToRoleMapper.setIdentityProviderMapper(AdvancedClaimToRoleMapper.PROVIDER_ID);
|
||||
advancedClaimToRoleMapper.setConfig(ImmutableMap.<String, String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(AdvancedClaimToRoleMapper.CLAIM_PROPERTY_NAME, claimsRepresentation)
|
||||
.put(AdvancedClaimToRoleMapper.ARE_CLAIM_VALUES_REGEX_PROPERTY_NAME, areClaimValuesRegex ? "true" : "false")
|
||||
.put(ConfigConstants.ROLE, CLIENT_ROLE_MAPPER_REPRESENTATION)
|
||||
|
@ -248,52 +215,6 @@ public class OidcAdvancedClaimToRoleMapperTest extends AbstractBaseBrokerTest {
|
|||
|
||||
IdentityProviderResource idpResource = realm.identityProviders().get(idp.getAlias());
|
||||
advancedClaimToRoleMapper.setIdentityProviderAlias(bc.getIDPAlias());
|
||||
resp = idpResource.addMapper(advancedClaimToRoleMapper);
|
||||
resp.close();
|
||||
}
|
||||
|
||||
private void createUserInProviderRealm(Map<String, List<String>> attributes) {
|
||||
log.debug("Creating user in realm " + bc.providerRealmName());
|
||||
|
||||
UserRepresentation user = UserBuilder.create()
|
||||
.username(bc.getUserLogin())
|
||||
.email(bc.getUserEmail())
|
||||
.build();
|
||||
user.setEmailVerified(true);
|
||||
user.setAttributes(attributes);
|
||||
this.userId = createUserAndResetPasswordWithAdminClient(adminClient.realm(bc.providerRealmName()), user, bc.getUserPassword());
|
||||
}
|
||||
|
||||
private UserRepresentation findUser(String realm, String userName, String email) {
|
||||
UsersResource consumerUsers = adminClient.realm(realm).users();
|
||||
|
||||
List<UserRepresentation> users = consumerUsers.list();
|
||||
assertThat("There must be exactly one user", users, hasSize(1));
|
||||
UserRepresentation user = users.get(0);
|
||||
assertThat("Username has to match", user.getUsername(), equalTo(userName));
|
||||
assertThat("Email has to match", user.getEmail(), equalTo(email));
|
||||
|
||||
MappingsRepresentation roles = consumerUsers.get(user.getId()).roles().getAll();
|
||||
|
||||
List<String> realmRoles = roles.getRealmMappings().stream()
|
||||
.map(RoleRepresentation::getName)
|
||||
.collect(Collectors.toList());
|
||||
user.setRealmRoles(realmRoles);
|
||||
|
||||
Map<String, List<String>> clientRoles = new HashMap<>();
|
||||
roles.getClientMappings().forEach((key, value) -> clientRoles.put(key, value.getMappings().stream()
|
||||
.map(RoleRepresentation::getName)
|
||||
.collect(Collectors.toList())));
|
||||
user.setClientRoles(clientRoles);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
private void assertThatRoleHasBeenAssigned(UserRepresentation user) {
|
||||
assertThat(user.getClientRoles().get(CLIENT), contains(CLIENT_ROLE));
|
||||
}
|
||||
|
||||
private void assertThatRoleHasNotBeenAssigned(UserRepresentation user) {
|
||||
assertThat(user.getClientRoles().get(CLIENT), not(contains(CLIENT_ROLE)));
|
||||
idpResource.addMapper(advancedClaimToRoleMapper).close();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import static org.keycloak.models.IdentityProviderMapperSyncMode.FORCE;
|
||||
import static org.keycloak.models.IdentityProviderMapperSyncMode.LEGACY;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.broker.oidc.mappers.ClaimToRoleMapper;
|
||||
import org.keycloak.broker.provider.ConfigConstants;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:external.martin.idel@bosch.io">Martin Idel</a>
|
||||
*/
|
||||
public class OidcClaimToRoleMapperTest extends AbstractRoleMapperTest {
|
||||
|
||||
private static final String CLAIM = KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME;
|
||||
private static final String CLAIM_VALUE = "value 1";
|
||||
private String claimOnSecondLogin = "";
|
||||
|
||||
@Override
|
||||
protected BrokerConfiguration getBrokerConfiguration() {
|
||||
return new KcOidcBrokerConfiguration();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allClaimValuesMatch() {
|
||||
createClaimToRoleMapper(CLAIM_VALUE);
|
||||
createUserInProviderRealm(ImmutableMap.<String, List<String>>builder()
|
||||
.put(CLAIM, ImmutableList.<String>builder().add(CLAIM_VALUE).build())
|
||||
.build());
|
||||
|
||||
logInAsUserInIDPForFirstTime();
|
||||
|
||||
UserRepresentation user = findUser(bc.consumerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
assertThatRoleHasBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void claimValuesMismatch() {
|
||||
createClaimToRoleMapper("other value");
|
||||
createUserInProviderRealm(ImmutableMap.<String, List<String>>builder()
|
||||
.put(CLAIM, ImmutableList.<String>builder().add(CLAIM_VALUE).build())
|
||||
.build());
|
||||
|
||||
logInAsUserInIDPForFirstTime();
|
||||
|
||||
UserRepresentation user = findUser(bc.consumerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
assertThatRoleHasNotBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBrokeredUserMismatchDeletesRoleInForceMode() {
|
||||
UserRepresentation user = loginWithClaimThenChangeClaimToValue("value mismatch", FORCE, false);
|
||||
|
||||
assertThatRoleHasNotBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBrokeredUserMismatchDeletesRoleInLegacyMode() {
|
||||
UserRepresentation user = createMapperThenLoginWithStandardClaimThenChangeClaimToValue("value mismatch", LEGACY);
|
||||
|
||||
assertThatRoleHasNotBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBrokeredUserNewMatchGrantsRoleAfterFirstLoginInForceMode() {
|
||||
UserRepresentation user = loginWithStandardClaimThenAddMapperAndLoginAgain(FORCE);
|
||||
|
||||
assertThatRoleHasBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBrokeredUserNewMatchDoesNotGrantRoleAfterFirstLoginInLegacyMode() {
|
||||
UserRepresentation user = loginWithStandardClaimThenAddMapperAndLoginAgain(LEGACY);
|
||||
|
||||
assertThatRoleHasNotBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBrokeredUserDoesNotDeleteRoleIfClaimStillMatches() {
|
||||
UserRepresentation user = createMapperThenLoginWithStandardClaimThenChangeClaimToValue(CLAIM_VALUE, FORCE);
|
||||
|
||||
assertThatRoleHasBeenAssignedInConsumerRealmTo(user);
|
||||
}
|
||||
|
||||
private UserRepresentation loginWithStandardClaimThenAddMapperAndLoginAgain(IdentityProviderMapperSyncMode syncMode) {
|
||||
return loginWithClaimThenChangeClaimToValue(OidcClaimToRoleMapperTest.CLAIM_VALUE, syncMode, true);
|
||||
}
|
||||
|
||||
private UserRepresentation createMapperThenLoginWithStandardClaimThenChangeClaimToValue(String claimOnSecondLogin, IdentityProviderMapperSyncMode syncMode) {
|
||||
return loginWithClaimThenChangeClaimToValue(claimOnSecondLogin, syncMode, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private UserRepresentation loginWithClaimThenChangeClaimToValue(String claimOnSecondLogin, IdentityProviderMapperSyncMode syncMode, boolean createAfterFirstLogin) {
|
||||
this.claimOnSecondLogin = claimOnSecondLogin;
|
||||
return loginAsUserTwiceWithMapper(syncMode, createAfterFirstLogin,
|
||||
ImmutableMap.<String, List<String>>builder()
|
||||
.put(CLAIM, ImmutableList.<String>builder().add(CLAIM_VALUE).build())
|
||||
.build());
|
||||
}
|
||||
|
||||
private void createClaimToRoleMapper(String claimValue) {
|
||||
IdentityProviderRepresentation idp = setupIdentityProvider();
|
||||
createClaimToRoleMapper(idp, claimValue, IdentityProviderMapperSyncMode.IMPORT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createMapperInIdp(IdentityProviderRepresentation idp, IdentityProviderMapperSyncMode syncMode) {
|
||||
createClaimToRoleMapper(idp, CLAIM_VALUE, syncMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateUser() {
|
||||
UserRepresentation user = findUser(bc.providerRealmName(), bc.getUserLogin(), bc.getUserEmail());
|
||||
ImmutableMap<String, List<String>> mismatchingAttributes = ImmutableMap.<String, List<String>>builder()
|
||||
.put(CLAIM, ImmutableList.<String>builder().add(claimOnSecondLogin).build())
|
||||
.build();
|
||||
user.setAttributes(mismatchingAttributes);
|
||||
adminClient.realm(bc.providerRealmName()).users().get(user.getId()).update(user);
|
||||
}
|
||||
|
||||
private void createClaimToRoleMapper(IdentityProviderRepresentation idp, String claimValue, IdentityProviderMapperSyncMode syncMode) {
|
||||
IdentityProviderMapperRepresentation claimToRoleMapper = new IdentityProviderMapperRepresentation();
|
||||
claimToRoleMapper.setName("claim-to-role-mapper");
|
||||
claimToRoleMapper.setIdentityProviderMapper(ClaimToRoleMapper.PROVIDER_ID);
|
||||
claimToRoleMapper.setConfig(ImmutableMap.<String, String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(ClaimToRoleMapper.CLAIM, OidcClaimToRoleMapperTest.CLAIM)
|
||||
.put(ClaimToRoleMapper.CLAIM_VALUE, claimValue)
|
||||
.put(ConfigConstants.ROLE, CLIENT_ROLE_MAPPER_REPRESENTATION)
|
||||
.build());
|
||||
|
||||
IdentityProviderResource idpResource = realm.identityProviders().get(idp.getAlias());
|
||||
claimToRoleMapper.setIdentityProviderAlias(bc.getIDPAlias());
|
||||
idpResource.addMapper(claimToRoleMapper).close();
|
||||
}
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.keycloak.broker.oidc.mappers.UserAttributeMapper;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
@ -14,11 +16,12 @@ public class OidcUserAttributeMapperTest extends AbstractUserAttributeMapperTest
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<IdentityProviderMapperRepresentation> createIdentityProviderMappers() {
|
||||
protected Iterable<IdentityProviderMapperRepresentation> createIdentityProviderMappers(IdentityProviderMapperSyncMode syncMode) {
|
||||
IdentityProviderMapperRepresentation attrMapper1 = new IdentityProviderMapperRepresentation();
|
||||
attrMapper1.setName("attribute-mapper");
|
||||
attrMapper1.setIdentityProviderMapper(UserAttributeMapper.PROVIDER_ID);
|
||||
attrMapper1.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(UserAttributeMapper.CLAIM, KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME)
|
||||
.put(UserAttributeMapper.USER_ATTRIBUTE, MAPPED_ATTRIBUTE_NAME)
|
||||
.build());
|
||||
|
@ -27,6 +30,7 @@ public class OidcUserAttributeMapperTest extends AbstractUserAttributeMapperTest
|
|||
emailAttrMapper.setName("attribute-mapper-email");
|
||||
emailAttrMapper.setIdentityProviderMapper(UserAttributeMapper.PROVIDER_ID);
|
||||
emailAttrMapper.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(UserAttributeMapper.CLAIM, "email")
|
||||
.put(UserAttributeMapper.USER_ATTRIBUTE, "email")
|
||||
.build());
|
||||
|
@ -35,6 +39,7 @@ public class OidcUserAttributeMapperTest extends AbstractUserAttributeMapperTest
|
|||
nestedEmailAttrMapper.setName("nested-attribute-mapper-email");
|
||||
nestedEmailAttrMapper.setIdentityProviderMapper(UserAttributeMapper.PROVIDER_ID);
|
||||
nestedEmailAttrMapper.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(UserAttributeMapper.CLAIM, "nested.email")
|
||||
.put(UserAttributeMapper.USER_ATTRIBUTE, "nested.email")
|
||||
.build());
|
||||
|
@ -43,6 +48,7 @@ public class OidcUserAttributeMapperTest extends AbstractUserAttributeMapperTest
|
|||
dottedEmailAttrMapper.setName("dotted-attribute-mapper-email");
|
||||
dottedEmailAttrMapper.setIdentityProviderMapper(UserAttributeMapper.PROVIDER_ID);
|
||||
dottedEmailAttrMapper.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(UserAttributeMapper.CLAIM, "dotted\\.email")
|
||||
.put(UserAttributeMapper.USER_ATTRIBUTE, "dotted.email")
|
||||
.build());
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.keycloak.broker.saml.mappers.UserAttributeMapper;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
@ -14,11 +16,12 @@ public class SamlUserAttributeMapperTest extends AbstractUserAttributeMapperTest
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<IdentityProviderMapperRepresentation> createIdentityProviderMappers() {
|
||||
protected Iterable<IdentityProviderMapperRepresentation> createIdentityProviderMappers(IdentityProviderMapperSyncMode syncMode) {
|
||||
IdentityProviderMapperRepresentation attrMapperEmail = new IdentityProviderMapperRepresentation();
|
||||
attrMapperEmail.setName("attribute-mapper-email");
|
||||
attrMapperEmail.setIdentityProviderMapper(UserAttributeMapper.PROVIDER_ID);
|
||||
attrMapperEmail.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(UserAttributeMapper.ATTRIBUTE_FRIENDLY_NAME, "email")
|
||||
.put(UserAttributeMapper.USER_ATTRIBUTE, "email")
|
||||
.build());
|
||||
|
@ -27,6 +30,7 @@ public class SamlUserAttributeMapperTest extends AbstractUserAttributeMapperTest
|
|||
attrMapperNestedEmail.setName("nested-attribute-mapper-email");
|
||||
attrMapperNestedEmail.setIdentityProviderMapper(UserAttributeMapper.PROVIDER_ID);
|
||||
attrMapperNestedEmail.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(UserAttributeMapper.ATTRIBUTE_NAME, "nested.email")
|
||||
.put(UserAttributeMapper.USER_ATTRIBUTE, "nested.email")
|
||||
.build());
|
||||
|
@ -35,6 +39,7 @@ public class SamlUserAttributeMapperTest extends AbstractUserAttributeMapperTest
|
|||
attrMapperDottedEmail.setName("dotted-attribute-mapper-email");
|
||||
attrMapperDottedEmail.setIdentityProviderMapper(UserAttributeMapper.PROVIDER_ID);
|
||||
attrMapperDottedEmail.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(UserAttributeMapper.ATTRIBUTE_NAME, "dotted.email")
|
||||
.put(UserAttributeMapper.USER_ATTRIBUTE, "dotted.email")
|
||||
.build());
|
||||
|
@ -43,6 +48,7 @@ public class SamlUserAttributeMapperTest extends AbstractUserAttributeMapperTest
|
|||
attrMapper1.setName("attribute-mapper");
|
||||
attrMapper1.setIdentityProviderMapper(UserAttributeMapper.PROVIDER_ID);
|
||||
attrMapper1.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(UserAttributeMapper.ATTRIBUTE_NAME, KcOidcBrokerConfiguration.ATTRIBUTE_TO_MAP_NAME)
|
||||
.put(UserAttributeMapper.USER_ATTRIBUTE, MAPPED_ATTRIBUTE_NAME)
|
||||
.build());
|
||||
|
@ -51,6 +57,7 @@ public class SamlUserAttributeMapperTest extends AbstractUserAttributeMapperTest
|
|||
attrMapper2.setName("attribute-mapper-friendly");
|
||||
attrMapper2.setIdentityProviderMapper(UserAttributeMapper.PROVIDER_ID);
|
||||
attrMapper2.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||
.put(UserAttributeMapper.ATTRIBUTE_FRIENDLY_NAME, ATTRIBUTE_TO_MAP_FRIENDLY_NAME)
|
||||
.put(UserAttributeMapper.USER_ATTRIBUTE, MAPPED_ATTRIBUTE_FRIENDLY_NAME)
|
||||
.build());
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.keycloak.authorization.model.ResourceServer;
|
|||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
|
@ -370,7 +371,10 @@ public class SocialLoginTest extends AbstractKeycloakTest {
|
|||
}
|
||||
|
||||
public IdentityProviderRepresentation buildIdp(Provider provider) {
|
||||
IdentityProviderRepresentation idp = IdentityProviderBuilder.create().alias(provider.id()).providerId(provider.id()).build();
|
||||
IdentityProviderRepresentation idp = IdentityProviderBuilder.create()
|
||||
.alias(provider.id())
|
||||
.providerId(provider.id())
|
||||
.build();
|
||||
idp.setEnabled(true);
|
||||
idp.setStoreToken(true);
|
||||
idp.getConfig().put("clientId", getConfig(provider, "clientId"));
|
||||
|
|
|
@ -2,17 +2,29 @@ package org.keycloak.testsuite.cli.admin;
|
|||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.broker.saml.SAMLIdentityProviderConfig;
|
||||
import org.keycloak.broker.saml.SAMLIdentityProviderFactory;
|
||||
import org.keycloak.client.admin.cli.config.FileConfigHandler;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.testsuite.cli.KcAdmExec;
|
||||
import org.keycloak.testsuite.updaters.IdentityProviderCreator;
|
||||
import org.keycloak.testsuite.util.IdentityProviderBuilder;
|
||||
import org.keycloak.testsuite.util.TempFileResource;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.keycloak.testsuite.cli.KcAdmExec.execute;
|
||||
|
||||
/**
|
||||
|
@ -40,6 +52,23 @@ public class KcAdmCreateTest extends AbstractAdmCliTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateIDPWithoutSyncMode() throws IOException {
|
||||
final String realm = "test";
|
||||
final RealmResource realmResource = adminClient.realm(realm);
|
||||
|
||||
FileConfigHandler handler = initCustomConfigFile();
|
||||
try (TempFileResource configFile = new TempFileResource(handler.getConfigFile())) {
|
||||
loginAsUser(configFile.getFile(), serverUrl, realm, "user1", "userpass");
|
||||
|
||||
final File idpJson = new File("target/test-classes/cli/idp-keycloak-without-sync-mode.json");
|
||||
KcAdmExec exe = execute("create identity-provider/instances/ -r " + realm + " -f " + idpJson.getAbsolutePath() + " --config " + configFile.getFile());
|
||||
assertExitCodeAndStdErrSize(exe, 0, 1);
|
||||
}
|
||||
|
||||
// If the sync mode is not present on creating the idp, it will never be added automatically. However, the model will always assume "LEGACY", so no errors should occur.
|
||||
Assert.assertNull(realmResource.identityProviders().get("idpAlias").toRepresentation().getConfig().get(IdentityProviderModel.SYNC_MODE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateThoroughly() throws IOException {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"linkOnly" : false,
|
||||
"firstBrokerLoginFlowAlias" : "first broker login",
|
||||
"config" : {
|
||||
"syncMode": "IMPORT",
|
||||
"nameIDPolicyFormat" : "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
|
||||
"postBindingResponse" : "false",
|
||||
"singleLogoutServiceUrl" : "https://saml.idp/saml",
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"alias" : "idpAlias",
|
||||
"displayName" : "SAML_UPDATED",
|
||||
"providerId" : "saml",
|
||||
"enabled" : true,
|
||||
"updateProfileFirstLoginMode" : "on",
|
||||
"trustEmail" : false,
|
||||
"storeToken" : false,
|
||||
"addReadTokenRoleOnCreate" : false,
|
||||
"authenticateByDefault" : false,
|
||||
"linkOnly" : false,
|
||||
"firstBrokerLoginFlowAlias" : "first broker login",
|
||||
"config" : {
|
||||
"nameIDPolicyFormat" : "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
|
||||
"postBindingResponse" : "false",
|
||||
"singleLogoutServiceUrl" : "https://saml.idp/saml",
|
||||
"postBindingAuthnRequest" : "false",
|
||||
"singleSignOnServiceUrl" : "https://saml.idp/saml",
|
||||
"backchannelSupported" : "false"
|
||||
}
|
||||
}
|
|
@ -1166,6 +1166,7 @@
|
|||
"alias" : "google1",
|
||||
"enabled": true,
|
||||
"config": {
|
||||
"syncMode": "IMPORT",
|
||||
"clientId": "googleId",
|
||||
"clientSecret": "googleSecret"
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
"alias" : "google1",
|
||||
"enabled": true,
|
||||
"config": {
|
||||
"syncMode": "IMPORT",
|
||||
"clientId": "googleId",
|
||||
"clientSecret": "googleSecret"
|
||||
}
|
||||
|
@ -33,6 +34,7 @@
|
|||
"alias" : "facebook1",
|
||||
"enabled": true,
|
||||
"config": {
|
||||
"syncMode": "IMPORT",
|
||||
"clientId": "facebookId",
|
||||
"clientSecret": "facebookSecret"
|
||||
}
|
||||
|
@ -42,6 +44,7 @@
|
|||
"alias" : "twitter1",
|
||||
"enabled": true,
|
||||
"config": {
|
||||
"syncMode": "IMPORT",
|
||||
"clientId": "twitterId",
|
||||
"clientSecret": "twitterSecret"
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
"authenticateByDefault" : false,
|
||||
"firstBrokerLoginFlowAlias" : "first broker login",
|
||||
"config" : {
|
||||
"syncMode": "IMPORT",
|
||||
"nameIDPolicyFormat" : "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
|
||||
"postBindingAuthnRequest" : "true",
|
||||
"postBindingResponse" : "true",
|
||||
|
@ -78,6 +79,7 @@
|
|||
"identityProviderAlias" : "saml-leaf",
|
||||
"identityProviderMapper" : "saml-role-idp-mapper",
|
||||
"config" : {
|
||||
"syncMode": "INHERIT",
|
||||
"attribute.value" : "manager",
|
||||
"role" : "${url.realm.consumer}/app/auth.manager",
|
||||
"attribute.name" : "Role"
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright 2019 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.testsuite.console.page.idp;
|
||||
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.keycloak.testsuite.console.page.AdminConsoleCreate;
|
||||
|
||||
/**
|
||||
* @author Martin Idel <external.Martin.Idel@bosch.io>
|
||||
*/
|
||||
public class CreateIdentityProviderMapper extends AdminConsoleCreate {
|
||||
public String idp;
|
||||
|
||||
@Page
|
||||
private IdentityProviderMapperForm form;
|
||||
|
||||
public CreateIdentityProviderMapper() {
|
||||
setEntity("identity-provider-mappers");
|
||||
}
|
||||
|
||||
public void setIdp(String idp) {
|
||||
this.idp = idp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUriFragment() {
|
||||
return super.getUriFragment() + "/" + idp + "";
|
||||
}
|
||||
|
||||
public IdentityProviderMapperForm form() {
|
||||
return form;
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ import org.keycloak.testsuite.console.page.fragment.KcPassword;
|
|||
import org.keycloak.testsuite.page.Form;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
import org.openqa.selenium.support.ui.Select;
|
||||
|
||||
import static org.keycloak.testsuite.util.UIUtils.setTextInputValue;
|
||||
|
||||
|
@ -34,6 +35,15 @@ public class IdentityProviderForm extends Form {
|
|||
@FindBy(id = "clientSecret")
|
||||
private KcPassword clientSecretInput;
|
||||
|
||||
@FindBy(id = "syncMode")
|
||||
private Select syncMode;
|
||||
|
||||
@FindBy(linkText = "Mappers")
|
||||
private WebElement mappersTab;
|
||||
|
||||
@FindBy(linkText = "Create")
|
||||
private WebElement mapperCreateButton;
|
||||
|
||||
public void setClientId(final String value) {
|
||||
setTextInputValue(clientIdInput, value);
|
||||
}
|
||||
|
@ -42,7 +52,20 @@ public class IdentityProviderForm extends Form {
|
|||
clientSecretInput.setValue(value);
|
||||
}
|
||||
|
||||
public void setSyncMode(final String value) {
|
||||
syncMode.selectByVisibleText(value);
|
||||
}
|
||||
|
||||
public String syncMode() {
|
||||
return syncMode.getFirstSelectedOption().getText();
|
||||
}
|
||||
|
||||
public KcPassword clientSecret() {
|
||||
return clientSecretInput;
|
||||
}
|
||||
|
||||
public void createMapper() {
|
||||
mappersTab.click();
|
||||
mapperCreateButton.click();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright 2019 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.testsuite.console.page.idp;
|
||||
|
||||
import org.keycloak.testsuite.page.Form;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
import org.openqa.selenium.support.ui.Select;
|
||||
|
||||
import static org.keycloak.testsuite.util.UIUtils.setTextInputValue;
|
||||
|
||||
/**
|
||||
* @author Martin Idel <external.Martin.Idel@bosch.io>
|
||||
*/
|
||||
public class IdentityProviderMapperForm extends Form {
|
||||
@FindBy(id = "name")
|
||||
private WebElement name;
|
||||
|
||||
@FindBy(id = "syncMode")
|
||||
private Select syncMode;
|
||||
|
||||
public void setName(final String value) {
|
||||
setTextInputValue(name, value);
|
||||
}
|
||||
|
||||
public void setSyncMode(final String value) {
|
||||
syncMode.selectByVisibleText(value);
|
||||
}
|
||||
|
||||
public String syncMode() {
|
||||
return syncMode.getFirstSelectedOption().getText();
|
||||
}
|
||||
}
|
|
@ -22,18 +22,22 @@ import org.junit.Before;
|
|||
import org.junit.Test;
|
||||
import org.keycloak.testsuite.console.AbstractConsoleTest;
|
||||
import org.keycloak.testsuite.console.page.idp.CreateIdentityProvider;
|
||||
import org.keycloak.testsuite.console.page.idp.CreateIdentityProviderMapper;
|
||||
import org.keycloak.testsuite.console.page.idp.IdentityProvider;
|
||||
import org.keycloak.testsuite.console.page.idp.IdentityProviders;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.keycloak.testsuite.util.UIUtils.refreshPageAndWaitForLoad;
|
||||
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
|
||||
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Petr Mensik
|
||||
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
||||
* @author Martin Idel <external.Martin.Idel@bosch.io>
|
||||
*/
|
||||
public class IdentityProviderTest extends AbstractConsoleTest {
|
||||
@Page
|
||||
|
@ -45,6 +49,9 @@ public class IdentityProviderTest extends AbstractConsoleTest {
|
|||
@Page
|
||||
private CreateIdentityProvider createIdentityProviderPage;
|
||||
|
||||
@Page
|
||||
private CreateIdentityProviderMapper createIdentityProviderMapperPage;
|
||||
|
||||
@Before
|
||||
public void beforeIdentityProviderTest() {
|
||||
identityProvidersPage.navigateTo();
|
||||
|
@ -91,6 +98,44 @@ public class IdentityProviderTest extends AbstractConsoleTest {
|
|||
assertPasswordIsMasked();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void settingAndSavingSyncMode() {
|
||||
createIdentityProviderPage.setProviderId("google");
|
||||
identityProviderPage.setIds("google", "google");
|
||||
|
||||
identityProvidersPage.addProvider("google");
|
||||
assertCurrentUrlEquals(createIdentityProviderPage);
|
||||
identityProviderPage.form().setSyncMode("force");
|
||||
|
||||
createIdentityProviderPage.form().setClientId("test-google");
|
||||
createIdentityProviderPage.form().setClientSecret("secret");
|
||||
|
||||
createIdentityProviderPage.form().save();
|
||||
assertAlertSuccess();
|
||||
refreshPageAndWaitForLoad();
|
||||
assertCurrentUrlEquals(identityProviderPage);
|
||||
assertSyncModeIsSetToForce();
|
||||
|
||||
identityProviderPage.form().createMapper();
|
||||
createIdentityProviderMapperPage.setIdp("google");
|
||||
assertCurrentUrlEquals(createIdentityProviderMapperPage);
|
||||
createIdentityProviderMapperPage.form().setName("TestMapper");
|
||||
createIdentityProviderMapperPage.form().setSyncMode("import");
|
||||
|
||||
createIdentityProviderMapperPage.form().save();
|
||||
assertAlertSuccess();
|
||||
refreshPageAndWaitForLoad();
|
||||
assertMapperSyncModeIsSetToImport();
|
||||
}
|
||||
|
||||
private void assertMapperSyncModeIsSetToImport() {
|
||||
assertEquals("import", createIdentityProviderMapperPage.form().syncMode());
|
||||
}
|
||||
|
||||
private void assertSyncModeIsSetToForce() {
|
||||
assertEquals("force", identityProviderPage.form().syncMode());
|
||||
}
|
||||
|
||||
private void assertEyeButtonIsDisabled() {
|
||||
assertTrue("Eye button is not disabled", identityProviderPage.form().clientSecret().isEyeButtonDisabled());
|
||||
}
|
||||
|
|
|
@ -501,6 +501,14 @@ last-refresh=Letzte Aktualisierung
|
|||
#gui-order=GUI order
|
||||
#first-broker-login-flow=First Login Flow
|
||||
#post-broker-login-flow=Post Login Flow
|
||||
sync-mode=Synchronisationsmodus
|
||||
sync-mode.tooltip=Standardsyncmodus für alle Mapper. Mögliche Werte sind: 'Legacy' um das alte Verhalten beizubehalten, 'Importieren' um den Nutzer einmalig zu importieren, 'Erzwingen' um den Nutzer immer zu importieren.
|
||||
sync-mode.inherit=Standard erben
|
||||
sync-mode.legacy=Legacy
|
||||
sync-mode.import=Importieren
|
||||
sync-mode.force=Erzwingen
|
||||
sync-mode-override=Überschriebene Synchronisation
|
||||
sync-mode-override.tooltip=Überschreibt den normalen Synchronisationsmodus des IDP für diesen Mapper. Were sind 'Legacy' um das alte Verhalten beizubehalten, 'Importieren' um den Nutzer einmalig zu importieren, 'Erzwingen' um den Nutzer immer zu updaten.
|
||||
#redirect-uri=Redirect URI
|
||||
#redirect-uri.tooltip=The redirect uri to use when configuring the identity provider.
|
||||
#alias=Alias
|
||||
|
|
|
@ -540,6 +540,14 @@ provider=Provider
|
|||
gui-order=GUI order
|
||||
first-broker-login-flow=First Login Flow
|
||||
post-broker-login-flow=Post Login Flow
|
||||
sync-mode=Sync Mode
|
||||
sync-mode.tooltip=Default sync mode for all mappers. The sync mode determines when user data will be synced using the mappers. Possible values are: 'legacy' to keep the behaviour before this option was introduced, 'import' to only import the user once during first login of the user with this identity provider, 'force' to always update the user during every login with this identity provider".
|
||||
sync-mode.inherit=inherit
|
||||
sync-mode.legacy=legacy
|
||||
sync-mode.import=import
|
||||
sync-mode.force=force
|
||||
sync-mode-override=Sync Mode Override
|
||||
sync-mode-override.tooltip=Overrides the default sync mode of the IDP for this mapper. Values are: 'legacy' to keep the behaviour before this option was introduced, 'import' to only import the user once during first login of the user with this identity provider, 'force' to always update the user during every login with this identity provider" and 'inherit' to use the sync mode defined in the identity provider for this mapper.
|
||||
redirect-uri=Redirect URI
|
||||
redirect-uri.tooltip=The redirect uri to use when configuring the identity provider.
|
||||
alias=Alias
|
||||
|
|
|
@ -900,6 +900,7 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
|
|||
$scope.identityProvider.authenticateByDefault = false;
|
||||
$scope.identityProvider.firstBrokerLoginFlowAlias = 'first broker login';
|
||||
$scope.identityProvider.config.useJwksUrl = 'true';
|
||||
$scope.identityProvider.config.syncMode = 'IMPORT';
|
||||
$scope.newIdentityProvider = true;
|
||||
}
|
||||
|
||||
|
@ -2090,6 +2091,7 @@ module.controller('IdentityProviderMapperCreateCtrl', function($scope, realm, id
|
|||
|
||||
// make first type the default
|
||||
$scope.mapperType = mapperTypes[Object.keys(mapperTypes)[0]];
|
||||
$scope.mapper.config.syncMode = 'INHERIT';
|
||||
|
||||
$scope.$watch(function() {
|
||||
return $location.path();
|
||||
|
|
|
@ -27,6 +27,22 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'mapper.name.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group clearfix">
|
||||
<label class="col-md-2 control-label" for="syncMode">{{:: 'sync-mode-override' | translate}} <span class="required">*</span></label>
|
||||
<div class="col-sm-6">
|
||||
<div>
|
||||
<select class="form-control" id="syncMode"
|
||||
ng-model="mapper.config.syncMode"
|
||||
required>
|
||||
<option id="syncMode_inherit" name="syncMode" value="INHERIT">{{:: 'sync-mode.inherit' | translate}}</option>
|
||||
<option id="syncMode_legacy" name="syncMode" value="LEGACY">{{:: 'sync-mode.legacy' | translate}}</option>
|
||||
<option id="syncMode_import" name="syncMode" value="IMPORT">{{:: 'sync-mode.import' | translate}}</option>
|
||||
<option id="syncMode_force" name="syncMode" value="FORCE">{{:: 'sync-mode.force' | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'sync-mode-override.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group" data-ng-show="create">
|
||||
<label class="col-md-2 control-label" for="mapperTypeCreate">{{:: 'mapper-type' | translate}}</label>
|
||||
<div class="col-sm-6">
|
||||
|
|
|
@ -113,6 +113,21 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'post-broker-login-flow.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="syncMode">{{:: 'sync-mode' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
<div>
|
||||
<select class="form-control" id="syncMode"
|
||||
ng-model="identityProvider.config.syncMode"
|
||||
required>
|
||||
<option id="syncMode_import" name="syncMode" value="IMPORT">{{:: 'sync-mode.import' | translate}}</option>
|
||||
<option id="syncMode_legacy" name="syncMode" value="LEGACY">{{:: 'sync-mode.legacy' | translate}}</option>
|
||||
<option id="syncMode_force" name="syncMode" value="FORCE">{{:: 'sync-mode.force' | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'sync-mode.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-group">
|
||||
|
|
|
@ -113,6 +113,21 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'post-broker-login-flow.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="syncMode">{{:: 'sync-mode' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
<div>
|
||||
<select class="form-control" id="syncMode"
|
||||
ng-model="identityProvider.config.syncMode"
|
||||
required>
|
||||
<option id="syncMode_import" name="syncMode" value="IMPORT">{{:: 'sync-mode.import' | translate}}</option>
|
||||
<option id="syncMode_legacy" name="syncMode" value="LEGACY">{{:: 'sync-mode.legacy' | translate}}</option>
|
||||
<option id="syncMode_force" name="syncMode" value="FORCE">{{:: 'sync-mode.force' | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'sync-mode.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-group">
|
||||
|
|
|
@ -107,6 +107,21 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'post-broker-login-flow.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="syncMode">{{:: 'sync-mode' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
<div>
|
||||
<select class="form-control" id="syncMode"
|
||||
ng-model="identityProvider.config.syncMode"
|
||||
required>
|
||||
<option id="syncMode_import" name="syncMode" value="IMPORT">{{:: 'sync-mode.import' | translate}}</option>
|
||||
<option id="syncMode_legacy" name="syncMode" value="LEGACY">{{:: 'sync-mode.legacy' | translate}}</option>
|
||||
<option id="syncMode_force" name="syncMode" value="FORCE">{{:: 'sync-mode.force' | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'sync-mode.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend uncollapsed><span class="text">{{:: 'openid-connect-config' | translate}}</span> <kc-tooltip>{{:: 'openid-connect-config.tooltip' | translate}}</kc-tooltip></legend>
|
||||
|
|
|
@ -135,6 +135,21 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'post-broker-login-flow.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="syncMode">{{:: 'sync-mode' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
<div>
|
||||
<select class="form-control" id="syncMode"
|
||||
ng-model="identityProvider.config.syncMode"
|
||||
required>
|
||||
<option id="syncMode_import" name="syncMode" value="IMPORT">{{:: 'sync-mode.import' | translate}}</option>
|
||||
<option id="syncMode_legacy" name="syncMode" value="LEGACY">{{:: 'sync-mode.legacy' | translate}}</option>
|
||||
<option id="syncMode_force" name="syncMode" value="FORCE">{{:: 'sync-mode.force' | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'sync-mode.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-group">
|
||||
|
|
|
@ -135,6 +135,21 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'post-broker-login-flow.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="syncMode">{{:: 'sync-mode' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
<div>
|
||||
<select class="form-control" id="syncMode"
|
||||
ng-model="identityProvider.config.syncMode"
|
||||
required>
|
||||
<option id="syncMode_import" name="syncMode" value="IMPORT">{{:: 'sync-mode.import' | translate}}</option>
|
||||
<option id="syncMode_legacy" name="syncMode" value="LEGACY">{{:: 'sync-mode.legacy' | translate}}</option>
|
||||
<option id="syncMode_force" name="syncMode" value="FORCE">{{:: 'sync-mode.force' | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'sync-mode.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-group">
|
||||
|
|
|
@ -107,6 +107,21 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'post-broker-login-flow.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="syncMode">{{:: 'sync-mode' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
<div>
|
||||
<select class="form-control" id="syncMode"
|
||||
ng-model="identityProvider.config.syncMode"
|
||||
required>
|
||||
<option id="syncMode_import" name="syncMode" value="IMPORT">{{:: 'sync-mode.import' | translate}}</option>
|
||||
<option id="syncMode_legacy" name="syncMode" value="LEGACY">{{:: 'sync-mode.legacy' | translate}}</option>
|
||||
<option id="syncMode_force" name="syncMode" value="FORCE">{{:: 'sync-mode.force' | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'sync-mode.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend uncollapsed><span class="text">{{:: 'saml-config' | translate}}</span> <kc-tooltip>{{:: 'identity-provider.saml-config.tooltip' | translate}}</kc-tooltip></legend>
|
||||
|
|
|
@ -128,6 +128,21 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'post-broker-login-flow.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="syncMode">{{:: 'sync-mode' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
<div>
|
||||
<select class="form-control" id="syncMode"
|
||||
ng-model="identityProvider.config.syncMode"
|
||||
required>
|
||||
<option id="syncMode_import" name="syncMode" value="IMPORT">{{:: 'sync-mode.import' | translate}}</option>
|
||||
<option id="syncMode_legacy" name="syncMode" value="LEGACY">{{:: 'sync-mode.legacy' | translate}}</option>
|
||||
<option id="syncMode_force" name="syncMode" value="FORCE">{{:: 'sync-mode.force' | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'sync-mode.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-group">
|
||||
|
|
Loading…
Reference in a new issue