keycloak-uncached
Changes
export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportConfig.java 4(+4 -0)
testsuite/pom.xml 1(+1 -0)
testsuite/tools/pom.xml 309(+309 -0)
testsuite/tools/src/main/webapp/index.html 32(+32 -0)
testsuite/tools/src/main/webapp/js/app.js 49(+49 -0)
Details
diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportConfig.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportConfig.java
index d87b55c..f21d4bd 100644
--- a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportConfig.java
+++ b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportConfig.java
@@ -35,6 +35,10 @@ public class ExportImportConfig {
return System.getProperty(DIR);
}
+ public static String setDir(String dir) {
+ return System.setProperty(DIR, dir);
+ }
+
public static String getZipFile() {
return System.getProperty(FILE);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
index 644dfbf..af42022 100755
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -104,7 +104,7 @@ public class KeycloakApplication extends Application {
return uriInfo.getBaseUriBuilder().replacePath(getContextPath()).build();
}
- protected void loadConfig() {
+ public static void loadConfig() {
try {
URL config = null;
testsuite/pom.xml 1(+1 -0)
diff --git a/testsuite/pom.xml b/testsuite/pom.xml
index 766e611..36085e7 100755
--- a/testsuite/pom.xml
+++ b/testsuite/pom.xml
@@ -27,6 +27,7 @@
<modules>
<module>integration</module>
<module>performance</module>
+ <module>tools</module>
</modules>
</project>
testsuite/tools/pom.xml 309(+309 -0)
diff --git a/testsuite/tools/pom.xml b/testsuite/tools/pom.xml
new file mode 100755
index 0000000..e6e0f79
--- /dev/null
+++ b/testsuite/tools/pom.xml
@@ -0,0 +1,309 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.0-beta-4-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>keycloak-testsuite-tools</artifactId>
+ <packaging>war</packaging>
+ <name>Keycloak Testsuite Tools</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>net.iharder</groupId>
+ <artifactId>base64</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core-jaxrs</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-services</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.zxing</groupId>
+ <artifactId>javase</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-invalidation-cache-model</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-jpa</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-audit-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-audit-jpa</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-audit-jboss-logging</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-audit-email</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <!-- social -->
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-social-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-social-github</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-social-google</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-social-twitter</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.twitter4j</groupId>
+ <artifactId>twitter4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-social-facebook</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <!-- forms -->
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-forms-common-freemarker</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.freemarker</groupId>
+ <artifactId>freemarker</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-forms-common-themes</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-account-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-account-freemarker</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-email-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-email-freemarker</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-login-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-login-freemarker</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-js-adapter</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <!-- authentication api -->
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-authentication-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-authentication-model</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-authentication-picketlink</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.picketlink</groupId>
+ <artifactId>picketlink-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.picketlink</groupId>
+ <artifactId>picketlink-idm-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.picketlink</groupId>
+ <artifactId>picketlink-idm-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.picketlink</groupId>
+ <artifactId>picketlink-idm-simple-schema</artifactId>
+ </dependency>
+
+ <!-- timer -->
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-timer-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-timer-basic</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- picketlink -->
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-picketlink-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-picketlink-realm</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.servlet</groupId>
+ <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <!-- resteasy -->
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jaxrs</artifactId>
+ <version>${resteasy.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-multipart-provider</artifactId>
+ <version>${resteasy.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>async-http-servlet-3.0</artifactId>
+ <version>${resteasy.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>jaxrs-api</artifactId>
+ <version>${resteasy.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jackson-provider</artifactId>
+ <version>${resteasy.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- Mongo dependencies -->
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-mongo</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-audit-mongo</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mongodb</groupId>
+ <artifactId>mongo-java-driver</artifactId>
+ </dependency>
+
+ <!-- export/import -->
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-export-import-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-export-import-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>de.idyl</groupId>
+ <artifactId>winzipaes</artifactId>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <finalName>keycloak-tools</finalName>
+ <plugins>
+ <plugin>
+ <groupId>org.jboss.as.plugins</groupId>
+ <artifactId>jboss-as-maven-plugin</artifactId>
+ <configuration>
+ <skip>false</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.wildfly.plugins</groupId>
+ <artifactId>wildfly-maven-plugin</artifactId>
+ <configuration>
+ <skip>false</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
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
new file mode 100644
index 0000000..2191e6c
--- /dev/null
+++ b/testsuite/tools/src/main/java/org/keycloak/test/tools/jobs/CreateUsers.java
@@ -0,0 +1,81 @@
+package org.keycloak.test.tools.jobs;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.cache.CacheKeycloakSession;
+import org.keycloak.provider.ProviderSession;
+import org.keycloak.provider.ProviderSessionFactory;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.test.tools.PerfTools;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class CreateUsers implements Runnable {
+
+ private PerfTools.Job job;
+ private final ProviderSessionFactory providerSessionFactory;
+ private final String realmName;
+ private int start;
+ private int count;
+ private String prefix;
+ private String[] roles;
+
+ public CreateUsers(PerfTools.Job job, ProviderSessionFactory providerSessionFactory, String realmName, int start, int count, String prefix, String[] roles) {
+ this.job = job;
+ this.providerSessionFactory = providerSessionFactory;
+ this.realmName = realmName;
+ this.start = start;
+ this.count = count;
+ this.prefix = prefix;
+ this.roles = roles;
+ }
+
+ @Override
+ public void run() {
+ job.start();
+
+ ProviderSession providerSession = providerSessionFactory.createSession();
+ try {
+ KeycloakSession session = providerSession.getProvider(CacheKeycloakSession.class);
+
+ 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 {
+ providerSession.close();
+ }
+ }
+
+}
diff --git a/testsuite/tools/src/main/java/org/keycloak/test/tools/KeycloakTestApplication.java b/testsuite/tools/src/main/java/org/keycloak/test/tools/KeycloakTestApplication.java
new file mode 100644
index 0000000..b8dcf4e
--- /dev/null
+++ b/testsuite/tools/src/main/java/org/keycloak/test/tools/KeycloakTestApplication.java
@@ -0,0 +1,42 @@
+package org.keycloak.test.tools;
+
+import org.jboss.resteasy.core.Dispatcher;
+import org.keycloak.provider.ProviderSessionFactory;
+import org.keycloak.services.resources.KeycloakApplication;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class KeycloakTestApplication extends Application {
+
+ protected ProviderSessionFactory providerSessionFactory;
+ protected Set<Class<?>> classes = new HashSet<Class<?>>();
+ protected Set<Object> singletons = new HashSet<Object>();
+
+ public KeycloakTestApplication(@Context ServletContext context, @Context Dispatcher dispatcher) {
+ KeycloakApplication.loadConfig();
+
+ this.providerSessionFactory = KeycloakApplication.createProviderSessionFactory();
+
+ context.setAttribute(ProviderSessionFactory.class.getName(), this.providerSessionFactory);
+
+ singletons.add(new PerfTools(providerSessionFactory));
+ }
+
+ @Override
+ public Set<Class<?>> getClasses() {
+ return classes;
+ }
+
+ @Override
+ public Set<Object> getSingletons() {
+ return singletons;
+ }
+
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..318f7ea
--- /dev/null
+++ b/testsuite/tools/src/main/java/org/keycloak/test/tools/PerfTools.java
@@ -0,0 +1,178 @@
+package org.keycloak.test.tools;
+
+import org.keycloak.exportimport.ExportImportConfig;
+import org.keycloak.exportimport.ExportImportProvider;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.provider.ProviderSession;
+import org.keycloak.provider.ProviderSessionFactory;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.test.tools.jobs.CreateUsers;
+import org.keycloak.util.ProviderLoader;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+@Path("perf")
+public class PerfTools {
+
+ private ExecutorService executor = Executors.newFixedThreadPool(20);
+
+ private final ProviderSessionFactory providerSessionFactory;
+
+ @Context
+ private KeycloakSession session;
+
+ private List<Job> jobs = new LinkedList<Job>();
+
+ public PerfTools(ProviderSessionFactory providerSessionFactory) {
+ this.providerSessionFactory = providerSessionFactory;
+ }
+
+ @GET
+ @Path("jobs")
+ @Produces("application/json")
+ public List<Job> jobs() {
+ return jobs;
+ }
+
+ @GET
+ @Path("delete-jobs")
+ public void deleteJobs() {
+ Iterator<Job> itr = jobs.iterator();
+ while(itr.hasNext()) {
+ Job j = itr.next();
+ if (j.getError() != null || j.getCount() == j.getTotal()) {
+ itr.remove();
+ }
+ }
+ }
+
+ @GET
+ @Path("{realm}/create-users")
+ public Response 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 {
+ if (count == null) {
+ count = 1;
+ }
+ if (batch == null) {
+ batch = 1000;
+ }
+ if (start == null) {
+ start = 0;
+ }
+ if (prefix == null) {
+ prefix = String.valueOf(System.currentTimeMillis());
+ }
+
+ String[] rolesArray = roles != null ? roles.split(",") : new String[0];
+
+ Job job = new Job("Create users " + prefix + "-" + start + " to " + prefix + "-" + (start + count), count);
+ jobs.add(job);
+
+ for (int s = start; s < (start + count); s += batch) {
+ int c = s + batch <= (start + count) ? batch : (start + count) - s;
+ executor.submit(new CreateUsers(job, providerSessionFactory, realmName, s, c, prefix, rolesArray));
+ }
+
+ return Response.noContent().build();
+ }
+
+ @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.getLoginName());
+ }
+ }
+
+ @GET
+ @Path("export")
+ public void export(@QueryParam("dir") String dir) {
+ ExportImportConfig.setAction("export");
+ ExportImportConfig.setProvider("dir");
+ ExportImportConfig.setDir(dir);
+
+ Iterator<ExportImportProvider> providers = ProviderLoader.load(ExportImportProvider.class).iterator();
+
+ if (providers.hasNext()) {
+ ExportImportProvider exportImport = providers.next();
+ exportImport.checkExportImport(providerSessionFactory);
+ } else {
+ throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ public class Job {
+ private final String description;
+ private final int total;
+ private AtomicInteger count = new AtomicInteger();
+ private String error;
+ private AtomicLong started = new AtomicLong();
+ private AtomicLong completed = new AtomicLong();
+
+ public Job(String description, int total) {
+ this.description = description;
+ this.total = total;
+ }
+
+ public Long getStarted() {
+ long s = started.get();
+ return s != 0 ? s : null;
+ }
+
+ public Long getCompleted() {
+ long c = completed.get();
+ return c != 0 ? c : null;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public int getTotal() {
+ return total;
+ }
+
+ public int getCount() {
+ return count.get();
+ }
+
+ public void start() {
+ started.compareAndSet(0, System.currentTimeMillis());
+ }
+
+ public void increment() {
+ if (count.incrementAndGet() == total) {
+ completed.set(System.currentTimeMillis());
+ }
+ }
+
+ public String getError() {
+ return error;
+ }
+
+ public void setError(String error) {
+ this.error = error;
+ }
+ }
+
+}
diff --git a/testsuite/tools/src/main/resources/META-INF/keycloak-server.json b/testsuite/tools/src/main/resources/META-INF/keycloak-server.json
new file mode 100755
index 0000000..fb726ca
--- /dev/null
+++ b/testsuite/tools/src/main/resources/META-INF/keycloak-server.json
@@ -0,0 +1,49 @@
+{
+ "admin": {
+ "realm": "master"
+ },
+
+ "audit": {
+ "provider": "jpa",
+ "jpa": {
+ "exclude-events": [ "REFRESH_TOKEN" ]
+ }
+ },
+
+ "model": {
+ "provider": "jpa"
+ },
+
+ "modelCache": {
+ "provider": "${keycloak.model.cache.provider:none}"
+ },
+
+ "timer": {
+ "provider": "basic"
+ },
+
+ "theme": {
+ "default": "keycloak",
+ "staticMaxAge": 2592000,
+ "cacheTemplates": true,
+ "folder": {
+ "dir": "${jboss.server.config.dir}/themes"
+ }
+ },
+
+ "login-forms": {
+ "provider": "freemarker"
+ },
+
+ "account": {
+ "provider": "freemarker"
+ },
+
+ "email": {
+ "provider": "freemarker"
+ },
+
+ "scheduled": {
+ "interval": 900
+ }
+}
\ No newline at end of file
diff --git a/testsuite/tools/src/main/resources/META-INF/persistence.xml b/testsuite/tools/src/main/resources/META-INF/persistence.xml
new file mode 100755
index 0000000..ee721fe
--- /dev/null
+++ b/testsuite/tools/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,41 @@
+<persistence xmlns="http://java.sun.com/xml/ns/persistence"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
+ version="1.0">
+ <persistence-unit name="jpa-keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
+ <jta-data-source>java:jboss/datasources/KeycloakDS</jta-data-source>
+ <class>org.keycloak.models.jpa.entities.ApplicationEntity</class>
+ <class>org.keycloak.models.jpa.entities.CredentialEntity</class>
+ <class>org.keycloak.models.jpa.entities.OAuthClientEntity</class>
+ <class>org.keycloak.models.jpa.entities.RealmEntity</class>
+ <class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>
+ <class>org.keycloak.models.jpa.entities.AuthenticationProviderEntity</class>
+ <class>org.keycloak.models.jpa.entities.RoleEntity</class>
+ <class>org.keycloak.models.jpa.entities.SocialLinkEntity</class>
+ <class>org.keycloak.models.jpa.entities.AuthenticationLinkEntity</class>
+ <class>org.keycloak.models.jpa.entities.UserEntity</class>
+ <class>org.keycloak.models.jpa.entities.UserSessionEntity</class>
+ <class>org.keycloak.models.jpa.entities.ClientUserSessionAssociationEntity</class>
+ <class>org.keycloak.models.jpa.entities.UsernameLoginFailureEntity</class>
+ <class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
+ <class>org.keycloak.models.jpa.entities.ScopeMappingEntity</class>
+
+ <exclude-unlisted-classes>true</exclude-unlisted-classes>
+
+ <properties>
+ <property name="hibernate.hbm2ddl.auto" value="update" />
+ </properties>
+ </persistence-unit>
+
+ <persistence-unit name="jpa-keycloak-audit-store" transaction-type="RESOURCE_LOCAL">
+ <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
+ <class>org.keycloak.audit.jpa.EventEntity</class>
+
+ <exclude-unlisted-classes>true</exclude-unlisted-classes>
+
+ <properties>
+ <property name="hibernate.hbm2ddl.auto" value="update" />
+ </properties>
+ </persistence-unit>
+
+</persistence>
testsuite/tools/src/main/webapp/index.html 32(+32 -0)
diff --git a/testsuite/tools/src/main/webapp/index.html b/testsuite/tools/src/main/webapp/index.html
new file mode 100644
index 0000000..19521ec
--- /dev/null
+++ b/testsuite/tools/src/main/webapp/index.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html lang="en">
+
+<head>
+ <meta charset="utf-8">
+ <title>Keycloak Testsuite Tools</title>
+
+ <link rel="stylesheet" href="/auth/admin/master/console/css/styles.css">
+
+ <script src="/auth/admin/master/console/lib/angular/angular.js"></script>
+ <script src="/auth/admin/master/console/lib/angular/angular-resource.js"></script>
+ <script src="/auth/admin/master/console/lib/angular/angular-route.js"></script>
+
+ <script src="js/app.js"></script>
+</head>
+
+<body data-ng-app="keycloak-tools">
+
+<header class="navbar navbar-default navbar-pf navbar-main header">
+
+ <ul class="nav navbar-nav navbar-primary persistent-secondary ng-scope">
+ <li><a href="#/">Home</a></li>
+ <li><a href="#/perf">Perf</a></li>
+ </ul>
+</header>
+
+
+
+<div style="background-color: #fff; margin: 2em; padding: 2em;" data-ng-view id="view"></div>
+
+</body>
+
testsuite/tools/src/main/webapp/js/app.js 49(+49 -0)
diff --git a/testsuite/tools/src/main/webapp/js/app.js b/testsuite/tools/src/main/webapp/js/app.js
new file mode 100644
index 0000000..76e4f96
--- /dev/null
+++ b/testsuite/tools/src/main/webapp/js/app.js
@@ -0,0 +1,49 @@
+var module = angular.module('keycloak-tools', [ 'ngRoute', 'ngResource' ]);
+
+module.config([ '$routeProvider', function ($routeProvider) {
+
+ $routeProvider
+ .when('/perf', {
+ templateUrl: 'pages/perf.html',
+ controller: 'PerfCtrl'
+ })
+ .otherwise({
+ templateUrl: 'pages/home.html'
+ });
+}]);
+
+module.filter('reverse', function() {
+ return function(items) {
+ return items.slice().reverse();
+ };
+});
+
+module.controller('PerfCtrl', function ($scope, $resource) {
+
+ $scope.createUsersData = {
+ realm: 'test',
+ count: 100,
+ start: 0,
+ batch: 25
+ }
+
+ $scope.loadJobs = function() {
+ $scope.jobs = $resource('/keycloak-tools/perf/jobs').query();
+ }
+
+ $scope.clearJobs = function() {
+ $scope.jobs = $resource('/keycloak-tools/perf/delete-jobs').query({}, function() {
+ $scope.loadJobs();
+ });
+ }
+
+ $scope.createUsers = function() {
+ console.debug($scope.createUsersData);
+ $resource('/keycloak-tools/perf/:realm/create-users').get($scope.createUsersData, function() {
+ $scope.loadJobs();
+ });
+ }
+
+ $scope.loadJobs();
+
+});
diff --git a/testsuite/tools/src/main/webapp/pages/home.html b/testsuite/tools/src/main/webapp/pages/home.html
new file mode 100644
index 0000000..357dd48
--- /dev/null
+++ b/testsuite/tools/src/main/webapp/pages/home.html
@@ -0,0 +1 @@
+Home
\ No newline at end of file
diff --git a/testsuite/tools/src/main/webapp/pages/perf.html b/testsuite/tools/src/main/webapp/pages/perf.html
new file mode 100644
index 0000000..84a3023
--- /dev/null
+++ b/testsuite/tools/src/main/webapp/pages/perf.html
@@ -0,0 +1,67 @@
+<h1>Perf</h1>
+
+<fieldset>
+ <legend><span class="text">Jobs</span></legend>
+ <button data-ng-click="loadJobs()" class="btn btn-default">Refresh</button>
+ <button data-ng-click="clearJobs()" class="btn btn-default">Clear completed</button>
+
+ <table class="table table-striped table-bordered">
+ <thead>
+ <tr>
+ <th>Description</th>
+ <th>Error</th>
+ <th>Count</th>
+ <th>Total</th>
+ <th>Started</th>
+ <th>Completed</th>
+ </tr>
+ </thead>
+ <tr data-ng-repeat="j in jobs|reverse">
+ <td>{{j.description}}</td>
+ <td>{{j.error}}</td>
+ <td>{{j.count}}</td>
+ <td>{{j.total}}</td>
+ <td>{{j.started|date:'medium'}}</td>
+ <td>{{j.completed|date:'medium'}}</td>
+ </tr>
+ </table>
+</fieldset>
+
+<fieldset>
+ <legend><span class="text">Create users</span></legend>
+
+ <div class="alert alert-info"><span class="pficon pficon-info"></span> Create users with username <code><prefix>-<start></code> to <code><prefix>-<start + count></code> and optionally add role mappings for the specified realm roles.</div>
+
+ <form class="form-horizontal">
+ <div class="form-group">
+ <label class="control-label col-sm-1">Realm</label>
+ <input data-ng-model="createUsersData.realm" value="test">
+ </div>
+ <div class="form-group">
+ <label class="control-label col-sm-1">Count</label>
+ <input data-ng-model="createUsersData.count" type="number" min="1" max="100000">
+ <span>(number of users to create)</span>
+ </div>
+ <div class="form-group">
+ <label class="control-label col-sm-1">Batch</label>
+ <input data-ng-model="createUsersData.batch" type="number" min="1" max="100">
+ <span>(number of users to create in one transaction)</span>
+ </div>
+ <div class="form-group">
+ <label class="control-label col-sm-1">Start</label>
+ <input data-ng-model="createUsersData.start" type="number" min="0">
+ </div>
+ <div class="form-group">
+ <label class="control-label col-sm-1">Prefix</label>
+ <input data-ng-model="createUsersData.prefix" type="number" min="0">
+ <span>(optional, by default currentTimeMillis is used)</span>
+ </div>
+ <div class="form-group">
+ <label class="control-label col-sm-1">Roles</label>
+ <input data-ng-model="createUsersData.roles" type="number" min="0">
+ <span>(comma separated list of realm roles)</span>
+ </div>
+
+ <button data-ng-click="createUsers()" class="btn btn-default">Create</button>
+ </form>
+</fieldset>
\ No newline at end of file
diff --git a/testsuite/tools/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/testsuite/tools/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
new file mode 100755
index 0000000..0925383
--- /dev/null
+++ b/testsuite/tools/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
@@ -0,0 +1,20 @@
+<jboss-deployment-structure>
+ <deployment>
+ <dependencies>
+ <module name="org.apache.httpcomponents"/>
+ <module name="org.bouncycastle"/>
+ <module name="org.jboss.resteasy.resteasy-jackson-provider" services="import"/>
+ <module name="org.codehaus.jackson.jackson-core-asl"/>
+ <module name="org.codehaus.jackson.jackson-mapper-asl"/>
+ </dependencies>
+ <exclusions>
+ <module name="org.jboss.resteasy.resteasy-jackson2-provider"/>
+
+ <!-- Exclude keycloak modules -->
+ <module name="org.keycloak.keycloak-core" />
+ <module name="org.keycloak.keycloak-adapter-core" />
+ <module name="org.keycloak.keycloak-undertow-adapter" />
+ <module name="org.keycloak.keycloak-as7-adapter" />
+ </exclusions>
+ </deployment>
+</jboss-deployment-structure>
\ No newline at end of file
diff --git a/testsuite/tools/src/main/webapp/WEB-INF/web.xml b/testsuite/tools/src/main/webapp/WEB-INF/web.xml
new file mode 100755
index 0000000..aea2f9c
--- /dev/null
+++ b/testsuite/tools/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0">
+
+ <module-name>keycloak-tools</module-name>
+
+ <servlet>
+ <servlet-name>Keycloak REST Interface</servlet-name>
+ <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServlet30Dispatcher</servlet-class>
+ <init-param>
+ <param-name>javax.ws.rs.Application</param-name>
+ <param-value>org.keycloak.test.tools.KeycloakTestApplication</param-value>
+ </init-param>
+ <init-param>
+ <param-name>resteasy.servlet.mapping.prefix</param-name>
+ <param-value>/</param-value>
+ </init-param>
+ <load-on-startup>1</load-on-startup>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <welcome-file-list>
+ <welcome-file>index.html</welcome-file>
+ </welcome-file-list>
+
+ <listener>
+ <listener-class>org.keycloak.services.listeners.KeycloakSessionDestroyListener</listener-class>
+ </listener>
+
+ <filter>
+ <filter-name>Keycloak Client Connection Filter</filter-name>
+ <filter-class>org.keycloak.services.filters.ClientConnectionFilter</filter-class>
+ </filter>
+
+ <filter>
+ <filter-name>Keycloak Session Management</filter-name>
+ <filter-class>org.keycloak.services.filters.KeycloakSessionServletFilter</filter-class>
+ </filter>
+
+ <filter-mapping>
+ <filter-name>Keycloak Session Management</filter-name>
+ <url-pattern>/perf/*</url-pattern>
+ </filter-mapping>
+
+ <filter-mapping>
+ <filter-name>Keycloak Client Connection Filter</filter-name>
+ <url-pattern>/perf/*</url-pattern>
+ </filter-mapping>
+
+ <servlet-mapping>
+ <servlet-name>Keycloak REST Interface</servlet-name>
+ <url-pattern>/perf/*</url-pattern>
+ </servlet-mapping>
+
+ <!--
+
+ <security-constraint>
+ <web-resource-collection>
+ <url-pattern>/*</url-pattern>
+ </web-resource-collection>
+ <user-data-constraint>
+ <transport-guarantee>CONFIDENTIAL</transport-guarantee>
+ </user-data-constraint>
+ </security-constraint>
+ -->
+
+
+</web-app>