/* * 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 thorTimestamp = thor.getCacheTimestamp(); realm.updateComponent(model); 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); Assert.assertFalse(thor2 instanceof CachedUserModel); 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"); CachedUserModel thor = (CachedUserModel)session.users().getUserByUsername("thor", realm); realm.updateComponent(model); 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 UserModel thor2 = session.users().getUserByUsername("thor", realm); Assert.assertTrue(thor2 instanceof CachedUserModel); 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"); 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); } @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"); CachedUserModel thor = (CachedUserModel)session.users().getUserByUsername("thor", realm); realm.updateComponent(model); keycloakRule.stopSession(session, true); Time.setOffset(60 * 5); // 5 minutes in future, should be cached still session = keycloakRule.startSession(); realm = session.realms().getRealmByName("test"); // test still UserModel thor2 = session.users().getUserByUsername("thor", realm); Assert.assertTrue(thor2 instanceof CachedUserModel); keycloakRule.stopSession(session, true); Time.setOffset(60 * 20); // 20 minutes into future, cache will be invalidated 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("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); } }