KEYCLOAK-2878 Testing of UserFederation admin REST endpoints

This commit is contained in:
mposolda 2016-04-21 22:28:13 +02:00
parent 631dcd93e6
commit f6a718f10a
14 changed files with 1117 additions and 17 deletions

View file

@ -17,6 +17,7 @@
package org.keycloak.representations.idm;
import java.util.List;
import java.util.Set;
/**
@ -25,7 +26,9 @@ import java.util.Set;
public class UserFederationProviderFactoryRepresentation {
private String id;
private Set<String> options;
private Set<String> options; // TODO:Remove as configurable providers are more flexible?
private String helpText; // Used for configurable providers
private List<ConfigPropertyRepresentation> properties; // Used for configurable providers
public String getId() {
return id;
@ -43,6 +46,22 @@ public class UserFederationProviderFactoryRepresentation {
this.options = options;
}
public String getHelpText() {
return helpText;
}
public void setHelpText(String helpText) {
this.helpText = helpText;
}
public List<ConfigPropertyRepresentation> getProperties() {
return properties;
}
public void setProperties(List<ConfigPropertyRepresentation> properties) {
this.properties = properties;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View file

@ -0,0 +1,82 @@
/*
* Copyright 2016 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.representations.idm;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class UserFederationSyncResultRepresentation {
private boolean ignored;
private int added;
private int updated;
private int removed;
private int failed;
private String status;
public boolean isIgnored() {
return ignored;
}
public void setIgnored(boolean ignored) {
this.ignored = ignored;
}
public int getAdded() {
return added;
}
public void setAdded(int added) {
this.added = added;
}
public int getUpdated() {
return updated;
}
public void setUpdated(int updated) {
this.updated = updated;
}
public int getRemoved() {
return removed;
}
public void setRemoved(int removed) {
this.removed = removed;
}
public int getFailed() {
return failed;
}
public void setFailed(int failed) {
this.failed = failed;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}

View file

@ -160,4 +160,14 @@ public interface RealmResource {
@Path("attack-detection")
AttackDetectionResource attackDetection();
@Path("user-federation")
UserFederationProvidersResource userFederation();
@Path("testLDAPConnection")
@GET
@NoCache
Response testLDAPConnection(@QueryParam("action") String action, @QueryParam("connectionUrl") String connectionUrl,
@QueryParam("bindDn") String bindDn, @QueryParam("bindCredential") String bindCredential,
@QueryParam("useTruststoreSpi") String useTruststoreSpi);
}

View file

@ -0,0 +1,103 @@
/*
* Copyright 2016 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.admin.client.resource;
import java.util.List;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.keycloak.representations.idm.UserFederationMapperRepresentation;
import org.keycloak.representations.idm.UserFederationMapperTypeRepresentation;
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
import org.keycloak.representations.idm.UserFederationSyncResultRepresentation;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface UserFederationProviderResource {
@PUT
@Consumes(MediaType.APPLICATION_JSON)
void update(UserFederationProviderRepresentation rep);
@GET
@Produces(MediaType.APPLICATION_JSON)
UserFederationProviderRepresentation toRepresentation();
@DELETE
void remove();
@POST
@Path("sync")
@Produces(MediaType.APPLICATION_JSON)
UserFederationSyncResultRepresentation syncUsers(@QueryParam("action") String action);
@GET
@Path("mapper-types")
Map<String, UserFederationMapperTypeRepresentation> getMapperTypes();
@GET
@Path("mappers")
@Produces(MediaType.APPLICATION_JSON)
List<UserFederationMapperRepresentation> getMappers();
@POST
@Path("mappers")
@Consumes(MediaType.APPLICATION_JSON)
Response addMapper(UserFederationMapperRepresentation mapper);
@GET
@Path("mappers/{id}")
@Produces(MediaType.APPLICATION_JSON)
UserFederationMapperRepresentation getMapperById(@PathParam("id") String id);
@PUT
@Path("mappers/{id}")
@Consumes(MediaType.APPLICATION_JSON)
void updateMapper(@PathParam("id") String id, UserFederationMapperRepresentation rep);
@DELETE
@Path("mappers/{id}")
void removeMapper(@PathParam("id") String id);
@POST
@Path("mappers/{id}/sync")
@Produces(MediaType.APPLICATION_JSON)
UserFederationSyncResultRepresentation syncMapperData(@PathParam("id") String mapperId, @QueryParam("direction") String direction);
}

View file

@ -0,0 +1,66 @@
/*
* Copyright 2016 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.admin.client.resource;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.keycloak.representations.idm.UserFederationProviderFactoryRepresentation;
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface UserFederationProvidersResource {
@GET
@Path("providers")
@Produces(MediaType.APPLICATION_JSON)
List<UserFederationProviderFactoryRepresentation> getProviderFactories();
@GET
@Path("providers/{id}")
@Produces(MediaType.APPLICATION_JSON)
UserFederationProviderFactoryRepresentation getProviderFactory(@PathParam("id") String id);
@POST
@Path("instances")
@Consumes(MediaType.APPLICATION_JSON)
Response create(UserFederationProviderRepresentation rep);
@GET
@Path("instances")
@Produces(MediaType.APPLICATION_JSON)
List<UserFederationProviderRepresentation> getProviderInstances();
@Path("instances/{id}")
UserFederationProviderResource get(@PathParam("id") String id);
}

View file

@ -158,6 +158,7 @@ public class UserFederationProviderResource {
@POST
@Path("sync")
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public UserFederationSyncResult syncUsers(@QueryParam("action") String action) {
auth.requireManage();
@ -352,6 +353,7 @@ public class UserFederationProviderResource {
@POST
@Path("mappers/{id}/sync")
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public UserFederationSyncResult syncMapperData(@PathParam("id") String mapperId, @QueryParam("direction") String direction) {
auth.requireManage();

View file

@ -0,0 +1,62 @@
/*
* Copyright 2016 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.federation;
import java.util.Arrays;
import java.util.List;
import org.keycloak.provider.ConfiguredProvider;
import org.keycloak.provider.ProviderConfigProperty;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class DummyConfigurableUserFederationProviderFactory extends DummyUserFederationProviderFactory implements ConfiguredProvider {
public static final String PROVIDER_NAME = "dummy-configurable";
@Override
public String getId() {
return PROVIDER_NAME;
}
@Override
public String getHelpText() {
return "Dummy User Federation Provider Help Text";
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
ProviderConfigProperty prop1 = new ProviderConfigProperty();
prop1.setName("prop1");
prop1.setLabel("Prop1");
prop1.setDefaultValue("prop1Default");
prop1.setHelpText("Prop1 HelpText");
prop1.setType(ProviderConfigProperty.STRING_TYPE);
ProviderConfigProperty prop2 = new ProviderConfigProperty();
prop2.setName("prop2");
prop2.setLabel("Prop2");
prop2.setDefaultValue("true");
prop2.setHelpText("Prop2 HelpText");
prop2.setType(ProviderConfigProperty.BOOLEAN_TYPE);
return Arrays.asList(prop1, prop2);
}
}

View file

@ -0,0 +1,149 @@
/*
* Copyright 2016 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.federation;
import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.GroupModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserModel;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class DummyUserFederationProvider implements UserFederationProvider {
private final Map<String, UserModel> users;
public DummyUserFederationProvider(Map<String, UserModel> users) {
this.users = users;
}
@Override
public UserModel validateAndProxy(RealmModel realm, UserModel local) {
return local;
}
@Override
public boolean synchronizeRegistrations() {
return true;
}
@Override
public UserModel register(RealmModel realm, UserModel user) {
users.put(user.getUsername(), user);
return user;
}
@Override
public boolean removeUser(RealmModel realm, UserModel user) {
return users.remove(user.getUsername()) != null;
}
@Override
public UserModel getUserByUsername(RealmModel realm, String username) {
return users.get(username);
}
@Override
public UserModel getUserByEmail(RealmModel realm, String email) {
return null;
}
@Override
public List<UserModel> searchByAttributes(Map<String, String> attributes, RealmModel realm, int maxResults) {
return Collections.emptyList();
}
@Override
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) {
return Collections.emptyList();
}
@Override
public void preRemove(RealmModel realm) {
}
@Override
public void preRemove(RealmModel realm, RoleModel role) {
}
@Override
public void preRemove(RealmModel realm, GroupModel group) {
}
@Override
public boolean isValid(RealmModel realm, UserModel local) {
String username = local.getUsername();
return users.containsKey(username);
}
@Override
public Set<String> getSupportedCredentialTypes(UserModel user) {
// Just user "test-user" is able to validate password with this federationProvider
if (user.getUsername().equals("test-user")) {
return Collections.singleton(UserCredentialModel.PASSWORD);
} else {
return Collections.emptySet();
}
}
@Override
public Set<String> getSupportedCredentialTypes() {
return Collections.singleton(UserCredentialModel.PASSWORD);
}
@Override
public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
if (user.getUsername().equals("test-user") && input.size() == 1) {
UserCredentialModel password = input.get(0);
if (password.getType().equals(UserCredentialModel.PASSWORD)) {
return "secret".equals(password.getValue());
}
}
return false;
}
@Override
public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
return validCredentials(realm, user, Arrays.asList(input));
}
@Override
public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel credential) {
return CredentialValidationOutput.failed();
}
@Override
public void close() {
}
}

View file

@ -0,0 +1,108 @@
/*
* Copyright 2016 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.federation;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderFactory;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserFederationSyncResult;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ConfiguredProvider;
import org.keycloak.provider.ProviderConfigProperty;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class DummyUserFederationProviderFactory implements UserFederationProviderFactory {
private static final Logger logger = Logger.getLogger(DummyUserFederationProviderFactory.class);
public static final String PROVIDER_NAME = "dummy";
private AtomicInteger fullSyncCounter = new AtomicInteger();
private AtomicInteger changedSyncCounter = new AtomicInteger();
private Map<String, UserModel> users = new HashMap<String, UserModel>();
@Override
public UserFederationProvider getInstance(KeycloakSession session, UserFederationProviderModel model) {
return new DummyUserFederationProvider(users);
}
@Override
public Set<String> getConfigurationOptions() {
Set<String> list = new HashSet<String>();
list.add("important.config");
return list;
}
@Override
public UserFederationProvider create(KeycloakSession session) {
return new DummyUserFederationProvider(users);
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return PROVIDER_NAME;
}
@Override
public UserFederationSyncResult syncAllUsers(KeycloakSessionFactory sessionFactory, String realmId, UserFederationProviderModel model) {
logger.info("syncAllUsers invoked");
fullSyncCounter.incrementAndGet();
return UserFederationSyncResult.empty();
}
@Override
public UserFederationSyncResult syncChangedUsers(KeycloakSessionFactory sessionFactory, String realmId, UserFederationProviderModel model, Date lastSync) {
logger.info("syncChangedUsers invoked");
changedSyncCounter.incrementAndGet();
return UserFederationSyncResult.empty();
}
public int getFullSyncCounter() {
return fullSyncCounter.get();
}
public int getChangedSyncCounter() {
return changedSyncCounter.get();
}
}

View file

@ -0,0 +1,36 @@
#
# Copyright 2016 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.
#
#
# Copyright 2016 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.
#
org.keycloak.testsuite.federation.DummyUserFederationProviderFactory
org.keycloak.testsuite.federation.DummyConfigurableUserFederationProviderFactory

View file

@ -22,16 +22,13 @@ import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.UserFederationProviderFactoryRepresentation;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import static org.junit.Assert.assertArrayEquals;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@ -49,16 +46,6 @@ public class Assert extends org.junit.Assert {
assertArrayEquals("Expected: " + Arrays.toString(expected) + ", was: " + Arrays.toString(actualNames), expected, actualNames);
}
private static <T> List<T> sort(List<T> list) {
Collections.sort(list, new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
return name(o1).compareTo(name(o2));
}
});
return list;
}
private static <T> String[] names(List<T> list) {
String[] names = new String[list.size()];
for (int i = 0; i < list.size(); i++) {
@ -69,7 +56,9 @@ public class Assert extends org.junit.Assert {
}
private static String name(Object o1) {
if (o1 instanceof RealmRepresentation) {
if (o1 instanceof String) {
return (String) o1;
} else if (o1 instanceof RealmRepresentation) {
return ((RealmRepresentation) o1).getRealm();
} else if (o1 instanceof ClientRepresentation) {
return ((ClientRepresentation) o1).getClientId();
@ -79,7 +68,10 @@ public class Assert extends org.junit.Assert {
return ((RoleRepresentation) o1).getName();
} else if (o1 instanceof UserRepresentation) {
return ((UserRepresentation) o1).getUsername();
} else if (o1 instanceof UserFederationProviderFactoryRepresentation) {
return ((UserFederationProviderFactoryRepresentation) o1).getId();
}
throw new IllegalArgumentException();
}
}

View file

@ -0,0 +1,363 @@
/*
* Copyright 2016 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.admin;
import java.util.LinkedList;
import java.util.List;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.Response;
import org.junit.Test;
import org.keycloak.admin.client.resource.UserFederationProvidersResource;
import org.keycloak.common.constants.KerberosConstants;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.LDAPConstants;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation;
import org.keycloak.representations.idm.ConfigPropertyRepresentation;
import org.keycloak.representations.idm.UserFederationProviderFactoryRepresentation;
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
import org.keycloak.representations.idm.UserFederationSyncResultRepresentation;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.admin.authentication.AbstractAuthenticationTest;
import org.keycloak.testsuite.util.UserFederationProviderBuilder;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class UserFederationTest extends AbstractAdminTest {
@Test
public void testProviderFactories() {
List<UserFederationProviderFactoryRepresentation> providerFactories = userFederation().getProviderFactories();
Assert.assertNames(providerFactories, "ldap", "kerberos", "dummy", "dummy-configurable");
// Builtin provider without properties
UserFederationProviderFactoryRepresentation ldapProvider = userFederation().getProviderFactory("ldap");
Assert.assertEquals(ldapProvider.getId(), "ldap");
Assert.assertEquals(0, ldapProvider.getOptions().size());
// Configurable through the "old-way" options
UserFederationProviderFactoryRepresentation dummyProvider = userFederation().getProviderFactory("dummy");
Assert.assertEquals(dummyProvider.getId(), "dummy");
Assert.assertNames(new LinkedList<>(dummyProvider.getOptions()), "important.config");
// Configurable through the "new-way" ConfiguredProvider
UserFederationProviderFactoryRepresentation dummyConfiguredProvider = userFederation().getProviderFactory("dummy-configurable");
Assert.assertEquals(dummyConfiguredProvider.getId(), "dummy-configurable");
Assert.assertTrue(dummyConfiguredProvider.getOptions() == null || dummyConfiguredProvider.getOptions().isEmpty());
Assert.assertEquals("Dummy User Federation Provider Help Text", dummyConfiguredProvider.getHelpText());
Assert.assertEquals(2, dummyConfiguredProvider.getProperties().size());
assertProviderConfigProperty(dummyConfiguredProvider.getProperties().get(0), "prop1", "Prop1", "prop1Default", "Prop1 HelpText", ProviderConfigProperty.STRING_TYPE);
assertProviderConfigProperty(dummyConfiguredProvider.getProperties().get(1), "prop2", "Prop2", "true", "Prop2 HelpText", ProviderConfigProperty.BOOLEAN_TYPE);
try {
userFederation().getProviderFactory("not-existent");
Assert.fail("Not expected to find not-existent provider");
} catch (NotFoundException nfe) {
nfe.getResponse().close();
}
}
private UserFederationProvidersResource userFederation() {
return realm.userFederation();
}
private void assertProviderConfigProperty(ConfigPropertyRepresentation property, String name, String label, String defaultValue, String helpText, String type) {
Assert.assertEquals(name, property.getName());
Assert.assertEquals(label, property.getLabel());
Assert.assertEquals(defaultValue, property.getDefaultValue());
Assert.assertEquals(helpText, property.getHelpText());
Assert.assertEquals(type, property.getType());
}
@Test
public void testCreateProvider() {
// create provider without configuration and displayName
UserFederationProviderRepresentation dummyRep1 = UserFederationProviderBuilder.create()
.providerName("dummy")
.displayName("")
.priority(2)
.fullSyncPeriod(1000)
.changedSyncPeriod(500)
.lastSync(123)
.build();
String id1 = createUserFederationProvider(dummyRep1);
// create provider with configuration and displayName
UserFederationProviderRepresentation dummyRep2 = UserFederationProviderBuilder.create()
.providerName("dummy")
.displayName("dn1")
.priority(1)
.configProperty("prop1", "prop1Val")
.configProperty("prop2", "true")
.build();
String id2 = createUserFederationProvider(dummyRep2);
// Assert provider instances available
assertFederationProvider(userFederation().get(id1).toRepresentation(), id1, id1, "dummy", 2, 1000, 500, 123);
assertFederationProvider(userFederation().get(id2).toRepresentation(), id2, "dn1", "dummy", 1, -1, -1, -1, "prop1", "prop1Val", "prop2", "true");
// Assert sorted
List<UserFederationProviderRepresentation> providerInstances = userFederation().getProviderInstances();
Assert.assertEquals(providerInstances.size(), 2);
assertFederationProvider(providerInstances.get(0), id2, "dn1", "dummy", 1, -1, -1, -1, "prop1", "prop1Val", "prop2", "true");
assertFederationProvider(providerInstances.get(1), id1, id1, "dummy", 2, 1000, 500, 123);
// Remove providers
userFederation().get(id1).remove();
userFederation().get(id2).remove();
}
@Test
public void testValidateAndCreateLdapProvider() {
// Invalid filter
UserFederationProviderRepresentation ldapRep = UserFederationProviderBuilder.create()
.displayName("ldap1")
.providerName("ldap")
.priority(1)
.configProperty(LDAPConstants.CUSTOM_USER_SEARCH_FILTER, "dc=something")
.build();
Response resp = userFederation().create(ldapRep);
Assert.assertEquals(400, resp.getStatus());
resp.close();
// Invalid filter
ldapRep.getConfig().put(LDAPConstants.CUSTOM_USER_SEARCH_FILTER, "(dc=something");
resp = userFederation().create(ldapRep);
Assert.assertEquals(400, resp.getStatus());
resp.close();
// Invalid filter
ldapRep.getConfig().put(LDAPConstants.CUSTOM_USER_SEARCH_FILTER, "dc=something)");
resp = userFederation().create(ldapRep);
Assert.assertEquals(400, resp.getStatus());
resp.close();
// Assert nothing created so far
Assert.assertTrue(userFederation().getProviderInstances().isEmpty());
// Valid filter. Creation success
ldapRep.getConfig().put(LDAPConstants.CUSTOM_USER_SEARCH_FILTER, "(dc=something)");
String id1 = createUserFederationProvider(ldapRep);
// Missing filter is ok too. Creation success
UserFederationProviderRepresentation ldapRep2 = UserFederationProviderBuilder.create()
.displayName("ldap2")
.providerName("ldap")
.priority(2)
.configProperty(LDAPConstants.BIND_DN, "cn=manager")
.configProperty(LDAPConstants.BIND_CREDENTIAL, "password")
.build();
String id2 = createUserFederationProvider(ldapRep2);
// Assert both providers created
List<UserFederationProviderRepresentation> providerInstances = userFederation().getProviderInstances();
Assert.assertEquals(providerInstances.size(), 2);
assertFederationProvider(providerInstances.get(0), id1, "ldap1", "ldap", 1, -1, -1, -1, LDAPConstants.CUSTOM_USER_SEARCH_FILTER, "(dc=something)");
assertFederationProvider(providerInstances.get(1), id2, "ldap2", "ldap", 2, -1, -1, -1, LDAPConstants.BIND_DN, "cn=manager", LDAPConstants.BIND_CREDENTIAL, "password");
// Cleanup
userFederation().get(id1).remove();
userFederation().get(id2).remove();
}
@Test
public void testUpdateProvider() {
UserFederationProviderRepresentation ldapRep = UserFederationProviderBuilder.create()
.providerName("ldap")
.priority(2)
.configProperty(LDAPConstants.BIND_DN, "cn=manager")
.configProperty(LDAPConstants.BIND_CREDENTIAL, "password")
.build();
String id = createUserFederationProvider(ldapRep);
assertFederationProvider(userFederation().get(id).toRepresentation(), id, id, "ldap", 2, -1, -1, -1, LDAPConstants.BIND_DN, "cn=manager", LDAPConstants.BIND_CREDENTIAL, "password");
// Assert update with invalid filter should fail
ldapRep = userFederation().get(id).toRepresentation();
ldapRep.setDisplayName("");
ldapRep.getConfig().put(LDAPConstants.CUSTOM_USER_SEARCH_FILTER, "(dc=something2");
ldapRep.getConfig().put(LDAPConstants.BIND_DN, "cn=manager-updated");
try {
userFederation().get(id).update(ldapRep);
Assert.fail("Not expected to successfull update");
} catch (BadRequestException bre) {
bre.getResponse().close();
}
// Assert nothing was updated
assertFederationProvider(userFederation().get(id).toRepresentation(), id, id, "ldap", 2, -1, -1, -1, LDAPConstants.BIND_DN, "cn=manager", LDAPConstants.BIND_CREDENTIAL, "password");
// Change filter to be valid
ldapRep.getConfig().put(LDAPConstants.CUSTOM_USER_SEARCH_FILTER, "(dc=something2)");
userFederation().get(id).update(ldapRep);
// Assert updated successfully
ldapRep = userFederation().get(id).toRepresentation();
assertFederationProvider(ldapRep, id, id, "ldap", 2, -1, -1, -1, LDAPConstants.BIND_DN, "cn=manager-updated", LDAPConstants.BIND_CREDENTIAL, "password",
LDAPConstants.CUSTOM_USER_SEARCH_FILTER, "(dc=something2)");
// Assert update displayName
ldapRep.setDisplayName("ldap2");
userFederation().get(id).update(ldapRep);
assertFederationProvider(userFederation().get(id).toRepresentation(), id, "ldap2", "ldap", 2, -1, -1, -1, LDAPConstants.BIND_DN, "cn=manager-updated", LDAPConstants.BIND_CREDENTIAL, "password",
LDAPConstants.CUSTOM_USER_SEARCH_FILTER, "(dc=something2)");
// Cleanup
userFederation().get(id).remove();
}
@Test
public void testKerberosAuthenticatorEnabledAutomatically() {
// Assert kerberos authenticator DISABLED
AuthenticationExecutionInfoRepresentation kerberosExecution = findKerberosExecution();
Assert.assertEquals(kerberosExecution.getRequirement(), AuthenticationExecutionModel.Requirement.DISABLED.toString());
// create LDAP provider with kerberos
UserFederationProviderRepresentation ldapRep = UserFederationProviderBuilder.create()
.displayName("ldap2")
.providerName("ldap")
.priority(2)
.configProperty(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION, "true")
.build();
String id = createUserFederationProvider(ldapRep);
// Assert kerberos authenticator ALTERNATIVE
kerberosExecution = findKerberosExecution();
Assert.assertEquals(kerberosExecution.getRequirement(), AuthenticationExecutionModel.Requirement.ALTERNATIVE.toString());
// Switch kerberos authenticator to DISABLED
kerberosExecution.setRequirement(AuthenticationExecutionModel.Requirement.DISABLED.toString());
realm.flows().updateExecutions("browser", kerberosExecution);
// update LDAP provider with kerberos
ldapRep = userFederation().get(id).toRepresentation();
userFederation().get(id).update(ldapRep);
// Assert kerberos authenticator ALTERNATIVE
kerberosExecution = findKerberosExecution();
Assert.assertEquals(kerberosExecution.getRequirement(), AuthenticationExecutionModel.Requirement.ALTERNATIVE.toString());
// Cleanup
kerberosExecution.setRequirement(AuthenticationExecutionModel.Requirement.DISABLED.toString());
realm.flows().updateExecutions("browser", kerberosExecution);
userFederation().get(id).remove();
}
@Test (expected = NotFoundException.class)
public void testLookupNotExistentProvider() {
userFederation().get("not-existent").toRepresentation();
}
@Test
public void testSyncFederationProvider() {
// create provider
UserFederationProviderRepresentation dummyRep1 = UserFederationProviderBuilder.create()
.providerName("dummy")
.build();
String id1 = createUserFederationProvider(dummyRep1);
// Sync with unknown action shouldn't pass
try {
userFederation().get(id1).syncUsers("unknown");
Assert.fail("Not expected to sync with unknown action");
} catch (NotFoundException nfe) {
nfe.getResponse().close();
}
// Assert sync didn't happen
Assert.assertEquals(-1, userFederation().get(id1).toRepresentation().getLastSync());
// Sync and assert it happened
UserFederationSyncResultRepresentation syncResult = userFederation().get(id1).syncUsers("triggerFullSync");
Assert.assertEquals("0 imported users, 0 updated users", syncResult.getStatus());
int fullSyncTime = userFederation().get(id1).toRepresentation().getLastSync();
Assert.assertTrue(fullSyncTime > 0);
// Changed sync
setTimeOffset(50);
syncResult = userFederation().get(id1).syncUsers("triggerChangedUsersSync");
Assert.assertEquals("0 imported users, 0 updated users", syncResult.getStatus());
int changedSyncTime = userFederation().get(id1).toRepresentation().getLastSync();
Assert.assertTrue(fullSyncTime + 50 <= changedSyncTime);
// Cleanup
resetTimeOffset();
userFederation().get(id1).remove();
}
private String createUserFederationProvider(UserFederationProviderRepresentation rep) {
Response resp = userFederation().create(rep);
Assert.assertEquals(201, resp.getStatus());
resp.close();
return ApiUtil.getCreatedId(resp);
}
private void assertFederationProvider(UserFederationProviderRepresentation rep, String id, String displayName, String providerName,
int priority, int fullSyncPeriod, int changeSyncPeriod, int lastSync,
String... config) {
Assert.assertEquals(id, rep.getId());
Assert.assertEquals(displayName, rep.getDisplayName());
Assert.assertEquals(providerName, rep.getProviderName());
Assert.assertEquals(priority, rep.getPriority());
Assert.assertEquals(fullSyncPeriod, rep.getFullSyncPeriod());
Assert.assertEquals(changeSyncPeriod, rep.getChangedSyncPeriod());
Assert.assertEquals(lastSync, rep.getLastSync());
if (config == null) {
config = new String[] {};
}
Assert.assertEquals(rep.getConfig().size() * 2, config.length);
for (int i=0 ; i<config.length ; i+=2) {
String key = config[i];
String value = config[i+1];
Assert.assertEquals(value, rep.getConfig().get(key));
}
}
private AuthenticationExecutionInfoRepresentation findKerberosExecution() {
AuthenticationExecutionInfoRepresentation kerberosExecution = null;
Response response = realm.flows().getExecutions("browser");
try {
List<AuthenticationExecutionInfoRepresentation> executionReps = response.readEntity(new GenericType<List<AuthenticationExecutionInfoRepresentation>>() {
});
kerberosExecution = AbstractAuthenticationTest.findExecutionByProvider("auth-spnego", executionReps);
} finally {
response.close();
}
Assert.assertNotNull(kerberosExecution);
return kerberosExecution;
}
}

View file

@ -63,7 +63,7 @@ public abstract class AbstractAuthenticationTest extends AbstractKeycloakTest {
}
AuthenticationExecutionInfoRepresentation findExecutionByProvider(String provider, List<AuthenticationExecutionInfoRepresentation> executions) {
public static AuthenticationExecutionInfoRepresentation findExecutionByProvider(String provider, List<AuthenticationExecutionInfoRepresentation> executions) {
for (AuthenticationExecutionInfoRepresentation exec : executions) {
if (provider.equals(exec.getProviderId())) {
return exec;

View file

@ -0,0 +1,108 @@
/*
* Copyright 2016 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.util;
import java.util.HashMap;
import java.util.Map;
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class UserFederationProviderBuilder {
private String id;
private String displayName;
private String providerName;
private Map<String, String> config;
private int priority = 1;
private int fullSyncPeriod = -1;
private int changedSyncPeriod = -1;
private int lastSync = -1;
private UserFederationProviderBuilder() {};
public static UserFederationProviderBuilder create() {
return new UserFederationProviderBuilder();
}
public void setId(String id) {
this.id = id;
}
public UserFederationProviderBuilder displayName(String displayName) {
this.displayName = displayName;
return this;
}
public UserFederationProviderBuilder providerName(String providerName) {
this.providerName = providerName;
return this;
}
public UserFederationProviderBuilder configProperty(String key, String value) {
if (this.config == null) {
this.config = new HashMap<>();
}
this.config.put(key, value);
return this;
}
public UserFederationProviderBuilder removeConfigProperty(String key) {
if (this.config != null) {
this.config.remove(key);
}
return this;
}
public UserFederationProviderBuilder priority(int priority) {
this.priority = priority;
return this;
}
public UserFederationProviderBuilder fullSyncPeriod(int fullSyncPeriod) {
this.fullSyncPeriod = fullSyncPeriod;
return this;
}
public UserFederationProviderBuilder changedSyncPeriod(int changedSyncPeriod) {
this.changedSyncPeriod = changedSyncPeriod;
return this;
}
public UserFederationProviderBuilder lastSync(int lastSync) {
this.lastSync = lastSync;
return this;
}
public UserFederationProviderRepresentation build() {
UserFederationProviderRepresentation rep = new UserFederationProviderRepresentation();
rep.setId(id);
rep.setDisplayName(displayName);
rep.setProviderName(providerName);
rep.setConfig(config);
rep.setPriority(priority);
rep.setFullSyncPeriod(fullSyncPeriod);
rep.setChangedSyncPeriod(changedSyncPeriod);
rep.setLastSync(lastSync);
return rep;
}
}