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.ClientRepresentation; 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 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.*; /** * * @author hmlnarik */ public abstract class AbstractUserAttributeMapperTest extends AbstractBaseBrokerTest { protected static final String MAPPED_ATTRIBUTE_NAME = "mapped-user-attribute"; protected static final String MAPPED_ATTRIBUTE_FRIENDLY_NAME = "mapped-user-attribute-friendly"; protected static final String ATTRIBUTE_TO_MAP_NAME = "user-attribute"; protected static final String ATTRIBUTE_TO_MAP_FRIENDLY_NAME = "user-attribute-friendly"; private static final Set<String> PROTECTED_NAMES = ImmutableSet.<String>builder().add("email").add("lastName").add("firstName").build(); private static final Map<String, String> ATTRIBUTE_NAME_TRANSLATION = ImmutableMap.<String, String>builder() .put(ATTRIBUTE_TO_MAP_FRIENDLY_NAME, MAPPED_ATTRIBUTE_FRIENDLY_NAME) .put(ATTRIBUTE_TO_MAP_NAME, MAPPED_ATTRIBUTE_NAME) .build(); protected abstract Iterable<IdentityProviderMapperRepresentation> createIdentityProviderMappers(); @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(); IdentityProviderResource idpResource = realm.identityProviders().get(idp.getAlias()); for (IdentityProviderMapperRepresentation mapper : createIdentityProviderMappers()) { mapper.setIdentityProviderAlias(bc.getIDPAlias()); resp = idpResource.addMapper(mapper); resp.close(); } } @Before public void addClients() { List<ClientRepresentation> clients = bc.createProviderClients(suiteContext); if (clients != null) { RealmResource providerRealm = adminClient.realm(bc.providerRealmName()); for (ClientRepresentation client : clients) { log.debug("adding client " + client.getName() + " to realm " + bc.providerRealmName()); Response resp = providerRealm.clients().create(client); resp.close(); } } clients = bc.createConsumerClients(suiteContext); if (clients != null) { RealmResource consumerRealm = adminClient.realm(bc.consumerRealmName()); for (ClientRepresentation client : clients) { log.debug("adding client " + client.getName() + " to realm " + bc.consumerRealmName()); Response resp = consumerRealm.clients().create(client); resp.close(); } } } 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()) .map(me -> me.getKey()) .filter(a -> ! PROTECTED_NAMES.contains(a)) .map(ATTRIBUTE_NAME_TRANSLATION::get) .collect(Collectors.toSet()); if (mappedAttrNames.isEmpty()) { assertThat("No attributes are expected to be present", userRep.getAttributes(), nullValue()); } else { assertThat(userRep.getAttributes(), notNullValue()); assertThat(userRep.getAttributes().keySet(), equalTo(mappedAttrNames)); for (Map.Entry<String, List<String>> me : attrs.entrySet()) { String mappedAttrName = ATTRIBUTE_NAME_TRANSLATION.get(me.getKey()); if (mappedAttrNames.contains(mappedAttrName)) { assertThat(userRep.getAttributes().get(mappedAttrName), containsInAnyOrder(me.getValue().toArray())); } } } if (attrs.containsKey("email")) { assertThat(userRep.getEmail(), equalTo(attrs.get("email").get(0))); } if (attrs.containsKey("firstName")) { assertThat(userRep.getFirstName(), equalTo(attrs.get("firstName").get(0))); } if (attrs.containsKey("lastName")) { assertThat(userRep.getLastName(), equalTo(attrs.get("lastName").get(0))); } } protected void testValueMapping(Map<String, List<String>> initialUserAttributes, Map<String, List<String>> modifiedUserAttributes) { String email = bc.getUserEmail(); createUserInProviderRealm(initialUserAttributes); logInAsUserInIDPForFirstTime(); UserRepresentation userRep = findUser(bc.consumerRealmName(), bc.getUserLogin(), email); assertUserAttributes(initialUserAttributes, userRep); logoutFromRealm(bc.consumerRealmName()); // update user in provider realm UserRepresentation userRepProvider = findUser(bc.providerRealmName(), bc.getUserLogin(), email); Map<String, List<String>> modifiedWithoutSpecialKeys = modifiedUserAttributes.entrySet().stream() .filter(a -> ! PROTECTED_NAMES.contains(a.getKey())) .filter(a -> a.getValue() != null) // Remove empty attributes .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue())); userRepProvider.setAttributes(modifiedWithoutSpecialKeys); if (modifiedUserAttributes.containsKey("email")) { userRepProvider.setEmail(modifiedUserAttributes.get("email").get(0)); email = modifiedUserAttributes.get("email").get(0); } if (modifiedUserAttributes.containsKey("firstName")) { userRepProvider.setFirstName(modifiedUserAttributes.get("firstName").get(0)); } if (modifiedUserAttributes.containsKey("lastName")) { userRepProvider.setLastName(modifiedUserAttributes.get("lastName").get(0)); } adminClient.realm(bc.providerRealmName()).users().get(userRepProvider.getId()).update(userRepProvider); logInAsUserInIDP(); userRep = findUser(bc.consumerRealmName(), bc.getUserLogin(), email); assertUserAttributes(modifiedUserAttributes, userRep); } @Test public void testBasicMappingSingleValue() { testValueMapping(ImmutableMap.<String, List<String>>builder() .put(ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("value 1").build()) .build(), ImmutableMap.<String, List<String>>builder() .put(ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("second value").build()) .build() ); } @Test public void testBasicMappingEmail() { testValueMapping(ImmutableMap.<String, List<String>>builder() .put("email", ImmutableList.<String>builder().add(bc.getUserEmail()).build()) .build(), ImmutableMap.<String, List<String>>builder() .put("email", ImmutableList.<String>builder().add("other_email@redhat.com").build()) .build() ); } @Test public void testBasicMappingClearValue() { testValueMapping(ImmutableMap.<String, List<String>>builder() .put(ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("value 1").build()) .build(), ImmutableMap.<String, List<String>>builder() .put(ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().build()) .build() ); } @Test public void testBasicMappingRemoveValue() { testValueMapping(ImmutableMap.<String, List<String>>builder() .put(ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("value 1").build()) .build(), ImmutableMap.<String, List<String>>builder() .build() ); } @Test public void testBasicMappingMultipleValues() { testValueMapping(ImmutableMap.<String, List<String>>builder() .put(ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("value 1").add("value 2").build()) .build(), ImmutableMap.<String, List<String>>builder() .put(ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("second value").add("second value 2").build()) .build() ); } @Test public void testAddBasicMappingMultipleValues() { testValueMapping(ImmutableMap.<String, List<String>>builder() .build(), ImmutableMap.<String, List<String>>builder() .put(ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("second value").add("second value 2").build()) .build() ); } @Test public void testDeleteBasicMappingMultipleValues() { testValueMapping(ImmutableMap.<String, List<String>>builder() .put(ATTRIBUTE_TO_MAP_NAME, ImmutableList.<String>builder().add("second value").add("second value 2").build()) .build(), ImmutableMap.<String, List<String>>builder() .build() ); } }