diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java index c401f0a349..45bb7cc755 100755 --- a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java +++ b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java @@ -201,6 +201,10 @@ public class KeycloakServer { return server; } + public KeycloakServerConfig getConfig() { + return config; + } + public void importRealm(InputStream realm) { RealmRepresentation rep = loadJson(realm, RealmRepresentation.class); importRealm(rep); diff --git a/testsuite/performance-web/README.md b/testsuite/performance-web/README.md index 950e90cc12..7546378c7e 100644 --- a/testsuite/performance-web/README.md +++ b/testsuite/performance-web/README.md @@ -53,10 +53,20 @@ http://localhost:8081/keycloak-tools/perf/perf-realm/get-users-count?prefix=user For adding 10000 new users into your database (will start from last added user, so you don't need to explicitly check how many users to create are needed: ```shell -http://localhost:8081/keycloak-tools/perf/perf-realm/create-available-users?prefix=user&count=10000&batch=100&roles=user +http://localhost:8081/keycloak-tools/perf/perf-realm/create-available-users?prefix=user&count=10000&batch=100&async=true&roles=role-0,role-1 ```` -Seeing progress of job for creating users +For update role mappings of all users: +```shell +http://localhost:8081/keycloak-tools/perf/perf-realm/update-all-users?prefix=user&async=true&roles=role-3,perf-app:approle-3,perf-app:approle-4 +```` + +For deleting all users: +```shell +http://localhost:8081/keycloak-tools/perf/perf-realm/delete-all-users?prefix=user +```` + +Seeing progress of job for creating/updating/deleting users ```shell http://localhost:8081/keycloak-tools/perf/jobs ```` diff --git a/testsuite/performance-web/pom.xml b/testsuite/performance-web/pom.xml index e2d61db79a..b9fee7c26c 100644 --- a/testsuite/performance-web/pom.xml +++ b/testsuite/performance-web/pom.xml @@ -216,11 +216,11 @@ false - + aggregatedRequests * request - + codes **/perf-app/perf-servlet?code=* diff --git a/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/KeycloakPerfServer.java b/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/KeycloakPerfServer.java index 5dde02ee22..cb9e239fa2 100644 --- a/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/KeycloakPerfServer.java +++ b/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/KeycloakPerfServer.java @@ -85,6 +85,7 @@ public class KeycloakPerfServer { ServletInfo servlet = new ServletInfo("PerfAppServlet", PerfAppServlet.class); servlet.addMapping("/perf-servlet/*"); + servlet.addInitParam(PerfAppServlet.BASE_URL_INIT_PARAM, "http://" + keycloakServer.getConfig().getHost() + ":" + keycloakServer.getConfig().getPort()); deploymentInfo.addServlet(servlet); diff --git a/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/OAuthClient.java b/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/OAuthClient.java index bb6e0c1535..40409ad432 100644 --- a/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/OAuthClient.java +++ b/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/OAuthClient.java @@ -42,7 +42,7 @@ import org.keycloak.util.BasicAuthHelper; */ public class OAuthClient { - private String baseUrl = "http://localhost:8081/auth"; + private String baseUrl; private String realm = "perf-realm"; @@ -52,16 +52,19 @@ public class OAuthClient { private String clientId = "perf-app"; - private String redirectUri = "http://localhost:8081/perf-app/perf-servlet"; + private String redirectUri; private String state = "123"; private PublicKey realmPublicKey; - public OAuthClient() { + public OAuthClient(String baseUrl) { try { JSONObject realmJson = new JSONObject(IOUtils.toString(getClass().getResourceAsStream("/perfrealm.json"))); realmPublicKey = PemUtils.decodePublicKey(realmJson.getString("publicKey")); + + this.baseUrl = (baseUrl != null) ? baseUrl + "/auth" : "http://localhost:8081/auth"; + this.redirectUri = baseUrl + "/perf-app/perf-servlet"; } catch (Exception e) { throw new RuntimeException("Failed to retrieve realm public key", e); } diff --git a/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/PerfAppServlet.java b/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/PerfAppServlet.java index 52cf1fa10b..dbab2dafec 100644 --- a/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/PerfAppServlet.java +++ b/testsuite/performance-web/src/main/java/org/keycloak/testsuite/performance/web/PerfAppServlet.java @@ -25,6 +25,8 @@ import org.keycloak.util.Time; */ public class PerfAppServlet extends HttpServlet { + public static final String BASE_URL_INIT_PARAM = "baseUrl"; + private Template indexTemplate; private OAuthClient oauthClient; @@ -35,7 +37,8 @@ public class PerfAppServlet extends HttpServlet { cfg.setTemplateLoader(new ClassTemplateLoader(getClass(), "/")); indexTemplate = cfg.getTemplate("perf-app-resources/index.ftl"); - oauthClient = new OAuthClient(); + String baseUrl = getInitParameter(BASE_URL_INIT_PARAM); + oauthClient = new OAuthClient(baseUrl); } catch (IOException ioe) { throw new ServletException(ioe); } diff --git a/testsuite/performance-web/src/main/resources/perfrealm.json b/testsuite/performance-web/src/main/resources/perfrealm.json index 1fab9a52b4..78d31ea3e4 100644 --- a/testsuite/performance-web/src/main/resources/perfrealm.json +++ b/testsuite/performance-web/src/main/resources/perfrealm.json @@ -8,7 +8,6 @@ "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "requiredCredentials": [ "password" ], - "defaultRoles": [ "user" ], "smtpServer": { "from": "auto@keycloak.org", "host": "localhost", @@ -30,7 +29,7 @@ "name" : "third-party", "enabled": true, "redirectUris": [ - "http://localhost:8081/app/*" + "/app/*" ], "secret": "password" } @@ -48,41 +47,55 @@ }, { "client": "perf-app", - "roles": ["user"] + "roles": [ "role-0", "role-1", "role-2", "role-3", "role-4" ] } ], "applications": [ { "name": "perf-app", "enabled": true, - "baseUrl": "http://localhost:8081/perf-app", + "baseUrl": "/perf-app", "redirectUris": [ - "http://localhost:8081/perf-app/*" + "/perf-app/*" ], - "adminUrl": "http://localhost:8081/perf-app/perf-servlet", + "adminUrl": "/perf-app/perf-servlet", "secret": "password" } ], "roles" : { "realm" : [ { - "name": "user", - "description": "Have User privileges" + "name": "role-0" }, { - "name": "admin", - "description": "Have Administrator privileges" + "name": "role-1" + }, + { + "name": "role-2" + }, + { + "name": "role-3" + }, + { + "name": "role-4" } ], "application" : { "perf-app" : [ { - "name": "customer-user", - "description": "Have Customer User privileges" + "name": "approle-0" }, { - "name": "customer-admin", - "description": "Have Customer Admin privileges" + "name": "approle-1" + }, + { + "name": "approle-2" + }, + { + "name": "approle-3" + }, + { + "name": "approle-4" } ] } diff --git a/testsuite/performance-web/src/test/jmeter/keycloak_web_perf_test.jmx b/testsuite/performance-web/src/test/jmeter/keycloak_web_perf_test.jmx index 07074aa998..07f16efe7f 100644 --- a/testsuite/performance-web/src/test/jmeter/keycloak_web_perf_test.jmx +++ b/testsuite/performance-web/src/test/jmeter/keycloak_web_perf_test.jmx @@ -1,5 +1,5 @@ - + @@ -166,7 +166,7 @@ - /auth/realms/perf-realm/tokens/auth/request/login?response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8081%2Fperf-app%2Fperf-servlet&state=123&client_id=perf-app + /auth/realms/perf-realm/tokens/auth/request/login?response_type=code&redirect_uri=http%3A%2F%2F${host}%3A${port}%2Fperf-app%2Fperf-servlet&state=123&client_id=perf-app POST true false @@ -322,6 +322,32 @@ true + + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + false + false + false + false + false + 0 + true + + diff --git a/testsuite/tools/src/main/java/org/keycloak/test/tools/PerfTools.java b/testsuite/tools/src/main/java/org/keycloak/test/tools/PerfTools.java index b2e8ed1361..30906545c8 100644 --- a/testsuite/tools/src/main/java/org/keycloak/test/tools/PerfTools.java +++ b/testsuite/tools/src/main/java/org/keycloak/test/tools/PerfTools.java @@ -5,8 +5,11 @@ import org.keycloak.exportimport.ExportImportProvider; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; -import org.keycloak.models.UserModel; -import org.keycloak.test.tools.jobs.CreateUsers; +import org.keycloak.test.tools.jobs.CreateUsersJob; +import org.keycloak.test.tools.jobs.DeleteUsersJob; +import org.keycloak.test.tools.jobs.UpdateUsersJob; +import org.keycloak.test.tools.jobs.UsersJob; +import org.keycloak.test.tools.jobs.UsersJobInitializer; import org.keycloak.util.ProviderLoader; import javax.ws.rs.GET; @@ -18,11 +21,11 @@ import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; -import java.util.HashMap; +import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import java.util.Map; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; @@ -41,7 +44,7 @@ public class PerfTools { @Context private KeycloakSession session; - private List jobs = new LinkedList(); + private List jobs = new LinkedList(); public PerfTools(KeycloakSessionFactory sessionFactory) { this.sessionFactory = sessionFactory; @@ -50,16 +53,16 @@ public class PerfTools { @GET @Path("jobs") @Produces("application/json") - public List jobs() { + public List jobs() { return jobs; } @GET @Path("delete-jobs") public void deleteJobs() { - Iterator itr = jobs.iterator(); + Iterator itr = jobs.iterator(); while(itr.hasNext()) { - Job j = itr.next(); + JobRepresentation j = itr.next(); if (j.getError() != null || j.getCount() == j.getTotal()) { itr.remove(); } @@ -68,7 +71,116 @@ public class PerfTools { @GET @Path("{realm}/create-users") - public void createUsers(@PathParam("realm") String realmName, @QueryParam("count") Integer count, @QueryParam("batch") Integer batch, @QueryParam("start") Integer start, @QueryParam("prefix") String prefix, @QueryParam("roles") String roles) throws InterruptedException { + public void createUsers(@PathParam("realm") String realmName, @QueryParam("count") Integer count, + @QueryParam("batch") Integer batch, @QueryParam("start") Integer start, @QueryParam("prefix") String prefix, + @QueryParam("async") Boolean async, @QueryParam("roles") String roles) throws InterruptedException { + final String[] rolesArray = roles != null ? roles.split(",") : new String[0]; + + createAndRunJob(realmName, count, batch, start, prefix, async, "Create users", new UsersJobInitializer() { + + @Override + public UsersJob instantiateJob() { + return new CreateUsersJob(rolesArray); + } + + }); + } + + // Same as createUsers, but dynamically compute "start" (Next available user) + @GET + @Path("{realm}/create-available-users") + public void createAvailableUsers(@PathParam("realm") String realmName, @QueryParam("count") Integer count, + @QueryParam("batch") Integer batch, @QueryParam("prefix") String prefix, + @QueryParam("async") Boolean async, @QueryParam("roles") String roles) throws InterruptedException { + int start = getUsersCount(realmName, prefix); + createUsers(realmName, count, batch, start, prefix, async, roles); + } + + @GET + @Path("{realm}/delete-users") + public void deleteUsers(@PathParam("realm") String realmName, @QueryParam("count") Integer count, + @QueryParam("batch") Integer batch, @QueryParam("start") Integer start, @QueryParam("prefix") String prefix, + @QueryParam("async") Boolean async) throws InterruptedException { + createAndRunJob(realmName, count, batch, start, prefix, async, "Delete users", new UsersJobInitializer() { + + @Override + public UsersJob instantiateJob() { + return new DeleteUsersJob(); + } + + }); + } + + @GET + @Path("{realm}/delete-all-users") + public void deleteUsers(@PathParam("realm") String realmName, @QueryParam("prefix") String prefix, @QueryParam("async") Boolean async) throws InterruptedException { + int count = getUsersCount(realmName, prefix); + if (count == 0) { + return; + } + + int batch = count / 10; + if (batch == 0) { + batch = 1; + } + + deleteUsers(realmName, count, batch, 0, prefix, async); + } + + @GET + @Path("{realm}/update-users") + public void updateUsers(@PathParam("realm") String realmName, @QueryParam("count") Integer count, + @QueryParam("batch") Integer batch, @QueryParam("start") Integer start, @QueryParam("prefix") String prefix, + @QueryParam("async") Boolean async, @QueryParam("roles") String roles) throws InterruptedException { + final String[] rolesArray = roles != null ? roles.split(",") : new String[0]; + + createAndRunJob(realmName, count, batch, start, prefix, async, "Update users", new UsersJobInitializer() { + + @Override + public UsersJob instantiateJob() { + return new UpdateUsersJob(rolesArray); + } + + }); + } + + @GET + @Path("{realm}/update-all-users") + public void updateAllUsers(@PathParam("realm") String realmName, @QueryParam("prefix") String prefix, @QueryParam("async") Boolean async, + @QueryParam("roles") String roles) throws InterruptedException { + int count = getUsersCount(realmName, prefix); + if (count == 0) { + return; + } + + int batch = count / 10; + if (batch == 0) { + batch = 1; + } + + updateUsers(realmName, count, batch, 0, prefix, async, roles); + } + + + @GET + @Path("{realm}/get-users-count") + public Response getUsersCountReq(@PathParam("realm") String realmName, @QueryParam("prefix") String prefix) { + int usersCount = getUsersCount(realmName, prefix); + return Response.ok(String.valueOf(usersCount)).build(); + } + + private int getUsersCount(String realmName, String prefix) { + RealmModel realm = session.getRealmByName(realmName); + + // TODO: method for count on model + if (prefix == null) { + return realm.getUsers().size(); + } else { + return realm.searchForUser(prefix).size(); + } + } + + private void createAndRunJob(String realmName, Integer count, Integer batch, Integer start, String prefix, Boolean async, String jobName, UsersJobInitializer initializer) throws InterruptedException { if (count == null) { count = 1; } @@ -81,51 +193,34 @@ public class PerfTools { if (prefix == null) { prefix = String.valueOf(System.currentTimeMillis()); } + if (async == null) { + async = true; + } - String[] rolesArray = roles != null ? roles.split(",") : new String[0]; + int executorsCount = count / batch; + if (count % batch > 0) { + executorsCount++; + } + CountDownLatch latch = new CountDownLatch(executorsCount); - Job job = new Job("Create users " + prefix + "-" + start + " to " + prefix + "-" + (start + count), count); + JobRepresentation job = new JobRepresentation(jobName + " " + prefix + "-" + start + " to " + prefix + "-" + (start + count), count); jobs.add(job); + List usersJobs = new ArrayList(); for (int s = start; s < (start + count); s += batch) { int c = s + batch <= (start + count) ? batch : (start + count) - s; - executor.submit(new CreateUsers(job, sessionFactory, realmName, s, c, prefix, rolesArray)); + UsersJob usersJob = initializer.instantiateJob(); + usersJob.init(job, sessionFactory, realmName, s, c, prefix, latch); + usersJobs.add(usersJob); } - } - @GET - @Path("{realm}/delete-users") - public void deleteUsers(@PathParam("realm") String realmName) { - RealmModel realm = session.getRealmByName(realmName); - for (UserModel user : realm.getUsers()) { - realm.removeUser(user.getUsername()); + // Run executors once all are initialized + for (UsersJob usersJob : usersJobs) { + executor.submit(usersJob); } - } - - @GET - @Path("{realm}/get-users-count") - public Response getUsersCountReq(@PathParam("realm") String realmName, @QueryParam("prefix") String prefix) { - int usersCount = getUsersCount(realmName, prefix); - return Response.ok(String.valueOf(usersCount)).build(); - } - - // Same as createUsers, but dynamically compute "start" (Next available user) - @GET - @Path("{realm}/create-available-users") - public void createAvailableUsers(@PathParam("realm") String realmName, @QueryParam("count") Integer count, @QueryParam("batch") Integer batch, @QueryParam("prefix") String prefix, @QueryParam("roles") String roles) throws InterruptedException { - int start = getUsersCount(realmName, prefix); - createUsers(realmName, count, batch, start, prefix, roles); - } - - private int getUsersCount(String realmName, String prefix) { - RealmModel realm = session.getRealmByName(realmName); - - // TODO: method for count on model - if (prefix == null) { - return realm.getUsers().size(); - } else { - return realm.searchForUser(prefix).size(); + if (!async) { + latch.await(); } } @@ -146,7 +241,7 @@ public class PerfTools { } } - public class Job { + public static class JobRepresentation { private final String description; private final int total; private AtomicInteger count = new AtomicInteger(); @@ -154,7 +249,7 @@ public class PerfTools { private AtomicLong started = new AtomicLong(); private AtomicLong completed = new AtomicLong(); - public Job(String description, int total) { + public JobRepresentation(String description, int total) { this.description = description; this.total = total; } diff --git a/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/CreateUsers.java b/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/CreateUsers.java deleted file mode 100644 index 87a932aefa..0000000000 --- a/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/CreateUsers.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.keycloak.test.tools.jobs; - -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserCredentialModel; -import org.keycloak.models.UserModel; -import org.keycloak.services.managers.RealmManager; -import org.keycloak.test.tools.PerfTools; - -import java.io.PrintWriter; -import java.io.StringWriter; - -/** - * @author Stian Thorgersen - */ -public class CreateUsers implements Runnable { - - private PerfTools.Job job; - private final KeycloakSessionFactory sessionFactory; - private final String realmName; - private int start; - private int count; - private String prefix; - private String[] roles; - - public CreateUsers(PerfTools.Job job, KeycloakSessionFactory sessionFactory, String realmName, int start, int count, String prefix, String[] roles) { - this.job = job; - this.sessionFactory = sessionFactory; - this.realmName = realmName; - this.start = start; - this.count = count; - this.prefix = prefix; - this.roles = roles; - } - - @Override - public void run() { - job.start(); - - KeycloakSession session = sessionFactory.create(); - try { - session.getTransaction().begin(); - - RealmModel realm = new RealmManager(session).getRealmByName(realmName); - - for (int i = start; i < (start + count); i++) { - UserModel user = realm.addUser(prefix + "-" + i); - user.setEnabled(true); - user.setFirstName("First"); - user.setLastName("Last"); - user.setEmail(prefix + "-" + i + "@localhost"); - - UserCredentialModel password = new UserCredentialModel(); - password.setType(UserCredentialModel.PASSWORD); - password.setValue("password"); - - user.updateCredential(password); - - for (String r : roles) { - user.grantRole(realm.getRole(r)); - } - - job.increment(); - } - - session.getTransaction().commit(); - } catch (Throwable t) { - StringWriter sw = new StringWriter(); - t.printStackTrace(new PrintWriter(sw)); - job.setError(sw.toString()); - } finally { - session.close(); - } - } - -} diff --git a/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/CreateUsersJob.java b/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/CreateUsersJob.java new file mode 100644 index 0000000000..b00f08fbf2 --- /dev/null +++ b/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/CreateUsersJob.java @@ -0,0 +1,50 @@ +package org.keycloak.test.tools.jobs; + +import org.keycloak.models.ApplicationModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.UserCredentialModel; +import org.keycloak.models.UserModel; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CountDownLatch; + +/** + * @author Stian Thorgersen + */ +public class CreateUsersJob extends UsersJob { + + private String[] roles; + + public CreateUsersJob(String[] roles) { + this.roles = roles; + } + + @Override + protected void before(KeycloakSession session) { + } + + @Override + protected void runIteration(RealmModel realm, Map apps, Set realmRoles, Map> appRoles, int counter) { + String username = prefix + "-" + counter; + UserModel user = realm.addUser(username); + user.setEnabled(true); + user.setFirstName("First"); + user.setLastName("Last"); + user.setEmail(username + "@localhost"); + + UserCredentialModel password = new UserCredentialModel(); + password.setType(UserCredentialModel.PASSWORD); + password.setValue("password"); + + user.updateCredential(password); + + for (String r : roles) { + grantRole(user, r, realmRoles, appRoles); + } + } + +} diff --git a/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/DeleteUsersJob.java b/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/DeleteUsersJob.java new file mode 100644 index 0000000000..0f98cb4b93 --- /dev/null +++ b/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/DeleteUsersJob.java @@ -0,0 +1,38 @@ +package org.keycloak.test.tools.jobs; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.keycloak.models.ApplicationModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.UserModel; +import org.keycloak.services.managers.RealmManager; + +/** + * @author Marek Posolda + */ +public class DeleteUsersJob extends UsersJob { + + private Iterator users; + + @Override + protected void before(KeycloakSession session) { + RealmModel realm = new RealmManager(session).getRealmByName(realmName); + + // TODO: pagination + List users = (prefix==null) ? realm.getUsers() : realm.searchForUser(prefix); + users = users.subList(start, start + count); + + this.users = users.iterator(); + } + + @Override + protected void runIteration(RealmModel realm, Map apps, Set realmRoles, Map> appRoles, int counter) { + String username = users.next().getUsername(); + realm.removeUser(username); + } +} diff --git a/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/UpdateUsersJob.java b/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/UpdateUsersJob.java new file mode 100644 index 0000000000..d0a1af99b4 --- /dev/null +++ b/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/UpdateUsersJob.java @@ -0,0 +1,54 @@ +package org.keycloak.test.tools.jobs; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.keycloak.models.ApplicationModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.UserModel; +import org.keycloak.services.managers.RealmManager; + +/** + * @author Marek Posolda + */ +public class UpdateUsersJob extends UsersJob { + + private String[] roles; + private Iterator users; + + public UpdateUsersJob(String[] roles) { + this.roles = roles; + } + + @Override + protected void before(KeycloakSession session) { + RealmModel realm = new RealmManager(session).getRealmByName(realmName); + + // TODO: pagination + List users = (prefix==null) ? realm.getUsers() : realm.searchForUser(prefix); + users = users.subList(start, start + count); + + this.users = users.iterator(); + } + + @Override + protected void runIteration(RealmModel realm, Map apps, Set realmRoles, Map> appRoles, int counter) { + String username = users.next().getUsername(); + + // Remove all role mappings first + UserModel user = realm.getUser(username); + Set currRoles = user.getRoleMappings(); + for (RoleModel role : currRoles) { + user.deleteRoleMapping(role); + } + + // Add new roles now + for (String r : roles) { + grantRole(user, r, realmRoles, appRoles); + } + } +} diff --git a/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/UsersJob.java b/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/UsersJob.java new file mode 100644 index 0000000000..efc598ab55 --- /dev/null +++ b/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/UsersJob.java @@ -0,0 +1,126 @@ +package org.keycloak.test.tools.jobs; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CountDownLatch; + +import org.keycloak.models.ApplicationModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.UserModel; +import org.keycloak.services.managers.RealmManager; +import org.keycloak.test.tools.PerfTools; + +/** + * @author Marek Posolda + */ +public abstract class UsersJob implements Runnable { + + protected PerfTools.JobRepresentation job; + protected KeycloakSessionFactory sessionFactory; + protected String realmName; + protected int start; + protected int count; + protected String prefix; + protected CountDownLatch latch; + + public void init(PerfTools.JobRepresentation job, KeycloakSessionFactory sessionFactory, String realmName, int start, int count, String prefix, CountDownLatch latch) { + this.sessionFactory = sessionFactory; + this.realmName = realmName; + this.start = start; + this.count = count; + this.prefix = prefix; + this.job = job; + this.latch = latch; + + KeycloakSession session = sessionFactory.create(); + try { + session.getTransaction().begin(); + + before(session); + + session.getTransaction().commit(); + } catch (Throwable t) { + handleThrowable(t, session); + } finally { + session.close(); + } + } + + @Override + public void run() { + job.start(); + + KeycloakSession session = sessionFactory.create(); + try { + session.getTransaction().begin(); + + RealmModel realm = new RealmManager(session).getRealmByName(realmName); + Map apps = realm.getApplicationNameMap(); + + Set realmRoles = realm.getRoles(); + Map> appRoles = new HashMap>(); + for (Map.Entry appEntry : apps.entrySet()) { + appRoles.put(appEntry.getKey(), appEntry.getValue().getRoles()); + } + + for (int i = start; i < (start + count); i++) { + runIteration(realm, apps, realmRoles, appRoles, i); + job.increment(); + } + + session.getTransaction().commit(); + } catch (Throwable t) { + handleThrowable(t, session); + } finally { + latch.countDown(); + session.close(); + } + + } + + protected abstract void before(KeycloakSession keycloakSession); + + protected abstract void runIteration(RealmModel realm, Map apps, Set realmRoles, Map> appRoles, int counter); + + protected RoleModel findRole(Set roles, String roleName) { + for (RoleModel role : roles) { + if (role.getName().equals(roleName)) { + return role; + } + } + + return null; + } + + protected void grantRole(UserModel user, String roleName, Set realmRoles, Map> appRoles) { + if (roleName.indexOf(':') == -1) { + // We expect "realmRoleName" + RoleModel realmRole = findRole(realmRoles, roleName); + user.grantRole(realmRole); + } else { + // We expect "appName:appRoleName" + String[] parts = roleName.split(":"); + Set currentAppRoles = appRoles.get(parts[0]); + if (currentAppRoles == null) { + throw new IllegalStateException("Application '" + parts[0] + "' not found"); + } + + RoleModel appRole = findRole(currentAppRoles, parts[1]); + user.grantRole(appRole); + } + } + + private void handleThrowable(Throwable t, KeycloakSession session) { + StringWriter sw = new StringWriter(); + t.printStackTrace(new PrintWriter(sw)); + job.setError(sw.toString()); + session.getTransaction().rollback(); + } + +} diff --git a/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/UsersJobInitializer.java b/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/UsersJobInitializer.java new file mode 100644 index 0000000000..d999856d5d --- /dev/null +++ b/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/UsersJobInitializer.java @@ -0,0 +1,9 @@ +package org.keycloak.test.tools.jobs; + +/** + * @author Marek Posolda + */ +public interface UsersJobInitializer { + + UsersJob instantiateJob(); +}