keycloak-aplcache
Changes
integration/admin-client/pom.xml 65(+65 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ApplicationResource.java 90(+90 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ApplicationsResource.java 27(+27 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/BasicAuthFilter.java 31(+31 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/BearerAuthFilter.java 25(+25 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/KeycloakAdminFactory.java 27(+27 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/OAuthClientResource.java 49(+49 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/OAuthClientsResource.java 25(+25 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java 35(+35 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmsResource.java 32(+32 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleMappingResource.java 24(+24 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleResource.java 51(+51 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleScopeResource.java 33(+33 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RolesResource.java 26(+26 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java 61(+61 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UsersResource.java 29(+29 -0)
integration/pom.xml 1(+1 -0)
testsuite/integration/pom.xml 5(+5 -0)
Details
integration/admin-client/pom.xml 65(+65 -0)
diff --git a/integration/admin-client/pom.xml b/integration/admin-client/pom.xml
new file mode 100644
index 0000000..ca53e39
--- /dev/null
+++ b/integration/admin-client/pom.xml
@@ -0,0 +1,65 @@
+<?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/xsd/maven-4.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-admin-client</artifactId>
+ <name>Keycloak Admin REST Client</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>${keycloak.apache.httpcomponents.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>jaxrs-api</artifactId>
+ <version>${resteasy.version.latest}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jaxrs</artifactId>
+ <version>${resteasy.version.latest}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-client</artifactId>
+ <version>${resteasy.version.latest}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jackson-provider</artifactId>
+ <version>${resteasy.version.latest}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>${maven.compiler.source}</source>
+ <target>${maven.compiler.target}</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/Config.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/Config.java
new file mode 100644
index 0000000..47285d6
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/Config.java
@@ -0,0 +1,76 @@
+package org.keycloak.admin.client;
+
+/**
+ * @author rodrigo.sasaki@icarros.com.br
+ */
+public class Config {
+
+ private String serverUrl;
+ private String realm;
+ private String username;
+ private String password;
+ private String clientId;
+ private String clientSecret;
+
+ public Config(String serverUrl, String realm, String username, String password, String clientId, String clientSecret) {
+ this.serverUrl = serverUrl;
+ this.realm = realm;
+ this.username = username;
+ this.password = password;
+ this.clientId = clientId;
+ this.clientSecret = clientSecret;
+ }
+
+ public String getServerUrl() {
+ return serverUrl;
+ }
+
+ public void setServerUrl(String serverUrl) {
+ this.serverUrl = serverUrl;
+ }
+
+ public String getRealm() {
+ return realm;
+ }
+
+ public void setRealm(String realm) {
+ this.realm = realm;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getClientId() {
+ return clientId;
+ }
+
+ public void setClientId(String clientId) {
+ this.clientId = clientId;
+ }
+
+ public String getClientSecret() {
+ return clientSecret;
+ }
+
+ public void setClientSecret(String clientSecret) {
+ this.clientSecret = clientSecret;
+ }
+
+ public boolean isPublicClient(){
+ return clientSecret == null;
+ }
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java
new file mode 100644
index 0000000..7f10374
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java
@@ -0,0 +1,50 @@
+package org.keycloak.admin.client;
+
+import org.jboss.resteasy.client.jaxrs.ResteasyClient;
+import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
+import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
+import org.keycloak.admin.client.resource.BearerAuthFilter;
+import org.keycloak.admin.client.resource.KeycloakAdminFactory;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.admin.client.resource.RealmsResource;
+import org.keycloak.admin.client.token.TokenManager;
+
+/**
+ * @author rodrigo.sasaki@icarros.com.br
+ */
+public class Keycloak {
+
+ private final Config config;
+ private final TokenManager tokenManager;
+
+ private Keycloak(String serverUrl, String realm, String username, String password, String clientId, String clientSecret){
+ config = new Config(serverUrl, realm, username, password, clientId, clientSecret);
+ tokenManager = new TokenManager(config);
+ }
+
+ public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId, String clientSecret){
+ return new Keycloak(serverUrl, realm, username, password, clientId, clientSecret);
+ }
+
+ public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId){
+ return new Keycloak(serverUrl, realm, username, password, clientId, null);
+ }
+
+ public RealmsResource realms(){
+ ResteasyClient client = new ResteasyClientBuilder().build();
+ ResteasyWebTarget target = client.target(config.getServerUrl());
+
+ target.register(new BearerAuthFilter(tokenManager.getAccessTokenString()));
+
+ return target.proxy(RealmsResource.class);
+ }
+
+ public RealmResource realm(String realmName){
+ return realms().realm(realmName);
+ }
+
+ public TokenManager tokenManager(){
+ return tokenManager;
+ }
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ApplicationResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ApplicationResource.java
new file mode 100644
index 0000000..cca4442
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ApplicationResource.java
@@ -0,0 +1,90 @@
+package org.keycloak.admin.client.resource;
+
+import org.keycloak.representations.idm.ApplicationRepresentation;
+import org.keycloak.representations.idm.ClaimRepresentation;
+import org.keycloak.representations.idm.CredentialRepresentation;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import java.util.Set;
+
+/**
+ * @author rodrigo.sasaki@icarros.com.br
+ */
+public interface ApplicationResource {
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public ApplicationRepresentation toRepresentation();
+
+ @PUT
+ @Consumes(MediaType.APPLICATION_JSON)
+ public void update(ApplicationRepresentation applicationRepresentation);
+
+ @DELETE
+ public void remove();
+
+ @GET
+ @Path("allowed-origins")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Set<String> getAllowedOrigins();
+
+ @PUT
+ @Path("allowed-origins")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public void updateAllowedOrigins(Set<String> allowedOrigins);
+
+ @DELETE
+ @Path("allowed-origins")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public void removeAllowedOrigins(Set<String> originsToRemove);
+
+ @GET
+ @Path("claims")
+ @Produces(MediaType.APPLICATION_JSON)
+ public ClaimRepresentation getClaims();
+
+ @PUT
+ @Path("claims")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public void updateClaims(ClaimRepresentation claimRepresentation);
+
+ @POST
+ @Path("client-secret")
+ @Produces(MediaType.APPLICATION_JSON)
+ public CredentialRepresentation generateNewSecret();
+
+ @GET
+ @Path("client-secret")
+ @Produces(MediaType.APPLICATION_JSON)
+ public CredentialRepresentation getSecret();
+
+ @GET
+ @Path("installation/jboss")
+ @Produces(MediaType.APPLICATION_XML)
+ public String getInstallationJbossXml();
+
+ @GET
+ @Path("installation/json")
+ @Produces(MediaType.APPLICATION_JSON)
+ public String getInstallationJson();
+
+ @POST
+ @Path("logout-all")
+ public void logoutAllUsers();
+
+ @POST
+ @Path("logout-user/{username}")
+ public void logoutUser(@PathParam("username") String username);
+
+ @POST
+ @Path("push-revocation")
+ public void pushRevocation();
+
+ @Path("/scope-mappings")
+ public RoleMappingResource getScopeMappings();
+
+ @Path("/roles")
+ public RolesResource roles();
+
+}
\ No newline at end of file
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ApplicationsResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ApplicationsResource.java
new file mode 100644
index 0000000..8fd4800
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ApplicationsResource.java
@@ -0,0 +1,27 @@
+package org.keycloak.admin.client.resource;
+
+import org.keycloak.representations.idm.ApplicationRepresentation;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import java.util.List;
+
+/**
+ * @author rodrigo.sasaki@icarros.com.br
+ */
+public interface ApplicationsResource {
+
+ @Path("{appName}")
+ public ApplicationResource get(@PathParam("appName") String appName);
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ public void create(ApplicationRepresentation applicationRepresentation);
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<ApplicationRepresentation> findAll();
+
+
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/BasicAuthFilter.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/BasicAuthFilter.java
new file mode 100644
index 0000000..323b1a7
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/BasicAuthFilter.java
@@ -0,0 +1,31 @@
+package org.keycloak.admin.client.resource;
+
+import org.jboss.resteasy.util.Base64;
+
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.core.HttpHeaders;
+import java.io.IOException;
+
+/**
+ * @author rodrigo.sasaki@icarros.com.br
+ */
+public class BasicAuthFilter implements ClientRequestFilter {
+
+ private final String username;
+ private final String password;
+
+ public BasicAuthFilter(String username, String password) {
+ this.username = username;
+ this.password = password;
+ }
+
+ @Override
+ public void filter(ClientRequestContext requestContext) throws IOException {
+ String pair = username + ":" + password;
+ String authHeader = "Basic " + new String(Base64.encodeBytes(pair.getBytes()));
+ requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader);
+ }
+
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/BearerAuthFilter.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/BearerAuthFilter.java
new file mode 100644
index 0000000..1442513
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/BearerAuthFilter.java
@@ -0,0 +1,25 @@
+package org.keycloak.admin.client.resource;
+
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.core.HttpHeaders;
+import java.io.IOException;
+
+/**
+ * @author rodrigo.sasaki@icarros.com.br
+ */
+public class BearerAuthFilter implements ClientRequestFilter {
+
+ private final String tokenString;
+
+ public BearerAuthFilter(String tokenString) {
+ this.tokenString = tokenString;
+ }
+
+ @Override
+ public void filter(ClientRequestContext requestContext) throws IOException {
+ String authHeader = "Bearer " + tokenString;
+ requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader);
+ }
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/KeycloakAdminFactory.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/KeycloakAdminFactory.java
new file mode 100644
index 0000000..3b3d11f
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/KeycloakAdminFactory.java
@@ -0,0 +1,27 @@
+package org.keycloak.admin.client.resource;
+
+import org.jboss.resteasy.client.jaxrs.ResteasyClient;
+import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
+import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
+import org.keycloak.admin.client.Config;
+import org.keycloak.admin.client.token.TokenManager;
+
+/**
+ * @author rodrigo.sasaki@icarros.com.br
+ */
+public class KeycloakAdminFactory {
+
+ private KeycloakAdminFactory(){}
+
+ public static RealmResource getRealm(Config config, TokenManager tokenManager, String realmName){
+ ResteasyClient client = new ResteasyClientBuilder().build();
+ ResteasyWebTarget target = client.target(config.getServerUrl());
+
+ target.register(new BearerAuthFilter(tokenManager.getAccessTokenString()));
+
+ RealmsResource adminRoot = target.proxy(RealmsResource.class);
+
+ return adminRoot.realm(realmName);
+ }
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/OAuthClientResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/OAuthClientResource.java
new file mode 100644
index 0000000..ceeddb2
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/OAuthClientResource.java
@@ -0,0 +1,49 @@
+package org.keycloak.admin.client.resource;
+
+import org.keycloak.representations.idm.ClaimRepresentation;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.OAuthClientRepresentation;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * @author rodrigo.sasaki@icarros.com.br
+ */
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public interface OAuthClientResource {
+
+ @GET
+ public OAuthClientRepresentation toRepresentation();
+
+ @PUT
+ public void update(OAuthClientRepresentation oAuthClientRepresentation);
+
+ @DELETE
+ public void remove();
+
+ @GET
+ @Path("claims")
+ public ClaimRepresentation getClaims();
+
+ @PUT
+ @Path("claims")
+ public ClaimRepresentation updateClaims(ClaimRepresentation claimRepresentation);
+
+ @POST
+ @Path("client-secret")
+ public CredentialRepresentation generateNewSecret();
+
+ @GET
+ @Path("client-secret")
+ public CredentialRepresentation getSecret();
+
+ @GET
+ @Path("installation")
+ public String getInstallationJson();
+
+ @Path("/scope-mappings")
+ public RoleMappingResource getScopeMappings();
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/OAuthClientsResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/OAuthClientsResource.java
new file mode 100644
index 0000000..68e3f81
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/OAuthClientsResource.java
@@ -0,0 +1,25 @@
+package org.keycloak.admin.client.resource;
+
+import org.keycloak.representations.idm.OAuthClientRepresentation;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import java.util.List;
+
+/**
+ * @author rodrigo.sasaki@icarros.com.br
+ */
+public interface OAuthClientsResource {
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<OAuthClientRepresentation> findAll();
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ public void create(OAuthClientRepresentation oAuthClientRepresentation);
+
+ @Path("{oAuthClientId}")
+ public OAuthClientResource get(@PathParam("oAuthClientId") String oAuthClientId);
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java
new file mode 100644
index 0000000..0ecb18b
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java
@@ -0,0 +1,35 @@
+package org.keycloak.admin.client.resource;
+
+import org.keycloak.representations.idm.RealmRepresentation;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * @author rodrigo.sasaki@icarros.com.br
+ */
+public interface RealmResource {
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public RealmRepresentation toRepresentation();
+
+ @Path("applications")
+ public ApplicationsResource applications();
+
+ @Path("users")
+ public UsersResource users();
+
+ @Path("oauth-clients")
+ public OAuthClientsResource oAuthClients();
+
+ @Path("roles")
+ public RolesResource roles();
+
+ @DELETE
+ public void remove();
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmsResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmsResource.java
new file mode 100644
index 0000000..9d8275c
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmsResource.java
@@ -0,0 +1,32 @@
+package org.keycloak.admin.client.resource;
+
+import org.keycloak.representations.idm.RealmRepresentation;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import java.util.List;
+
+/**
+ * @author rodrigo.sasaki@icarros.com.br
+ */
+@Path("/admin/realms")
+@Consumes(MediaType.APPLICATION_JSON)
+public interface RealmsResource {
+
+ @Path("/{realm}")
+ public RealmResource realm(@PathParam("realm") String realm);
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ public void create(RealmRepresentation realmRepresentation);
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<RealmRepresentation> findAll();
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleMappingResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleMappingResource.java
new file mode 100644
index 0000000..7d01d9f
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleMappingResource.java
@@ -0,0 +1,24 @@
+package org.keycloak.admin.client.resource;
+
+import org.keycloak.representations.idm.MappingsRepresentation;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * @author rodrigo.sasaki@icarros.com.br
+ */
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public interface RoleMappingResource {
+
+ @GET
+ public MappingsRepresentation getAll();
+
+ @Path("realm")
+ public RoleScopeResource realmLevel();
+
+ @Path("applications/{appName}")
+ public RoleScopeResource applicationLevel(@PathParam("appName") String appName);
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleResource.java
new file mode 100644
index 0000000..84a292f
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleResource.java
@@ -0,0 +1,51 @@
+package org.keycloak.admin.client.resource;
+
+import org.keycloak.representations.idm.RoleRepresentation;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author rodrigo.sasaki@icarros.com.br
+ */
+public interface RoleResource {
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public RoleRepresentation toRepresentation();
+
+ @PUT
+ @Consumes(MediaType.APPLICATION_JSON)
+ public void update(RoleRepresentation roleRepresentation);
+
+ @DELETE
+ public void remove();
+
+ @GET
+ @Path("composites")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Set<RoleRepresentation> getChildren();
+
+ @GET
+ @Path("composites/realm")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Set<RoleRepresentation> getRealmLevelChildren();
+
+ @GET
+ @Path("composites/application/{appName}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Set<RoleRepresentation> getApplicationLevelChildren(@PathParam("appName") String appName);
+
+ @POST
+ @Path("composites")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public void addChildren(List<RoleRepresentation> rolesToAdd);
+
+ @DELETE
+ @Path("composites")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public void removeChildren(List<RoleRepresentation> rolesToRemove);
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleScopeResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleScopeResource.java
new file mode 100644
index 0000000..21a4645
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleScopeResource.java
@@ -0,0 +1,33 @@
+package org.keycloak.admin.client.resource;
+
+import org.keycloak.representations.idm.RoleRepresentation;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import java.util.List;
+
+/**
+ * @author rodrigo.sasaki@icarros.com.br
+ */
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public interface RoleScopeResource {
+
+ @GET
+ public List<RoleRepresentation> listAll();
+
+ @GET
+ @Path("available")
+ public List<RoleRepresentation> listAvailable();
+
+ @GET
+ @Path("composite")
+ public List<RoleRepresentation> listEffective();
+
+ @POST
+ public void add(List<RoleRepresentation> rolesToAdd);
+
+ @DELETE
+ public void remove(List<RoleRepresentation> rolesToRemove);
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RolesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RolesResource.java
new file mode 100644
index 0000000..6d705f5
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RolesResource.java
@@ -0,0 +1,26 @@
+package org.keycloak.admin.client.resource;
+
+import org.keycloak.representations.idm.RoleRepresentation;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import java.util.List;
+
+/**
+ * @author rodrigo.sasaki@icarros.com.br
+ */
+public interface RolesResource {
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<RoleRepresentation> list();
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ public void create(RoleRepresentation roleRepresentation);
+
+ @Path("{roleName}")
+ public RoleResource get(@PathParam("roleName") String roleName);
+
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java
new file mode 100644
index 0000000..ce5dde2
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java
@@ -0,0 +1,61 @@
+package org.keycloak.admin.client.resource;
+
+import org.keycloak.representations.adapters.action.UserStats;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.SocialLinkRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.UserSessionRepresentation;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author rodrigo.sasaki@icarros.com.br
+ */
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public interface UserResource {
+
+ @GET
+ public UserRepresentation toRepresentation();
+
+ @PUT
+ public void update(UserRepresentation userRepresentation);
+
+ @DELETE
+ public void remove();
+
+ @POST
+ @Path("logout")
+ public void logout();
+
+ @PUT
+ @Path("remove-totp")
+ public void removeTotp();
+
+ @PUT
+ @Path("reset-password")
+ public void resetPassword(CredentialRepresentation credentialRepresentation);
+
+ @PUT
+ @Path("reset-password-email")
+ public void resetPasswordEmail();
+
+ @GET
+ @Path("session-stats")
+ public Map<String, UserStats> getUserStats();
+
+ @GET
+ @Path("sessions")
+ public List<UserSessionRepresentation> getUserSessions();
+
+ @GET
+ @Path("social-links")
+ public List<SocialLinkRepresentation> getSocialLinks();
+
+ @Path("role-mappings")
+ public RoleMappingResource roles();
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UsersResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UsersResource.java
new file mode 100644
index 0000000..640a900
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UsersResource.java
@@ -0,0 +1,29 @@
+package org.keycloak.admin.client.resource;
+
+import org.keycloak.representations.idm.UserRepresentation;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import java.util.List;
+
+public interface UsersResource {
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<UserRepresentation> search(@QueryParam("username") String username,
+ @QueryParam("firstName") String firstName,
+ @QueryParam("lastName") String lastName,
+ @QueryParam("email") String email);
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<UserRepresentation> search(@QueryParam("search") String search);
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ public void create(UserRepresentation userRepresentation);
+
+ @Path("{username}")
+ public UserResource get(@PathParam("username") String username);
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/token/TokenManager.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/token/TokenManager.java
new file mode 100644
index 0000000..3e3d923
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/token/TokenManager.java
@@ -0,0 +1,99 @@
+package org.keycloak.admin.client.token;
+
+import org.jboss.resteasy.client.jaxrs.ResteasyClient;
+import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
+import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
+import org.keycloak.admin.client.Config;
+import org.keycloak.admin.client.resource.BasicAuthFilter;
+import org.keycloak.representations.AccessTokenResponse;
+
+import javax.ws.rs.core.Form;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * @author rodrigo.sasaki@icarros.com.br
+ */
+public class TokenManager {
+
+ private AccessTokenResponse currentToken;
+ private Date expirationTime;
+ private Config config;
+
+ public TokenManager(Config config){
+ this.config = config;
+ }
+
+ public String getAccessTokenString(){
+ return getAccessToken().getToken();
+ }
+
+ public AccessTokenResponse getAccessToken(){
+ if(currentToken == null){
+ grantToken();
+ }else if(tokenExpired()){
+ refreshToken();
+ }
+ return currentToken;
+ }
+
+ public AccessTokenResponse grantToken(){
+ ResteasyClient client = new ResteasyClientBuilder().build();
+ ResteasyWebTarget target = client.target(config.getServerUrl());
+
+ Form form = new Form()
+ .param("username", config.getUsername())
+ .param("password", config.getPassword());
+
+ if(config.isPublicClient()){
+ form.param("client_id", config.getClientId());
+ } else {
+ target.register(new BasicAuthFilter(config.getClientId(), config.getClientSecret()));
+ }
+
+ TokenService tokenService = target.proxy(TokenService.class);
+
+ AccessTokenResponse response = tokenService.grantToken(config.getRealm(), form.asMap());
+
+ defineCurrentToken(response);
+ return response;
+ }
+
+ public AccessTokenResponse refreshToken(){
+ ResteasyClient client = new ResteasyClientBuilder().build();
+ ResteasyWebTarget target = client.target(config.getServerUrl());
+
+ Form form = new Form()
+ .param("username", config.getUsername())
+ .param("password", config.getPassword());
+
+ if(config.isPublicClient()){
+ form.param("client_id", config.getClientId());
+ } else {
+ target.register(new BasicAuthFilter(config.getClientId(), config.getClientSecret()));
+ }
+
+ TokenService tokenService = target.proxy(TokenService.class);
+
+ AccessTokenResponse response = tokenService.refreshToken(config.getRealm(), form.asMap());
+
+ defineCurrentToken(response);
+ return response;
+ }
+
+ private void setExpirationTime() {
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.SECOND, (int) currentToken.getExpiresIn());
+ expirationTime = cal.getTime();
+ }
+
+ private boolean tokenExpired() {
+ return new Date().after(expirationTime);
+ }
+
+ private void defineCurrentToken(AccessTokenResponse accessTokenResponse){
+ currentToken = accessTokenResponse;
+ setExpirationTime();
+ }
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/token/TokenService.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/token/TokenService.java
new file mode 100644
index 0000000..ff4c8f7
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/token/TokenService.java
@@ -0,0 +1,24 @@
+package org.keycloak.admin.client.token;
+
+import org.keycloak.representations.AccessTokenResponse;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+
+/**
+ * @author rodrigo.sasaki@icarros.com.br
+ */
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+public interface TokenService {
+
+ @POST
+ @Path("/realms/{realm}/tokens/grants/access")
+ public AccessTokenResponse grantToken(@PathParam("realm") String realm, MultivaluedMap<String, String> map);
+
+ @POST
+ @Path("/realms/{realm}/tokens/refresh")
+ public AccessTokenResponse refreshToken(@PathParam("realm") String realm, MultivaluedMap<String, String> map);
+
+}
integration/pom.xml 1(+1 -0)
diff --git a/integration/pom.xml b/integration/pom.xml
index 6efed51..02ce18f 100755
--- a/integration/pom.xml
+++ b/integration/pom.xml
@@ -26,5 +26,6 @@
<module>as7-eap-subsystem</module>
<module>js</module>
<module>installed</module>
+ <module>admin-client</module>
</modules>
</project>
testsuite/integration/pom.xml 5(+5 -0)
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index 55fe1a0..3aab521 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -31,6 +31,11 @@
<type>pom</type>
</dependency>
<dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-admin-client</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AbstractClientTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AbstractClientTest.java
new file mode 100644
index 0000000..4df9370
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AbstractClientTest.java
@@ -0,0 +1,110 @@
+package org.keycloak.testsuite.admin;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.keycloak.admin.client.Keycloak;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.models.Constants;
+import org.keycloak.models.RealmModel;
+import org.keycloak.representations.idm.ApplicationRepresentation;
+import org.keycloak.representations.idm.OAuthClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.rule.KeycloakRule;
+
+import javax.ws.rs.ClientErrorException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public abstract class AbstractClientTest {
+
+ protected static final String REALM_NAME = "admin-client-test";
+
+ @ClassRule
+ public static KeycloakRule keycloakRule = new KeycloakRule();
+
+ protected Keycloak keycloak;
+ protected RealmResource realm;
+
+ @Before
+ public void before() {
+ keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ adminstrationRealm.setPasswordCredentialGrantAllowed(true);
+
+ RealmModel testRealm = manager.createRealm(REALM_NAME);
+ testRealm.setEnabled(true);
+ }
+ });
+
+ keycloak = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", Constants.ADMIN_CONSOLE_APPLICATION);
+ realm = keycloak.realm(REALM_NAME);
+ }
+
+ @After
+ public void after() {
+ keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ adminstrationRealm.setPasswordCredentialGrantAllowed(false);
+
+ RealmModel realm = manager.getRealmByName(REALM_NAME);
+ if (realm != null) {
+ manager.removeRealm(realm);
+ }
+ }
+ });
+ }
+
+ public static <T> void assertNames(List<T> actual, String... expected) {
+ Arrays.sort(expected);
+ String[] actualNames = names(actual);
+ assertArrayEquals("Expected: " + Arrays.toString(expected) + ", was: " + Arrays.toString(actualNames), expected, actualNames);
+ }
+
+ public static <T> List<T> sort(List<T> list) {
+ Collections.sort(list, new Comparator<Object>() {
+ @Override
+ public int compare(Object o1, Object o2) {
+ return name(o1).compareTo(name(o2));
+ }
+ });
+ return list;
+ }
+
+ public static <T> String[] names(List<T> list) {
+ String[] names = new String[list.size()];
+ for (int i = 0; i < list.size(); i++) {
+ names[i] = name(list.get(i));
+ }
+ Arrays.sort(names);
+ return names;
+ }
+
+ public static String name(Object o1) {
+ if (o1 instanceof RealmRepresentation) {
+ return ((RealmRepresentation) o1).getRealm();
+ } else if (o1 instanceof ApplicationRepresentation) {
+ return ((ApplicationRepresentation) o1).getName();
+ } else if (o1 instanceof OAuthClientRepresentation) {
+ return ((OAuthClientRepresentation) o1).getName();
+ }
+ throw new IllegalArgumentException();
+ }
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ApplicationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ApplicationTest.java
new file mode 100644
index 0000000..61a1b9d
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ApplicationTest.java
@@ -0,0 +1,47 @@
+package org.keycloak.testsuite.admin;
+
+import org.junit.Test;
+import org.keycloak.representations.idm.ApplicationRepresentation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ApplicationTest extends AbstractClientTest {
+
+ @Test
+ public void getApplications() {
+ assertNames(realm.applications().findAll(), "account", "realm-management", "security-admin-console");
+ }
+
+ @Test
+ public void createApplication() {
+ ApplicationRepresentation rep = new ApplicationRepresentation();
+ rep.setName("my-app");
+ rep.setEnabled(true);
+ realm.applications().create(rep);
+
+ assertNames(realm.applications().findAll(), "account", "realm-management", "security-admin-console", "my-app");
+ }
+
+ @Test
+ public void removeApplication() {
+ createApplication();
+
+ realm.applications().get("my-app").remove();
+ }
+
+ @Test
+ public void getApplicationRepresentation() {
+ createApplication();
+
+ ApplicationRepresentation rep = realm.applications().get("my-app").toRepresentation();
+ assertEquals("my-app", rep.getName());
+ assertTrue(rep.isEnabled());
+ }
+
+
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/OAuthClientTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/OAuthClientTest.java
new file mode 100644
index 0000000..e66a8ea
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/OAuthClientTest.java
@@ -0,0 +1,50 @@
+package org.keycloak.testsuite.admin;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.keycloak.representations.idm.OAuthClientRepresentation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class OAuthClientTest extends AbstractClientTest {
+
+ @Test
+ public void getOAuthClients() {
+ assertTrue(realm.oAuthClients().findAll().isEmpty());
+ }
+
+ @Test
+ public void createOAuthClient() {
+ OAuthClientRepresentation rep = new OAuthClientRepresentation();
+ rep.setName("my-client");
+ rep.setEnabled(true);
+ realm.oAuthClients().create(rep);
+
+ assertNames(realm.oAuthClients().findAll(), "my-client");
+ }
+
+ @Test
+ @Ignore
+ // TODO For some reason clients are retrieved using id, not client-id
+ public void removeOAuthClient() {
+ createOAuthClient();
+
+ realm.oAuthClients().get("my-client").remove();
+ }
+
+ @Test
+ @Ignore
+ // TODO For some reason clients are retrieved using id, not client-id
+ public void getOAuthClientRepresentation() {
+ createOAuthClient();
+
+ OAuthClientRepresentation rep = realm.oAuthClients().get("my-client").toRepresentation();
+ assertEquals("my-client", rep.getName());
+ assertTrue(rep.isEnabled());
+ }
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java
new file mode 100644
index 0000000..b0a7c8f
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java
@@ -0,0 +1,56 @@
+package org.keycloak.testsuite.admin;
+
+import org.junit.Test;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.managers.RealmManager;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class RealmTest extends AbstractClientTest {
+
+ @Test
+ public void getRealms() {
+ assertNames(keycloak.realms().findAll(), "master", "test", REALM_NAME);
+ }
+
+ @Test
+ public void createRealm() {
+ try {
+ RealmRepresentation rep = new RealmRepresentation();
+ rep.setRealm("new-realm");
+
+ keycloak.realms().create(rep);
+
+ assertNames(keycloak.realms().findAll(), "master", "test", REALM_NAME, "new-realm");
+ } finally {
+ KeycloakSession session = keycloakRule.startSession();
+ RealmManager manager = new RealmManager(session);
+ RealmModel newRealm = manager.getRealmByName("new-realm");
+ if (newRealm != null) {
+ manager.removeRealm(newRealm);
+ }
+ keycloakRule.stopSession(session, true);
+ }
+ }
+
+ @Test
+ public void removeRealm() {
+ realm.remove();
+
+ assertNames(keycloak.realms().findAll(), "master", "test");
+ }
+
+ @Test
+ public void getRealmRepresentation() {
+ RealmRepresentation rep = realm.toRepresentation();
+ assertEquals(REALM_NAME, rep.getRealm());
+ assertTrue(rep.isEnabled());
+ }
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/UserTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/UserTest.java
new file mode 100644
index 0000000..d3981c4
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/UserTest.java
@@ -0,0 +1,39 @@
+package org.keycloak.testsuite.admin;
+
+import org.junit.Test;
+import org.keycloak.representations.idm.UserRepresentation;
+
+import javax.ws.rs.ClientErrorException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UserTest extends AbstractClientTest {
+
+ @Test
+ public void createUser() {
+ UserRepresentation user = new UserRepresentation();
+ user.setUsername("user1");
+ user.setEmail("user1@localhost");
+
+ realm.users().create(user);
+ }
+
+ @Test
+ public void createDuplicatedUser() {
+ createUser();
+
+ try {
+ UserRepresentation user = new UserRepresentation();
+ user.setUsername("user1");
+ realm.users().create(user);
+ fail("Expected failure");
+ } catch (ClientErrorException e) {
+ assertEquals(409, e.getResponse().getStatus());
+ }
+ }
+
+}