Merge pull request #564 from stianst/master

Admin REST client
This commit is contained in:
Stian Thorgersen 2014-07-30 13:26:04 +01:00
commit e2ce33a86a
27 changed files with 1187 additions and 0 deletions

View 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>

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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();
}
}

View file

@ -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);
}

View file

@ -26,5 +26,6 @@
<module>as7-eap-subsystem</module>
<module>js</module>
<module>installed</module>
<module>admin-client</module>
</modules>
</project>

View file

@ -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>

View file

@ -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();
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}
}