KEYCLOAK-3159 Migrate federation package from old testsuite

This commit is contained in:
mposolda 2019-02-21 09:23:54 +01:00 committed by Pavel Drozd
parent f295a2e303
commit 89d0c51e13
26 changed files with 1149 additions and 1658 deletions

View file

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Red Hat, Inc. and/or its affiliates * Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags. * and other contributors as indicated by the @author tags.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.keycloak.testsuite.federation.storage; package org.keycloak.testsuite.federation;
import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;

View file

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Red Hat, Inc. and/or its affiliates * Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags. * and other contributors as indicated by the @author tags.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.keycloak.testsuite.federation.storage; package org.keycloak.testsuite.federation;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;

View file

@ -16,13 +16,16 @@
*/ */
package org.keycloak.testsuite.federation; package org.keycloak.testsuite.federation;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.storage.UserStorageProviderFactory; import org.keycloak.storage.UserStorageProviderFactory;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
@ -34,8 +37,22 @@ public class UserMapStorageFactory implements UserStorageProviderFactory<UserMap
public static final String PROVIDER_ID = "user-password-map-arq"; public static final String PROVIDER_ID = "user-password-map-arq";
protected static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
static {
ProviderConfigProperty attr = new ProviderConfigProperty("attr", "attr",
"This is some attribute",
ProviderConfigProperty.STRING_TYPE, null);
configProperties.add(attr);
}
protected Map<String, String> userPasswords = new HashMap<>(); protected Map<String, String> userPasswords = new HashMap<>();
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
}
@Override @Override
public UserMapStorage create(KeycloakSession session, ComponentModel model) { public UserMapStorage create(KeycloakSession session, ComponentModel model) {
return new UserMapStorage(session, model, userPasswords); return new UserMapStorage(session, model, userPasswords);

View file

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Red Hat, Inc. and/or its affiliates * Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags. * and other contributors as indicated by the @author tags.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -41,10 +41,10 @@ import java.util.concurrent.TimeUnit;
public class SyncDummyUserFederationProviderFactory extends DummyUserFederationProviderFactory { public class SyncDummyUserFederationProviderFactory extends DummyUserFederationProviderFactory {
// Used during SyncFederationTest // Used during SyncFederationTest
static volatile CountDownLatch latch1 = new CountDownLatch(1); public static volatile CountDownLatch latch1 = new CountDownLatch(1);
static volatile CountDownLatch latch2 = new CountDownLatch(1); public static volatile CountDownLatch latch2 = new CountDownLatch(1);
static void restartLatches() { public static void restartLatches() {
latch1 = new CountDownLatch(1); latch1 = new CountDownLatch(1);
latch2 = new CountDownLatch(1); latch2 = new CountDownLatch(1);
} }

View file

@ -1,4 +1,6 @@
org.keycloak.testsuite.federation.DummyUserFederationProviderFactory org.keycloak.testsuite.federation.DummyUserFederationProviderFactory
org.keycloak.testsuite.federation.FailableHardcodedStorageProviderFactory
org.keycloak.testsuite.federation.UserMapStorageFactory org.keycloak.testsuite.federation.UserMapStorageFactory
org.keycloak.testsuite.federation.UserPropertyFileStorageFactory org.keycloak.testsuite.federation.UserPropertyFileStorageFactory
org.keycloak.testsuite.federation.PassThroughFederatedUserStorageProviderFactory org.keycloak.testsuite.federation.PassThroughFederatedUserStorageProviderFactory
org.keycloak.testsuite.federation.sync.SyncDummyUserFederationProviderFactory

View file

@ -171,7 +171,7 @@ public final class TestContext {
public TestCleanup getOrCreateCleanup(String realmName) { public TestCleanup getOrCreateCleanup(String realmName) {
TestCleanup cleanup = cleanups.get(realmName); TestCleanup cleanup = cleanups.get(realmName);
if (cleanup == null) { if (cleanup == null) {
cleanup = new TestCleanup(adminClient, realmName); cleanup = new TestCleanup(this, realmName);
TestCleanup existing = cleanups.putIfAbsent(realmName, cleanup); TestCleanup existing = cleanups.putIfAbsent(realmName, cleanup);
if (existing != null) { if (existing != null) {

View file

@ -24,6 +24,7 @@ import javax.ws.rs.NotFoundException;
import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.common.util.ConcurrentMultivaluedHashMap; import org.keycloak.common.util.ConcurrentMultivaluedHashMap;
import org.keycloak.testsuite.arquillian.TestContext;
/** /**
* Enlist resources to be cleaned after test method * Enlist resources to be cleaned after test method
@ -42,15 +43,15 @@ public class TestCleanup {
private static final String AUTH_FLOW_IDS = "AUTH_FLOW_IDS"; private static final String AUTH_FLOW_IDS = "AUTH_FLOW_IDS";
private static final String AUTH_CONFIG_IDS = "AUTH_CONFIG_IDS"; private static final String AUTH_CONFIG_IDS = "AUTH_CONFIG_IDS";
private final Keycloak adminClient; private final TestContext testContext;
private final String realmName; private final String realmName;
// Key is kind of entity (eg. "client", "role", "user" etc), Values are all kind of entities of given type to cleanup // Key is kind of entity (eg. "client", "role", "user" etc), Values are all kind of entities of given type to cleanup
private ConcurrentMultivaluedHashMap<String, String> entities = new ConcurrentMultivaluedHashMap<>(); private ConcurrentMultivaluedHashMap<String, String> entities = new ConcurrentMultivaluedHashMap<>();
public TestCleanup(Keycloak adminClient, String realmName) { public TestCleanup(TestContext testContext, String realmName) {
this.adminClient = adminClient; this.testContext = testContext;
this.realmName = realmName; this.realmName = realmName;
} }
@ -101,7 +102,7 @@ public class TestCleanup {
public void executeCleanup() { public void executeCleanup() {
RealmResource realm = adminClient.realm(realmName); RealmResource realm = getAdminClient().realm(realmName);
List<String> userIds = entities.get(USER_IDS); List<String> userIds = entities.get(USER_IDS);
if (userIds != null) { if (userIds != null) {
@ -204,4 +205,8 @@ public class TestCleanup {
} }
} }
private Keycloak getAdminClient() {
return testContext.getAdminClient();
}
} }

View file

@ -0,0 +1,130 @@
/*
* Copyright 2017 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.storage;
import org.jboss.arquillian.container.test.api.ContainerController;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.graphene.page.Page;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.jpa.RealmAdapter;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.UserStorageProviderModel;
import org.keycloak.testsuite.AbstractAuthTest;
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
import java.util.List;
/**
* KEYCLOAK-3903 and KEYCLOAK-3620
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class BrokenUserStorageTest extends AbstractTestRealmKeycloakTest {
@Deployment
public static WebArchive deploy() {
return RunOnServerDeployment.create(ComponentExportImportTest.class, AbstractAuthTest.class, RealmResource.class)
.addPackages(true, "org.keycloak.testsuite");
}
@ArquillianResource
protected ContainerController controller;
@Page
protected LoginPage loginPage;
@Page
protected AppPage appPage;
@Override
public void configureTestRealm(RealmRepresentation testRealm) {
}
private void loginSuccessAndLogout(String username, String password) {
loginPage.open();
loginPage.login(username, password);
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
oauth.openLogout();
}
@Test
public void testBootWithBadProviderId() throws Exception {
testingClient.server().run(session -> {
// set this system property
System.setProperty(RealmAdapter.COMPONENT_PROVIDER_EXISTS_DISABLED, "true");
RealmModel realm = session.realms().getRealmByName("master");
UserStorageProviderModel model = new UserStorageProviderModel();
model.setName("bad-provider-id");
model.setPriority(2);
model.setParentId(realm.getId());
model.setProviderId("error");
ComponentModel component = realm.importComponentModel(model);
});
controller.stop(suiteContext.getAuthServerInfo().getQualifier());
controller.start(suiteContext.getAuthServerInfo().getQualifier());
reconnectAdminClient();
loginSuccessAndLogout("test-user@localhost", "password");
// make sure we can list components and delete provider as this is an admin console operation
RealmResource master = adminClient.realms().realm("master");
String masterId = master.toRepresentation().getId();
List<ComponentRepresentation> components = master.components().query(masterId, UserStorageProvider.class.getName());
ComponentRepresentation found = null;
for (ComponentRepresentation rep : components) {
if (rep.getName().equals("bad-provider-id")) {
found = rep;
}
}
Assert.assertNotNull(found);
master.components().component(found.getId()).remove();
List<ComponentRepresentation> components2 = master.components().query(masterId, UserStorageProvider.class.getName());
Assert.assertEquals(components.size() - 1, components2.size());
}
@After
public void resetSystemProperty() {
testingClient.server().run(session -> {
System.getProperties().remove(RealmAdapter.COMPONENT_PROVIDER_EXISTS_DISABLED);
});
}
}

View file

@ -18,19 +18,26 @@ import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.exportimport.ExportImportConfig; import org.keycloak.exportimport.ExportImportConfig;
import org.keycloak.exportimport.ExportImportManager; import org.keycloak.exportimport.ExportImportManager;
import org.keycloak.exportimport.singlefile.SingleFileExportProviderFactory; import org.keycloak.exportimport.singlefile.SingleFileExportProviderFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.representations.idm.ComponentRepresentation; import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.storage.UserStorageProvider; import org.keycloak.storage.UserStorageProvider;
import org.keycloak.testsuite.AbstractAuthTest; import org.keycloak.testsuite.AbstractAuthTest;
import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.admin.ApiUtil;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST; import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
import org.keycloak.testsuite.client.KeycloakTestingClient;
import org.keycloak.testsuite.federation.UserMapStorageFactory; import org.keycloak.testsuite.federation.UserMapStorageFactory;
import org.keycloak.testsuite.runonserver.RunOnServerDeployment; import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
import org.keycloak.testsuite.util.RealmBuilder;
/** /**
* *
* @author tkyjovsk * @author tkyjovsk
*/ */
public class ComponentExportImportTest extends AbstractAuthTest implements Serializable { public class ComponentExportImportTest extends AbstractAuthTest {
private static final String REALM_NAME = "exported-component";
private File exportFile; private File exportFile;
@ -44,9 +51,25 @@ public class ComponentExportImportTest extends AbstractAuthTest implements Seria
public void setDirs() { public void setDirs() {
exportFile = new File (new File(System.getProperty("auth.server.config.dir", "target")), "singleFile-full.json"); exportFile = new File (new File(System.getProperty("auth.server.config.dir", "target")), "singleFile-full.json");
log.infof("Export file: %s", exportFile); log.infof("Export file: %s", exportFile);
// Remove realm if exists
testingClient.server().run(session -> {
RealmModel realm = session.realms().getRealmByName(REALM_NAME);
if (realm != null) {
session.realms().removeRealm(realm.getId());
}
});
} }
public void clearExportImportProperties() {
@Override
public RealmResource testRealmResource() {
return adminClient.realm(REALM_NAME);
}
static void clearExportImportProperties(KeycloakTestingClient testingClient) {
testingClient.server().run(session -> {
// Clear export/import properties after test // Clear export/import properties after test
Properties systemProps = System.getProperties(); Properties systemProps = System.getProperties();
Set<String> propsToRemove = new HashSet<>(); Set<String> propsToRemove = new HashSet<>();
@ -60,19 +83,23 @@ public class ComponentExportImportTest extends AbstractAuthTest implements Seria
for (String propToRemove : propsToRemove) { for (String propToRemove : propsToRemove) {
systemProps.remove(propToRemove); systemProps.remove(propToRemove);
} }
});
} }
protected String addComponent(ComponentRepresentation component) { protected String addComponent(ComponentRepresentation component) {
return ApiUtil.getCreatedId(testRealmResource().components().add(component)); return ApiUtil.getCreatedId(testRealmResource().components().add(component));
} }
@Test @Test
@Ignore
public void testSingleFile() { public void testSingleFile() {
clearExportImportProperties(); clearExportImportProperties(testingClient);
RealmRepresentation realmRep = RealmBuilder.create()
.name(REALM_NAME)
.build();
adminClient.realms().create(realmRep);
String realmId = testRealmResource().toRepresentation().getId(); String realmId = testRealmResource().toRepresentation().getId();
String realmName = testRealmResource().toRepresentation().getRealm();
ComponentRepresentation parentComponent = new ComponentRepresentation(); ComponentRepresentation parentComponent = new ComponentRepresentation();
parentComponent.setParentId(realmId); parentComponent.setParentId(realmId);
@ -96,23 +123,36 @@ public class ComponentExportImportTest extends AbstractAuthTest implements Seria
subcomponent.getConfig().putSingle("attr", "value2"); subcomponent.getConfig().putSingle("attr", "value2");
String subcomponentId = addComponent(subcomponent); String subcomponentId = addComponent(subcomponent);
final String exportFilePath = exportFile.getAbsolutePath();
// export // export
testingClient.server().run(session -> { testingClient.server().run(session -> {
ExportImportConfig.setProvider(SingleFileExportProviderFactory.PROVIDER_ID); ExportImportConfig.setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
ExportImportConfig.setFile(exportFile.getAbsolutePath()); ExportImportConfig.setFile(exportFilePath);
ExportImportConfig.setRealmName(realmName); ExportImportConfig.setRealmName(REALM_NAME);
ExportImportConfig.setAction(ExportImportConfig.ACTION_EXPORT); ExportImportConfig.setAction(ExportImportConfig.ACTION_EXPORT);
new ExportImportManager(session).runExport(); new ExportImportManager(session).runExport();
}); });
testRealmResource().remove();
try {
testRealmResource().toRepresentation();
Assert.fail("Realm wasn't expected to be found");
} catch (NotFoundException nfe) {
// Expected
}
// import // import
testingClient.server().run(session -> { testingClient.server().run(session -> {
Assert.assertNull(session.realms().getRealmByName(TEST)); Assert.assertNull(session.realms().getRealmByName(REALM_NAME));
ExportImportConfig.setAction(ExportImportConfig.ACTION_IMPORT); ExportImportConfig.setAction(ExportImportConfig.ACTION_IMPORT);
new ExportImportManager(session).runImport(); new ExportImportManager(session).runImport();
Assert.assertNotNull(session.realms().getRealmByName(TEST));
}); });
// Assert realm was imported
Assert.assertNotNull(testRealmResource().toRepresentation());
try { try {
parentComponent = testRealmResource().components().component(parentComponentId).toRepresentation(); parentComponent = testRealmResource().components().component(parentComponentId).toRepresentation();
subcomponent = testRealmResource().components().component(subcomponentId).toRepresentation(); subcomponent = testRealmResource().components().component(subcomponentId).toRepresentation();
@ -127,7 +167,7 @@ public class ComponentExportImportTest extends AbstractAuthTest implements Seria
Assert.assertEquals(parentComponent.getProviderType(), UserStorageProvider.class.getName()); Assert.assertEquals(parentComponent.getProviderType(), UserStorageProvider.class.getName());
Assert.assertEquals(parentComponent.getConfig().getFirst("attr"), "value"); Assert.assertEquals(parentComponent.getConfig().getFirst("attr"), "value");
Assert.assertEquals(subcomponent.getParentId(), realmId); Assert.assertEquals(subcomponent.getParentId(), parentComponent.getId());
Assert.assertEquals(subcomponent.getName(), "child"); Assert.assertEquals(subcomponent.getName(), "child");
Assert.assertEquals(subcomponent.getSubType(), "subtype2"); Assert.assertEquals(subcomponent.getSubType(), "subtype2");
Assert.assertEquals(subcomponent.getProviderId(), UserMapStorageFactory.PROVIDER_ID); Assert.assertEquals(subcomponent.getProviderId(), UserMapStorageFactory.PROVIDER_ID);

View file

@ -0,0 +1,237 @@
/*
* Copyright 2017 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.storage;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.credential.CredentialModel;
import org.keycloak.credential.hash.PasswordHashProvider;
import org.keycloak.exportimport.ExportImportConfig;
import org.keycloak.exportimport.ExportImportManager;
import org.keycloak.exportimport.dir.DirExportProviderFactory;
import org.keycloak.exportimport.singlefile.SingleFileExportProviderFactory;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.AbstractAuthTest;
import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
import java.io.File;
import java.util.LinkedList;
import java.util.List;
import javax.ws.rs.NotFoundException;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class FederatedStorageExportImportTest extends AbstractAuthTest {
private static final String REALM_NAME = "exported";
private String exportFileAbsolutePath;
private String exportDirAbsolutePath;
@Deployment
public static WebArchive deploy() {
return RunOnServerDeployment.create(ComponentExportImportTest.class, AbstractAuthTest.class, RealmResource.class)
.addPackages(true, "org.keycloak.testsuite");
}
@Before
public void setDirs() {
File baseDir = new File(System.getProperty("auth.server.config.dir", "target"));
exportFileAbsolutePath = new File (baseDir, "singleFile-full.json").getAbsolutePath();
log.infof("Export file: %s", exportFileAbsolutePath);
exportDirAbsolutePath = baseDir.getAbsolutePath() + File.separator + "dirExport";
log.infof("Export dir: %s", exportDirAbsolutePath);
}
@Override
public RealmResource testRealmResource() {
return adminClient.realm(REALM_NAME);
}
@After
public void cleanup() {
try {
testRealmResource().remove();
} catch (NotFoundException ignore) {
}
}
public static PasswordHashProvider getHashProvider(KeycloakSession session, PasswordPolicy policy) {
PasswordHashProvider hash = session.getProvider(PasswordHashProvider.class, policy.getHashAlgorithm());
if (hash == null) {
return session.getProvider(PasswordHashProvider.class, PasswordPolicy.HASH_ALGORITHM_DEFAULT);
}
return hash;
}
@Test
public void testSingleFile() throws Exception {
ComponentExportImportTest.clearExportImportProperties(testingClient);
final String userId = "f:1:path";
testingClient.server().run(session -> {
RealmModel realm = new RealmManager(session).createRealm(REALM_NAME);
RoleModel role = realm.addRole("test-role");
GroupModel group = realm.createGroup("test-group");
List<String> attrValues = new LinkedList<>();
attrValues.add("1");
attrValues.add("2");
session.userFederatedStorage().setSingleAttribute(realm, userId, "single1", "value1");
session.userFederatedStorage().setAttribute(realm, userId, "list1", attrValues);
session.userFederatedStorage().addRequiredAction(realm, userId, "UPDATE_PASSWORD");
CredentialModel credential = new CredentialModel();
FederatedStorageExportImportTest.getHashProvider(session, realm.getPasswordPolicy()).encode("password", realm.
getPasswordPolicy().getHashIterations(), credential);
session.userFederatedStorage().createCredential(realm, userId, credential);
session.userFederatedStorage().grantRole(realm, userId, role);
session.userFederatedStorage().joinGroup(realm, userId, group);
});
final String realmId = testRealmResource().toRepresentation().getId();
final String groupId = testRealmResource().getGroupByPath("/test-group").getId();
final String exportFileAbsolutePath = this.exportFileAbsolutePath;
testingClient.server().run(session -> {
ExportImportConfig.setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
ExportImportConfig.setFile(exportFileAbsolutePath);
ExportImportConfig.setRealmName(REALM_NAME);
ExportImportConfig.setAction(ExportImportConfig.ACTION_EXPORT);
new ExportImportManager(session).runExport();
session.realms().removeRealm(realmId);
});
testingClient.server().run(session -> {
Assert.assertNull(session.realms().getRealmByName(REALM_NAME));
ExportImportConfig.setAction(ExportImportConfig.ACTION_IMPORT);
new ExportImportManager(session).runImport();
});
testingClient.server().run(session -> {
RealmModel realm = session.realms().getRealmByName(REALM_NAME);
Assert.assertNotNull(realm);
RoleModel role = realm.getRole("test-role");
GroupModel group = realm.getGroupById(groupId);
Assert.assertEquals(1, session.userFederatedStorage().getStoredUsersCount(realm));
MultivaluedHashMap<String, String> attributes = session.userFederatedStorage().getAttributes(realm, userId);
Assert.assertEquals(3, attributes.size());
Assert.assertEquals("value1", attributes.getFirst("single1"));
Assert.assertTrue(attributes.getList("list1").contains("1"));
Assert.assertTrue(attributes.getList("list1").contains("2"));
Assert.assertTrue(session.userFederatedStorage().getRequiredActions(realm, userId).contains("UPDATE_PASSWORD"));
Assert.assertTrue(session.userFederatedStorage().getRoleMappings(realm, userId).contains(role));
Assert.assertTrue(session.userFederatedStorage().getGroups(realm, userId).contains(group));
List<CredentialModel> creds = session.userFederatedStorage().getStoredCredentials(realm, userId);
Assert.assertEquals(1, creds.size());
Assert.assertTrue(FederatedStorageExportImportTest.getHashProvider(session, realm.getPasswordPolicy()).verify("password", creds.get(0)));
});
}
@Test
public void testDir() throws Exception {
ComponentExportImportTest.clearExportImportProperties(testingClient);
final String userId = "f:1:path";
testingClient.server().run(session -> {
RealmModel realm = new RealmManager(session).createRealm(REALM_NAME);
RoleModel role = realm.addRole("test-role");
GroupModel group = realm.createGroup("test-group");
List<String> attrValues = new LinkedList<>();
attrValues.add("1");
attrValues.add("2");
session.userFederatedStorage().setSingleAttribute(realm, userId, "single1", "value1");
session.userFederatedStorage().setAttribute(realm, userId, "list1", attrValues);
session.userFederatedStorage().addRequiredAction(realm, userId, "UPDATE_PASSWORD");
CredentialModel credential = new CredentialModel();
FederatedStorageExportImportTest.getHashProvider(session, realm.getPasswordPolicy()).encode("password", realm.
getPasswordPolicy().getHashIterations(), credential);
session.userFederatedStorage().createCredential(realm, userId, credential);
session.userFederatedStorage().grantRole(realm, userId, role);
session.userFederatedStorage().joinGroup(realm, userId, group);
session.userFederatedStorage().setNotBeforeForUser(realm, userId, 50);
});
final String realmId = testRealmResource().toRepresentation().getId();
final String groupId = testRealmResource().getGroupByPath("/test-group").getId();
final String exportDirAbsolutePath = this.exportDirAbsolutePath;
testingClient.server().run(session -> {
ExportImportConfig.setProvider(DirExportProviderFactory.PROVIDER_ID);
ExportImportConfig.setDir(exportDirAbsolutePath);
ExportImportConfig.setRealmName(REALM_NAME);
ExportImportConfig.setAction(ExportImportConfig.ACTION_EXPORT);
new ExportImportManager(session).runExport();
session.realms().removeRealm(realmId);
});
testingClient.server().run(session -> {
Assert.assertNull(session.realms().getRealmByName(REALM_NAME));
ExportImportConfig.setAction(ExportImportConfig.ACTION_IMPORT);
new ExportImportManager(session).runImport();
});
testingClient.server().run(session -> {
RealmModel realm = session.realms().getRealmByName(REALM_NAME);
Assert.assertNotNull(realm);
RoleModel role = realm.getRole("test-role");
GroupModel group = realm.getGroupById(groupId);
Assert.assertEquals(1, session.userFederatedStorage().getStoredUsersCount(realm));
MultivaluedHashMap<String, String> attributes = session.userFederatedStorage().getAttributes(realm, userId);
Assert.assertEquals(3, attributes.size());
Assert.assertEquals("value1", attributes.getFirst("single1"));
Assert.assertTrue(attributes.getList("list1").contains("1"));
Assert.assertTrue(attributes.getList("list1").contains("2"));
Assert.assertTrue(session.userFederatedStorage().getRequiredActions(realm, userId).contains("UPDATE_PASSWORD"));
Assert.assertTrue(session.userFederatedStorage().getRoleMappings(realm, userId).contains(role));
Assert.assertTrue(session.userFederatedStorage().getGroups(realm, userId).contains(group));
Assert.assertEquals(50, session.userFederatedStorage().getNotBeforeOfUser(realm, userId));
List<CredentialModel> creds = session.userFederatedStorage().getStoredCredentials(realm, userId);
Assert.assertEquals(1, creds.size());
Assert.assertTrue(FederatedStorageExportImportTest.getHashProvider(session, realm.getPasswordPolicy()).verify("password", creds.get(0)));
});
}
}

View file

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Red Hat, Inc. and/or its affiliates * Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags. * and other contributors as indicated by the @author tags.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -16,17 +16,22 @@
*/ */
package org.keycloak.testsuite.federation.storage; package org.keycloak.testsuite.federation.storage;
import org.jboss.arquillian.container.test.api.ContainerController;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.graphene.page.Page;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Assert; import org.junit.Assert;
import org.junit.ClassRule; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.common.constants.ServiceAccountConstants; import org.keycloak.common.constants.ServiceAccountConstants;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;
import org.keycloak.events.Details; import org.keycloak.events.Details;
import org.keycloak.events.Event;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
@ -34,19 +39,27 @@ import org.keycloak.models.cache.CachedUserModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import org.keycloak.representations.RefreshToken; import org.keycloak.representations.RefreshToken;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.RealmManager;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.UserStorageProviderModel; import org.keycloak.storage.UserStorageProviderModel;
import org.keycloak.testsuite.ApplicationServlet; import org.keycloak.testsuite.AbstractAuthTest;
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.Constants; import org.keycloak.testsuite.auth.page.AuthRealm;
import org.keycloak.testsuite.OAuthClient; import org.keycloak.testsuite.federation.FailableHardcodedStorageProvider;
import org.keycloak.testsuite.federation.FailableHardcodedStorageProviderFactory;
import org.keycloak.testsuite.pages.AccountApplicationsPage;
import org.keycloak.testsuite.pages.AppPage; import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.LoginPage; import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.rule.KeycloakRule; import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
import org.keycloak.testsuite.rule.WebResource; import org.keycloak.testsuite.util.Matchers;
import org.keycloak.testsuite.rule.WebRule; import org.keycloak.testsuite.util.OAuthClient;
import org.openqa.selenium.WebDriver;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -56,22 +69,52 @@ import java.util.Map;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class UserStorageFailureTest { public class UserStorageFailureTest extends AbstractTestRealmKeycloakTest {
public static ComponentModel memoryProvider = null;
public static String realmName; private static boolean initialized = false;
public static final String LOCAL_USER = "localUser";
@ClassRule @Deployment
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() { public static WebArchive deploy() {
return RunOnServerDeployment.create(ComponentExportImportTest.class, AbstractAuthTest.class, RealmResource.class)
.addPackages(true, "org.keycloak.testsuite");
}
private static final String LOCAL_USER = "localUser";
private String failureProviderId;
@ArquillianResource
protected ContainerController controller;
@Page
protected LoginPage loginPage;
@Page
protected AppPage appPage;
@Rule
public AssertEvents events = new AssertEvents(this);
@Override @Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { public void configureTestRealm(RealmRepresentation testRealm) {
UserStorageProviderModel model = new UserStorageProviderModel(); }
model.setName("failure");
model.setPriority(0);
model.setProviderId(FailableHardcodedStorageProviderFactory.PROVIDER_ID); @Before
model.setParentId(appRealm.getId()); public void addProvidersBeforeTest() throws URISyntaxException, IOException {
memoryProvider = appRealm.addComponentModel(model); ComponentRepresentation memProvider = new ComponentRepresentation();
realmName = appRealm.getName(); memProvider.setName("failure");
memProvider.setProviderId(FailableHardcodedStorageProviderFactory.PROVIDER_ID);
memProvider.setProviderType(UserStorageProvider.class.getName());
memProvider.setConfig(new MultivaluedHashMap<>());
memProvider.getConfig().putSingle("priority", Integer.toString(0));
failureProviderId = addComponent(memProvider);
if (initialized) return;
testingClient.server().run(session -> {
RealmManager manager = new RealmManager(session);
RealmModel appRealm = manager.getRealmByName(AuthRealm.TEST);
ClientModel offlineClient = appRealm.addClient("offline-client"); ClientModel offlineClient = appRealm.addClient("offline-client");
offlineClient.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); offlineClient.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
@ -79,7 +122,7 @@ public class UserStorageFailureTest {
offlineClient.setDirectAccessGrantsEnabled(true); offlineClient.setDirectAccessGrantsEnabled(true);
offlineClient.setSecret("secret"); offlineClient.setSecret("secret");
HashSet<String> redirects = new HashSet<>(); HashSet<String> redirects = new HashSet<>();
redirects.add(Constants.AUTH_SERVER_ROOT + "/offline-client"); redirects.add(OAuthClient.AUTH_SERVER_ROOT + "/offline-client");
offlineClient.setRedirectUris(redirects); offlineClient.setRedirectUris(redirects);
offlineClient.setServiceAccountsEnabled(true); offlineClient.setServiceAccountsEnabled(true);
offlineClient.setFullScopeAllowed(true); offlineClient.setFullScopeAllowed(true);
@ -93,29 +136,19 @@ public class UserStorageFailureTest {
UserModel localUser = manager.getSession().userLocalStorage().addUser(appRealm, LOCAL_USER); UserModel localUser = manager.getSession().userLocalStorage().addUser(appRealm, LOCAL_USER);
localUser.setEnabled(true); localUser.setEnabled(true);
}
}); });
@Rule initialized = true;
public WebRule webRule = new WebRule(this); }
@WebResource
protected OAuthClient oauth;
@WebResource public RealmResource testRealmResource() {
protected WebDriver driver; return adminClient.realm(AuthRealm.TEST);
}
@WebResource
protected AppPage appPage;
@WebResource
protected LoginPage loginPage;
@Rule
public AssertEvents events = new AssertEvents(keycloakRule);
private String addComponent(ComponentRepresentation component) {
return UserStorageTest.addComponent(testRealmResource(), getCleanup(), component);
}
// this is a hack so that UserModel doesn't have to be available when offline token is imported. // this is a hack so that UserModel doesn't have to be available when offline token is imported.
// see related JIRA - KEYCLOAK-5350 and corresponding test // see related JIRA - KEYCLOAK-5350 and corresponding test
@ -124,16 +157,17 @@ public class UserStorageFailureTest {
* KEYCLOAK-5350 * KEYCLOAK-5350
*/ */
@Test @Test
public void testKeycloak5350() { public void testKeycloak5350() throws Exception {
oauth.scope(OAuth2Constants.OFFLINE_ACCESS); oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
oauth.clientId("offline-client"); oauth.clientId("offline-client");
oauth.redirectUri(Constants.AUTH_SERVER_ROOT + "/offline-client"); oauth.redirectUri(OAuthClient.AUTH_SERVER_ROOT + "/offline-client");
oauth.doLogin(FailableHardcodedStorageProvider.username, "password"); oauth.doLogin(FailableHardcodedStorageProvider.username, "password");
Event loginEvent = events.expectLogin() EventRepresentation loginEvent = events.expectLogin()
.user(AssertEvents.isUUID())
.client("offline-client") .client("offline-client")
.detail(Details.REDIRECT_URI, Constants.AUTH_SERVER_ROOT + "/offline-client") .detail(Details.REDIRECT_URI, OAuthClient.AUTH_SERVER_ROOT + "/offline-client")
.event(); .assertEvent();
final String sessionId = loginEvent.getSessionId(); final String sessionId = loginEvent.getSessionId();
String codeId = loginEvent.getDetails().get(Details.CODE_ID); String codeId = loginEvent.getDetails().get(Details.CODE_ID);
@ -148,27 +182,23 @@ public class UserStorageFailureTest {
evictUser(FailableHardcodedStorageProvider.username); evictUser(FailableHardcodedStorageProvider.username);
KeycloakSession session;
RealmModel realm;
UserModel user;
toggleForceFail(true); toggleForceFail(true);
// make sure failure is turned on // make sure failure is turned on
session = keycloakRule.startSession(); testingClient.server().run(session -> {
realm = session.realms().getRealmByName(realmName); RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST);
try { try {
user = session.users().getUserByUsername(FailableHardcodedStorageProvider.username, realm); UserModel user = session.users().getUserByUsername(FailableHardcodedStorageProvider.username, realm);
Assert.fail(); Assert.fail();
} catch (Exception e) { } catch (Exception e) {
Assert.assertEquals("FORCED FAILURE", e.getMessage()); Assert.assertEquals("FORCED FAILURE", e.getMessage());
} }
keycloakRule.stopSession(session, false); });
// restart server to make sure we can still boot if user storage is down controller.stop(suiteContext.getAuthServerInfo().getQualifier());
keycloakRule.restartServer(); controller.start(suiteContext.getAuthServerInfo().getQualifier());
keycloakRule.deployServlet("app", "/app", ApplicationServlet.class); reconnectAdminClient();
toggleForceFail(false); toggleForceFail(false);
@ -184,33 +214,35 @@ public class UserStorageFailureTest {
} }
protected void evictUser(String username) { protected void evictUser(final String username) {
KeycloakSession session = keycloakRule.startSession(); testingClient.server().run(session -> {
RealmModel realm = session.realms().getRealmByName(realmName); RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST);
UserModel user = session.users().getUserByUsername(username, realm); UserModel user = session.users().getUserByUsername(username, realm);
session.userCache().evict(realm, user); session.userCache().evict(realm, user);
keycloakRule.stopSession(session, true); });
} }
protected void toggleForceFail(boolean toggle) { protected void toggleForceFail(final boolean toggle) {
KeycloakSession session; final String failureProviderId = this.failureProviderId;
RealmModel realm;
session = keycloakRule.startSession(); testingClient.server().run(session -> {
RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST);
ComponentModel memoryProvider = realm.getComponent(failureProviderId);
memoryProvider.getConfig().putSingle("fail", Boolean.toString(toggle)); memoryProvider.getConfig().putSingle("fail", Boolean.toString(toggle));
realm = session.realms().getRealmByName(realmName);
realm.updateComponent(memoryProvider); realm.updateComponent(memoryProvider);
keycloakRule.stopSession(session, true); });
} }
protected void toggleProviderEnabled(boolean toggle) { protected void toggleProviderEnabled(final boolean toggle) {
KeycloakSession session; final String failureProviderId = this.failureProviderId;
RealmModel realm;
session = keycloakRule.startSession(); testingClient.server().run(session -> {
RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST);
ComponentModel memoryProvider = realm.getComponent(failureProviderId);
UserStorageProviderModel model = new UserStorageProviderModel(memoryProvider); UserStorageProviderModel model = new UserStorageProviderModel(memoryProvider);
model.setEnabled(toggle); model.setEnabled(toggle);
realm = session.realms().getRealmByName(realmName);
realm.updateComponent(model); realm.updateComponent(model);
keycloakRule.stopSession(session, true); });
} }
private void loginSuccessAndLogout(String username, String password) { private void loginSuccessAndLogout(String username, String password) {
@ -226,37 +258,37 @@ public class UserStorageFailureTest {
@Test @Test
public void testKeycloak5926() { public void testKeycloak5926() {
oauth.clientId("test-app");
oauth.redirectUri(OAuthClient.APP_AUTH_ROOT);
// make sure local copy is deleted // make sure local copy is deleted
{ testingClient.server().run(session -> {
KeycloakSession session = keycloakRule.startSession(); RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST);
RealmModel realm = session.realms().getRealmByName(realmName);
UserModel user = session.userLocalStorage().getUserByUsername(FailableHardcodedStorageProvider.username, realm); UserModel user = session.userLocalStorage().getUserByUsername(FailableHardcodedStorageProvider.username, realm);
if (user != null) { if (user != null) {
session.userLocalStorage().removeUser(realm, user); session.userLocalStorage().removeUser(realm, user);
} }
keycloakRule.stopSession(session, true); });
}
// query user to make sure its imported // query user to make sure its imported
{ testingClient.server().run(session -> {
KeycloakSession session = keycloakRule.startSession(); RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST);
RealmModel realm = session.realms().getRealmByName(realmName);
UserModel user = session.users().getUserByUsername(FailableHardcodedStorageProvider.username, realm); UserModel user = session.users().getUserByUsername(FailableHardcodedStorageProvider.username, realm);
Assert.assertNotNull(user); Assert.assertNotNull(user);
keycloakRule.stopSession(session, true);
}
});
evictUser(FailableHardcodedStorageProvider.username); evictUser(FailableHardcodedStorageProvider.username);
evictUser(LOCAL_USER); evictUser(LOCAL_USER);
toggleForceFail(true); toggleForceFail(true);
{
KeycloakSession session = keycloakRule.startSession(); testingClient.server().run(session -> {
// make sure we can still query local users RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST);
RealmModel realm = session.realms().getRealmByName(realmName);
UserModel local = session.users().getUserByUsername(LOCAL_USER, realm); UserModel local = session.users().getUserByUsername(LOCAL_USER, realm);
Assert.assertNotNull(local); Assert.assertNotNull(local);
// assert that lookup of user storage user fails // assert that lookup of user storage user fails
@ -268,16 +300,16 @@ public class UserStorageFailureTest {
} }
keycloakRule.stopSession(session, true); });
}
// test that we can still login to a user // test that we can still login to a user
loginSuccessAndLogout("test-user@localhost", "password"); loginSuccessAndLogout("test-user@localhost", "password");
toggleProviderEnabled(false); toggleProviderEnabled(false);
{
KeycloakSession session = keycloakRule.startSession(); testingClient.server().run(session -> {
// make sure we can still query local users RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST);
RealmModel realm = session.realms().getRealmByName(realmName);
UserModel local = session.users().getUserByUsername(LOCAL_USER, realm); UserModel local = session.users().getUserByUsername(LOCAL_USER, realm);
Assert.assertNotNull(local); Assert.assertNotNull(local);
List<UserModel> result; List<UserModel> result;
@ -319,29 +351,27 @@ public class UserStorageFailureTest {
} catch (Exception ex) { } catch (Exception ex) {
} }
keycloakRule.stopSession(session, true); });
}
// make sure user isn't cached as provider is disabled // make sure user isn't cached as provider is disabled
{ testingClient.server().run(session -> {
KeycloakSession session = keycloakRule.startSession(); RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST);
RealmModel realm = session.realms().getRealmByName(realmName);
UserModel user = session.users().getUserByUsername(FailableHardcodedStorageProvider.username, realm); UserModel user = session.users().getUserByUsername(FailableHardcodedStorageProvider.username, realm);
Assert.assertFalse(user instanceof CachedUserModel); Assert.assertFalse(user instanceof CachedUserModel);
Assert.assertEquals(FailableHardcodedStorageProvider.username, user.getUsername()); Assert.assertEquals(FailableHardcodedStorageProvider.username, user.getUsername());
Assert.assertEquals(FailableHardcodedStorageProvider.email, user.getEmail()); Assert.assertEquals(FailableHardcodedStorageProvider.email, user.getEmail());
keycloakRule.stopSession(session, true); });
}
// make ABSOLUTELY sure user isn't cached as provider is disabled // make ABSOLUTELY sure user isn't cached as provider is disabled
{ testingClient.server().run(session -> {
KeycloakSession session = keycloakRule.startSession(); RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST);
RealmModel realm = session.realms().getRealmByName(realmName);
UserModel user = session.users().getUserByUsername(FailableHardcodedStorageProvider.username, realm); UserModel user = session.users().getUserByUsername(FailableHardcodedStorageProvider.username, realm);
Assert.assertFalse(user instanceof CachedUserModel); Assert.assertFalse(user instanceof CachedUserModel);
Assert.assertEquals(FailableHardcodedStorageProvider.username, user.getUsername()); Assert.assertEquals(FailableHardcodedStorageProvider.username, user.getUsername());
Assert.assertEquals(FailableHardcodedStorageProvider.email, user.getEmail()); Assert.assertEquals(FailableHardcodedStorageProvider.email, user.getEmail());
keycloakRule.stopSession(session, true); });
}
@ -349,15 +379,14 @@ public class UserStorageFailureTest {
toggleForceFail(false); toggleForceFail(false);
// user should be cachable now // user should be cachable now
{ testingClient.server().run(session -> {
KeycloakSession session = keycloakRule.startSession(); RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST);
RealmModel realm = session.realms().getRealmByName(realmName);
UserModel user = session.users().getUserByUsername(FailableHardcodedStorageProvider.username, realm); UserModel user = session.users().getUserByUsername(FailableHardcodedStorageProvider.username, realm);
Assert.assertTrue(user instanceof CachedUserModel); Assert.assertTrue(user instanceof CachedUserModel);
Assert.assertEquals(FailableHardcodedStorageProvider.username, user.getUsername()); Assert.assertEquals(FailableHardcodedStorageProvider.username, user.getUsername());
Assert.assertEquals(FailableHardcodedStorageProvider.email, user.getEmail()); Assert.assertEquals(FailableHardcodedStorageProvider.email, user.getEmail());
keycloakRule.stopSession(session, true); });
}
events.clear(); events.clear();
} }

View file

@ -28,8 +28,13 @@ import static org.junit.Assert.fail;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.UserResource; import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.component.ComponentModel;
import org.keycloak.credential.CredentialAuthentication;
import org.keycloak.credential.UserCredentialStoreManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import static org.keycloak.models.UserModel.RequiredAction.UPDATE_PROFILE; import static org.keycloak.models.UserModel.RequiredAction.UPDATE_PROFILE;
@ -48,6 +53,7 @@ import static org.keycloak.storage.UserStorageProviderModel.EVICTION_MINUTE;
import static org.keycloak.storage.UserStorageProviderModel.MAX_LIFESPAN; import static org.keycloak.storage.UserStorageProviderModel.MAX_LIFESPAN;
import org.keycloak.testsuite.AbstractAuthTest; import org.keycloak.testsuite.AbstractAuthTest;
import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.arquillian.annotation.ModelTest;
import org.keycloak.testsuite.federation.UserMapStorage; import org.keycloak.testsuite.federation.UserMapStorage;
import org.keycloak.testsuite.federation.UserMapStorageFactory; import org.keycloak.testsuite.federation.UserMapStorageFactory;
import org.keycloak.testsuite.federation.UserPropertyFileStorageFactory; import org.keycloak.testsuite.federation.UserPropertyFileStorageFactory;
@ -61,6 +67,8 @@ import java.util.Map;
import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMessage;
import org.jboss.arquillian.graphene.page.Page; import org.jboss.arquillian.graphene.page.Page;
import org.junit.Rule; import org.junit.Rule;
import org.keycloak.testsuite.util.TestCleanup;
import static org.keycloak.testsuite.actions.RequiredActionEmailVerificationTest.getPasswordResetEmailLink; import static org.keycloak.testsuite.actions.RequiredActionEmailVerificationTest.getPasswordResetEmailLink;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlDoesntStartWith; import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlDoesntStartWith;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith; import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
@ -154,11 +162,15 @@ public class UserStorageTest extends AbstractAuthTest {
return propProviderRW; return propProviderRW;
} }
protected String addComponent(ComponentRepresentation component) { private String addComponent(ComponentRepresentation component) {
Response resp = testRealmResource().components().add(component); return addComponent(testRealmResource(), getCleanup(), component);
}
static String addComponent(RealmResource realmResource, TestCleanup testCleanup, ComponentRepresentation component) {
Response resp = realmResource.components().add(component);
resp.close(); resp.close();
String id = ApiUtil.getCreatedId(resp); String id = ApiUtil.getCreatedId(resp);
getCleanup().addComponentId(id); testCleanup.addComponentId(id);
return id; return id;
} }
@ -183,6 +195,17 @@ public class UserStorageTest extends AbstractAuthTest {
}); });
} }
/**
* KEYCLOAK-4013
*
* @throws Exception
*/
@Test
@ModelTest
public void testCast(KeycloakSession session) throws Exception {
List<CredentialAuthentication> list = UserCredentialStoreManager.getCredentialProviders(session, null, CredentialAuthentication.class);
}
@Test @Test
public void testLoginSuccess() { public void testLoginSuccess() {
loginSuccessAndLogout("tbrady", "goat"); loginSuccessAndLogout("tbrady", "goat");
@ -416,13 +439,28 @@ public class UserStorageTest extends AbstractAuthTest {
Assert.assertTrue(usernames.contains("thor")); Assert.assertTrue(usernames.contains("thor"));
// search by single attribute // search by single attribute
// FIXME - no equivalent for model in REST testingClient.server().run(session -> {
System.out.println("search by single attribute");
RealmModel realm = session.realms().getRealmByName("test");
UserModel userModel = session.users().getUserByUsername("thor", realm);
userModel.setSingleAttribute("weapon", "hammer");
List<UserModel> userModels = session.users().searchForUserByUserAttribute("weapon", "hammer", realm);
for (UserModel u : userModels) {
System.out.println(u.getUsername());
}
Assert.assertEquals(1, userModels.size());
Assert.assertEquals("thor", userModels.get(0).getUsername());
});
} }
@Deployment @Deployment
public static WebArchive deploy() { public static WebArchive deploy() {
return RunOnServerDeployment.create(UserResource.class) return RunOnServerDeployment.create(UserResource.class)
.addPackages(true, "org.keycloak.testsuite"); .addPackages(true, "org.keycloak.testsuite")
.addPackages(true, "org.keycloak.admin.client.resource");
} }
private void setDailyEvictionTime(int hour, int minutes) { private void setDailyEvictionTime(int hour, int minutes) {
@ -677,6 +715,26 @@ public class UserStorageTest extends AbstractAuthTest {
System.out.println("User class: " + user.getClass()); System.out.println("User class: " + user.getClass());
Assert.assertFalse(user instanceof CachedUserModel); // should be evicted Assert.assertFalse(user instanceof CachedUserModel); // should be evicted
}); });
testingClient.server().run(session -> {
RealmModel realm = session.realms().getRealmByName("test");
UserModel thor2 = session.users().getUserByUsername("thor", realm);
Assert.assertFalse(thor2 instanceof CachedUserModel);
});
propProviderRW = testRealmResource().components().component(propProviderRWId).toRepresentation();
propProviderRW.getConfig().putSingle(CACHE_POLICY, CachePolicy.DEFAULT.name());
propProviderRW.getConfig().remove("evictionHour");
propProviderRW.getConfig().remove("evictionMinute");
propProviderRW.getConfig().remove("evictionDay");
testRealmResource().components().component(propProviderRWId).update(propProviderRW);
testingClient.server().run(session -> {
RealmModel realm = session.realms().getRealmByName("test");
UserModel thor = session.users().getUserByUsername("thor", realm);
System.out.println("Foo");
});
} }
@Test @Test

View file

@ -0,0 +1,338 @@
/*
* Copyright 2017 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.sync;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.logging.Logger;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Assert;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.services.managers.UserStorageSyncManager;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.UserStorageProviderModel;
import org.keycloak.storage.user.SynchronizationResult;
import org.keycloak.testsuite.AbstractAuthTest;
import org.keycloak.testsuite.auth.page.AuthRealm;
import org.keycloak.testsuite.federation.DummyUserFederationProviderFactory;
import org.keycloak.testsuite.federation.storage.ComponentExportImportTest;
import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
import org.keycloak.timer.TimerProvider;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Test with Dummy providers
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SyncFederationTest extends AbstractAuthTest {
private static final Logger log = Logger.getLogger(SyncFederationTest.class);
@Deployment
public static WebArchive deploy() {
return RunOnServerDeployment.create(ComponentExportImportTest.class, AbstractAuthTest.class, RealmResource.class)
.addPackages(true, "org.keycloak.testsuite");
}
/**
* Test that period sync is triggered when creating a synchronized User Storage Provider
*
*/
@Test
public void test01PeriodicSyncOnCreate() {
final Map<String, Integer> state = testingClient.server().fetch(session -> {
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
DummyUserFederationProviderFactory dummyFedFactory = (DummyUserFederationProviderFactory) sessionFactory.getProviderFactory(UserStorageProvider.class, DummyUserFederationProviderFactory.PROVIDER_NAME);
int full = dummyFedFactory.getFullSyncCounter();
int changed = dummyFedFactory.getChangedSyncCounter();
Map<String, Integer> state1 = new HashMap<>();
state1.put("full", full);
state1.put("changed", changed);
return state1;
}, Map.class);
// Enable timer for SyncDummyUserFederationProvider
testingClient.server().run(session -> {
RealmModel appRealm = session.realms().getRealmByName(AuthRealm.TEST);
UserStorageProviderModel model = new UserStorageProviderModel();
model.setProviderId(DummyUserFederationProviderFactory.PROVIDER_NAME);
model.setPriority(1);
model.setName("test-sync-dummy");
model.setFullSyncPeriod(-1);
model.setChangedSyncPeriod(1);
model.setLastSync(0);
ComponentModel dummyModel = new UserStorageProviderModel(appRealm.addComponentModel(model));
});
testingClient.server().run(session -> {
RealmModel appRealm = session.realms().getRealmByName(AuthRealm.TEST);
UserStorageProviderModel dummyModel = findDummyProviderModel(appRealm);
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
DummyUserFederationProviderFactory dummyFedFactory = (DummyUserFederationProviderFactory) sessionFactory.getProviderFactory(UserStorageProvider.class, DummyUserFederationProviderFactory.PROVIDER_NAME);
// Assert that after some period was DummyUserFederationProvider triggered
UserStorageSyncManager usersSyncManager = new UserStorageSyncManager();
sleep(1800);
// Cancel timer
usersSyncManager.notifyToRefreshPeriodicSync(session, appRealm, dummyModel, true);
log.infof("Notified sync manager about cancel periodic sync");
// This sync is here just to ensure that we have lock (doublecheck that periodic sync, which was possibly triggered before canceling timer is finished too)
while (true) {
SynchronizationResult result = usersSyncManager.syncChangedUsers(session.getKeycloakSessionFactory(), appRealm.getId(), dummyModel);
if (result.isIgnored()) {
log.infof("Still waiting for lock before periodic sync is finished", result.toString());
sleep(1000);
} else {
break;
}
}
int full = state.get("full");
int changed = state.get("changed");
// Assert that DummyUserFederationProviderFactory.syncChangedUsers was invoked at least 2 times (once periodically and once for us)
int newChanged = dummyFedFactory.getChangedSyncCounter();
Assert.assertEquals(full, dummyFedFactory.getFullSyncCounter());
Assert.assertTrue("Assertion failed. newChanged=" + newChanged + ", changed=" + changed, newChanged > (changed + 1));
// Assert that dummy provider won't be invoked anymore
sleep(1800);
Assert.assertEquals(full, dummyFedFactory.getFullSyncCounter());
int newestChanged = dummyFedFactory.getChangedSyncCounter();
Assert.assertEquals("Assertion failed. newChanged=" + newChanged + ", newestChanged=" + newestChanged, newChanged, newestChanged);
});
// remove dummyProvider
testingClient.server().run(session -> {
RealmModel appRealm = session.realms().getRealmByName(AuthRealm.TEST);
UserStorageProviderModel dummyModel = findDummyProviderModel(appRealm);
appRealm.removeComponent(dummyModel);
});
}
private static final UserStorageProviderModel findDummyProviderModel(RealmModel realm) {
for (ComponentModel component : realm.getComponents()) {
if ("test-sync-dummy".equals(component.getName())) {
return new UserStorageProviderModel(component);
}
}
return null;
}
/**
* Test that period sync is triggered when updating a synchronized User Storage Provider to have a non-negative sync period
*
*/
@Test
public void test02PeriodicSyncOnUpdate() {
final Map<String, Integer> state = testingClient.server().fetch(session -> {
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
DummyUserFederationProviderFactory dummyFedFactory = (DummyUserFederationProviderFactory) sessionFactory.getProviderFactory(UserStorageProvider.class, DummyUserFederationProviderFactory.PROVIDER_NAME);
int full = dummyFedFactory.getFullSyncCounter();
int changed = dummyFedFactory.getChangedSyncCounter();
Map<String, Integer> state1 = new HashMap<>();
state1.put("full", full);
state1.put("changed", changed);
return state1;
}, Map.class);
// Configure sync without timer for SyncDummyUserFederationProvider
testingClient.server().run(session -> {
RealmModel appRealm = session.realms().getRealmByName(AuthRealm.TEST);
UserStorageProviderModel model = new UserStorageProviderModel();
model.setProviderId(DummyUserFederationProviderFactory.PROVIDER_NAME);
model.setPriority(1);
model.setName("test-sync-dummy");
model.setFullSyncPeriod(-1);
model.setChangedSyncPeriod(-1);
model.setLastSync(0);
ComponentModel dummyModel = new UserStorageProviderModel(appRealm.addComponentModel(model));
});
testingClient.server().run(session -> {
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
DummyUserFederationProviderFactory dummyFedFactory = (DummyUserFederationProviderFactory) sessionFactory.getProviderFactory(UserStorageProvider.class, DummyUserFederationProviderFactory.PROVIDER_NAME);
// Assert that after some period was DummyUserFederationProvider triggered
UserStorageSyncManager usersSyncManager = new UserStorageSyncManager();
// Assert that dummy provider wasn't invoked anymore
sleep(1800);
int full = state.get("full");
int changed = state.get("changed");
Assert.assertEquals(full, dummyFedFactory.getFullSyncCounter());
int newChanged = dummyFedFactory.getChangedSyncCounter();
Assert.assertEquals("Assertion failed. changed=" + changed + ", newChanged=" + newChanged, changed, newChanged);
});
// Re-enable periodic sync for changed users
testingClient.server().run(session -> {
RealmModel appRealm = session.realms().getRealmByName(AuthRealm.TEST);
UserStorageProviderModel dummyModel = findDummyProviderModel(appRealm);
dummyModel.setChangedSyncPeriod(1);
appRealm.updateComponent(dummyModel);
});
testingClient.server().run(session -> {
RealmModel appRealm = session.realms().getRealmByName(AuthRealm.TEST);
UserStorageProviderModel dummyModel = findDummyProviderModel(appRealm);
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
DummyUserFederationProviderFactory dummyFedFactory = (DummyUserFederationProviderFactory) sessionFactory.getProviderFactory(UserStorageProvider.class, DummyUserFederationProviderFactory.PROVIDER_NAME);
// Assert that after some period was DummyUserFederationProvider triggered
UserStorageSyncManager usersSyncManager = new UserStorageSyncManager();
sleep(1800);
// Cancel timer
usersSyncManager.notifyToRefreshPeriodicSync(session, appRealm, dummyModel, true);
log.infof("Notified sync manager about cancel periodic sync");
// This sync is here just to ensure that we have lock (doublecheck that periodic sync, which was possibly triggered before canceling timer is finished too)
while (true) {
SynchronizationResult result = usersSyncManager.syncChangedUsers(session.getKeycloakSessionFactory(), appRealm.getId(), dummyModel);
if (result.isIgnored()) {
log.infof("Still waiting for lock before periodic sync is finished", result.toString());
sleep(1000);
} else {
break;
}
}
int full = state.get("full");
int changed = state.get("changed");
// Assert that DummyUserFederationProviderFactory.syncChangedUsers was invoked at least 1 time
int newChanged = dummyFedFactory.getChangedSyncCounter();
Assert.assertEquals(full, dummyFedFactory.getFullSyncCounter());
log.info("Asserting. newChanged=" + newChanged + " > changed=" + changed);
Assert.assertTrue("Assertion failed. newChanged=" + newChanged + ", changed=" + changed, newChanged > (changed + 1));
// Assert that dummy provider won't be invoked anymore
sleep(1800);
Assert.assertEquals(full, dummyFedFactory.getFullSyncCounter());
int newestChanged = dummyFedFactory.getChangedSyncCounter();
Assert.assertEquals("Assertion failed. newChanged=" + newChanged + ", newestChanged=" + newestChanged, newChanged, newestChanged);
});
// remove dummyProvider
testingClient.server().run(session -> {
RealmModel appRealm = session.realms().getRealmByName(AuthRealm.TEST);
UserStorageProviderModel dummyModel = findDummyProviderModel(appRealm);
appRealm.removeComponent(dummyModel);
});
}
@Test
public void test03ConcurrentSync() throws Exception {
// Enable timer for SyncDummyUserFederationProvider
testingClient.server().run(session -> {
SyncDummyUserFederationProviderFactory.restartLatches();
RealmModel appRealm = session.realms().getRealmByName(AuthRealm.TEST);
UserStorageProviderModel model = new UserStorageProviderModel();
model.setProviderId(SyncDummyUserFederationProviderFactory.SYNC_PROVIDER_ID);
model.setPriority(1);
model.setName("test-sync-dummy");
model.setFullSyncPeriod(-1);
model.setChangedSyncPeriod(1);
model.setLastSync(0);
model.getConfig().putSingle(SyncDummyUserFederationProviderFactory.WAIT_TIME, "2000");
ComponentModel dummyModel = new UserStorageProviderModel(appRealm.addComponentModel(model));
});
testingClient.server().run(session -> {
RealmModel appRealm = session.realms().getRealmByName(AuthRealm.TEST);
UserStorageProviderModel dummyModel = findDummyProviderModel(appRealm);
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
// bootstrap periodic sync
UserStorageSyncManager usersSyncManager = new UserStorageSyncManager();
usersSyncManager.bootstrapPeriodic(sessionFactory, session.getProvider(TimerProvider.class));
// Wait and then trigger sync manually. Assert it will be ignored
sleep(1800);
SynchronizationResult syncResult = usersSyncManager.syncChangedUsers(sessionFactory, appRealm.getId(), dummyModel);
Assert.assertTrue(syncResult.isIgnored());
// Cancel timer
usersSyncManager.notifyToRefreshPeriodicSync(session, appRealm, dummyModel, true);
// Signal to factory to finish waiting
SyncDummyUserFederationProviderFactory.latch1.countDown();
try {
SyncDummyUserFederationProviderFactory.latch2.await(20000, TimeUnit.MILLISECONDS);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
// remove provider
testingClient.server().run(session -> {
RealmModel appRealm = session.realms().getRealmByName(AuthRealm.TEST);
UserStorageProviderModel dummyModel = findDummyProviderModel(appRealm);
appRealm.removeComponent(dummyModel);
});
}
private static void sleep(long ms) {
try {
log.infof("Sleeping for %d ms", ms);
Thread.sleep(ms);
} catch (InterruptedException ie) {
throw new RuntimeException(ie);
}
}
}

View file

@ -13,6 +13,8 @@
<module name="org.keycloak.keycloak-kerberos-federation"/> <module name="org.keycloak.keycloak-kerberos-federation"/>
<module name="org.keycloak.keycloak-ldap-federation"/> <module name="org.keycloak.keycloak-ldap-federation"/>
<module name="org.infinispan"/> <module name="org.infinispan"/>
<module name="org.apache.commons.io"/>
<module name="org.apache.httpcomponents.core"/>
</dependencies> </dependencies>
</deployment> </deployment>
</jboss-deployment-structure> </jboss-deployment-structure>

View file

@ -1,140 +0,0 @@
/*
* 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.storage;
import org.junit.After;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.common.util.Time;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.Constants;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.cache.CachedUserModel;
import org.keycloak.models.cache.infinispan.UserAdapter;
import org.keycloak.models.jpa.RealmAdapter;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.UserStorageProviderModel;
import org.keycloak.testsuite.ApplicationServlet;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule;
import org.openqa.selenium.WebDriver;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* KEYCLOAK-3903 and KEYCLOAK-3620
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class BrokenUserStorageTest {
@ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule();
@Rule
public WebRule webRule = new WebRule(this);
@WebResource
protected OAuthClient oauth;
@WebResource
protected WebDriver driver;
@WebResource
protected AppPage appPage;
@WebResource
protected LoginPage loginPage;
private void loginSuccessAndLogout(String username, String password) {
loginPage.open();
loginPage.login(username, password);
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
oauth.openLogout();
}
protected String AUTH_SERVER_URL = "http://localhost:8081/auth";
@Test
public void testBootWithBadProviderId() throws Exception {
KeycloakSession session = keycloakRule.startSession();
// set this system property
System.setProperty(RealmAdapter.COMPONENT_PROVIDER_EXISTS_DISABLED, "true");
RealmModel realm = session.realms().getRealmByName("master");
String masterId = realm.getId();
UserStorageProviderModel model;
model = new UserStorageProviderModel();
model.setName("bad-provider-id");
model.setPriority(2);
model.setParentId(realm.getId());
model.setProviderId("error");
ComponentModel component = realm.importComponentModel(model);
keycloakRule.stopSession(session, true);
keycloakRule.restartServer();
keycloakRule.deployServlet("app", "/app", ApplicationServlet.class);
loginSuccessAndLogout("test-user@localhost", "password");
// make sure we can list components and delete provider as this is an admin console operation
try (Keycloak keycloakAdmin = Keycloak.getInstance(AUTH_SERVER_URL, "master", "admin", "admin", Constants.ADMIN_CLI_CLIENT_ID)) {
RealmResource master = keycloakAdmin.realms().realm("master");
List<ComponentRepresentation> components = master.components().query(masterId, UserStorageProvider.class.getName());
boolean found = false;
for (ComponentRepresentation rep : components) {
if (rep.getName().equals("bad-provider-id")) {
found = true;
}
}
Assert.assertTrue(found);
master.components().component(component.getId()).remove();
List<ComponentRepresentation> components2 = master.components().query(masterId, UserStorageProvider.class.getName());
Assert.assertEquals(components.size() - 1, components2.size());
}
}
@After
public void resetTimeoffset() {
Time.setOffset(0);
}
}

View file

@ -1,159 +0,0 @@
/*
* 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.storage;
import org.junit.After;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.keycloak.component.ComponentModel;
import org.keycloak.exportimport.ExportImportConfig;
import org.keycloak.exportimport.ExportImportManager;
import org.keycloak.exportimport.singlefile.SingleFileExportProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.testsuite.rule.KeycloakRule;
import java.io.File;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ComponentExportImportTest {
@ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
}
});
public static String basePath = null;
@BeforeClass
public static void setDirs() {
basePath = new File(System.getProperty("project.build.directory", "target")).getAbsolutePath();
}
@After
public void cleanup() {
KeycloakSession session = keycloakRule.startSession();
RealmModel realm = session.realms().getRealmByName("exported-component");
if (realm != null) {
session.realms().removeRealm(realm.getId());
}
keycloakRule.stopSession(session, true);
}
@Test
public void testSingleFile() throws Exception {
clearExportImportProperties();
KeycloakSession session = keycloakRule.startSession();
RealmModel realm = new RealmManager(session).createRealm("exported-component");
String realmId = realm.getId();
ComponentModel component = new ComponentModel();
component.setParentId(realm.getId());
component.setProviderId(UserMapStorageFactory.PROVIDER_ID);
component.setProviderType(UserStorageProvider.class.getName());
component.setName("parent");
component.setSubType("subtype");
component.put("attr", "value");
component = realm.addComponentModel(component);
ComponentModel subComponent = new ComponentModel();
subComponent.setParentId(component.getId());
subComponent.setProviderId(UserMapStorageFactory.PROVIDER_ID);
subComponent.setProviderType(UserStorageProvider.class.getName());
subComponent.setName("child");
subComponent.setSubType("subtype2");
subComponent.put("attr", "value2");
subComponent = realm.addComponentModel(subComponent);
keycloakRule.stopSession(session, true);
String targetFilePath = basePath + File.separator + "singleFile-full.json";
System.out.println("export file: " + targetFilePath);
session = keycloakRule.startSession();
ExportImportConfig.setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
ExportImportConfig.setFile(targetFilePath);
ExportImportConfig.setRealmName("exported-component");
ExportImportConfig.setAction(ExportImportConfig.ACTION_EXPORT);
new ExportImportManager(session).runExport();
session.realms().removeRealm(realmId);
keycloakRule.stopSession(session, true);
session = keycloakRule.startSession();
Assert.assertNull(session.realms().getRealmByName("exported-component"));
ExportImportConfig.setAction(ExportImportConfig.ACTION_IMPORT);
new ExportImportManager(session).runImport();
keycloakRule.stopSession(session, true);
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("exported-component");
Assert.assertNotNull(realm);
component = realm.getComponent(component.getId());
Assert.assertNotNull(component);
Assert.assertEquals(component.getParentId(), realm.getId());
Assert.assertEquals(component.getName(), "parent");
Assert.assertEquals(component.getSubType(), "subtype");
Assert.assertEquals(component.getProviderId(), UserMapStorageFactory.PROVIDER_ID);
Assert.assertEquals(component.getProviderType(), UserStorageProvider.class.getName());
Assert.assertEquals(component.getConfig().getFirst("attr"), "value");
subComponent = realm.getComponents(component.getId()).get(0);
Assert.assertEquals(subComponent.getParentId(), component.getId());
Assert.assertEquals(subComponent.getName(), "child");
Assert.assertEquals(subComponent.getSubType(), "subtype2");
Assert.assertEquals(subComponent.getProviderId(), UserMapStorageFactory.PROVIDER_ID);
Assert.assertEquals(subComponent.getProviderType(), UserStorageProvider.class.getName());
Assert.assertEquals(subComponent.getConfig().getFirst("attr"), "value2");
keycloakRule.stopSession(session, true);
}
public void clearExportImportProperties() {
// Clear export/import properties after test
Properties systemProps = System.getProperties();
Set<String> propsToRemove = new HashSet<String>();
for (Object key : systemProps.keySet()) {
if (key.toString().startsWith(ExportImportConfig.PREFIX)) {
propsToRemove.add(key.toString());
}
}
for (String propToRemove : propsToRemove) {
systemProps.remove(propToRemove);
}
}
}

View file

@ -1,242 +0,0 @@
/*
* 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.storage;
import org.junit.After;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.credential.CredentialModel;
import org.keycloak.credential.hash.PasswordHashProvider;
import org.keycloak.exportimport.ExportImportConfig;
import org.keycloak.exportimport.ExportImportManager;
import org.keycloak.exportimport.dir.DirExportProviderFactory;
import org.keycloak.exportimport.singlefile.SingleFileExportProviderFactory;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.policy.HashAlgorithmPasswordPolicyProviderFactory;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.rule.KeycloakRule;
import java.io.File;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class FederatedStorageExportImportTest {
@ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
}
});
public static String basePath = null;
@BeforeClass
public static void setDirs() {
basePath = new File(System.getProperty("project.build.directory", "target")).getAbsolutePath();
}
@After
public void cleanup() {
KeycloakSession session = keycloakRule.startSession();
RealmModel realm = session.realms().getRealmByName("exported");
if (realm != null) {
session.realms().removeRealm(realm.getId());
}
keycloakRule.stopSession(session, true);
}
protected PasswordHashProvider getHashProvider(KeycloakSession session, PasswordPolicy policy) {
PasswordHashProvider hash = session.getProvider(PasswordHashProvider.class, policy.getHashAlgorithm());
if (hash == null) {
return session.getProvider(PasswordHashProvider.class, PasswordPolicy.HASH_ALGORITHM_DEFAULT);
}
return hash;
}
@Test
public void testSingleFile() throws Exception {
clearExportImportProperties();
KeycloakSession session = keycloakRule.startSession();
RealmModel realm = new RealmManager(session).createRealm("exported");
String realmId = realm.getId();
RoleModel role = realm.addRole("test-role");
GroupModel group = realm.createGroup("test-group");
String groupId = group.getId();
String userId = "f:1:path";
List<String> attrValues = new LinkedList<>();
attrValues.add("1");
attrValues.add("2");
session.userFederatedStorage().setSingleAttribute(realm, userId, "single1", "value1");
session.userFederatedStorage().setAttribute(realm, userId, "list1", attrValues);
session.userFederatedStorage().addRequiredAction(realm, userId, "UPDATE_PASSWORD");
CredentialModel credential = new CredentialModel();
getHashProvider(session, realm.getPasswordPolicy()).encode("password", realm.
getPasswordPolicy().getHashIterations(), credential);
session.userFederatedStorage().createCredential(realm, userId, credential);
session.userFederatedStorage().grantRole(realm, userId, role);
session.userFederatedStorage().joinGroup(realm, userId, group);
keycloakRule.stopSession(session, true);
String targetFilePath = basePath + File.separator + "singleFile-full.json";
System.out.println("export file: " + targetFilePath);
session = keycloakRule.startSession();
ExportImportConfig.setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
ExportImportConfig.setFile(targetFilePath);
ExportImportConfig.setRealmName("exported");
ExportImportConfig.setAction(ExportImportConfig.ACTION_EXPORT);
new ExportImportManager(session).runExport();
session.realms().removeRealm(realmId);
keycloakRule.stopSession(session, true);
session = keycloakRule.startSession();
Assert.assertNull(session.realms().getRealmByName("exported"));
ExportImportConfig.setAction(ExportImportConfig.ACTION_IMPORT);
new ExportImportManager(session).runImport();
keycloakRule.stopSession(session, true);
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("exported");
Assert.assertNotNull(realm);
role = realm.getRole("test-role");
group = realm.getGroupById(groupId);
Assert.assertEquals(1, session.userFederatedStorage().getStoredUsersCount(realm));
MultivaluedHashMap<String, String> attributes = session.userFederatedStorage().getAttributes(realm, userId);
Assert.assertEquals(3, attributes.size());
Assert.assertEquals("value1", attributes.getFirst("single1"));
Assert.assertTrue(attributes.getList("list1").contains("1"));
Assert.assertTrue(attributes.getList("list1").contains("2"));
Assert.assertTrue(session.userFederatedStorage().getRequiredActions(realm, userId).contains("UPDATE_PASSWORD"));
Assert.assertTrue(session.userFederatedStorage().getRoleMappings(realm, userId).contains(role));
Assert.assertTrue(session.userFederatedStorage().getGroups(realm, userId).contains(group));
List<CredentialModel> creds = session.userFederatedStorage().getStoredCredentials(realm, userId);
Assert.assertEquals(1, creds.size());
Assert.assertTrue(getHashProvider(session, realm.getPasswordPolicy()).verify("password", creds.get(0)));
keycloakRule.stopSession(session, true);
}
@Test
public void testDir() throws Exception {
clearExportImportProperties();
KeycloakSession session = keycloakRule.startSession();
RealmModel realm = new RealmManager(session).createRealm("exported");
String realmId = realm.getId();
RoleModel role = realm.addRole("test-role");
GroupModel group = realm.createGroup("test-group");
String groupId = group.getId();
String userId = "f:1:path";
List<String> attrValues = new LinkedList<>();
attrValues.add("1");
attrValues.add("2");
session.userFederatedStorage().setSingleAttribute(realm, userId, "single1", "value1");
session.userFederatedStorage().setAttribute(realm, userId, "list1", attrValues);
session.userFederatedStorage().addRequiredAction(realm, userId, "UPDATE_PASSWORD");
CredentialModel credential = new CredentialModel();
getHashProvider(session, realm.getPasswordPolicy()).encode("password", realm.
getPasswordPolicy().getHashIterations(), credential);
session.userFederatedStorage().createCredential(realm, userId, credential);
session.userFederatedStorage().grantRole(realm, userId, role);
session.userFederatedStorage().joinGroup(realm, userId, group);
session.userFederatedStorage().setNotBeforeForUser(realm, userId, 50);
keycloakRule.stopSession(session, true);
String targetFilePath = basePath + File.separator + "dirExport";
session = keycloakRule.startSession();
ExportImportConfig.setProvider(DirExportProviderFactory.PROVIDER_ID);
ExportImportConfig.setDir(targetFilePath);
ExportImportConfig.setRealmName("exported");
ExportImportConfig.setAction(ExportImportConfig.ACTION_EXPORT);
new ExportImportManager(session).runExport();
session.realms().removeRealm(realmId);
keycloakRule.stopSession(session, true);
session = keycloakRule.startSession();
Assert.assertNull(session.realms().getRealmByName("exported"));
ExportImportConfig.setAction(ExportImportConfig.ACTION_IMPORT);
new ExportImportManager(session).runImport();
keycloakRule.stopSession(session, true);
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("exported");
Assert.assertNotNull(realm);
role = realm.getRole("test-role");
group = realm.getGroupById(groupId);
Assert.assertEquals(1, session.userFederatedStorage().getStoredUsersCount(realm));
MultivaluedHashMap<String, String> attributes = session.userFederatedStorage().getAttributes(realm, userId);
Assert.assertEquals(3, attributes.size());
Assert.assertEquals("value1", attributes.getFirst("single1"));
Assert.assertTrue(attributes.getList("list1").contains("1"));
Assert.assertTrue(attributes.getList("list1").contains("2"));
Assert.assertTrue(session.userFederatedStorage().getRequiredActions(realm, userId).contains("UPDATE_PASSWORD"));
Assert.assertTrue(session.userFederatedStorage().getRoleMappings(realm, userId).contains(role));
Assert.assertTrue(session.userFederatedStorage().getGroups(realm, userId).contains(group));
Assert.assertEquals(50, session.userFederatedStorage().getNotBeforeOfUser(realm, userId));
List<CredentialModel> creds = session.userFederatedStorage().getStoredCredentials(realm, userId);
Assert.assertEquals(1, creds.size());
Assert.assertTrue(getHashProvider(session, realm.getPasswordPolicy()).verify("password", creds.get(0)));
keycloakRule.stopSession(session, true);
}
public void clearExportImportProperties() {
// Clear export/import properties after test
Properties systemProps = System.getProperties();
Set<String> propsToRemove = new HashSet<String>();
for (Object key : systemProps.keySet()) {
if (key.toString().startsWith(ExportImportConfig.PREFIX)) {
propsToRemove.add(key.toString());
}
}
for (String propToRemove : propsToRemove) {
systemProps.remove(propToRemove);
}
}
}

View file

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Red Hat, Inc. and/or its affiliates * Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags. * and other contributors as indicated by the @author tags.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -16,6 +16,12 @@
*/ */
package org.keycloak.testsuite.federation.storage; package org.keycloak.testsuite.federation.storage;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.jboss.logging.Logger;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;
import org.keycloak.credential.CredentialInput; import org.keycloak.credential.CredentialInput;
import org.keycloak.credential.CredentialInputUpdater; import org.keycloak.credential.CredentialInputUpdater;
@ -33,23 +39,23 @@ import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage;
import org.keycloak.storage.user.UserLookupProvider; import org.keycloak.storage.user.UserLookupProvider;
import org.keycloak.storage.user.UserRegistrationProvider; import org.keycloak.storage.user.UserRegistrationProvider;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class UserMapStorage implements UserLookupProvider, UserStorageProvider, UserRegistrationProvider, CredentialInputUpdater, CredentialInputValidator { public class UserMapStorage implements UserLookupProvider, UserStorageProvider, UserRegistrationProvider, CredentialInputUpdater, CredentialInputValidator {
private static final Logger log = Logger.getLogger(UserMapStorage.class);
protected Map<String, String> userPasswords; protected Map<String, String> userPasswords;
protected ComponentModel model; protected ComponentModel model;
protected KeycloakSession session; protected KeycloakSession session;
public static final AtomicInteger allocations = new AtomicInteger(0); public static final AtomicInteger allocations = new AtomicInteger(0);
public static final AtomicInteger closings = new AtomicInteger(0); public static final AtomicInteger closings = new AtomicInteger(0);
public static final AtomicInteger realmRemovals = new AtomicInteger(0);
public static final AtomicInteger groupRemovals = new AtomicInteger(0);
public static final AtomicInteger roleRemovals = new AtomicInteger(0);
public UserMapStorage(KeycloakSession session, ComponentModel model, Map<String, String> userPasswords) { public UserMapStorage(KeycloakSession session, ComponentModel model, Map<String, String> userPasswords) {
this.session = session; this.session = session;
@ -58,12 +64,13 @@ public class UserMapStorage implements UserLookupProvider, UserStorageProvider,
allocations.incrementAndGet(); allocations.incrementAndGet();
} }
@Override @Override
public UserModel getUserById(String id, RealmModel realm) { public UserModel getUserById(String id, RealmModel realm) {
StorageId storageId = new StorageId(id); StorageId storageId = new StorageId(id);
final String username = storageId.getExternalId(); final String username = storageId.getExternalId();
if (!userPasswords.containsKey(username)) return null; if (!userPasswords.containsKey(username)) {
return null;
}
return createUser(realm, username); return createUser(realm, username);
} }
@ -90,7 +97,9 @@ public class UserMapStorage implements UserLookupProvider, UserStorageProvider,
@Override @Override
public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) { public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
if (!(input instanceof UserCredentialModel)) return false; if (!(input instanceof UserCredentialModel)) {
return false;
}
if (input.getType().equals(UserCredentialModel.PASSWORD)) { if (input.getType().equals(UserCredentialModel.PASSWORD)) {
userPasswords.put(user.getUsername(), ((UserCredentialModel) input).getValue()); userPasswords.put(user.getUsername(), ((UserCredentialModel) input).getValue());
return true; return true;
@ -117,7 +126,9 @@ public class UserMapStorage implements UserLookupProvider, UserStorageProvider,
@Override @Override
public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) { public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
if (!(input instanceof UserCredentialModel)) return false; if (!(input instanceof UserCredentialModel)) {
return false;
}
if (input.getType().equals(UserCredentialModel.PASSWORD)) { if (input.getType().equals(UserCredentialModel.PASSWORD)) {
String pw = userPasswords.get(user.getUsername()); String pw = userPasswords.get(user.getUsername());
return pw != null && pw.equals(((UserCredentialModel) input).getValue()); return pw != null && pw.equals(((UserCredentialModel) input).getValue());
@ -128,7 +139,9 @@ public class UserMapStorage implements UserLookupProvider, UserStorageProvider,
@Override @Override
public UserModel getUserByUsername(String username, RealmModel realm) { public UserModel getUserByUsername(String username, RealmModel realm) {
if (!userPasswords.containsKey(username)) return null; if (!userPasswords.containsKey(username)) {
return null;
}
return createUser(realm, username); return createUser(realm, username);
} }
@ -151,22 +164,25 @@ public class UserMapStorage implements UserLookupProvider, UserStorageProvider,
@Override @Override
public void preRemove(RealmModel realm) { public void preRemove(RealmModel realm) {
log.infof("preRemove: realm=%s", realm.getName());
realmRemovals.incrementAndGet();
} }
@Override @Override
public void preRemove(RealmModel realm, GroupModel group) { public void preRemove(RealmModel realm, GroupModel group) {
log.infof("preRemove: realm=%s, group=%s", realm.getName(), group.getName());
groupRemovals.incrementAndGet();
} }
@Override @Override
public void preRemove(RealmModel realm, RoleModel role) { public void preRemove(RealmModel realm, RoleModel role) {
log.infof("preRemove: realm=%s, role=%s", realm.getName(), role.getName());
roleRemovals.incrementAndGet();
} }
@Override @Override
public void close() { public void close() {
closings.incrementAndGet(); closings.incrementAndGet();
}
} }
}

View file

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Red Hat, Inc. and/or its affiliates * Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags. * and other contributors as indicated by the @author tags.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -16,15 +16,18 @@
*/ */
package org.keycloak.testsuite.federation.storage; package org.keycloak.testsuite.federation.storage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.storage.UserStorageProviderFactory; import org.keycloak.storage.UserStorageProviderFactory;
import java.util.Hashtable;
import java.util.Map;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
@ -32,9 +35,23 @@ import java.util.Map;
public class UserMapStorageFactory implements UserStorageProviderFactory<UserMapStorage> { public class UserMapStorageFactory implements UserStorageProviderFactory<UserMapStorage> {
public static final String PROVIDER_ID = "user-password-map"; public static final String PROVIDER_ID = "user-password-map-arq";
protected Map<String, String> userPasswords = new Hashtable<>(); protected static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
static {
ProviderConfigProperty attr = new ProviderConfigProperty("attr", "attr",
"This is some attribute",
ProviderConfigProperty.STRING_TYPE, null);
configProperties.add(attr);
}
protected Map<String, String> userPasswords = new HashMap<>();
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
}
@Override @Override
public UserMapStorage create(KeycloakSession session, ComponentModel model) { public UserMapStorage create(KeycloakSession session, ComponentModel model) {

View file

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Red Hat, Inc. and/or its affiliates * Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags. * and other contributors as indicated by the @author tags.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -16,6 +16,12 @@
*/ */
package org.keycloak.testsuite.federation.storage; package org.keycloak.testsuite.federation.storage;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;
import org.keycloak.credential.CredentialInput; import org.keycloak.credential.CredentialInput;
import org.keycloak.credential.CredentialInputValidator; import org.keycloak.credential.CredentialInputValidator;
@ -32,12 +38,6 @@ import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage;
import org.keycloak.storage.user.UserLookupProvider; import org.keycloak.storage.user.UserLookupProvider;
import org.keycloak.storage.user.UserQueryProvider; import org.keycloak.storage.user.UserQueryProvider;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
@ -137,7 +137,6 @@ public class UserPropertyFileStorage implements UserLookupProvider, UserStorageP
} }
} }
@Override @Override
public int getUsersCount(RealmModel realm) { public int getUsersCount(RealmModel realm) {
return userPasswords.size(); return userPasswords.size();
@ -191,7 +190,6 @@ public class UserPropertyFileStorage implements UserLookupProvider, UserStorageP
@Override @Override
public List<UserModel> searchForUser(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) { public List<UserModel> searchForUser(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
if (attributes.size() != 1) return Collections.EMPTY_LIST;
String username = attributes.get(UserModel.USERNAME); String username = attributes.get(UserModel.USERNAME);
if (username == null) return Collections.EMPTY_LIST; if (username == null) return Collections.EMPTY_LIST;
return searchForUser(username, realm, firstResult, maxResults); return searchForUser(username, realm, firstResult, maxResults);

View file

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Red Hat, Inc. and/or its affiliates * Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags. * and other contributors as indicated by the @author tags.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -16,39 +16,85 @@
*/ */
package org.keycloak.testsuite.federation.storage; package org.keycloak.testsuite.federation.storage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.common.util.EnvUtil;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;
import org.keycloak.component.ComponentValidationException;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderConfigurationBuilder;
import org.keycloak.storage.UserStorageProviderFactory; import org.keycloak.storage.UserStorageProviderFactory;
import org.keycloak.storage.UserStorageProviderModel; import org.keycloak.storage.UserStorageProviderModel;
import org.keycloak.storage.user.ImportSynchronization; import org.keycloak.storage.user.ImportSynchronization;
import org.keycloak.storage.user.SynchronizationResult; import org.keycloak.storage.user.SynchronizationResult;
import java.io.IOException;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class UserPropertyFileStorageFactory implements UserStorageProviderFactory<UserPropertyFileStorage>, ImportSynchronization { public class UserPropertyFileStorageFactory implements UserStorageProviderFactory<UserPropertyFileStorage>, ImportSynchronization {
public static final String PROVIDER_ID = "user-password-props-arq";
public static final String PROPERTY_FILE = "propertyFile";
public static final String PROVIDER_ID = "user-password-props"; public static final String VALIDATION_PROP_FILE_NOT_CONFIGURED = "user property file is not configured";
public static final String VALIDATION_PROP_FILE_DOESNT_EXIST = "user property file does not exist";
protected static final List<ProviderConfigProperty> CONFIG_PROPERTIES;
static {
CONFIG_PROPERTIES = ProviderConfigurationBuilder.create()
.property().name(PROPERTY_FILE)
.type(ProviderConfigProperty.STRING_TYPE)
.label("Property File")
.helpText("File that contains name value pairs")
.defaultValue(null)
.add()
.property().name("federatedStorage")
.type(ProviderConfigProperty.BOOLEAN_TYPE)
.label("User Federated Storage")
.helpText("User Federated Storage")
.defaultValue(null)
.add()
.build();
}
@Override
public void validateConfiguration(KeycloakSession session, RealmModel realm, ComponentModel config) throws ComponentValidationException {
String fp = config.getConfig().getFirst(PROPERTY_FILE);
if (fp == null) {
throw new ComponentValidationException(VALIDATION_PROP_FILE_NOT_CONFIGURED);
}
fp = EnvUtil.replace(fp);
File file = new File(fp);
if (!file.exists()) {
throw new ComponentValidationException(VALIDATION_PROP_FILE_DOESNT_EXIST);
}
}
@Override @Override
public UserPropertyFileStorage create(KeycloakSession session, ComponentModel model) { public UserPropertyFileStorage create(KeycloakSession session, ComponentModel model) {
String path = model.getConfig().getFirst(PROPERTY_FILE);
path = EnvUtil.replace(path);
Properties props = new Properties(); Properties props = new Properties();
try { try (InputStream is = new FileInputStream(path)) {
props.load(getClass().getResourceAsStream(model.getConfig().getFirst("propertyFile"))); props.load(is);
is.close();
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
return new UserPropertyFileStorage(session, model, props); return new UserPropertyFileStorage(session, model, props);
} }
@ -57,17 +103,9 @@ public class UserPropertyFileStorageFactory implements UserStorageProviderFactor
return PROVIDER_ID; return PROVIDER_ID;
} }
static List<ProviderConfigProperty> OPTIONS = new LinkedList<>();
static {
ProviderConfigProperty prop = new ProviderConfigProperty("propertyFile", "Property File", "file that contains name value pairs", ProviderConfigProperty.STRING_TYPE, null);
OPTIONS.add(prop);
prop = new ProviderConfigProperty("federatedStorage", "User Federated Storage", "use federated storage?", ProviderConfigProperty.BOOLEAN_TYPE, null);
OPTIONS.add(prop);
}
@Override @Override
public List<ProviderConfigProperty> getConfigProperties() { public List<ProviderConfigProperty> getConfigProperties() {
return OPTIONS; return CONFIG_PROPERTIES;
} }
@Override @Override
@ -94,4 +132,5 @@ public class UserPropertyFileStorageFactory implements UserStorageProviderFactor
public SynchronizationResult syncSince(Date lastSync, KeycloakSessionFactory sessionFactory, String realmId, UserStorageProviderModel model) { public SynchronizationResult syncSince(Date lastSync, KeycloakSessionFactory sessionFactory, String realmId, UserStorageProviderModel model) {
return SynchronizationResult.ignored(); return SynchronizationResult.ignored();
} }
} }

View file

@ -1,570 +0,0 @@
/*
* 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.storage;
import org.junit.After;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.common.util.Time;
import org.keycloak.component.ComponentModel;
import org.keycloak.credential.CredentialAuthentication;
import org.keycloak.credential.UserCredentialStoreManager;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.cache.CachedUserModel;
import org.keycloak.models.cache.infinispan.UserAdapter;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.UserStorageProviderModel;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule;
import org.openqa.selenium.WebDriver;
import java.util.Calendar;
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 UserStorageTest {
public static ComponentModel memoryProvider = null;
public static ComponentModel writableProvider = null;
@ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
UserStorageProviderModel model = new UserStorageProviderModel();
model.setName("memory");
model.setPriority(0);
model.setProviderId(UserMapStorageFactory.PROVIDER_ID);
model.setParentId(appRealm.getId());
memoryProvider = appRealm.addComponentModel(model);
model = new UserStorageProviderModel();
model.setName("read-only-user-props");
model.setPriority(1);
model.setProviderId(UserPropertyFileStorageFactory.PROVIDER_ID);
model.setParentId(appRealm.getId());
model.getConfig().putSingle("propertyFile", "/storage-test/read-only-user-password.properties");
appRealm.addComponentModel(model);
createUserPropModel(appRealm);
}
});
private static void createUserPropModel(RealmModel appRealm) {
UserStorageProviderModel model;
model = new UserStorageProviderModel();
model.setName("user-props");
model.setPriority(2);
model.setParentId(appRealm.getId());
model.setProviderId(UserPropertyFileStorageFactory.PROVIDER_ID);
model.getConfig().putSingle("propertyFile", "/storage-test/user-password.properties");
model.getConfig().putSingle("federatedStorage", "true");
writableProvider = appRealm.addComponentModel(model);
}
@Rule
public WebRule webRule = new WebRule(this);
@WebResource
protected OAuthClient oauth;
@WebResource
protected WebDriver driver;
@WebResource
protected AppPage appPage;
@WebResource
protected LoginPage loginPage;
private void loginSuccessAndLogout(String username, String password) {
loginPage.open();
loginPage.login(username, password);
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
oauth.openLogout();
}
public void loginBadPassword(String username) {
loginPage.open();
loginPage.login("username", "badpassword");
Assert.assertEquals("Invalid username or password.", loginPage.getError());
}
@Test
public void testLoginSuccess() {
loginSuccessAndLogout("tbrady", "goat");
loginSuccessAndLogout("thor", "hammer");
loginBadPassword("tbrady");
}
@After
public void resetTimeoffset() {
Time.setOffset(0);
}
//@Test
public void testIDE() throws Exception {
Thread.sleep(100000000);
}
/**
* KEYCLOAK-4013
*
* @throws Exception
*/
@Test
public void testCast() throws Exception {
KeycloakSession session = keycloakRule.startSession();
List<CredentialAuthentication> list = UserCredentialStoreManager.getCredentialProviders(session, null, CredentialAuthentication.class);
keycloakRule.stopSession(session, true);
}
@Test
public void testDailyEviction() {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.HOUR, 1);
int hour = cal.get(Calendar.HOUR_OF_DAY);
int min = cal.get(Calendar.MINUTE);
UserStorageProviderModel model = new UserStorageProviderModel(writableProvider);
model.setCachePolicy(UserStorageProviderModel.CachePolicy.EVICT_DAILY);
model.setEvictionHour(cal.get(Calendar.HOUR_OF_DAY));
model.setEvictionMinute(cal.get(Calendar.MINUTE));
KeycloakSession session = keycloakRule.startSession();
RealmModel realm = session.realms().getRealmByName("test");
CachedUserModel thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
long lastTimestamp = thor.getCacheTimestamp();
realm.updateComponent(model);
keycloakRule.stopSession(session, true);
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
lastTimestamp = thor.getCacheTimestamp();
realm.updateComponent(model);
keycloakRule.stopSession(session, true);
// test is cached
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
// thor should be evicted because we changed the model
Assert.assertTrue(thor.getCacheTimestamp() > lastTimestamp);
lastTimestamp = thor.getCacheTimestamp();
keycloakRule.stopSession(session, true);
Time.setOffset(60 * 2 * 60); // 2 hours
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
UserModel thor2 = session.users().getUserByUsername("thor", realm);
// thor should be evicted because we put it 2 hours in the future
if (thor2 instanceof CachedUserModel) {
Assert.assertTrue(((CachedUserModel)thor2).getCacheTimestamp() > lastTimestamp);
}
model.getConfig().remove("cachePolicy");
model.getConfig().remove("evictionHour");
model.getConfig().remove("evictionMinute");
realm.updateComponent(model);
keycloakRule.stopSession(session, true);
}
@Test
public void testWeeklyEviction() {
Calendar cal = Calendar.getInstance();
// sets day of the week to 4 days from now
cal.add(Calendar.HOUR, 4 * 24);
UserStorageProviderModel model = new UserStorageProviderModel(writableProvider);
model.setCachePolicy(UserStorageProviderModel.CachePolicy.EVICT_WEEKLY);
model.setEvictionDay(cal.get(Calendar.DAY_OF_WEEK));
model.setEvictionHour(cal.get(Calendar.HOUR_OF_DAY));
model.setEvictionMinute(cal.get(Calendar.MINUTE));
KeycloakSession session = keycloakRule.startSession();
RealmModel realm = session.realms().getRealmByName("test");
realm.updateComponent(model);
keycloakRule.stopSession(session, true);
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
CachedUserModel thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
long lastTimestamp = thor.getCacheTimestamp();
keycloakRule.stopSession(session, true);
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
lastTimestamp = thor.getCacheTimestamp();
keycloakRule.stopSession(session, true);
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
lastTimestamp = thor.getCacheTimestamp();
keycloakRule.stopSession(session, true);
Time.setOffset(60 * 60 * 24 * 2); // 2 days in future, should be cached still
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
// test still
thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
Assert.assertEquals(thor.getCacheTimestamp(), lastTimestamp);
lastTimestamp = thor.getCacheTimestamp();
keycloakRule.stopSession(session, true);
Time.setOffset(Time.getOffset() + 60 * 60 * 24 * 3); // 3 days into future, cache will be invalidated
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
UserModel thor2 = session.users().getUserByUsername("thor", realm);
// thor should be evicted because we put it 2 hours in the future
if (thor2 instanceof CachedUserModel) {
Assert.assertTrue(((CachedUserModel)thor2).getCacheTimestamp() > lastTimestamp);
}
model.getConfig().remove("cachePolicy");
model.getConfig().remove("evictionHour");
model.getConfig().remove("evictionMinute");
model.getConfig().remove("evictionDay");
realm.updateComponent(model);
keycloakRule.stopSession(session, true);
}
@Test
public void testMaxLifespanEviction() {
UserStorageProviderModel model = new UserStorageProviderModel(writableProvider);
model.setCachePolicy(UserStorageProviderModel.CachePolicy.MAX_LIFESPAN);
model.setMaxLifespan(600000); // Lifetime is 10 minutes
KeycloakSession session = keycloakRule.startSession();
RealmModel realm = session.realms().getRealmByName("test");
realm.updateComponent(model);
keycloakRule.stopSession(session, true);
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
CachedUserModel thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
long lastTimestamp = thor.getCacheTimestamp();
keycloakRule.stopSession(session, true);
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
lastTimestamp = thor.getCacheTimestamp();
keycloakRule.stopSession(session, true);
Time.setOffset(60 * 5); // 5 minutes in future, should be cached still
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
Assert.assertEquals(thor.getCacheTimestamp(), lastTimestamp);
lastTimestamp = thor.getCacheTimestamp();
keycloakRule.stopSession(session, true);
Time.setOffset(60 * 20); // 20 minutes into future, cache will be invalidated
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
UserModel thor2 = session.users().getUserByUsername("thor", realm);
// thor should be evicted because we put it 2 hours in the future
if (thor2 instanceof CachedUserModel) {
Assert.assertTrue(((CachedUserModel)thor2).getCacheTimestamp() > lastTimestamp);
}
keycloakRule.stopSession(session, true);
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
model.getConfig().remove("cachePolicy");
model.getConfig().remove("maxLifespan");
realm.updateComponent(model);
keycloakRule.stopSession(session, true);
}
@Test
public void testNoCache() {
UserStorageProviderModel model = new UserStorageProviderModel(writableProvider);
model.setCachePolicy(UserStorageProviderModel.CachePolicy.NO_CACHE);
KeycloakSession session = keycloakRule.startSession();
RealmModel realm = session.realms().getRealmByName("test");
CachedUserModel thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
realm.updateComponent(model);
keycloakRule.stopSession(session, true);
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
// test still
UserModel thor2 = session.users().getUserByUsername("thor", realm);
Assert.assertFalse(thor2 instanceof CachedUserModel);
keycloakRule.stopSession(session, true);
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
thor2 = session.users().getUserByUsername("thor", realm);
Assert.assertFalse(thor2 instanceof CachedUserModel);
model.getConfig().remove("cachePolicy");
model.getConfig().remove("evictionHour");
model.getConfig().remove("evictionMinute");
model.getConfig().remove("evictionDay");
realm.updateComponent(model);
keycloakRule.stopSession(session, true);
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
keycloakRule.stopSession(session, true);
}
@Test
public void testUpdate() {
KeycloakSession session = keycloakRule.startSession();
RealmModel realm = session.realms().getRealmByName("test");
UserModel thor = session.users().getUserByUsername("thor", realm);
thor.setFirstName("Stian");
thor.setLastName("Thorgersen");
thor.setEmailVerified(true);
long thorCreated = System.currentTimeMillis() - 100;
thor.setCreatedTimestamp(thorCreated);
thor.setEmail("thor@hammer.com");
thor.setSingleAttribute("test-attribute", "value");
RoleModel role = realm.addRole("foo-role");
thor.grantRole(role);
GroupModel group = realm.createGroup("my-group");
thor.joinGroup(group);
thor.addRequiredAction("POOP");
keycloakRule.stopSession(session, true);
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
thor = session.users().getUserByUsername("thor", realm);
Assert.assertEquals("Stian", thor.getFirstName());
Assert.assertEquals("Thorgersen", thor.getLastName());
Assert.assertEquals("thor@hammer.com", thor.getEmail());
Assert.assertEquals("value", thor.getFirstAttribute("test-attribute"));
Assert.assertTrue(thor.isEmailVerified());
Assert.assertTrue(thor instanceof UserAdapter);
Set<RoleModel> roles = thor.getRoleMappings();
System.out.println("num roles " + roles.size());
Assert.assertTrue(roles.size() > 1);
role = realm.getRole("foo-role");
Assert.assertTrue(thor.hasRole(role));
Set<GroupModel> groups = thor.getGroups();
boolean foundGroup = false;
for (GroupModel g : groups) {
if (g.getName().equals("my-group")) foundGroup = true;
}
Assert.assertTrue(foundGroup);
System.out.println("num groups " + groups.size());
Assert.assertTrue(thor.getRequiredActions().iterator().next().equals("POOP"));
thor.removeRequiredAction("POOP");
session.userCredentialManager().updateCredential(realm, thor, UserCredentialModel.password("lightning"));
keycloakRule.stopSession(session, true);
loginSuccessAndLogout("thor", "lightning");
// test removal of provider
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
realm.removeComponent(writableProvider);
keycloakRule.stopSession(session, true);
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
createUserPropModel(realm);
keycloakRule.stopSession(session, true);
loginSuccessAndLogout("thor", "hammer");
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
thor = session.users().getUserByUsername("thor", realm);
Assert.assertNull(thor.getFirstName());
Assert.assertNull(thor.getLastName());
Assert.assertNull(thor.getEmail());
Assert.assertNull(thor.getFirstAttribute("test-attribute"));
Assert.assertFalse(thor.isEmailVerified());
role = realm.getRole("foo-role");
Assert.assertFalse(thor.hasRole(role));
groups = thor.getGroups();
foundGroup = false;
for (GroupModel g : groups) {
if (g.getName().equals("my-group")) foundGroup = true;
}
Assert.assertFalse(foundGroup);
}
@Test
public void testQuery() {
KeycloakSession session = keycloakRule.startSession();
RealmModel realm = session.realms().getRealmByName("test");
// Test paging
List<UserModel> localUsers = session.userLocalStorage().getUsers(realm, false);
Set<UserModel> queried = new HashSet<>();
// tests assumes that local storage is queried first
int first = localUsers.size();
while (queried.size() < 8) {
List<UserModel> results = session.users().getUsers(realm, first, 3);
if (results.size() == 0) break;
first += results.size();
queried.addAll(results);
}
Set<String> usernames = new HashSet<>();
for (UserModel user : queried) {
usernames.add(user.getUsername());
System.out.println(user.getUsername());
}
Assert.assertEquals(8, queried.size());
Assert.assertTrue(usernames.contains("thor"));
Assert.assertTrue(usernames.contains("zeus"));
Assert.assertTrue(usernames.contains("apollo"));
Assert.assertTrue(usernames.contains("perseus"));
Assert.assertTrue(usernames.contains("tbrady"));
Assert.assertTrue(usernames.contains("rob"));
Assert.assertTrue(usernames.contains("jules"));
Assert.assertTrue(usernames.contains("danny"));
// test searchForUser
List<UserModel> users = session.users().searchForUser("tbrady", realm);
Assert.assertTrue(users.size() == 1);
Assert.assertTrue(users.get(0).getUsername().equals("tbrady"));
// test getGroupMembers()
GroupModel gods = realm.createGroup("gods");
UserModel user = null;
user = session.users().getUserByUsername("apollo", realm);
user.joinGroup(gods);
user = session.users().getUserByUsername("zeus", realm);
user.joinGroup(gods);
user = session.users().getUserByUsername("thor", realm);
user.joinGroup(gods);
queried.clear();
usernames.clear();
first = 0;
while (queried.size() < 8) {
List<UserModel> results = session.users().getGroupMembers(realm, gods, first, 1);
if (results.size() == 0) break;
first += results.size();
queried.addAll(results);
}
for (UserModel u : queried) {
usernames.add(u.getUsername());
System.out.println(u.getUsername());
}
Assert.assertEquals(3, queried.size());
Assert.assertTrue(usernames.contains("apollo"));
Assert.assertTrue(usernames.contains("zeus"));
Assert.assertTrue(usernames.contains("thor"));
// search by single attribute
System.out.println("search by single attribute");
user = session.users().getUserByUsername("thor", realm);
user.setSingleAttribute("weapon", "hammer");
users = session.users().searchForUserByUserAttribute("weapon", "hammer", realm);
for (UserModel u : users) {
System.out.println(u.getUsername());
}
Assert.assertEquals(1, users.size());
Assert.assertEquals("thor", users.get(0).getUsername());
keycloakRule.stopSession(session, true);
}
@Test
public void testRegistration() {
KeycloakSession session = keycloakRule.startSession();
RealmModel realm = session.realms().getRealmByName("test");
UserModel user = session.users().addUser(realm, "memuser");
session.userCredentialManager().updateCredential(realm, user, UserCredentialModel.password("password"));
keycloakRule.stopSession(session, true);
loginSuccessAndLogout("memuser", "password");
loginSuccessAndLogout("memuser", "password");
loginSuccessAndLogout("memuser", "password");
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
user = session.users().getUserByUsername("memuser", realm);
Assert.assertEquals(memoryProvider.getId(), StorageId.resolveProviderId(user));
session.users().removeUser(realm, user);
Assert.assertNull(session.users().getUserByUsername("memuser", realm));
keycloakRule.stopSession(session, true);
}
@Test
public void testLifecycle() {
UserMapStorage.allocations.set(0);
UserMapStorage.closings.set(0);
KeycloakSession session = keycloakRule.startSession();
RealmModel realm = session.realms().getRealmByName("test");
UserModel user = session.users().addUser(realm, "memuser");
Assert.assertNotNull(user);
user = session.users().getUserByUsername("nonexistent", realm);
Assert.assertNull(user);
keycloakRule.stopSession(session, true);
Assert.assertEquals(1, UserMapStorage.allocations.get());
Assert.assertEquals(1, UserMapStorage.closings.get());
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("test");
user = session.users().getUserByUsername("memuser", realm);
session.users().removeUser(realm, user);
Assert.assertNull(session.users().getUserByUsername("memuser", realm));
keycloakRule.stopSession(session, true);
}
}

View file

@ -1,314 +0,0 @@
/*
* 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.sync;
import org.jboss.logging.Logger;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.keycloak.common.util.Time;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.UserStorageSyncManager;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.UserStorageProviderModel;
import org.keycloak.storage.user.SynchronizationResult;
import org.keycloak.testsuite.federation.DummyUserFederationProviderFactory;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.timer.TimerProvider;
import java.util.concurrent.TimeUnit;
/**
* Test with Dummy providers
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SyncFederationTest {
private static final Logger log = Logger.getLogger(SyncFederationTest.class);
private static UserStorageProviderModel dummyModel = null;
@ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
// Other tests may left Time offset uncleared, which could cause issues
Time.setOffset(0);
}
});
/**
* Test that period sync is triggered when creating a synchronized User Storage Provider
*
*/
@Test
public void test01PeriodicSyncOnCreate() {
KeycloakSession session = keycloakRule.startSession();
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
DummyUserFederationProviderFactory dummyFedFactory = (DummyUserFederationProviderFactory) sessionFactory.getProviderFactory(UserStorageProvider.class, DummyUserFederationProviderFactory.PROVIDER_NAME);
int full = dummyFedFactory.getFullSyncCounter();
int changed = dummyFedFactory.getChangedSyncCounter();
keycloakRule.stopSession(session, false);
// Enable timer for SyncDummyUserFederationProvider
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
UserStorageProviderModel model = new UserStorageProviderModel();
model.setProviderId(DummyUserFederationProviderFactory.PROVIDER_NAME);
model.setPriority(1);
model.setName("test-sync-dummy");
model.setFullSyncPeriod(-1);
model.setChangedSyncPeriod(1);
model.setLastSync(0);
dummyModel = new UserStorageProviderModel(appRealm.addComponentModel(model));
}
});
session = keycloakRule.startSession();
try {
// Assert that after some period was DummyUserFederationProvider triggered
UserStorageSyncManager usersSyncManager = new UserStorageSyncManager();
sleep(1800);
// Cancel timer
RealmModel appRealm = session.realms().getRealmByName("test");
usersSyncManager.notifyToRefreshPeriodicSync(session, appRealm, dummyModel, true);
log.infof("Notified sync manager about cancel periodic sync");
// This sync is here just to ensure that we have lock (doublecheck that periodic sync, which was possibly triggered before canceling timer is finished too)
while (true) {
SynchronizationResult result = usersSyncManager.syncChangedUsers(session.getKeycloakSessionFactory(), appRealm.getId(), dummyModel);
if (result.isIgnored()) {
log.infof("Still waiting for lock before periodic sync is finished", result.toString());
sleep(1000);
} else {
break;
}
}
// Assert that DummyUserFederationProviderFactory.syncChangedUsers was invoked at least 2 times (once periodically and once for us)
int newChanged = dummyFedFactory.getChangedSyncCounter();
Assert.assertEquals(full, dummyFedFactory.getFullSyncCounter());
Assert.assertTrue("Assertion failed. newChanged=" + newChanged + ", changed=" + changed, newChanged > (changed + 1));
// Assert that dummy provider won't be invoked anymore
sleep(1800);
Assert.assertEquals(full, dummyFedFactory.getFullSyncCounter());
int newestChanged = dummyFedFactory.getChangedSyncCounter();
Assert.assertEquals("Assertion failed. newChanged=" + newChanged + ", newestChanged=" + newestChanged, newChanged, newestChanged);
} finally {
keycloakRule.stopSession(session, true);
}
// remove dummyProvider
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
appRealm.removeComponent(dummyModel);
}
});
}
/**
* Test that period sync is triggered when updating a synchronized User Storage Provider to have a non-negative sync period
*
*/
@Test
public void test02PeriodicSyncOnUpdate() {
KeycloakSession session = keycloakRule.startSession();
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
DummyUserFederationProviderFactory dummyFedFactory = (DummyUserFederationProviderFactory) sessionFactory.getProviderFactory(UserStorageProvider.class, DummyUserFederationProviderFactory.PROVIDER_NAME);
int full = dummyFedFactory.getFullSyncCounter();
int changed = dummyFedFactory.getChangedSyncCounter();
keycloakRule.stopSession(session, false);
// Enable timer for SyncDummyUserFederationProvider
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
UserStorageProviderModel model = new UserStorageProviderModel();
model.setProviderId(DummyUserFederationProviderFactory.PROVIDER_NAME);
model.setPriority(1);
model.setName("test-sync-dummy");
model.setFullSyncPeriod(-1);
model.setChangedSyncPeriod(-1);
model.setLastSync(0);
dummyModel = new UserStorageProviderModel(appRealm.addComponentModel(model));
}
});
session = keycloakRule.startSession();
try {
// Assert that after some period was DummyUserFederationProvider triggered
UserStorageSyncManager usersSyncManager = new UserStorageSyncManager();
// Assert that dummy provider won't be invoked anymore
sleep(1800);
Assert.assertEquals(full, dummyFedFactory.getFullSyncCounter());
int newestChanged = dummyFedFactory.getChangedSyncCounter();
Assert.assertEquals("Assertion failed. newChanged=" + changed + ", newestChanged=" + newestChanged, changed, newestChanged);
} finally {
keycloakRule.stopSession(session, true);
}
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
dummyModel.setChangedSyncPeriod(1);
appRealm.updateComponent(dummyModel);
}
});
session = keycloakRule.startSession();
try {
// Assert that after some period was DummyUserFederationProvider triggered
UserStorageSyncManager usersSyncManager = new UserStorageSyncManager();
sleep(1800);
// Cancel timer
RealmModel appRealm = session.realms().getRealmByName("test");
usersSyncManager.notifyToRefreshPeriodicSync(session, appRealm, dummyModel, true);
log.infof("Notified sync manager about cancel periodic sync");
// This sync is here just to ensure that we have lock (doublecheck that periodic sync, which was possibly triggered before canceling timer is finished too)
while (true) {
SynchronizationResult result = usersSyncManager.syncChangedUsers(session.getKeycloakSessionFactory(), appRealm.getId(), dummyModel);
if (result.isIgnored()) {
log.infof("Still waiting for lock before periodic sync is finished", result.toString());
sleep(1000);
} else {
break;
}
}
// Assert that DummyUserFederationProviderFactory.syncChangedUsers was invoked at least 2 times (once periodically and once for us)
int newChanged = dummyFedFactory.getChangedSyncCounter();
Assert.assertEquals(full, dummyFedFactory.getFullSyncCounter());
log.info("Asserting. newChanged=" + newChanged + " > changed=" + changed);
Assert.assertTrue("Assertion failed. newChanged=" + newChanged + ", changed=" + changed, newChanged > (changed + 1));
// Assert that dummy provider won't be invoked anymore
sleep(1800);
Assert.assertEquals(full, dummyFedFactory.getFullSyncCounter());
int newestChanged = dummyFedFactory.getChangedSyncCounter();
Assert.assertEquals("Assertion failed. newChanged=" + newChanged + ", newestChanged=" + newestChanged, newChanged, newestChanged);
} finally {
keycloakRule.stopSession(session, true);
}
// remove dummyProvider
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
appRealm.removeComponent(dummyModel);
}
});
}
@Test
public void test03ConcurrentSync() throws Exception {
SyncDummyUserFederationProviderFactory.restartLatches();
// Enable timer for SyncDummyUserFederationProvider
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
UserStorageProviderModel model = new UserStorageProviderModel();
model.setProviderId(SyncDummyUserFederationProviderFactory.SYNC_PROVIDER_ID);
model.setPriority(1);
model.setName("test-sync-dummy");
model.setFullSyncPeriod(-1);
model.setChangedSyncPeriod(1);
model.setLastSync(0);
model.getConfig().putSingle(SyncDummyUserFederationProviderFactory.WAIT_TIME, "2000");
dummyModel = new UserStorageProviderModel(appRealm.addComponentModel(model));
}
});
KeycloakSession session = keycloakRule.startSession();
try {
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
// bootstrap periodic sync
UserStorageSyncManager usersSyncManager = new UserStorageSyncManager();
usersSyncManager.bootstrapPeriodic(sessionFactory, session.getProvider(TimerProvider.class));
// Wait and then trigger sync manually. Assert it will be ignored
sleep(1800);
RealmModel realm = session.realms().getRealm("test");
SynchronizationResult syncResult = usersSyncManager.syncChangedUsers(sessionFactory, realm.getId(), dummyModel);
Assert.assertTrue(syncResult.isIgnored());
// Cancel timer
usersSyncManager.notifyToRefreshPeriodicSync(session, realm, dummyModel, true);
// Signal to factory to finish waiting
SyncDummyUserFederationProviderFactory.latch1.countDown();
} finally {
keycloakRule.stopSession(session, true);
}
SyncDummyUserFederationProviderFactory.latch2.await(20000, TimeUnit.MILLISECONDS);
// remove provider
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
appRealm.removeComponent(dummyModel);
}
});
}
private void sleep(int time) {
try {
Thread.sleep(time);
} catch (InterruptedException ie) {
throw new RuntimeException(ie);
}
}
}

View file

@ -1,4 +0,0 @@
org.keycloak.testsuite.federation.sync.SyncDummyUserFederationProviderFactory
org.keycloak.testsuite.federation.storage.UserPropertyFileStorageFactory
org.keycloak.testsuite.federation.storage.UserMapStorageFactory
org.keycloak.testsuite.federation.storage.FailableHardcodedStorageProviderFactory

View file

@ -1,4 +0,0 @@
tbrady=goat
rob=pw
jules=pw
danny=pw

View file

@ -1,4 +0,0 @@
thor=hammer
zeus=pw
apollo=pw
perseus=pw