Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
d39aee0a72
29 changed files with 995 additions and 56 deletions
31
client-api/pom.xml
Executable file
31
client-api/pom.xml
Executable file
|
@ -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;
|
package org.keycloak.representations.idm;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import org.codehaus.jackson.annotate.JsonIgnore;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
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>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
|
|
|
@ -209,6 +209,7 @@ new Keycloak({ url: 'http://localhost/auth', realm: 'myrealm', clientId: 'myApp'
|
||||||
<listitem>redirectUri - specifies the uri to redirect to after login</listitem>
|
<listitem>redirectUri - specifies the uri to redirect to after login</listitem>
|
||||||
<listitem>prompt - can be set to 'none' to check if the user is logged in already (if not logged in, a login form is not displayed)</listitem>
|
<listitem>prompt - can be set to 'none' to check if the user is logged in already (if not logged in, a login form is not displayed)</listitem>
|
||||||
<listitem>loginHint - used to pre-fill the username/email field on the login form</listitem>
|
<listitem>loginHint - used to pre-fill the username/email field on the login form</listitem>
|
||||||
|
<listitem>action - if value is 'register' then user is redirected to registration page, otherwise to login page</listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
</para>
|
</para>
|
||||||
</simplesect>
|
</simplesect>
|
||||||
|
@ -246,6 +247,20 @@ new Keycloak({ url: 'http://localhost/auth', realm: 'myrealm', clientId: 'myApp'
|
||||||
</para>
|
</para>
|
||||||
</simplesect>
|
</simplesect>
|
||||||
|
|
||||||
|
<simplesect>
|
||||||
|
<title>register(options)</title>
|
||||||
|
|
||||||
|
<para>Redirects to registration form. It's a shortcut for doing login with option action = 'register'</para>
|
||||||
|
<para>Options are same as login method but 'action' is overwritten to 'register'</para>
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
<simplesect>
|
||||||
|
<title>createRegisterUrl(options)</title>
|
||||||
|
|
||||||
|
<para>Returns the url to registration page. It's a shortcut for doing createRegisterUrl with option action = 'register'</para>
|
||||||
|
<para>Options are same as createLoginUrl method but 'action' is overwritten to 'register'</para>
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
<simplesect>
|
<simplesect>
|
||||||
<title>accountManagement()</title>
|
<title>accountManagement()</title>
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,16 @@ public enum EventType {
|
||||||
IMPERSONATE(true),
|
IMPERSONATE(true),
|
||||||
CUSTOM_REQUIRED_ACTION(true),
|
CUSTOM_REQUIRED_ACTION(true),
|
||||||
CUSTOM_REQUIRED_ACTION_ERROR(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;
|
private boolean saveByDefault;
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,6 @@ Open the Keycloak admin console, click on Add Realm, click on 'Choose a JSON fil
|
||||||
|
|
||||||
Deploy the JS Console to Keycloak by running:
|
Deploy the JS Console to Keycloak by running:
|
||||||
|
|
||||||
mvn install jboss-as:deploy
|
mvn install wildfly:deploy
|
||||||
|
|
||||||
Open the console at http://localhost:8080/js-console and login with username: 'user', and password: 'password'.
|
Open the console at http://localhost:8080/js-console and login with username: 'user', and password: 'password'.
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<div>
|
<div>
|
||||||
<button onclick="keycloak.login()">Login</button>
|
<button onclick="keycloak.login()">Login</button>
|
||||||
<button onclick="keycloak.logout()">Logout</button>
|
<button onclick="keycloak.logout()">Logout</button>
|
||||||
|
<button onclick="keycloak.register()">Register</button>
|
||||||
<button onclick="refreshToken(9999)">Refresh Token</button>
|
<button onclick="refreshToken(9999)">Refresh Token</button>
|
||||||
<button onclick="refreshToken(30)">Refresh Token (if <30s validity)</button>
|
<button onclick="refreshToken(30)">Refresh Token (if <30s validity)</button>
|
||||||
<button onclick="loadProfile()">Get Profile</button>
|
<button onclick="loadProfile()">Get Profile</button>
|
||||||
|
@ -18,6 +19,7 @@
|
||||||
<button onclick="output(keycloak)">Show Details</button>
|
<button onclick="output(keycloak)">Show Details</button>
|
||||||
<button onclick="output(keycloak.createLoginUrl())">Show Login URL</button>
|
<button onclick="output(keycloak.createLoginUrl())">Show Login URL</button>
|
||||||
<button onclick="output(keycloak.createLogoutUrl())">Show Logout URL</button>
|
<button onclick="output(keycloak.createLogoutUrl())">Show Logout URL</button>
|
||||||
|
<button onclick="output(keycloak.createRegisterUrl())">Show Register URL</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>Result</h2>
|
<h2>Result</h2>
|
||||||
|
|
|
@ -7,6 +7,7 @@ onText=AN
|
||||||
offText=AUS
|
offText=AUS
|
||||||
client=de Client
|
client=de Client
|
||||||
clear=de Clear
|
clear=de Clear
|
||||||
|
selectOne=de Select One...
|
||||||
|
|
||||||
# Realm settings
|
# Realm settings
|
||||||
realm-detail.enabled.tooltip=de Users and clients can only access a realm if it's enabled
|
realm-detail.enabled.tooltip=de Users and clients can only access a realm if it's enabled
|
||||||
|
@ -114,3 +115,15 @@ not-before.tooltip=de Revoke any tokens issued before this date.
|
||||||
set-to-now=de Set To Now
|
set-to-now=de Set To Now
|
||||||
push=de Push
|
push=de Push
|
||||||
push.tooltip=de For every client that has an admin URL, notify them of the new revocation policy.
|
push.tooltip=de For every client that has an admin URL, notify them of the new revocation policy.
|
||||||
|
|
||||||
|
#Protocol Mapper
|
||||||
|
usermodel.prop.label=de Property
|
||||||
|
usermodel.prop.tooltip=de Name of the property method in the UserModel interface. For example, a value of 'email' would reference the UserModel.getEmail() method.
|
||||||
|
usermodel.attr.label=de User Attribute
|
||||||
|
usermodel.attr.tooltip=de Name of stored user attribute which is the name of an attribute within the UserModel.attribute map.
|
||||||
|
userSession.modelNote.label=de User Session Note
|
||||||
|
userSession.modelNote.tooltip=de Name of stored user session note within the UserSessionModel.note map.
|
||||||
|
multivalued.label=de Multivalued
|
||||||
|
multivalued.tooltip=de Indicates if attribute supports multiple values. If true, then the list of all values of this attribute will be set as claim. If false, then just first value will be set as claim
|
||||||
|
selectRole.label=de Select Role
|
||||||
|
selectRole.tooltip=de Enter role in the textbox to the left, or click this button to browse and select the role you want
|
||||||
|
|
|
@ -7,6 +7,7 @@ onText=ON
|
||||||
offText=OFF
|
offText=OFF
|
||||||
client=Client
|
client=Client
|
||||||
clear=Clear
|
clear=Clear
|
||||||
|
selectOne=Select One...
|
||||||
|
|
||||||
# Realm settings
|
# Realm settings
|
||||||
realm-detail.enabled.tooltip=Users and clients can only access a realm if it's enabled
|
realm-detail.enabled.tooltip=Users and clients can only access a realm if it's enabled
|
||||||
|
@ -114,3 +115,15 @@ not-before.tooltip=Revoke any tokens issued before this date.
|
||||||
set-to-now=Set To Now
|
set-to-now=Set To Now
|
||||||
push=Push
|
push=Push
|
||||||
push.tooltip=For every client that has an admin URL, notify them of the new revocation policy.
|
push.tooltip=For every client that has an admin URL, notify them of the new revocation policy.
|
||||||
|
|
||||||
|
#Protocol Mapper
|
||||||
|
usermodel.prop.label=Property
|
||||||
|
usermodel.prop.tooltip=Name of the property method in the UserModel interface. For example, a value of 'email' would reference the UserModel.getEmail() method.
|
||||||
|
usermodel.attr.label=User Attribute
|
||||||
|
usermodel.attr.tooltip=Name of stored user attribute which is the name of an attribute within the UserModel.attribute map.
|
||||||
|
userSession.modelNote.label=User Session Note
|
||||||
|
userSession.modelNote.tooltip=Name of stored user session note within the UserSessionModel.note map.
|
||||||
|
multivalued.label=Multivalued
|
||||||
|
multivalued.tooltip=Indicates if attribute supports multiple values. If true, then the list of all values of this attribute will be set as claim. If false, then just first value will be set as claim
|
||||||
|
selectRole.label=Select Role
|
||||||
|
selectRole.tooltip=Enter role in the textbox to the left, or click this button to browse and select the role you want
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
<div>
|
<div>
|
||||||
<div data-ng-repeat="option in properties" class="form-group" data-ng-controller="ProviderConfigCtrl">
|
<div data-ng-repeat="option in properties" class="form-group" data-ng-controller="ProviderConfigCtrl">
|
||||||
<label class="col-md-2 control-label">{{option.label}}</label>
|
<label class="col-md-2 control-label">{{:: option.label | translate}}</label>
|
||||||
|
|
||||||
<div class="col-sm-6" data-ng-hide="option.type == 'boolean' || option.type == 'List' || option.type == 'Role' || option.type == 'ClientList'">
|
<div class="col-sm-6" data-ng-hide="option.type == 'boolean' || option.type == 'List' || option.type == 'Role' || option.type == 'ClientList'">
|
||||||
<input class="form-control" type="text" data-ng-model="config[ option.name ]" >
|
<input class="form-control" type="text" data-ng-model="config[ option.name ]" >
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6" data-ng-show="option.type == 'boolean'">
|
<div class="col-sm-6" data-ng-show="option.type == 'boolean'">
|
||||||
<input ng-model="config[ option.name ]" value="'true'" name="option.name" id="option.name" onoffswitchstring />
|
<input ng-model="config[ option.name ]" value="'true'" name="option.name" id="option.name" onoffswitchstring on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6" data-ng-show="option.type == 'List'">
|
<div class="col-sm-6" data-ng-show="option.type == 'List'">
|
||||||
<select ng-model="config[ option.name ]" ng-options="data for data in option.defaultValue">
|
<select ng-model="config[ option.name ]" ng-options="data for data in option.defaultValue">
|
||||||
<option value="" selected> Select one... </option>
|
<option value="" selected> {{:: 'selectOne' | translate}} </option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6" data-ng-show="option.type == 'Role'">
|
<div class="col-sm-6" data-ng-show="option.type == 'Role'">
|
||||||
|
@ -19,16 +19,16 @@
|
||||||
<input class="form-control" type="text" data-ng-model="config[ option.name ]" >
|
<input class="form-control" type="text" data-ng-model="config[ option.name ]" >
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-2">
|
<div class="col-sm-2">
|
||||||
<button type="submit" data-ng-click="openRoleSelector(option.name, config)" class="btn btn-default" tooltip-placement="top" tooltip-trigger="mouseover mouseout" tooltip="Enter role in the textbox to the left, or click this button to browse and select the role you want">Select Role</button>
|
<button type="submit" data-ng-click="openRoleSelector(option.name, config)" class="btn btn-default" tooltip-placement="top" tooltip-trigger="mouseover mouseout" tooltip="{{:: 'selectRole.tooltip' | translate}}">{{:: 'selectRole.label' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-4" data-ng-show="option.type == 'ClientList'">
|
<div class="col-sm-4" data-ng-show="option.type == 'ClientList'">
|
||||||
<select ng-model="config[ option.name ]" ng-options="client.clientId as client.clientId for client in clients">
|
<select ng-model="config[ option.name ]" ng-options="client.clientId as client.clientId for client in clients">
|
||||||
<option value="" selected> Select one... </option>
|
<option value="" selected> {{:: 'selectOne' | translate}} </option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<kc-tooltip>{{option.helpText}}</kc-tooltip>
|
<kc-tooltip>{{:: option.helpText | translate}}</kc-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -89,6 +89,7 @@ personalInfo=Personal Info:
|
||||||
role_admin=Admin
|
role_admin=Admin
|
||||||
role_realm-admin=Realm Admin
|
role_realm-admin=Realm Admin
|
||||||
role_create-realm=Create realm
|
role_create-realm=Create realm
|
||||||
|
role_create-client=Create client
|
||||||
role_view-realm=View realm
|
role_view-realm=View realm
|
||||||
role_view-users=View users
|
role_view-users=View users
|
||||||
role_view-applications=View applications
|
role_view-applications=View applications
|
||||||
|
|
|
@ -183,6 +183,18 @@
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kc.register = function (options) {
|
||||||
|
return adapter.register(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
kc.createRegisterUrl = function(options) {
|
||||||
|
if (!options) {
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
options.action = 'register';
|
||||||
|
return kc.createLoginUrl(options);
|
||||||
|
}
|
||||||
|
|
||||||
kc.createAccountUrl = function(options) {
|
kc.createAccountUrl = function(options) {
|
||||||
var url = getRealmUrl()
|
var url = getRealmUrl()
|
||||||
+ '/account'
|
+ '/account'
|
||||||
|
@ -760,6 +772,11 @@
|
||||||
return createPromise().promise;
|
return createPromise().promise;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
register: function(options) {
|
||||||
|
window.location.href = kc.createRegisterUrl(options);
|
||||||
|
return createPromise().promise;
|
||||||
|
},
|
||||||
|
|
||||||
accountManagement : function() {
|
accountManagement : function() {
|
||||||
window.location.href = kc.createAccountUrl();
|
window.location.href = kc.createAccountUrl();
|
||||||
return createPromise().promise;
|
return createPromise().promise;
|
||||||
|
@ -858,6 +875,16 @@
|
||||||
return promise.promise;
|
return promise.promise;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
register : function() {
|
||||||
|
var registerUrl = kc.createRegisterUrl();
|
||||||
|
var ref = window.open(registerUrl, '_blank', 'location=no');
|
||||||
|
ref.addEventListener('loadstart', function(event) {
|
||||||
|
if (event.url.indexOf('http://localhost') == 0) {
|
||||||
|
ref.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
accountManagement : function() {
|
accountManagement : function() {
|
||||||
var accountUrl = kc.createAccountUrl();
|
var accountUrl = kc.createAccountUrl();
|
||||||
var ref = window.open(accountUrl, '_blank', 'location=no');
|
var ref = window.open(accountUrl, '_blank', 'location=no');
|
||||||
|
|
|
@ -70,6 +70,15 @@ public class MigrateTo1_6_0 {
|
||||||
if ((adminConsoleClient != null) && !localeMapperAdded(adminConsoleClient)) {
|
if ((adminConsoleClient != null) && !localeMapperAdded(adminConsoleClient)) {
|
||||||
adminConsoleClient.addProtocolMapper(localeMapper);
|
adminConsoleClient.addProtocolMapper(localeMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ClientModel client = realm.getMasterAdminClient();
|
||||||
|
if (client.getRole(AdminRoles.CREATE_CLIENT) == null) {
|
||||||
|
RoleModel role = client.addRole(AdminRoles.CREATE_CLIENT);
|
||||||
|
role.setDescription("${role_" + AdminRoles.CREATE_CLIENT + "}");
|
||||||
|
role.setScopeParamRequired(false);
|
||||||
|
|
||||||
|
realm.getRole(AdminRoles.ADMIN).addCompositeRole(role);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ public class AdminRoles {
|
||||||
public static String REALM_ADMIN = "realm-admin";
|
public static String REALM_ADMIN = "realm-admin";
|
||||||
|
|
||||||
public static String CREATE_REALM = "create-realm";
|
public static String CREATE_REALM = "create-realm";
|
||||||
|
public static String CREATE_CLIENT = "create-client";
|
||||||
|
|
||||||
public static String VIEW_REALM = "view-realm";
|
public static String VIEW_REALM = "view-realm";
|
||||||
public static String VIEW_USERS = "view-users";
|
public static String VIEW_USERS = "view-users";
|
||||||
|
@ -26,6 +27,6 @@ public class AdminRoles {
|
||||||
public static String MANAGE_CLIENTS = "manage-clients";
|
public static String MANAGE_CLIENTS = "manage-clients";
|
||||||
public static String MANAGE_EVENTS = "manage-events";
|
public static String MANAGE_EVENTS = "manage-events";
|
||||||
|
|
||||||
public static String[] ALL_REALM_ROLES = {VIEW_REALM, VIEW_USERS, VIEW_CLIENTS, VIEW_EVENTS, VIEW_IDENTITY_PROVIDERS, MANAGE_REALM, MANAGE_USERS, MANAGE_CLIENTS, MANAGE_EVENTS, MANAGE_IDENTITY_PROVIDERS};
|
public static String[] ALL_REALM_ROLES = {CREATE_CLIENT, VIEW_REALM, VIEW_USERS, VIEW_CLIENTS, VIEW_EVENTS, VIEW_IDENTITY_PROVIDERS, MANAGE_REALM, MANAGE_USERS, MANAGE_CLIENTS, MANAGE_EVENTS, MANAGE_IDENTITY_PROVIDERS};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
6
pom.xml
6
pom.xml
|
@ -137,6 +137,7 @@
|
||||||
<module>common</module>
|
<module>common</module>
|
||||||
<module>core</module>
|
<module>core</module>
|
||||||
<module>core-jaxrs</module>
|
<module>core-jaxrs</module>
|
||||||
|
<module>client-api</module>
|
||||||
<module>connections</module>
|
<module>connections</module>
|
||||||
<module>dependencies</module>
|
<module>dependencies</module>
|
||||||
<module>events</module>
|
<module>events</module>
|
||||||
|
@ -650,6 +651,11 @@
|
||||||
<artifactId>keycloak-core</artifactId>
|
<artifactId>keycloak-core</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-client-api</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-core-jaxrs</artifactId>
|
<artifactId>keycloak-core-jaxrs</artifactId>
|
||||||
|
|
|
@ -7,6 +7,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
@ -44,7 +45,11 @@ public class ClientIdAndSecretAuthenticator extends AbstractClientAuthenticator
|
||||||
String clientSecret = null;
|
String clientSecret = null;
|
||||||
|
|
||||||
String authorizationHeader = context.getHttpRequest().getHttpHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
String authorizationHeader = context.getHttpRequest().getHttpHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
||||||
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
|
|
||||||
|
MediaType mediaType = context.getHttpRequest().getHttpHeaders().getMediaType();
|
||||||
|
boolean hasFormData = mediaType != null && mediaType.isCompatible(MediaType.APPLICATION_FORM_URLENCODED_TYPE);
|
||||||
|
|
||||||
|
MultivaluedMap<String, String> formData = hasFormData ? context.getHttpRequest().getDecodedFormParameters() : null;
|
||||||
|
|
||||||
if (authorizationHeader != null) {
|
if (authorizationHeader != null) {
|
||||||
String[] usernameSecret = BasicAuthHelper.parseHeader(authorizationHeader);
|
String[] usernameSecret = BasicAuthHelper.parseHeader(authorizationHeader);
|
||||||
|
@ -54,7 +59,7 @@ public class ClientIdAndSecretAuthenticator extends AbstractClientAuthenticator
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Don't send 401 if client_id parameter was sent in request. For example IE may automatically send "Authorization: Negotiate" in XHR requests even for public clients
|
// Don't send 401 if client_id parameter was sent in request. For example IE may automatically send "Authorization: Negotiate" in XHR requests even for public clients
|
||||||
if (!formData.containsKey(OAuth2Constants.CLIENT_ID)) {
|
if (formData != null && !formData.containsKey(OAuth2Constants.CLIENT_ID)) {
|
||||||
Response challengeResponse = Response.status(Response.Status.UNAUTHORIZED).header(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"" + context.getRealm().getName() + "\"").build();
|
Response challengeResponse = Response.status(Response.Status.UNAUTHORIZED).header(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"" + context.getRealm().getName() + "\"").build();
|
||||||
context.challenge(challengeResponse);
|
context.challenge(challengeResponse);
|
||||||
return;
|
return;
|
||||||
|
@ -62,7 +67,7 @@ public class ClientIdAndSecretAuthenticator extends AbstractClientAuthenticator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client_id == null) {
|
if (formData != null && client_id == null) {
|
||||||
client_id = formData.getFirst(OAuth2Constants.CLIENT_ID);
|
client_id = formData.getFirst(OAuth2Constants.CLIENT_ID);
|
||||||
clientSecret = formData.getFirst("client_secret");
|
clientSecret = formData.getFirst("client_secret");
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,14 +17,14 @@ public class ProtocolMapperUtils {
|
||||||
public static final String USER_ATTRIBUTE = "user.attribute";
|
public static final String USER_ATTRIBUTE = "user.attribute";
|
||||||
public static final String USER_SESSION_NOTE = "user.session.note";
|
public static final String USER_SESSION_NOTE = "user.session.note";
|
||||||
public static final String MULTIVALUED = "multivalued";
|
public static final String MULTIVALUED = "multivalued";
|
||||||
public static final String USER_MODEL_PROPERTY_LABEL = "User Property";
|
public static final String USER_MODEL_PROPERTY_LABEL = "usermodel.prop.label";
|
||||||
public static final String USER_MODEL_PROPERTY_HELP_TEXT = "Name of the property method in the UserModel interface. For example, a value of 'email' would reference the UserModel.getEmail() method.";
|
public static final String USER_MODEL_PROPERTY_HELP_TEXT = "usermodel.prop.tooltip";
|
||||||
public static final String USER_MODEL_ATTRIBUTE_LABEL = "User Attribute";
|
public static final String USER_MODEL_ATTRIBUTE_LABEL = "usermodel.attr.label";
|
||||||
public static final String USER_MODEL_ATTRIBUTE_HELP_TEXT = "Name of stored user attribute which is the name of an attribute within the UserModel.attribute map.";
|
public static final String USER_MODEL_ATTRIBUTE_HELP_TEXT = "usermodel.attr.tooltip";
|
||||||
public static final String USER_SESSION_MODEL_NOTE_LABEL = "User Session Note";
|
public static final String USER_SESSION_MODEL_NOTE_LABEL = "userSession.modelNote.label";
|
||||||
public static final String USER_SESSION_MODEL_NOTE_HELP_TEXT = "Name of stored user session note within the UserSessionModel.note map.";
|
public static final String USER_SESSION_MODEL_NOTE_HELP_TEXT = "userSession.modelNote.tooltip";
|
||||||
public static final String MULTIVALUED_LABEL = "Multivalued";
|
public static final String MULTIVALUED_LABEL = "multivalued.label";
|
||||||
public static final String MULTIVALUED_HELP_TEXT = "Indicates if attribute supports multiple values. If true, then the list of all values of this attribute will be set as claim. If false, then just first value will be set as claim";
|
public static final String MULTIVALUED_HELP_TEXT = "multivalued.tooltip";
|
||||||
|
|
||||||
public static String getUserModelValue(UserModel user, String propertyName) {
|
public static String getUserModelValue(UserModel user, String propertyName) {
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.keycloak.services.managers;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.spi.UnauthorizedException;
|
import org.jboss.resteasy.spi.UnauthorizedException;
|
||||||
import org.keycloak.ClientConnection;
|
import org.keycloak.ClientConnection;
|
||||||
|
import org.keycloak.models.KeycloakContext;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
|
||||||
|
@ -39,6 +40,11 @@ public class AppAuthManager extends AuthenticationManager {
|
||||||
return tokenString;
|
return tokenString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AuthResult authenticateBearerToken(KeycloakSession session, RealmModel realm) {
|
||||||
|
KeycloakContext ctx = session.getContext();
|
||||||
|
return authenticateBearerToken(session, realm, ctx.getUri(), ctx.getConnection(), ctx.getRequestHeaders());
|
||||||
|
}
|
||||||
|
|
||||||
public AuthResult authenticateBearerToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
|
public AuthResult authenticateBearerToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
|
||||||
String tokenString = extractAuthorizationHeaderToken(headers);
|
String tokenString = extractAuthorizationHeaderToken(headers);
|
||||||
if (tokenString == null) return null;
|
if (tokenString == null) return null;
|
||||||
|
|
|
@ -3,21 +3,27 @@ package org.keycloak.services.resources;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.spi.BadRequestException;
|
import org.jboss.resteasy.spi.BadRequestException;
|
||||||
import org.jboss.resteasy.spi.NotFoundException;
|
import org.jboss.resteasy.spi.NotFoundException;
|
||||||
|
import org.jboss.resteasy.spi.UnauthorizedException;
|
||||||
|
import org.keycloak.events.Errors;
|
||||||
import org.keycloak.events.EventBuilder;
|
import org.keycloak.events.EventBuilder;
|
||||||
|
import org.keycloak.events.EventType;
|
||||||
import org.keycloak.exportimport.ClientDescriptionConverter;
|
import org.keycloak.exportimport.ClientDescriptionConverter;
|
||||||
import org.keycloak.exportimport.KeycloakClientDescriptionConverter;
|
import org.keycloak.exportimport.KeycloakClientDescriptionConverter;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.*;
|
||||||
import org.keycloak.models.KeycloakSession;
|
|
||||||
import org.keycloak.models.ModelDuplicateException;
|
|
||||||
import org.keycloak.models.RealmModel;
|
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
import org.keycloak.models.utils.RepresentationToModel;
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
|
import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
|
||||||
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.services.ErrorResponse;
|
import org.keycloak.services.ErrorResponse;
|
||||||
|
import org.keycloak.services.ErrorResponseException;
|
||||||
|
import org.keycloak.services.ForbiddenException;
|
||||||
|
import org.keycloak.services.managers.AppAuthManager;
|
||||||
|
import org.keycloak.services.managers.AuthenticationManager;
|
||||||
|
|
||||||
import javax.ws.rs.*;
|
import javax.ws.rs.*;
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
@ -36,6 +42,8 @@ public class ClientRegistrationService {
|
||||||
@Context
|
@Context
|
||||||
private KeycloakSession session;
|
private KeycloakSession session;
|
||||||
|
|
||||||
|
private AppAuthManager authManager = new AppAuthManager();
|
||||||
|
|
||||||
public ClientRegistrationService(RealmModel realm, EventBuilder event) {
|
public ClientRegistrationService(RealmModel realm, EventBuilder event) {
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
this.event = event;
|
this.event = event;
|
||||||
|
@ -44,6 +52,10 @@ public class ClientRegistrationService {
|
||||||
@POST
|
@POST
|
||||||
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN })
|
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN })
|
||||||
public Response create(String description, @QueryParam("format") String format) {
|
public Response create(String description, @QueryParam("format") String format) {
|
||||||
|
event.event(EventType.CLIENT_REGISTER);
|
||||||
|
|
||||||
|
authenticate(true, null);
|
||||||
|
|
||||||
if (format == null) {
|
if (format == null) {
|
||||||
format = KeycloakClientDescriptionConverter.ID;
|
format = KeycloakClientDescriptionConverter.ID;
|
||||||
}
|
}
|
||||||
|
@ -58,6 +70,10 @@ public class ClientRegistrationService {
|
||||||
ClientModel clientModel = RepresentationToModel.createClient(session, realm, rep, true);
|
ClientModel clientModel = RepresentationToModel.createClient(session, realm, rep, true);
|
||||||
rep = ModelToRepresentation.toRepresentation(clientModel);
|
rep = ModelToRepresentation.toRepresentation(clientModel);
|
||||||
URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(clientModel.getId()).build();
|
URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(clientModel.getId()).build();
|
||||||
|
|
||||||
|
logger.infov("Created client {0}", rep.getClientId());
|
||||||
|
|
||||||
|
event.client(rep.getClientId()).success();
|
||||||
return Response.created(uri).entity(rep).build();
|
return Response.created(uri).entity(rep).build();
|
||||||
} catch (ModelDuplicateException e) {
|
} catch (ModelDuplicateException e) {
|
||||||
return ErrorResponse.exists("Client " + rep.getClientId() + " already exists");
|
return ErrorResponse.exists("Client " + rep.getClientId() + " already exists");
|
||||||
|
@ -67,34 +83,79 @@ public class ClientRegistrationService {
|
||||||
@GET
|
@GET
|
||||||
@Path("{clientId}")
|
@Path("{clientId}")
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public ClientRepresentation get(@PathParam("clientId") String clientId) {
|
public Response get(@PathParam("clientId") String clientId) {
|
||||||
AuthorizeClientUtil.ClientAuthResult clientAuth = AuthorizeClientUtil.authorizeClient(session, event, realm);
|
event.event(EventType.CLIENT_INFO);
|
||||||
ClientModel client = clientAuth.getClient();
|
|
||||||
|
ClientModel client = authenticate(false, clientId);
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
throw new NotFoundException("Client not found");
|
return Response.status(Response.Status.NOT_FOUND).build();
|
||||||
}
|
}
|
||||||
return ModelToRepresentation.toRepresentation(client);
|
return Response.ok(ModelToRepresentation.toRepresentation(client)).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@PUT
|
@PUT
|
||||||
@Path("{clientId}")
|
@Path("{clientId}")
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
public void update(@PathParam("clientId") String clientId, ClientRepresentation rep) {
|
public Response update(@PathParam("clientId") String clientId, ClientRepresentation rep) {
|
||||||
ClientModel client = realm.getClientByClientId(clientId);
|
event.event(EventType.CLIENT_UPDATE).client(clientId);
|
||||||
if (client == null) {
|
|
||||||
throw new NotFoundException("Client not found");
|
ClientModel client = authenticate(false, clientId);
|
||||||
}
|
|
||||||
RepresentationToModel.updateClient(rep, client);
|
RepresentationToModel.updateClient(rep, client);
|
||||||
|
|
||||||
|
logger.infov("Updated client {0}", rep.getClientId());
|
||||||
|
|
||||||
|
event.success();
|
||||||
|
return Response.status(Response.Status.OK).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@DELETE
|
@DELETE
|
||||||
@Path("{clientId}")
|
@Path("{clientId}")
|
||||||
public void delete(@PathParam("clientId") String clientId) {
|
public Response delete(@PathParam("clientId") String clientId) {
|
||||||
ClientModel client = realm.getClientByClientId(clientId);
|
event.event(EventType.CLIENT_DELETE).client(clientId);
|
||||||
if (client == null) {
|
|
||||||
throw new NotFoundException("Client not found");
|
ClientModel client = authenticate(false, clientId);
|
||||||
|
if (realm.removeClient(client.getId())) {
|
||||||
|
event.success();
|
||||||
|
return Response.ok().build();
|
||||||
|
} else {
|
||||||
|
return Response.status(Response.Status.NOT_FOUND).build();
|
||||||
}
|
}
|
||||||
realm.removeClient(client.getId());
|
}
|
||||||
|
|
||||||
|
private ClientModel authenticate(boolean create, String clientId) {
|
||||||
|
String authorizationHeader = session.getContext().getRequestHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
||||||
|
|
||||||
|
boolean bearer = authorizationHeader != null && authorizationHeader.split(" ")[0].equalsIgnoreCase("Bearer");
|
||||||
|
|
||||||
|
if (bearer) {
|
||||||
|
AuthenticationManager.AuthResult authResult = authManager.authenticateBearerToken(session, realm);
|
||||||
|
AccessToken.Access realmAccess = authResult.getToken().getResourceAccess(Constants.REALM_MANAGEMENT_CLIENT_ID);
|
||||||
|
if (realmAccess != null) {
|
||||||
|
if (realmAccess.isUserInRole(AdminRoles.MANAGE_CLIENTS)) {
|
||||||
|
return create ? null : realm.getClientByClientId(clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (create && realmAccess.isUserInRole(AdminRoles.CREATE_CLIENT)) {
|
||||||
|
return create ? null : realm.getClientByClientId(clientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!create) {
|
||||||
|
ClientModel client;
|
||||||
|
|
||||||
|
try {
|
||||||
|
AuthorizeClientUtil.ClientAuthResult clientAuth = AuthorizeClientUtil.authorizeClient(session, event, realm);
|
||||||
|
client = clientAuth.getClient();
|
||||||
|
|
||||||
|
if (client != null && !client.isPublicClient() && client.getClientId().equals(clientId)) {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event.error(Errors.NOT_ALLOWED);
|
||||||
|
|
||||||
|
throw new ForbiddenException();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,14 +112,14 @@ public class RealmsResource {
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Path("{realm}/client-registration")
|
@Path("{realm}/client-registration")
|
||||||
// public ClientRegistrationService getClientsService(final @PathParam("realm") String name) {
|
public ClientRegistrationService getClientsService(final @PathParam("realm") String name) {
|
||||||
// RealmModel realm = init(name);
|
RealmModel realm = init(name);
|
||||||
// EventBuilder event = new EventBuilder(realm, session, clientConnection);
|
EventBuilder event = new EventBuilder(realm, session, clientConnection);
|
||||||
// ClientRegistrationService service = new ClientRegistrationService(realm, event);
|
ClientRegistrationService service = new ClientRegistrationService(realm, event);
|
||||||
// ResteasyProviderFactory.getInstance().injectProperties(service);
|
ResteasyProviderFactory.getInstance().injectProperties(service);
|
||||||
// return service;
|
return service;
|
||||||
// }
|
}
|
||||||
|
|
||||||
@Path("{realm}/clients-managements")
|
@Path("{realm}/clients-managements")
|
||||||
public ClientsManagementService getClientsManagementService(final @PathParam("realm") String name) {
|
public ClientsManagementService getClientsManagementService(final @PathParam("realm") String name) {
|
||||||
|
|
|
@ -20,6 +20,8 @@ import org.keycloak.admin.client.Keycloak;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
|
import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||||
|
import org.keycloak.testsuite.util.OAuthClient;
|
||||||
|
|
||||||
import static org.keycloak.testsuite.auth.page.AuthRealm.ADMIN;
|
import static org.keycloak.testsuite.auth.page.AuthRealm.ADMIN;
|
||||||
import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
|
import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
|
||||||
|
|
||||||
|
@ -55,6 +57,10 @@ public class ContainersTestEnricher {
|
||||||
@ClassScoped
|
@ClassScoped
|
||||||
private InstanceProducer<Keycloak> adminClient;
|
private InstanceProducer<Keycloak> adminClient;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@ClassScoped
|
||||||
|
private InstanceProducer<OAuthClient> oauthClient;
|
||||||
|
|
||||||
private ContainerController controller;
|
private ContainerController controller;
|
||||||
|
|
||||||
private final boolean migrationTests = System.getProperty("migration", "false").equals("true");
|
private final boolean migrationTests = System.getProperty("migration", "false").equals("true");
|
||||||
|
@ -92,6 +98,7 @@ public class ContainersTestEnricher {
|
||||||
|
|
||||||
initializeTestContext(testClass);
|
initializeTestContext(testClass);
|
||||||
initializeAdminClient();
|
initializeAdminClient();
|
||||||
|
initializeOAuthClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeTestContext(Class testClass) {
|
private void initializeTestContext(Class testClass) {
|
||||||
|
@ -116,6 +123,10 @@ public class ContainersTestEnricher {
|
||||||
MASTER, ADMIN, ADMIN, Constants.ADMIN_CONSOLE_CLIENT_ID));
|
MASTER, ADMIN, ADMIN, Constants.ADMIN_CONSOLE_CLIENT_ID));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initializeOAuthClient() {
|
||||||
|
oauthClient.set(new OAuthClient(getAuthServerContextRootFromSystemProperty() + "/auth"));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param testClass
|
* @param testClass
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package org.keycloak.testsuite.arquillian;
|
package org.keycloak.testsuite.arquillian;
|
||||||
|
|
||||||
import org.keycloak.testsuite.arquillian.provider.URLProvider;
|
import org.keycloak.testsuite.arquillian.provider.*;
|
||||||
import org.keycloak.testsuite.arquillian.provider.SuiteContextProvider;
|
|
||||||
import org.keycloak.testsuite.arquillian.provider.TestContextProvider;
|
|
||||||
import org.jboss.arquillian.container.spi.client.container.DeployableContainer;
|
import org.jboss.arquillian.container.spi.client.container.DeployableContainer;
|
||||||
import org.jboss.arquillian.container.test.impl.enricher.resource.URLResourceProvider;
|
import org.jboss.arquillian.container.test.impl.enricher.resource.URLResourceProvider;
|
||||||
import org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor;
|
import org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor;
|
||||||
|
@ -12,7 +10,6 @@ import org.jboss.arquillian.graphene.location.CustomizableURLResourceProvider;
|
||||||
import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider;
|
import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider;
|
||||||
import org.jboss.arquillian.test.spi.execution.TestExecutionDecider;
|
import org.jboss.arquillian.test.spi.execution.TestExecutionDecider;
|
||||||
import org.keycloak.testsuite.arquillian.jira.JiraTestExecutionDecider;
|
import org.keycloak.testsuite.arquillian.jira.JiraTestExecutionDecider;
|
||||||
import org.keycloak.testsuite.arquillian.provider.AdminClientProvider;
|
|
||||||
import org.keycloak.testsuite.arquillian.undertow.CustomUndertowContainer;
|
import org.keycloak.testsuite.arquillian.undertow.CustomUndertowContainer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,7 +24,8 @@ public class KeycloakArquillianExtension implements LoadableExtension {
|
||||||
builder
|
builder
|
||||||
.service(ResourceProvider.class, SuiteContextProvider.class)
|
.service(ResourceProvider.class, SuiteContextProvider.class)
|
||||||
.service(ResourceProvider.class, TestContextProvider.class)
|
.service(ResourceProvider.class, TestContextProvider.class)
|
||||||
.service(ResourceProvider.class, AdminClientProvider.class);
|
.service(ResourceProvider.class, AdminClientProvider.class)
|
||||||
|
.service(ResourceProvider.class, OAuthClientProvider.class);
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.service(DeploymentScenarioGenerator.class, DeploymentTargetModifier.class)
|
.service(DeploymentScenarioGenerator.class, DeploymentTargetModifier.class)
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package org.keycloak.testsuite.arquillian.provider;
|
||||||
|
|
||||||
|
import org.jboss.arquillian.core.api.Instance;
|
||||||
|
import org.jboss.arquillian.core.api.annotation.Inject;
|
||||||
|
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||||
|
import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider;
|
||||||
|
import org.keycloak.testsuite.util.OAuthClient;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class OAuthClientProvider implements ResourceProvider {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
Instance<OAuthClient> oauthClient;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canProvide(Class<?> type) {
|
||||||
|
return OAuthClient.class.isAssignableFrom(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object lookup(ArquillianResource resource, Annotation... qualifiers) {
|
||||||
|
return oauthClient.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
package org.keycloak.testsuite.util;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.NameValuePair;
|
||||||
|
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
|
import org.keycloak.OAuth2Constants;
|
||||||
|
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
||||||
|
import org.keycloak.representations.AccessTokenResponse;
|
||||||
|
import org.keycloak.util.BasicAuthHelper;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.UriBuilder;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class OAuthClient {
|
||||||
|
|
||||||
|
private String baseUrl;
|
||||||
|
|
||||||
|
public OAuthClient(String baseUrl) {
|
||||||
|
this.baseUrl = baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccessTokenResponse getToken(String realm, String clientId, String clientSecret, String username, String password) {
|
||||||
|
CloseableHttpClient httpclient = HttpClients.createDefault();
|
||||||
|
try {
|
||||||
|
HttpPost post = new HttpPost(OIDCLoginProtocolService.tokenUrl(UriBuilder.fromUri(baseUrl)).build(realm));
|
||||||
|
|
||||||
|
List<NameValuePair> parameters = new LinkedList<NameValuePair>();
|
||||||
|
parameters.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD));
|
||||||
|
parameters.add(new BasicNameValuePair("username", username));
|
||||||
|
parameters.add(new BasicNameValuePair("password", password));
|
||||||
|
if (clientSecret != null) {
|
||||||
|
String authorization = BasicAuthHelper.createHeader(clientId, clientSecret);
|
||||||
|
post.setHeader("Authorization", authorization);
|
||||||
|
} else {
|
||||||
|
parameters.add(new BasicNameValuePair("client_id", clientId));
|
||||||
|
}
|
||||||
|
|
||||||
|
UrlEncodedFormEntity formEntity;
|
||||||
|
try {
|
||||||
|
formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
post.setEntity(formEntity);
|
||||||
|
|
||||||
|
CloseableHttpResponse response = httpclient.execute(post);
|
||||||
|
|
||||||
|
if (response.getStatusLine().getStatusCode() != 200) {
|
||||||
|
throw new RuntimeException("Failed to retrieve token: " + response.getStatusLine().toString() + " / " + IOUtils.toString(response.getEntity().getContent()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonSerialization.readValue(response.getEntity().getContent(), AccessTokenResponse.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
httpclient.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import static org.keycloak.testsuite.admin.Users.setPasswordFor;
|
import static org.keycloak.testsuite.admin.Users.setPasswordFor;
|
||||||
import org.keycloak.testsuite.arquillian.SuiteContext;
|
import org.keycloak.testsuite.arquillian.SuiteContext;
|
||||||
|
import org.keycloak.testsuite.util.OAuthClient;
|
||||||
import org.openqa.selenium.WebDriver;
|
import org.openqa.selenium.WebDriver;
|
||||||
import org.keycloak.testsuite.auth.page.AuthServer;
|
import org.keycloak.testsuite.auth.page.AuthServer;
|
||||||
import org.keycloak.testsuite.auth.page.AuthServerContextRoot;
|
import org.keycloak.testsuite.auth.page.AuthServerContextRoot;
|
||||||
|
@ -51,6 +52,9 @@ public abstract class AbstractKeycloakTest {
|
||||||
@ArquillianResource
|
@ArquillianResource
|
||||||
protected Keycloak adminClient;
|
protected Keycloak adminClient;
|
||||||
|
|
||||||
|
@ArquillianResource
|
||||||
|
protected OAuthClient oauthClient;
|
||||||
|
|
||||||
protected List<RealmRepresentation> testRealmReps;
|
protected List<RealmRepresentation> testRealmReps;
|
||||||
|
|
||||||
@Drone
|
@Drone
|
||||||
|
|
|
@ -0,0 +1,306 @@
|
||||||
|
package org.keycloak.testsuite.client;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.client.registration.ClientRegistration;
|
||||||
|
import org.keycloak.client.registration.ClientRegistrationException;
|
||||||
|
import org.keycloak.client.registration.HttpErrorException;
|
||||||
|
import org.keycloak.models.AdminRoles;
|
||||||
|
import org.keycloak.models.Constants;
|
||||||
|
import org.keycloak.representations.AccessTokenResponse;
|
||||||
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class ClientRegistrationTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
|
private static final String REALM_NAME = "test";
|
||||||
|
private static final String CLIENT_ID = "test-client";
|
||||||
|
private static final String CLIENT_SECRET = "test-client-secret";
|
||||||
|
|
||||||
|
private ClientRegistration clientRegistrationAsAdmin;
|
||||||
|
private ClientRegistration clientRegistrationAsClient;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() throws ClientRegistrationException {
|
||||||
|
clientRegistrationAsAdmin = clientBuilder().auth(getToken("manage-clients", "password")).build();
|
||||||
|
clientRegistrationAsClient = clientBuilder().auth(CLIENT_ID, CLIENT_SECRET).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() throws ClientRegistrationException {
|
||||||
|
clientRegistrationAsAdmin.close();
|
||||||
|
clientRegistrationAsClient.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||||
|
RealmRepresentation rep = new RealmRepresentation();
|
||||||
|
rep.setEnabled(true);
|
||||||
|
rep.setRealm(REALM_NAME);
|
||||||
|
rep.setUsers(new LinkedList<UserRepresentation>());
|
||||||
|
|
||||||
|
LinkedList<CredentialRepresentation> credentials = new LinkedList<>();
|
||||||
|
CredentialRepresentation password = new CredentialRepresentation();
|
||||||
|
password.setType(CredentialRepresentation.PASSWORD);
|
||||||
|
password.setValue("password");
|
||||||
|
credentials.add(password);
|
||||||
|
|
||||||
|
UserRepresentation user = new UserRepresentation();
|
||||||
|
user.setEnabled(true);
|
||||||
|
user.setUsername("manage-clients");
|
||||||
|
user.setCredentials(credentials);
|
||||||
|
user.setClientRoles(Collections.singletonMap(Constants.REALM_MANAGEMENT_CLIENT_ID, Collections.singletonList(AdminRoles.MANAGE_CLIENTS)));
|
||||||
|
|
||||||
|
rep.getUsers().add(user);
|
||||||
|
|
||||||
|
UserRepresentation user2 = new UserRepresentation();
|
||||||
|
user2.setEnabled(true);
|
||||||
|
user2.setUsername("create-clients");
|
||||||
|
user2.setCredentials(credentials);
|
||||||
|
user2.setClientRoles(Collections.singletonMap(Constants.REALM_MANAGEMENT_CLIENT_ID, Collections.singletonList(AdminRoles.CREATE_CLIENT)));
|
||||||
|
|
||||||
|
rep.getUsers().add(user2);
|
||||||
|
|
||||||
|
UserRepresentation user3 = new UserRepresentation();
|
||||||
|
user3.setEnabled(true);
|
||||||
|
user3.setUsername("no-access");
|
||||||
|
user3.setCredentials(credentials);
|
||||||
|
|
||||||
|
rep.getUsers().add(user3);
|
||||||
|
|
||||||
|
testRealms.add(rep);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerClient(ClientRegistration clientRegistration) throws ClientRegistrationException {
|
||||||
|
ClientRepresentation client = new ClientRepresentation();
|
||||||
|
client.setClientId(CLIENT_ID);
|
||||||
|
client.setSecret(CLIENT_SECRET);
|
||||||
|
|
||||||
|
ClientRepresentation createdClient = clientRegistration.create(client);
|
||||||
|
assertEquals(CLIENT_ID, createdClient.getClientId());
|
||||||
|
|
||||||
|
client = adminClient.realm(REALM_NAME).clients().get(createdClient.getId()).toRepresentation();
|
||||||
|
assertEquals(CLIENT_ID, client.getClientId());
|
||||||
|
|
||||||
|
AccessTokenResponse token2 = oauthClient.getToken(REALM_NAME, CLIENT_ID, CLIENT_SECRET, "manage-clients", "password");
|
||||||
|
assertNotNull(token2.getToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void registerClientAsAdmin() throws ClientRegistrationException {
|
||||||
|
registerClient(clientRegistrationAsAdmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void registerClientAsAdminWithCreateOnly() throws ClientRegistrationException {
|
||||||
|
ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build();
|
||||||
|
try {
|
||||||
|
registerClient(clientRegistration);
|
||||||
|
} finally {
|
||||||
|
clientRegistration.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void registerClientAsAdminWithNoAccess() throws ClientRegistrationException {
|
||||||
|
ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build();
|
||||||
|
try {
|
||||||
|
registerClient(clientRegistration);
|
||||||
|
fail("Expected 403");
|
||||||
|
} catch (ClientRegistrationException e) {
|
||||||
|
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
|
||||||
|
} finally {
|
||||||
|
clientRegistration.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getClientAsAdminWithCreateOnly() throws ClientRegistrationException {
|
||||||
|
registerClient(clientRegistrationAsAdmin);
|
||||||
|
ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build();
|
||||||
|
try {
|
||||||
|
clientRegistration.get(CLIENT_ID);
|
||||||
|
fail("Expected 403");
|
||||||
|
} catch (ClientRegistrationException e) {
|
||||||
|
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
|
||||||
|
} finally {
|
||||||
|
clientRegistration.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void wrongClient() throws ClientRegistrationException {
|
||||||
|
registerClient(clientRegistrationAsAdmin);
|
||||||
|
|
||||||
|
ClientRepresentation client = new ClientRepresentation();
|
||||||
|
client.setClientId("test-client-2");
|
||||||
|
client.setSecret("test-client-2-secret");
|
||||||
|
|
||||||
|
clientRegistrationAsAdmin.create(client);
|
||||||
|
|
||||||
|
ClientRegistration clientRegistration = clientBuilder().auth("test-client-2", "test-client-2-secret").build();
|
||||||
|
|
||||||
|
client = clientRegistration.get("test-client-2");
|
||||||
|
assertNotNull(client);
|
||||||
|
assertEquals("test-client-2", client.getClientId());
|
||||||
|
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
clientRegistration.get(CLIENT_ID);
|
||||||
|
fail("Expected 403");
|
||||||
|
} catch (ClientRegistrationException e) {
|
||||||
|
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
client = clientRegistrationAsAdmin.get(CLIENT_ID);
|
||||||
|
try {
|
||||||
|
clientRegistration.update(client);
|
||||||
|
fail("Expected 403");
|
||||||
|
} catch (ClientRegistrationException e) {
|
||||||
|
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
clientRegistration.delete(CLIENT_ID);
|
||||||
|
fail("Expected 403");
|
||||||
|
} catch (ClientRegistrationException e) {
|
||||||
|
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
clientRegistration.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getClientAsAdminWithNoAccess() throws ClientRegistrationException {
|
||||||
|
registerClient(clientRegistrationAsAdmin);
|
||||||
|
ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build();
|
||||||
|
try {
|
||||||
|
clientRegistration.get(CLIENT_ID);
|
||||||
|
fail("Expected 403");
|
||||||
|
} catch (ClientRegistrationException e) {
|
||||||
|
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
|
||||||
|
} finally {
|
||||||
|
clientRegistration.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateClient(ClientRegistration clientRegistration) throws ClientRegistrationException {
|
||||||
|
ClientRepresentation client = clientRegistration.get(CLIENT_ID);
|
||||||
|
client.setRedirectUris(Collections.singletonList("http://localhost:8080/app"));
|
||||||
|
|
||||||
|
clientRegistration.update(client);
|
||||||
|
|
||||||
|
ClientRepresentation updatedClient = clientRegistration.get(CLIENT_ID);
|
||||||
|
|
||||||
|
assertEquals(1, updatedClient.getRedirectUris().size());
|
||||||
|
assertEquals("http://localhost:8080/app", updatedClient.getRedirectUris().get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateClientAsAdmin() throws ClientRegistrationException {
|
||||||
|
registerClient(clientRegistrationAsAdmin);
|
||||||
|
updateClient(clientRegistrationAsAdmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateClientAsAdminWithCreateOnly() throws ClientRegistrationException {
|
||||||
|
ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build();
|
||||||
|
try {
|
||||||
|
updateClient(clientRegistration);
|
||||||
|
fail("Expected 403");
|
||||||
|
} catch (ClientRegistrationException e) {
|
||||||
|
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
|
||||||
|
} finally {
|
||||||
|
clientRegistration.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateClientAsAdminWithNoAccess() throws ClientRegistrationException {
|
||||||
|
ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build();
|
||||||
|
try {
|
||||||
|
updateClient(clientRegistration);
|
||||||
|
fail("Expected 403");
|
||||||
|
} catch (ClientRegistrationException e) {
|
||||||
|
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
|
||||||
|
} finally {
|
||||||
|
clientRegistration.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateClientAsClient() throws ClientRegistrationException {
|
||||||
|
registerClient(clientRegistrationAsAdmin);
|
||||||
|
updateClient(clientRegistrationAsClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteClient(ClientRegistration clientRegistration) throws ClientRegistrationException {
|
||||||
|
clientRegistration.delete(CLIENT_ID);
|
||||||
|
|
||||||
|
// Can't authenticate as client after client is deleted
|
||||||
|
ClientRepresentation client = clientRegistrationAsAdmin.get(CLIENT_ID);
|
||||||
|
assertNull(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteClientAsAdmin() throws ClientRegistrationException {
|
||||||
|
registerClient(clientRegistrationAsAdmin);
|
||||||
|
deleteClient(clientRegistrationAsAdmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteClientAsAdminWithCreateOnly() throws ClientRegistrationException {
|
||||||
|
ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build();
|
||||||
|
try {
|
||||||
|
deleteClient(clientRegistration);
|
||||||
|
fail("Expected 403");
|
||||||
|
} catch (ClientRegistrationException e) {
|
||||||
|
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
|
||||||
|
} finally {
|
||||||
|
clientRegistration.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteClientAsAdminWithNoAccess() throws ClientRegistrationException {
|
||||||
|
ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build();
|
||||||
|
try {
|
||||||
|
deleteClient(clientRegistration);
|
||||||
|
fail("Expected 403");
|
||||||
|
} catch (ClientRegistrationException e) {
|
||||||
|
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
|
||||||
|
} finally {
|
||||||
|
clientRegistration.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteClientAsClient() throws ClientRegistrationException {
|
||||||
|
registerClient(clientRegistrationAsAdmin);
|
||||||
|
deleteClient(clientRegistrationAsClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClientRegistration.ClientRegistrationBuilder clientBuilder() {
|
||||||
|
return ClientRegistration.create().realm("test").authServerUrl(testContext.getAuthServerContextRoot() + "/auth");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getToken(String username, String password) {
|
||||||
|
return oauthClient.getToken(REALM_NAME, "security-admin-console", null, username, password).getToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -201,6 +201,10 @@
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-admin-client</artifactId>
|
<artifactId>keycloak-admin-client</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-client-api</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-services</artifactId>
|
<artifactId>keycloak-services</artifactId>
|
||||||
|
|
Loading…
Reference in a new issue