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()
);
}
}