commit
e2ce33a86a
27 changed files with 1187 additions and 0 deletions
65
integration/admin-client/pom.xml
Normal file
65
integration/admin-client/pom.xml
Normal file
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -26,5 +26,6 @@
|
|||
<module>as7-eap-subsystem</module>
|
||||
<module>js</module>
|
||||
<module>installed</module>
|
||||
<module>admin-client</module>
|
||||
</modules>
|
||||
</project>
|
||||
|
|
|
@ -30,6 +30,11 @@
|
|||
<version>${project.version}</version>
|
||||
<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>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue