Skip to content

Commit

Permalink
KEYCLOAK-1749 Client registration service and client java api
Browse files Browse the repository at this point in the history
  • Loading branch information
stianst committed Oct 8, 2015
1 parent 7fec167 commit 366a162
Show file tree
Hide file tree
Showing 21 changed files with 910 additions and 41 deletions.
31 changes: 31 additions & 0 deletions client-api/pom.xml
@@ -0,0 +1,31 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.6.0.Final-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>keycloak-client-api</artifactId>
<name>Keycloak Client API</name>
<description/>

<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
@@ -0,0 +1,275 @@
package org.keycloak.client.registration;

import org.apache.http.HttpHeaders;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.util.Base64;
import org.keycloak.util.JsonSerialization;

import java.io.IOException;
import java.io.InputStream;

/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ClientRegistration {

private String clientRegistrationUrl;
private HttpClient httpClient;
private Auth auth;

public static ClientRegistrationBuilder create() {
return new ClientRegistrationBuilder();
}

private ClientRegistration() {
}

public ClientRepresentation create(ClientRepresentation client) throws ClientRegistrationException {
String content = serialize(client);
InputStream resultStream = doPost(content);
return deserialize(resultStream, ClientRepresentation.class);
}

public ClientRepresentation get() throws ClientRegistrationException {
if (auth instanceof ClientIdSecretAuth) {
String clientId = ((ClientIdSecretAuth) auth).clientId;
return get(clientId);
} else {
throw new ClientRegistrationException("Requires client authentication");
}
}

public ClientRepresentation get(String clientId) throws ClientRegistrationException {
InputStream resultStream = doGet(clientId);
return resultStream != null ? deserialize(resultStream, ClientRepresentation.class) : null;
}

public void update(ClientRepresentation client) throws ClientRegistrationException {
String content = serialize(client);
doPut(content, client.getClientId());
}

public void delete() throws ClientRegistrationException {
if (auth instanceof ClientIdSecretAuth) {
String clientId = ((ClientIdSecretAuth) auth).clientId;
delete(clientId);
} else {
throw new ClientRegistrationException("Requires client authentication");
}
}

public void delete(String clientId) throws ClientRegistrationException {
doDelete(clientId);
}

public void close() throws ClientRegistrationException {
if (httpClient instanceof CloseableHttpClient) {
try {
((CloseableHttpClient) httpClient).close();
} catch (IOException e) {
throw new ClientRegistrationException("Failed to close http client", e);
}
}
}

private InputStream doPost(String content) throws ClientRegistrationException {
try {
HttpPost request = new HttpPost(clientRegistrationUrl);

request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
request.setHeader(HttpHeaders.ACCEPT, "application/json");
request.setEntity(new StringEntity(content));

auth.addAuth(request);

HttpResponse response = httpClient.execute(request);
InputStream responseStream = null;
if (response.getEntity() != null) {
responseStream = response.getEntity().getContent();
}

if (response.getStatusLine().getStatusCode() == 201) {
return responseStream;
} else {
responseStream.close();
throw new HttpErrorException(response.getStatusLine());
}
} catch (IOException e) {
throw new ClientRegistrationException("Failed to send request", e);
}
}

private InputStream doGet(String endpoint) throws ClientRegistrationException {
try {
HttpGet request = new HttpGet(clientRegistrationUrl + "/" + endpoint);

request.setHeader(HttpHeaders.ACCEPT, "application/json");

auth.addAuth(request);

HttpResponse response = httpClient.execute(request);
InputStream responseStream = null;
if (response.getEntity() != null) {
responseStream = response.getEntity().getContent();
}

if (response.getStatusLine().getStatusCode() == 200) {
return responseStream;
} else if (response.getStatusLine().getStatusCode() == 404) {
responseStream.close();
return null;
} else {
responseStream.close();
throw new HttpErrorException(response.getStatusLine());
}
} catch (IOException e) {
throw new ClientRegistrationException("Failed to send request", e);
}
}

private void doPut(String content, String endpoint) throws ClientRegistrationException {
try {
HttpPut request = new HttpPut(clientRegistrationUrl + "/" + endpoint);

request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
request.setHeader(HttpHeaders.ACCEPT, "application/json");
request.setEntity(new StringEntity(content));

auth.addAuth(request);

HttpResponse response = httpClient.execute(request);
if (response.getEntity() != null) {
response.getEntity().getContent().close();
}

if (response.getStatusLine().getStatusCode() != 200) {
throw new HttpErrorException(response.getStatusLine());
}
} catch (IOException e) {
throw new ClientRegistrationException("Failed to send request", e);
}
}

private void doDelete(String endpoint) throws ClientRegistrationException {
try {
HttpDelete request = new HttpDelete(clientRegistrationUrl + "/" + endpoint);

auth.addAuth(request);

HttpResponse response = httpClient.execute(request);
if (response.getEntity() != null) {
response.getEntity().getContent().close();
}

if (response.getStatusLine().getStatusCode() != 200) {
throw new HttpErrorException(response.getStatusLine());
}
} catch (IOException e) {
throw new ClientRegistrationException("Failed to send request", e);
}
}

private String serialize(ClientRepresentation client) throws ClientRegistrationException {
try {
return JsonSerialization.writeValueAsString(client);
} catch (IOException e) {
throw new ClientRegistrationException("Failed to write json object", e);
}
}

private <T> T deserialize(InputStream inputStream, Class<T> clazz) throws ClientRegistrationException {
try {
return JsonSerialization.readValue(inputStream, clazz);
} catch (IOException e) {
throw new ClientRegistrationException("Failed to read json object", e);
}
}

public static class ClientRegistrationBuilder {

private String realm;

private String authServerUrl;

private Auth auth;

private HttpClient httpClient;

public ClientRegistrationBuilder realm(String realm) {
this.realm = realm;
return this;
}
public ClientRegistrationBuilder authServerUrl(String authServerUrl) {
this.authServerUrl = authServerUrl;
return this;
}

public ClientRegistrationBuilder auth(String token) {
this.auth = new TokenAuth(token);
return this;
}

public ClientRegistrationBuilder auth(String clientId, String clientSecret) {
this.auth = new ClientIdSecretAuth(clientId, clientSecret);
return this;
}

public ClientRegistrationBuilder httpClient(HttpClient httpClient) {
this.httpClient = httpClient;
return this;
}

public ClientRegistration build() {
ClientRegistration clientRegistration = new ClientRegistration();
clientRegistration.clientRegistrationUrl = authServerUrl + "/realms/" + realm + "/client-registration";

clientRegistration.httpClient = httpClient != null ? httpClient : HttpClients.createDefault();
clientRegistration.auth = auth;

return clientRegistration;
}

}

public interface Auth {
void addAuth(HttpRequest httpRequest);
}

public static class AuthorizationHeaderAuth implements Auth {
private String credentials;

public AuthorizationHeaderAuth(String credentials) {
this.credentials = credentials;
}

public void addAuth(HttpRequest httpRequest) {
httpRequest.setHeader(HttpHeaders.AUTHORIZATION, credentials);
}
}

public static class TokenAuth extends AuthorizationHeaderAuth {
public TokenAuth(String token) {
super("Bearer " + token);
}
}

public static class ClientIdSecretAuth extends AuthorizationHeaderAuth {
private String clientId;

public ClientIdSecretAuth(String clientId, String clientSecret) {
super("Basic " + Base64.encodeBytes((clientId + ":" + clientSecret).getBytes()));
this.clientId = clientId;
}
}

}
@@ -0,0 +1,16 @@
package org.keycloak.client.registration;

/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ClientRegistrationException extends Exception {

public ClientRegistrationException(String s, Throwable throwable) {
super(s, throwable);
}

public ClientRegistrationException(String s) {
super(s);
}

}
@@ -0,0 +1,22 @@
package org.keycloak.client.registration;

import org.apache.http.StatusLine;

import java.io.IOException;

/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class HttpErrorException extends IOException {

private StatusLine statusLine;

public HttpErrorException(StatusLine statusLine) {
this.statusLine = statusLine;
}

public StatusLine getStatusLine() {
return statusLine;
}

}
@@ -1,14 +1,12 @@
package org.keycloak.representations.idm;

import java.util.ArrayList;
import org.codehaus.jackson.annotate.JsonIgnore;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.codehaus.jackson.annotate.JsonIgnore;
import org.keycloak.util.MultivaluedHashMap;

/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
Expand Down
11 changes: 10 additions & 1 deletion events/api/src/main/java/org/keycloak/events/EventType.java
Expand Up @@ -69,7 +69,16 @@ public enum EventType {
IMPERSONATE(true),
CUSTOM_REQUIRED_ACTION(true),
CUSTOM_REQUIRED_ACTION_ERROR(true),
EXECUTE_ACTIONS(true);
EXECUTE_ACTIONS(true),

CLIENT_INFO(false),
CLIENT_INFO_ERROR(false),
CLIENT_REGISTER(true),
CLIENT_REGISTER_ERROR(true),
CLIENT_UPDATE(true),
CLIENT_UPDATE_ERROR(true),
CLIENT_DELETE(true),
CLIENT_DELETE_ERROR(true);

private boolean saveByDefault;

Expand Down
Expand Up @@ -89,6 +89,7 @@ personalInfo=Personal Info:
role_admin=Admin
role_realm-admin=Realm Admin
role_create-realm=Create realm
role_create-client=Create client
role_view-realm=View realm
role_view-users=View users
role_view-applications=View applications
Expand Down

0 comments on commit 366a162

Please sign in to comment.