keycloak-aplcache
Changes
model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java 9(+9 -0)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java 6(+6 -0)
saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProvider.java 79(+79 -0)
saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProviderFactory.java 38(+38 -0)
saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java 4(+3 -1)
saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory 1(+1 -0)
services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCClientRepresentation.java 132(+132 -0)
services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProvider.java 92(+92 -0)
services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProviderFactory.java 26(+13 -13)
services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java 2(+1 -1)
services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java 21(+14 -7)
services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java 98(+27 -71)
services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java 38(+38 -0)
services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java 100(+100 -0)
services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProviderFactory.java 4(+3 -1)
services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientResponseRepresentation.java 77(+77 -0)
services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory 4(+3 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientRegistrationTest.java 96(+96 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java 103(+103 -0)
Details
diff --git a/client-api/src/main/java/org/keycloak/client/registration/Auth.java b/client-api/src/main/java/org/keycloak/client/registration/Auth.java
new file mode 100644
index 0000000..5b0e85f
--- /dev/null
+++ b/client-api/src/main/java/org/keycloak/client/registration/Auth.java
@@ -0,0 +1,58 @@
+package org.keycloak.client.registration;
+
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpRequest;
+import org.keycloak.common.util.Base64;
+import org.keycloak.representations.idm.ClientRepresentation;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public abstract class Auth {
+
+ public abstract void addAuth(HttpRequest request);
+
+ public static Auth token(String token) {
+ return new BearerTokenAuth(token);
+ }
+
+ public static Auth token(ClientRepresentation client) {
+ return new BearerTokenAuth(client.getRegistrationAccessToken());
+ }
+
+ public static Auth client(String clientId, String clientSecret) {
+ return new BasicAuth(clientId, clientSecret);
+ }
+
+ private static class BearerTokenAuth extends Auth {
+
+ private String token;
+
+ public BearerTokenAuth(String token) {
+ this.token = token;
+ }
+
+ @Override
+ public void addAuth(HttpRequest request) {
+ request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token);
+ }
+ }
+
+ private static class BasicAuth extends Auth {
+
+ private String username;
+ private String password;
+
+ public BasicAuth(String username, String password) {
+ this.username = username;
+ this.password = password;
+ }
+
+ @Override
+ public void addAuth(HttpRequest request) {
+ String val = Base64.encodeBytes((username + ":" + password).getBytes());
+ request.setHeader(HttpHeaders.AUTHORIZATION, "Basic " + val);
+ }
+ }
+
+}
diff --git a/client-api/src/main/java/org/keycloak/client/registration/ClientRegistration.java b/client-api/src/main/java/org/keycloak/client/registration/ClientRegistration.java
index 82a3b37..e59de76 100644
--- a/client-api/src/main/java/org/keycloak/client/registration/ClientRegistration.java
+++ b/client-api/src/main/java/org/keycloak/client/registration/ClientRegistration.java
@@ -1,18 +1,9 @@
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.adapters.config.AdapterConfig;
import org.keycloak.representations.idm.ClientRepresentation;
-import org.keycloak.common.util.Base64;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
@@ -23,160 +14,58 @@ import java.io.InputStream;
*/
public class ClientRegistration {
- private String clientRegistrationUrl;
- private HttpClient httpClient;
- private Auth auth;
+ private final String DEFAULT = "default";
+ private final String INSTALLATION = "install";
- public static ClientRegistrationBuilder create() {
- return new ClientRegistrationBuilder();
- }
+ private HttpUtil httpUtil;
- private ClientRegistration() {
+ public ClientRegistration(String authServerUrl, String realm) {
+ httpUtil = new HttpUtil(HttpClients.createDefault(), HttpUtil.getUrl(authServerUrl, "realms", realm, "clients"));
}
- public ClientRepresentation create(ClientRepresentation client) throws ClientRegistrationException {
- String content = serialize(client);
- InputStream resultStream = doPost(content);
- return deserialize(resultStream, ClientRepresentation.class);
+ public ClientRegistration(String authServerUrl, String realm, HttpClient httpClient) {
+ httpUtil = new HttpUtil(httpClient, HttpUtil.getUrl(authServerUrl, "realms", realm, "clients"));
}
- 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 void close() throws ClientRegistrationException {
+ if (httpUtil != null) {
+ httpUtil.close();
}
+ httpUtil = null;
}
- public ClientRepresentation get(String clientId) throws ClientRegistrationException {
- InputStream resultStream = doGet(clientId);
- return resultStream != null ? deserialize(resultStream, ClientRepresentation.class) : null;
+ public ClientRegistration auth(Auth auth) {
+ httpUtil.setAuth(auth);
+ return this;
}
- public void update(ClientRepresentation client) throws ClientRegistrationException {
+ public ClientRepresentation create(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");
- }
+ InputStream resultStream = httpUtil.doPost(content, DEFAULT);
+ return deserialize(resultStream, ClientRepresentation.class);
}
- public void delete(String clientId) throws ClientRegistrationException {
- doDelete(clientId);
+ public ClientRepresentation get(String clientId) throws ClientRegistrationException {
+ InputStream resultStream = httpUtil.doGet(DEFAULT, clientId);
+ return resultStream != null ? deserialize(resultStream, ClientRepresentation.class) : null;
}
- 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);
- }
- }
+ public AdapterConfig getAdapterConfig(String clientId) throws ClientRegistrationException {
+ InputStream resultStream = httpUtil.doGet(INSTALLATION, clientId);
+ return resultStream != null ? deserialize(resultStream, AdapterConfig.class) : null;
}
- 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);
- }
+ public void update(ClientRepresentation client) throws ClientRegistrationException {
+ String content = serialize(client);
+ httpUtil.doPut(content, DEFAULT, client.getClientId());
}
- 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);
- }
+ public void delete(ClientRepresentation client) throws ClientRegistrationException {
+ delete(client.getClientId());
}
- 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);
- }
+ public void delete(String clientId) throws ClientRegistrationException {
+ httpUtil.doDelete(DEFAULT, clientId);
}
private String serialize(ClientRepresentation client) throws ClientRegistrationException {
@@ -195,81 +84,4 @@ public class ClientRegistration {
}
}
- 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/default";
-
- 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;
- }
- }
-
}
diff --git a/client-api/src/main/java/org/keycloak/client/registration/HttpUtil.java b/client-api/src/main/java/org/keycloak/client/registration/HttpUtil.java
new file mode 100644
index 0000000..699d378
--- /dev/null
+++ b/client-api/src/main/java/org/keycloak/client/registration/HttpUtil.java
@@ -0,0 +1,159 @@
+package org.keycloak.client.registration;
+
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.*;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.keycloak.client.registration.Auth;
+import org.keycloak.client.registration.ClientRegistrationException;
+import org.keycloak.client.registration.HttpErrorException;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+class HttpUtil {
+
+ private HttpClient httpClient;
+
+ private String baseUri;
+
+ private Auth auth;
+
+ HttpUtil(HttpClient httpClient, String baseUri) {
+ this.httpClient = httpClient;
+ this.baseUri = baseUri;
+ }
+
+ void setAuth(Auth auth) {
+ this.auth = auth;
+ }
+
+ InputStream doPost(String content, String... path) throws ClientRegistrationException {
+ try {
+ HttpPost request = new HttpPost(getUrl(baseUri, path));
+
+ request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
+ request.setHeader(HttpHeaders.ACCEPT, "application/json");
+ request.setEntity(new StringEntity(content));
+
+ 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);
+ }
+ }
+
+ InputStream doGet(String... path) throws ClientRegistrationException {
+ try {
+ HttpGet request = new HttpGet(getUrl(baseUri, path));
+
+ request.setHeader(HttpHeaders.ACCEPT, "application/json");
+
+ 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);
+ }
+ }
+
+ void doPut(String content, String... path) throws ClientRegistrationException {
+ try {
+ HttpPut request = new HttpPut(getUrl(baseUri, path));
+
+ request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
+ request.setHeader(HttpHeaders.ACCEPT, "application/json");
+ request.setEntity(new StringEntity(content));
+
+ 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);
+ }
+ }
+
+ void doDelete(String... path) throws ClientRegistrationException {
+ try {
+ HttpDelete request = new HttpDelete(getUrl(baseUri, path));
+
+ 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);
+ }
+ }
+
+ void close() throws ClientRegistrationException {
+ if (httpClient instanceof CloseableHttpClient) {
+ try {
+ ((CloseableHttpClient) httpClient).close();
+ } catch (IOException e) {
+ throw new ClientRegistrationException("Failed to close http client", e);
+ }
+ }
+ }
+
+ static String getUrl(String baseUri, String... path) {
+ StringBuilder s = new StringBuilder();
+ s.append(baseUri);
+ for (String p : path) {
+ s.append('/');
+ s.append(p);
+ }
+ return s.toString();
+ }
+
+ private void addAuth(HttpRequestBase request) {
+ if (auth != null) {
+ auth.addAuth(request);
+ }
+ }
+
+}
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.7.0.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.7.0.xml
index 1120252..aed99fc 100755
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.7.0.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.7.0.xml
@@ -58,6 +58,9 @@
<addForeignKeyConstraint baseColumnNames="GROUP_ID" baseTableName="GROUP_ROLE_MAPPING" constraintName="FK_GROUP_ROLE_GROUP" referencedColumnNames="ID" referencedTableName="KEYCLOAK_GROUP"/>
<addForeignKeyConstraint baseColumnNames="ROLE_ID" baseTableName="GROUP_ROLE_MAPPING" constraintName="FK_GROUP_ROLE_ROLE" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
+ <addColumn tableName="CLIENT">
+ <column name="REGISTRATION_SECRET" type="VARCHAR(255)"/>
+ </addColumn>
</changeSet>
</databaseChangeLog>
\ No newline at end of file
diff --git a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
index 0999505..514c0fb 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
@@ -19,6 +19,7 @@ public class ClientRepresentation {
protected Boolean enabled;
protected String clientAuthenticatorType;
protected String secret;
+ protected String registrationAccessToken;
protected String[] defaultRoles;
protected List<String> redirectUris;
protected List<String> webOrigins;
@@ -124,6 +125,14 @@ public class ClientRepresentation {
this.secret = secret;
}
+ public String getRegistrationAccessToken() {
+ return registrationAccessToken;
+ }
+
+ public void setRegistrationAccessToken(String registrationAccessToken) {
+ this.registrationAccessToken = registrationAccessToken;
+ }
+
public List<String> getRedirectUris() {
return redirectUris;
}
@@ -251,4 +260,5 @@ public class ClientRepresentation {
public void setProtocolMappers(List<ProtocolMapperRepresentation> protocolMappers) {
this.protocolMappers = protocolMappers;
}
+
}
diff --git a/model/api/src/main/java/org/keycloak/models/ClientModel.java b/model/api/src/main/java/org/keycloak/models/ClientModel.java
index 8753f45..c421aea 100755
--- a/model/api/src/main/java/org/keycloak/models/ClientModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ClientModel.java
@@ -90,6 +90,9 @@ public interface ClientModel extends RoleContainerModel {
String getSecret();
public void setSecret(String secret);
+ String getRegistrationSecret();
+ void setRegistrationSecret(String registrationSecret);
+
boolean isFullScopeAllowed();
void setFullScopeAllowed(boolean value);
diff --git a/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java b/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java
index aa4f725..f15614f 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java
@@ -17,6 +17,7 @@ public class ClientEntity extends AbstractIdentifiableEntity {
private boolean enabled;
private String clientAuthenticatorType;
private String secret;
+ private String registrationSecret;
private String protocol;
private int notBefore;
private boolean publicClient;
@@ -90,6 +91,14 @@ public class ClientEntity extends AbstractIdentifiableEntity {
this.secret = secret;
}
+ public String getRegistrationSecret() {
+ return registrationSecret;
+ }
+
+ public void setRegistrationSecret(String registrationSecret) {
+ this.registrationSecret = registrationSecret;
+ }
+
public int getNotBefore() {
return notBefore;
}
diff --git a/model/api/src/main/java/org/keycloak/models/KeycloakContext.java b/model/api/src/main/java/org/keycloak/models/KeycloakContext.java
index cf8bc6d..f430e5f 100755
--- a/model/api/src/main/java/org/keycloak/models/KeycloakContext.java
+++ b/model/api/src/main/java/org/keycloak/models/KeycloakContext.java
@@ -5,6 +5,7 @@ import org.keycloak.models.utils.RealmImporter;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.UriInfo;
+import java.net.URI;
import java.util.Locale;
/**
@@ -12,6 +13,8 @@ import java.util.Locale;
*/
public interface KeycloakContext {
+ URI getAuthServerUrl();
+
String getContextPath();
UriInfo getUri();
diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 59c640c..0df3516 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -11,8 +11,6 @@ import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.ModelException;
import org.keycloak.models.OTPPolicy;
-import org.keycloak.models.session.PersistentClientSessionModel;
-import org.keycloak.models.session.PersistentUserSessionModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredActionProviderModel;
@@ -47,7 +45,6 @@ import org.keycloak.representations.idm.UserSessionRepresentation;
import org.keycloak.common.util.Time;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
@@ -390,6 +387,7 @@ public class ModelToRepresentation {
rep.setNotBefore(clientModel.getNotBefore());
rep.setNodeReRegistrationTimeout(clientModel.getNodeReRegistrationTimeout());
rep.setClientAuthenticatorType(clientModel.getClientAuthenticatorType());
+ rep.setRegistrationAccessToken(clientModel.getRegistrationSecret());
Set<String> redirectUris = clientModel.getRedirectUris();
if (redirectUris != null) {
diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index a31d355..a46ee57 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -737,6 +737,8 @@ public class RepresentationToModel {
KeycloakModelUtils.generateSecret(client);
}
+ client.setRegistrationSecret(resourceRep.getRegistrationAccessToken());
+
if (resourceRep.getAttributes() != null) {
for (Map.Entry<String, String> entry : resourceRep.getAttributes().entrySet()) {
client.setAttribute(entry.getKey(), entry.getValue());
@@ -813,6 +815,7 @@ public class RepresentationToModel {
if (rep.isSurrogateAuthRequired() != null) resource.setSurrogateAuthRequired(rep.isSurrogateAuthRequired());
if (rep.getNodeReRegistrationTimeout() != null) resource.setNodeReRegistrationTimeout(rep.getNodeReRegistrationTimeout());
if (rep.getClientAuthenticatorType() != null) resource.setClientAuthenticatorType(rep.getClientAuthenticatorType());
+ if (rep.getRegistrationAccessToken() != null) resource.setRegistrationSecret(rep.getRegistrationAccessToken());
resource.updateClient();
if (rep.getProtocol() != null) resource.setProtocol(rep.getProtocol());
diff --git a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
index 477c5d6..7a87373 100755
--- a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
+++ b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
@@ -120,6 +120,15 @@ public class ClientAdapter implements ClientModel {
getDelegateForUpdate();
updated.setSecret(secret);
}
+ public String getRegistrationSecret() {
+ if (updated != null) return updated.getRegistrationSecret();
+ return cached.getRegistrationSecret();
+ }
+
+ public void setRegistrationSecret(String registrationsecret) {
+ getDelegateForUpdate();
+ updated.setRegistrationSecret(registrationsecret);
+ }
public boolean isPublicClient() {
if (updated != null) return updated.isPublicClient();
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java
index 2d58222..1c04b9d 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java
@@ -31,6 +31,7 @@ public class CachedClient implements Serializable {
private boolean enabled;
private String clientAuthenticatorType;
private String secret;
+ private String registrationSecret;
private String protocol;
private Map<String, String> attributes = new HashMap<String, String>();
private boolean publicClient;
@@ -57,6 +58,7 @@ public class CachedClient implements Serializable {
id = model.getId();
clientAuthenticatorType = model.getClientAuthenticatorType();
secret = model.getSecret();
+ registrationSecret = model.getRegistrationSecret();
clientId = model.getClientId();
name = model.getName();
description = model.getDescription();
@@ -129,6 +131,10 @@ public class CachedClient implements Serializable {
return secret;
}
+ public String getRegistrationSecret() {
+ return registrationSecret;
+ }
+
public boolean isPublicClient() {
return publicClient;
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
index 3baddd1..5ea0b11 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
@@ -178,6 +178,16 @@ public class ClientAdapter implements ClientModel {
}
@Override
+ public String getRegistrationSecret() {
+ return entity.getRegistrationSecret();
+ }
+
+ @Override
+ public void setRegistrationSecret(String registrationSecret) {
+ entity.setRegistrationSecret(registrationSecret);
+ }
+
+ @Override
public boolean validateSecret(String secret) {
return secret.equals(entity.getSecret());
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
index f26f651..881b129 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
@@ -42,6 +42,8 @@ public class ClientEntity {
private boolean enabled;
@Column(name="SECRET")
private String secret;
+ @Column(name="REGISTRATION_SECRET")
+ private String registrationSecret;
@Column(name="CLIENT_AUTHENTICATOR_TYPE")
private String clientAuthenticatorType;
@Column(name="NOT_BEFORE")
@@ -201,6 +203,14 @@ public class ClientEntity {
this.secret = secret;
}
+ public String getRegistrationSecret() {
+ return registrationSecret;
+ }
+
+ public void setRegistrationSecret(String registrationSecret) {
+ this.registrationSecret = registrationSecret;
+ }
+
public int getNotBefore() {
return notBefore;
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
index e99f142..cbacd09 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
@@ -178,6 +178,17 @@ public class ClientAdapter extends AbstractMongoAdapter<MongoClientEntity> imple
}
@Override
+ public String getRegistrationSecret() {
+ return getMongoEntity().getRegistrationSecret();
+ }
+
+ @Override
+ public void setRegistrationSecret(String registrationSecretsecret) {
+ getMongoEntity().setRegistrationSecret(registrationSecretsecret);
+ updateMongoEntity();
+ }
+
+ @Override
public boolean isPublicClient() {
return getMongoEntity().isPublicClient();
}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProvider.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProvider.java
new file mode 100644
index 0000000..298623d
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProvider.java
@@ -0,0 +1,79 @@
+package org.keycloak.protocol.saml.clientregistration;
+
+import org.jboss.logging.Logger;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
+import org.keycloak.exportimport.ClientDescriptionConverter;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.protocol.saml.EntityDescriptorDescriptionConverter;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.clientregistration.ClientRegAuth;
+import org.keycloak.services.clientregistration.ClientRegistrationProvider;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.net.URI;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class EntityDescriptorClientRegistrationProvider implements ClientRegistrationProvider {
+
+ private static final Logger logger = Logger.getLogger(EntityDescriptorClientRegistrationProvider.class);
+
+ private KeycloakSession session;
+ private EventBuilder event;
+ private ClientRegAuth auth;
+
+ public EntityDescriptorClientRegistrationProvider(KeycloakSession session) {
+ this.session = session;
+ }
+
+// @POST
+// @Consumes(MediaType.APPLICATION_XML)
+// @Produces(MediaType.APPLICATION_JSON)
+// public Response create(String descriptor) {
+// event.event(EventType.CLIENT_REGISTER);
+//
+// auth.requireCreate();
+//
+// ClientRepresentation client = session.getProvider(ClientDescriptionConverter.class, EntityDescriptorDescriptionConverter.ID).convertToInternal(descriptor);
+//
+// try {
+// ClientModel clientModel = RepresentationToModel.createClient(session, session.getContext().getRealm(), client, true);
+// client = ModelToRepresentation.toRepresentation(clientModel);
+// URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(clientModel.getId()).build();
+//
+// logger.infov("Created client {0}", client.getClientId());
+//
+// event.client(client.getClientId()).success();
+//
+// return Response.created(uri).entity(client).build();
+// } catch (ModelDuplicateException e) {
+// return ErrorResponse.exists("Client " + client.getClientId() + " already exists");
+// }
+// }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public void setAuth(ClientRegAuth auth) {
+ this.auth = auth;
+ }
+
+ @Override
+ public void setEvent(EventBuilder event) {
+ this.event = event;
+ }
+
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProviderFactory.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProviderFactory.java
new file mode 100644
index 0000000..393ff36
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProviderFactory.java
@@ -0,0 +1,38 @@
+package org.keycloak.protocol.saml.clientregistration;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.services.clientregistration.ClientRegistrationProvider;
+import org.keycloak.services.clientregistration.ClientRegistrationProviderFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class EntityDescriptorClientRegistrationProviderFactory implements ClientRegistrationProviderFactory {
+
+ public static final String ID = "saml2-entity-descriptor";
+
+ @Override
+ public ClientRegistrationProvider create(KeycloakSession session) {
+ return new EntityDescriptorClientRegistrationProvider(session);
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java
index da99613..42ff3ee 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java
@@ -33,6 +33,8 @@ import java.util.Map;
*/
public class EntityDescriptorDescriptionConverter implements ClientDescriptionConverter, ClientDescriptionConverterFactory {
+ public static final String ID = "saml2-entity-descriptor";
+
@Override
public boolean isSupported(String description) {
description = description.trim();
@@ -161,7 +163,7 @@ public class EntityDescriptorDescriptionConverter implements ClientDescriptionCo
@Override
public String getId() {
- return "saml2-entity-descriptor";
+ return ID;
}
}
diff --git a/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory
new file mode 100644
index 0000000..e4f8117
--- /dev/null
+++ b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory
@@ -0,0 +1 @@
+org.keycloak.protocol.saml.clientregistration.EntityDescriptorClientRegistrationProviderFactory
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java
index 9b862ef..2250dd0 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java
@@ -190,7 +190,7 @@ public class LogoutEndpoint {
}
private ClientModel authorizeClient() {
- ClientModel client = AuthorizeClientUtil.authorizeClient(session, event, realm).getClient();
+ ClientModel client = AuthorizeClientUtil.authorizeClient(session, event).getClient();
if (client.isBearerOnly()) {
throw new ErrorResponseException("invalid_client", "Bearer-only not allowed", Response.Status.BAD_REQUEST);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
index 1fb3e4a..34161d8 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
@@ -145,7 +145,7 @@ public class TokenEndpoint {
}
private void checkClient() {
- AuthorizeClientUtil.ClientAuthResult clientAuth = AuthorizeClientUtil.authorizeClient(session, event, realm);
+ AuthorizeClientUtil.ClientAuthResult clientAuth = AuthorizeClientUtil.authorizeClient(session, event);
client = clientAuth.getClient();
clientAuthAttributes = clientAuth.getClientAuthAttributes();
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverter.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverter.java
index 56b47bb..955dfe4 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverter.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverter.java
@@ -8,6 +8,7 @@ import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.representations.OIDCClientRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.services.clientregistration.oidc.DescriptionConverter;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
@@ -17,6 +18,8 @@ import java.io.IOException;
*/
public class OIDCClientDescriptionConverter implements ClientDescriptionConverter, ClientDescriptionConverterFactory {
+ public static final String ID = "openid-connect";
+
@Override
public boolean isSupported(String description) {
description = description.trim();
@@ -26,15 +29,8 @@ public class OIDCClientDescriptionConverter implements ClientDescriptionConverte
@Override
public ClientRepresentation convertToInternal(String description) {
try {
- OIDCClientRepresentation oidcRep = JsonSerialization.readValue(description, OIDCClientRepresentation.class);
-
- ClientRepresentation client = new ClientRepresentation();
- client.setClientId(KeycloakModelUtils.generateId());
- client.setName(oidcRep.getClientName());
- client.setRedirectUris(oidcRep.getRedirectUris());
- client.setBaseUrl(oidcRep.getClientUri());
-
- return client;
+ OIDCClientRepresentation clientOIDC = JsonSerialization.readValue(description, OIDCClientRepresentation.class);
+ return DescriptionConverter.toInternal(clientOIDC);
} catch (IOException e) {
throw new RuntimeException(e);
}
@@ -59,7 +55,7 @@ public class OIDCClientDescriptionConverter implements ClientDescriptionConverte
@Override
public String getId() {
- return "openid-connect";
+ return ID;
}
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCClientRepresentation.java b/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCClientRepresentation.java
index 7de415c..4a83ebd 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCClientRepresentation.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCClientRepresentation.java
@@ -12,12 +12,48 @@ public class OIDCClientRepresentation {
@JsonProperty("redirect_uris")
private List<String> redirectUris;
+ @JsonProperty("token_endpoint_auth_method")
+ private String tokenEndpointAuthMethod;
+
+ @JsonProperty("grant_types")
+ private String grantTypes;
+
+ @JsonProperty("response_types")
+ private String responseTypes;
+
@JsonProperty("client_name")
private String clientName;
@JsonProperty("client_uri")
private String clientUri;
+ @JsonProperty("logo_uri")
+ private String logoUri;
+
+ @JsonProperty("scope")
+ private String scope;
+
+ @JsonProperty("contacts")
+ private String contacts;
+
+ @JsonProperty("tos_uri")
+ private String tos_uri;
+
+ @JsonProperty("policy_uri")
+ private String policy_uri;
+
+ @JsonProperty("jwks_uri")
+ private String jwks_uri;
+
+ @JsonProperty("jwks")
+ private String jwks;
+
+ @JsonProperty("software_id")
+ private String softwareId;
+
+ @JsonProperty("software_version")
+ private String softwareVersion;
+
public List<String> getRedirectUris() {
return redirectUris;
}
@@ -26,6 +62,30 @@ public class OIDCClientRepresentation {
this.redirectUris = redirectUris;
}
+ public String getTokenEndpointAuthMethod() {
+ return tokenEndpointAuthMethod;
+ }
+
+ public void setTokenEndpointAuthMethod(String tokenEndpointAuthMethod) {
+ this.tokenEndpointAuthMethod = tokenEndpointAuthMethod;
+ }
+
+ public String getGrantTypes() {
+ return grantTypes;
+ }
+
+ public void setGrantTypes(String grantTypes) {
+ this.grantTypes = grantTypes;
+ }
+
+ public String getResponseTypes() {
+ return responseTypes;
+ }
+
+ public void setResponseTypes(String responseTypes) {
+ this.responseTypes = responseTypes;
+ }
+
public String getClientName() {
return clientName;
}
@@ -42,4 +102,76 @@ public class OIDCClientRepresentation {
this.clientUri = clientUri;
}
+ public String getLogoUri() {
+ return logoUri;
+ }
+
+ public void setLogoUri(String logoUri) {
+ this.logoUri = logoUri;
+ }
+
+ public String getScope() {
+ return scope;
+ }
+
+ public void setScope(String scope) {
+ this.scope = scope;
+ }
+
+ public String getContacts() {
+ return contacts;
+ }
+
+ public void setContacts(String contacts) {
+ this.contacts = contacts;
+ }
+
+ public String getTos_uri() {
+ return tos_uri;
+ }
+
+ public void setTos_uri(String tos_uri) {
+ this.tos_uri = tos_uri;
+ }
+
+ public String getPolicy_uri() {
+ return policy_uri;
+ }
+
+ public void setPolicy_uri(String policy_uri) {
+ this.policy_uri = policy_uri;
+ }
+
+ public String getJwks_uri() {
+ return jwks_uri;
+ }
+
+ public void setJwks_uri(String jwks_uri) {
+ this.jwks_uri = jwks_uri;
+ }
+
+ public String getJwks() {
+ return jwks;
+ }
+
+ public void setJwks(String jwks) {
+ this.jwks = jwks;
+ }
+
+ public String getSoftwareId() {
+ return softwareId;
+ }
+
+ public void setSoftwareId(String softwareId) {
+ this.softwareId = softwareId;
+ }
+
+ public String getSoftwareVersion() {
+ return softwareVersion;
+ }
+
+ public void setSoftwareVersion(String softwareVersion) {
+ this.softwareVersion = softwareVersion;
+ }
+
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/AuthorizeClientUtil.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/AuthorizeClientUtil.java
index c017ba3..a2d0d6c 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/utils/AuthorizeClientUtil.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/AuthorizeClientUtil.java
@@ -19,18 +19,8 @@ import javax.ws.rs.core.Response;
*/
public class AuthorizeClientUtil {
- public static ClientAuthResult authorizeClient(KeycloakSession session, EventBuilder event, RealmModel realm) {
- AuthenticationFlowModel clientAuthFlow = realm.getClientAuthenticationFlow();
- String flowId = clientAuthFlow.getId();
-
- AuthenticationProcessor processor = new AuthenticationProcessor();
- processor.setFlowId(flowId)
- .setConnection(session.getContext().getConnection())
- .setEventBuilder(event)
- .setRealm(realm)
- .setSession(session)
- .setUriInfo(session.getContext().getUri())
- .setRequest(session.getContext().getContextObject(HttpRequest.class));
+ public static ClientAuthResult authorizeClient(KeycloakSession session, EventBuilder event) {
+ AuthenticationProcessor processor = getAuthenticationProcessor(session, event);
Response response = processor.authenticateClient();
if (response != null) {
@@ -45,6 +35,24 @@ public class AuthorizeClientUtil {
return new ClientAuthResult(client, processor.getClientAuthAttributes());
}
+ public static AuthenticationProcessor getAuthenticationProcessor(KeycloakSession session, EventBuilder event) {
+ RealmModel realm = session.getContext().getRealm();
+
+ AuthenticationFlowModel clientAuthFlow = realm.getClientAuthenticationFlow();
+ String flowId = clientAuthFlow.getId();
+
+ AuthenticationProcessor processor = new AuthenticationProcessor();
+ processor.setFlowId(flowId)
+ .setConnection(session.getContext().getConnection())
+ .setEventBuilder(event)
+ .setRealm(realm)
+ .setSession(session)
+ .setUriInfo(session.getContext().getUri())
+ .setRequest(session.getContext().getContextObject(HttpRequest.class));
+
+ return processor;
+ }
+
public static class ClientAuthResult {
private final ClientModel client;
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProvider.java
new file mode 100644
index 0000000..feffc5f
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProvider.java
@@ -0,0 +1,92 @@
+package org.keycloak.services.clientregistration;
+
+import org.jboss.resteasy.spi.UnauthorizedException;
+import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.events.Errors;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
+import org.keycloak.services.ForbiddenException;
+import org.keycloak.services.managers.ClientManager;
+import org.keycloak.services.managers.RealmManager;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class AdapterInstallationClientRegistrationProvider implements ClientRegistrationProvider {
+
+ private KeycloakSession session;
+ private EventBuilder event;
+ private ClientRegAuth auth;
+
+ public AdapterInstallationClientRegistrationProvider(KeycloakSession session) {
+ this.session = session;
+ }
+
+ @GET
+ @Path("{clientId}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response get(@PathParam("clientId") String clientId) {
+ event.event(EventType.CLIENT_INFO);
+
+ ClientModel client = session.getContext().getRealm().getClientByClientId(clientId);
+
+ if (auth.isAuthenticated()) {
+ auth.requireView(client);
+ } else {
+ authenticateClient(client);
+ }
+
+ ClientManager clientManager = new ClientManager(new RealmManager(session));
+ Object rep = clientManager.toInstallationRepresentation(session.getContext().getRealm(), client, session.getContext().getAuthServerUrl());
+
+ event.client(client.getClientId()).success();
+ return Response.ok(rep).build();
+ }
+
+ @Override
+ public void setAuth(ClientRegAuth auth) {
+ this.auth = auth;
+ }
+
+ @Override
+ public void setEvent(EventBuilder event) {
+ this.event = event;
+ }
+
+ @Override
+ public void close() {
+ }
+
+ private void authenticateClient(ClientModel client) {
+ if (client.isPublicClient()) {
+ return;
+ }
+
+ AuthenticationProcessor processor = AuthorizeClientUtil.getAuthenticationProcessor(session, event);
+
+ Response response = processor.authenticateClient();
+ if (response != null) {
+ event.client(client.getClientId()).error(Errors.NOT_ALLOWED);
+ throw new ForbiddenException();
+ }
+
+ ClientModel authClient = processor.getClient();
+ if (client == null) {
+ event.client(client.getClientId()).error(Errors.NOT_ALLOWED);
+ throw new ForbiddenException();
+ }
+
+ if (!authClient.getClientId().equals(client.getClientId())) {
+ event.client(client.getClientId()).error(Errors.NOT_ALLOWED);
+ throw new ForbiddenException();
+ }
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegAuth.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegAuth.java
new file mode 100644
index 0000000..4964349
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegAuth.java
@@ -0,0 +1,128 @@
+package org.keycloak.services.clientregistration;
+
+import org.jboss.resteasy.spi.UnauthorizedException;
+import org.keycloak.events.Errors;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.models.*;
+import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.services.ForbiddenException;
+import org.keycloak.services.managers.AppAuthManager;
+import org.keycloak.services.managers.AuthenticationManager;
+
+import javax.ws.rs.core.HttpHeaders;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ClientRegAuth {
+
+ private KeycloakSession session;
+ private EventBuilder event;
+
+ private String token;
+ private AccessToken.Access bearerRealmAccess;
+
+ private boolean authenticated = false;
+
+ public ClientRegAuth(KeycloakSession session, EventBuilder event) {
+ this.session = session;
+ this.event = event;
+
+ init();
+ }
+
+ private void init() {
+ RealmModel realm = session.getContext().getRealm();
+
+ String authorizationHeader = session.getContext().getRequestHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
+ if (authorizationHeader == null) {
+ return;
+ }
+
+ String[] split = authorizationHeader.split(" ");
+ if (!split[0].equalsIgnoreCase("bearer")) {
+ return;
+ }
+
+ if (split[1].indexOf('.') == -1) {
+ token = split[1];
+ authenticated = true;
+ } else {
+ AuthenticationManager.AuthResult authResult = new AppAuthManager().authenticateBearerToken(session, realm);
+ bearerRealmAccess = authResult.getToken().getResourceAccess(Constants.REALM_MANAGEMENT_CLIENT_ID);
+ authenticated = true;
+ }
+ }
+
+ public boolean isAuthenticated() {
+ return authenticated;
+ }
+
+ public void requireCreate() {
+ if (!authenticated) {
+ event.error(Errors.NOT_ALLOWED);
+ throw new UnauthorizedException();
+ }
+
+ if (bearerRealmAccess != null) {
+ if (bearerRealmAccess.isUserInRole(AdminRoles.MANAGE_CLIENTS) || bearerRealmAccess.isUserInRole(AdminRoles.CREATE_CLIENT)) {
+ return;
+ }
+ }
+
+ event.error(Errors.NOT_ALLOWED);
+ throw new ForbiddenException();
+ }
+
+ public void requireView(ClientModel client) {
+ if (!authenticated) {
+ event.error(Errors.NOT_ALLOWED);
+ throw new UnauthorizedException();
+ }
+
+ if (client == null) {
+ event.error(Errors.NOT_ALLOWED);
+ throw new ForbiddenException();
+ }
+
+ if (bearerRealmAccess != null) {
+ if (bearerRealmAccess.isUserInRole(AdminRoles.MANAGE_CLIENTS) || bearerRealmAccess.isUserInRole(AdminRoles.VIEW_CLIENTS)) {
+ return;
+ }
+ } else if (token != null) {
+ if (client.getRegistrationSecret() != null && client.getRegistrationSecret().equals(token)) {
+ return;
+ }
+ }
+
+ event.error(Errors.NOT_ALLOWED);
+ throw new ForbiddenException();
+ }
+
+ public void requireUpdate(ClientModel client) {
+ if (!authenticated) {
+ event.error(Errors.NOT_ALLOWED);
+ throw new UnauthorizedException();
+ }
+
+ if (client == null) {
+ event.error(Errors.NOT_ALLOWED);
+ throw new ForbiddenException();
+ }
+
+ if (bearerRealmAccess != null) {
+ if (bearerRealmAccess.isUserInRole(AdminRoles.MANAGE_CLIENTS) || bearerRealmAccess.isUserInRole(AdminRoles.VIEW_CLIENTS)) {
+ return;
+ }
+ } else if (token != null) {
+ if (client.getRegistrationSecret() != null && client.getRegistrationSecret().equals(token)) {
+ return;
+ }
+ }
+
+ event.error(Errors.NOT_ALLOWED);
+ throw new ForbiddenException();
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java
index d1d6648..0c6bc4e 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java
@@ -9,7 +9,7 @@ import org.keycloak.provider.Provider;
*/
public interface ClientRegistrationProvider extends Provider {
- void setRealm(RealmModel realm);
+ void setAuth(ClientRegAuth auth);
void setEvent(EventBuilder event);
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java
index 8b215fa..ea508de 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java
@@ -2,36 +2,43 @@ package org.keycloak.services.clientregistration;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.RealmModel;
-import org.keycloak.services.managers.AppAuthManager;
+import org.keycloak.services.ErrorResponseException;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ClientRegistrationService {
- private RealmModel realm;
-
private EventBuilder event;
@Context
private KeycloakSession session;
- public ClientRegistrationService(RealmModel realm, EventBuilder event) {
- this.realm = realm;
+ public ClientRegistrationService(EventBuilder event) {
this.event = event;
}
@Path("{provider}")
public Object getProvider(@PathParam("provider") String providerId) {
+ checkSsl();
+
ClientRegistrationProvider provider = session.getProvider(ClientRegistrationProvider.class, providerId);
- provider.setRealm(realm);
provider.setEvent(event);
+ provider.setAuth(new ClientRegAuth(session, event));
return provider;
}
+ private void checkSsl() {
+ if (!session.getContext().getUri().getBaseUri().getScheme().equals("https")) {
+ if (session.getContext().getRealm().getSslRequired().isRequired(session.getContext().getConnection())) {
+ throw new ErrorResponseException("invalid_request", "HTTPS required", Response.Status.FORBIDDEN);
+ }
+ }
+ }
+
}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java
index 04cb46a..603ed08 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java
@@ -1,22 +1,16 @@
package org.keycloak.services.clientregistration;
-import org.jboss.logging.Logger;
-import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
-import org.keycloak.models.*;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.RepresentationToModel;
-import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
-import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.services.ErrorResponse;
-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.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.net.URI;
@@ -26,31 +20,29 @@ import java.net.URI;
*/
public class DefaultClientRegistrationProvider implements ClientRegistrationProvider {
- private static final Logger logger = Logger.getLogger(DefaultClientRegistrationProvider.class);
-
private KeycloakSession session;
private EventBuilder event;
- private RealmModel realm;
+ private ClientRegAuth auth;
public DefaultClientRegistrationProvider(KeycloakSession session) {
this.session = session;
}
-
@POST
@Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
public Response create(ClientRepresentation client) {
event.event(EventType.CLIENT_REGISTER);
- authenticate(true, null);
+ auth.requireCreate();
try {
- ClientModel clientModel = RepresentationToModel.createClient(session, realm, client, true);
+ ClientModel clientModel = RepresentationToModel.createClient(session, session.getContext().getRealm(), client, true);
+ clientModel.setRegistrationSecret(TokenGenerator.createRegistrationAccessToken());
+
client = ModelToRepresentation.toRepresentation(clientModel);
URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(clientModel.getId()).build();
- logger.infov("Created client {0}", client.getClientId());
-
event.client(client.getClientId()).success();
return Response.created(uri).entity(client).build();
} catch (ModelDuplicateException e) {
@@ -64,10 +56,10 @@ public class DefaultClientRegistrationProvider implements ClientRegistrationProv
public Response get(@PathParam("clientId") String clientId) {
event.event(EventType.CLIENT_INFO);
- ClientModel client = authenticate(false, clientId);
- if (client == null) {
- return Response.status(Response.Status.NOT_FOUND).build();
- }
+ ClientModel client = session.getContext().getRealm().getClientByClientId(clientId);
+ auth.requireView(client);
+
+ event.client(client.getClientId()).success();
return Response.ok(ModelToRepresentation.toRepresentation(client)).build();
}
@@ -77,12 +69,12 @@ public class DefaultClientRegistrationProvider implements ClientRegistrationProv
public Response update(@PathParam("clientId") String clientId, ClientRepresentation rep) {
event.event(EventType.CLIENT_UPDATE).client(clientId);
- ClientModel client = authenticate(false, clientId);
- RepresentationToModel.updateClient(rep, client);
+ ClientModel client = session.getContext().getRealm().getClientByClientId(clientId);
+ auth.requireUpdate(client);
- logger.infov("Updated client {0}", rep.getClientId());
+ RepresentationToModel.updateClient(rep, client);
- event.success();
+ event.client(client.getClientId()).success();
return Response.status(Response.Status.OK).build();
}
@@ -91,9 +83,11 @@ public class DefaultClientRegistrationProvider implements ClientRegistrationProv
public Response delete(@PathParam("clientId") String clientId) {
event.event(EventType.CLIENT_DELETE).client(clientId);
- ClientModel client = authenticate(false, clientId);
- if (realm.removeClient(client.getId())) {
- event.success();
+ ClientModel client = session.getContext().getRealm().getClientByClientId(clientId);
+ auth.requireUpdate(client);
+
+ if (session.getContext().getRealm().removeClient(client.getId())) {
+ event.client(client.getClientId()).success();
return Response.ok().build();
} else {
return Response.status(Response.Status.NOT_FOUND).build();
@@ -101,55 +95,17 @@ public class DefaultClientRegistrationProvider implements ClientRegistrationProv
}
@Override
- public void close() {
-
- }
-
-
- 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 = new AppAuthManager().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();
+ public void setAuth(ClientRegAuth auth) {
+ this.auth = auth;
}
@Override
- public void setRealm(RealmModel realm) {
-this.realm = realm;
+ public void setEvent(EventBuilder event) {
+ this.event = event;
}
@Override
- public void setEvent(EventBuilder event) {
- this.event = event;
+ public void close() {
}
}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java b/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java
new file mode 100644
index 0000000..1e0784c
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java
@@ -0,0 +1,38 @@
+package org.keycloak.services.clientregistration.oidc;
+
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.representations.OIDCClientRepresentation;
+import org.keycloak.representations.idm.ClientRepresentation;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class DescriptionConverter {
+
+ public static ClientRepresentation toInternal(OIDCClientRepresentation clientOIDC) {
+ ClientRepresentation client = new ClientRepresentation();
+ client.setClientId(KeycloakModelUtils.generateId());
+ client.setName(clientOIDC.getClientName());
+ client.setRedirectUris(clientOIDC.getRedirectUris());
+ client.setBaseUrl(clientOIDC.getClientUri());
+ return client;
+ }
+
+ public static OIDCClientResponseRepresentation toExternalResponse(ClientRepresentation client) {
+ OIDCClientResponseRepresentation response = new OIDCClientResponseRepresentation();
+ response.setClientId(client.getClientId());
+
+ response.setClientName(client.getName());
+ response.setClientUri(client.getBaseUrl());
+
+ response.setClientSecret(client.getSecret());
+ response.setClientSecretExpiresAt(0);
+
+ response.setRedirectUris(client.getRedirectUris());
+
+ response.setRegistrationAccessToken(client.getRegistrationAccessToken());
+
+ return response;
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java
new file mode 100644
index 0000000..4d131cc
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java
@@ -0,0 +1,100 @@
+package org.keycloak.services.clientregistration.oidc;
+
+import org.jboss.logging.Logger;
+import org.keycloak.common.util.Time;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.protocol.oidc.OIDCClientDescriptionConverter;
+import org.keycloak.protocol.oidc.representations.OIDCClientRepresentation;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.clientregistration.ClientRegAuth;
+import org.keycloak.services.clientregistration.ClientRegistrationProvider;
+import org.keycloak.services.clientregistration.TokenGenerator;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.net.URI;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class OIDCClientRegistrationProvider implements ClientRegistrationProvider {
+
+ private static final Logger logger = Logger.getLogger(OIDCClientRegistrationProvider.class);
+
+ private KeycloakSession session;
+ private EventBuilder event;
+ private ClientRegAuth auth;
+
+ public OIDCClientRegistrationProvider(KeycloakSession session) {
+ this.session = session;
+ }
+
+// @POST
+// @Consumes(MediaType.APPLICATION_JSON)
+// @Produces(MediaType.APPLICATION_JSON)
+// public Response create(OIDCClientRepresentation clientOIDC) {
+// event.event(EventType.CLIENT_REGISTER);
+//
+// auth.requireCreate();
+//
+// ClientRepresentation client = DescriptionConverter.toInternal(clientOIDC);
+//
+// try {
+// ClientModel clientModel = RepresentationToModel.createClient(session, session.getContext().getRealm(), client, true);
+//
+// client = ModelToRepresentation.toRepresentation(clientModel);
+//
+// String registrationAccessToken = TokenGenerator.createRegistrationAccessToken();
+//
+// clientModel.setRegistrationSecret(registrationAccessToken);
+//
+// URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(clientModel.getId()).build();
+//
+// logger.infov("Created client {0}", client.getClientId());
+//
+// event.client(client.getClientId()).success();
+//
+// OIDCClientResponseRepresentation response = DescriptionConverter.toExternalResponse(client);
+//
+// response.setClientName(client.getName());
+// response.setClientUri(client.getBaseUrl());
+//
+// response.setClientSecret(client.getSecret());
+// response.setClientSecretExpiresAt(0);
+//
+// response.setRedirectUris(client.getRedirectUris());
+//
+// response.setRegistrationAccessToken(registrationAccessToken);
+// response.setRegistrationClientUri(uri.toString());
+//
+// return Response.created(uri).entity(response).build();
+// } catch (ModelDuplicateException e) {
+// return ErrorResponse.exists("Client " + client.getClientId() + " already exists");
+// }
+// }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public void setAuth(ClientRegAuth auth) {
+ this.auth = auth;
+ }
+
+ @Override
+ public void setEvent(EventBuilder event) {
+ this.event = event;
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientResponseRepresentation.java b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientResponseRepresentation.java
new file mode 100644
index 0000000..cd4eea4
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientResponseRepresentation.java
@@ -0,0 +1,77 @@
+package org.keycloak.services.clientregistration.oidc;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.keycloak.protocol.oidc.representations.OIDCClientRepresentation;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class OIDCClientResponseRepresentation extends OIDCClientRepresentation {
+
+ @JsonProperty("client_id")
+ private String clientId;
+
+ @JsonProperty("client_secret")
+ private String clientSecret;
+
+ @JsonProperty("client_id_issued_at")
+ private int clientIdIssuedAt;
+
+ @JsonProperty("client_secret_expires_at")
+ private int clientSecretExpiresAt;
+
+ @JsonProperty("registration_client_uri")
+ private String registrationClientUri;
+
+ @JsonProperty("registration_access_token")
+ private String registrationAccessToken;
+
+ 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 int getClientIdIssuedAt() {
+ return clientIdIssuedAt;
+ }
+
+ public void setClientIdIssuedAt(int clientIdIssuedAt) {
+ this.clientIdIssuedAt = clientIdIssuedAt;
+ }
+
+ public int getClientSecretExpiresAt() {
+ return clientSecretExpiresAt;
+ }
+
+ public void setClientSecretExpiresAt(int clientSecretExpiresAt) {
+ this.clientSecretExpiresAt = clientSecretExpiresAt;
+ }
+
+ public String getRegistrationClientUri() {
+ return registrationClientUri;
+ }
+
+ public void setRegistrationClientUri(String registrationClientUri) {
+ this.registrationClientUri = registrationClientUri;
+ }
+
+ public String getRegistrationAccessToken() {
+ return registrationAccessToken;
+ }
+
+ public void setRegistrationAccessToken(String registrationAccessToken) {
+ this.registrationAccessToken = registrationAccessToken;
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/TokenGenerator.java b/services/src/main/java/org/keycloak/services/clientregistration/TokenGenerator.java
new file mode 100644
index 0000000..3c49d0f
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/TokenGenerator.java
@@ -0,0 +1,27 @@
+package org.keycloak.services.clientregistration;
+
+import org.keycloak.common.util.Base64Url;
+
+import java.security.SecureRandom;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class TokenGenerator {
+
+ private static final int REGISTRATION_ACCESS_TOKEN_BYTES = 32;
+
+ private TokenGenerator() {
+ }
+
+ public String createInitialAccessToken() {
+ return null;
+ }
+
+ public static String createRegistrationAccessToken() {
+ byte[] buf = new byte[REGISTRATION_ACCESS_TOKEN_BYTES];
+ new SecureRandom().nextBytes(buf);
+ return Base64Url.encode(buf);
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java
index a78f99e..26aba4b 100755
--- a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java
@@ -10,6 +10,7 @@ import org.keycloak.services.util.LocaleHelper;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.UriInfo;
+import java.net.URI;
import java.util.Locale;
/**
@@ -30,6 +31,13 @@ public class DefaultKeycloakContext implements KeycloakContext {
}
@Override
+ public URI getAuthServerUrl() {
+ UriInfo uri = getUri();
+ KeycloakApplication keycloakApplication = getContextObject(KeycloakApplication.class);
+ return keycloakApplication.getBaseUri(uri);
+ }
+
+ @Override
public String getContextPath() {
KeycloakApplication app = getContextObject(KeycloakApplication.class);
return app.getContextPath();
diff --git a/services/src/main/java/org/keycloak/services/resources/ClientsManagementService.java b/services/src/main/java/org/keycloak/services/resources/ClientsManagementService.java
index ddb301d..a933712 100755
--- a/services/src/main/java/org/keycloak/services/resources/ClientsManagementService.java
+++ b/services/src/main/java/org/keycloak/services/resources/ClientsManagementService.java
@@ -153,7 +153,7 @@ public class ClientsManagementService {
}
protected ClientModel authorizeClient() {
- ClientModel client = AuthorizeClientUtil.authorizeClient(session, event, realm).getClient();
+ ClientModel client = AuthorizeClientUtil.authorizeClient(session, event).getClient();
if (client.isPublicClient()) {
Map<String, String> error = new HashMap<String, String>();
diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
index f18f000..4b5e4f2 100755
--- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
@@ -113,11 +113,11 @@ public class RealmsResource {
return service;
}
- @Path("{realm}/client-registration")
+ @Path("{realm}/clients")
public ClientRegistrationService getClientsService(final @PathParam("realm") String name) {
RealmModel realm = init(name);
EventBuilder event = new EventBuilder(realm, session, clientConnection);
- ClientRegistrationService service = new ClientRegistrationService(realm, event);
+ ClientRegistrationService service = new ClientRegistrationService(event);
ResteasyProviderFactory.getInstance().injectProperties(service);
return service;
}
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory b/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory
index 3e8773a..d9b8c41 100644
--- a/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory
+++ b/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory
@@ -1 +1,3 @@
-org.keycloak.services.clientregistration.DefaultClientRegistrationProviderFactory
\ No newline at end of file
+org.keycloak.services.clientregistration.DefaultClientRegistrationProviderFactory
+org.keycloak.services.clientregistration.oidc.OIDCClientRegistrationProviderFactory
+org.keycloak.services.clientregistration.AdapterInstallationClientRegistrationProviderFactory
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientRegistrationTest.java
new file mode 100644
index 0000000..8b8dfad
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientRegistrationTest.java
@@ -0,0 +1,96 @@
+package org.keycloak.testsuite.client;
+
+import org.junit.After;
+import org.junit.Before;
+import org.keycloak.client.registration.ClientRegistration;
+import org.keycloak.client.registration.ClientRegistrationException;
+import org.keycloak.models.AdminRoles;
+import org.keycloak.models.Constants;
+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 javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public abstract class AbstractClientRegistrationTest extends AbstractKeycloakTest {
+
+ static final String REALM_NAME = "test";
+
+ ClientRegistration reg;
+
+ @Before
+ public void before() throws Exception {
+ reg = new ClientRegistration(testContext.getAuthServerContextRoot() + "/auth", "test");
+ }
+
+ @After
+ public void after() throws Exception {
+ reg.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);
+ }
+
+ public ClientRepresentation createClient(ClientRepresentation client) {
+ Response response = adminClient.realm(REALM_NAME).clients().create(client);
+ String id = response.getLocation().toString();
+ id = id.substring(id.lastIndexOf('/') + 1);
+ client.setId(id);
+ response.close();
+ return client;
+ }
+
+ public ClientRepresentation getClient(String clientId) {
+ try {
+ return adminClient.realm(REALM_NAME).clients().get(clientId).toRepresentation();
+ } catch (NotFoundException e) {
+ return null;
+ }
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java
new file mode 100644
index 0000000..b8bdb41
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java
@@ -0,0 +1,103 @@
+package org.keycloak.testsuite.client;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.client.registration.Auth;
+import org.keycloak.client.registration.ClientRegistrationException;
+import org.keycloak.client.registration.HttpErrorException;
+import org.keycloak.representations.adapters.config.AdapterConfig;
+import org.keycloak.representations.idm.ClientRepresentation;
+
+import javax.ws.rs.core.Response;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class AdapterInstallationConfigTest extends AbstractClientRegistrationTest {
+
+ private ClientRepresentation client;
+ private ClientRepresentation client2;
+ private ClientRepresentation clientPublic;
+
+ @Before
+ public void before() throws Exception {
+ super.before();
+
+ client = new ClientRepresentation();
+ client.setEnabled(true);
+ client.setClientId("RegistrationAccessTokenTest");
+ client.setSecret("RegistrationAccessTokenTestClientSecret");
+ client.setPublicClient(false);
+ client.setRegistrationAccessToken("RegistrationAccessTokenTestRegistrationAccessToken");
+ client.setRootUrl("http://root");
+ client = createClient(client);
+
+ client2 = new ClientRepresentation();
+ client2.setEnabled(true);
+ client2.setClientId("RegistrationAccessTokenTest2");
+ client2.setSecret("RegistrationAccessTokenTestClientSecret");
+ client2.setPublicClient(false);
+ client2.setRegistrationAccessToken("RegistrationAccessTokenTestRegistrationAccessToken");
+ client2.setRootUrl("http://root");
+ client2 = createClient(client2);
+
+ clientPublic = new ClientRepresentation();
+ clientPublic.setEnabled(true);
+ clientPublic.setClientId("RegistrationAccessTokenTestPublic");
+ clientPublic.setPublicClient(true);
+ clientPublic.setRegistrationAccessToken("RegistrationAccessTokenTestRegistrationAccessTokenPublic");
+ clientPublic.setRootUrl("http://root");
+ clientPublic = createClient(clientPublic);
+ }
+
+ @Test
+ public void getConfigWithRegistrationAccessToken() throws ClientRegistrationException {
+ reg.auth(Auth.token(client.getRegistrationAccessToken()));
+
+ AdapterConfig config = reg.getAdapterConfig(client.getClientId());
+ assertNotNull(config);
+ }
+
+ @Test
+ public void getConfig() throws ClientRegistrationException {
+ reg.auth(Auth.client(client.getClientId(), client.getSecret()));
+
+ AdapterConfig config = reg.getAdapterConfig(client.getClientId());
+ assertNotNull(config);
+ }
+
+ @Test
+ public void getConfigMissingSecret() throws ClientRegistrationException {
+ reg.auth(null);
+
+ try {
+ reg.getAdapterConfig(client.getClientId());
+ fail("Expected 403");
+ } catch (ClientRegistrationException e) {
+ assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+ }
+ }
+
+ @Test
+ public void getConfigWrongClient() throws ClientRegistrationException {
+ reg.auth(Auth.client(client.getClientId(), client.getSecret()));
+
+ try {
+ reg.getAdapterConfig(client2.getClientId());
+ fail("Expected 403");
+ } catch (ClientRegistrationException e) {
+ assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+ }
+ }
+
+ @Test
+ public void getConfigPublicClient() throws ClientRegistrationException {
+ reg.auth(null);
+
+ AdapterConfig config = reg.getAdapterConfig(clientPublic.getClientId());
+ assertNotNull(config);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java
index 1d4b011..8b84b10 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java
@@ -1,302 +1,211 @@
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.Auth;
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 javax.ws.rs.NotFoundException;
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 {
+public class ClientRegistrationTest extends AbstractClientRegistrationTest {
- 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 {
+ private ClientRepresentation registerClient() throws ClientRegistrationException {
ClientRepresentation client = new ClientRepresentation();
client.setClientId(CLIENT_ID);
client.setSecret(CLIENT_SECRET);
- ClientRepresentation createdClient = clientRegistration.create(client);
+ ClientRepresentation createdClient = reg.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());
+ return client;
}
@Test
public void registerClientAsAdmin() throws ClientRegistrationException {
- registerClient(clientRegistrationAsAdmin);
+ authManageClients();
+ registerClient();
}
@Test
public void registerClientAsAdminWithCreateOnly() throws ClientRegistrationException {
- ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build();
- try {
- registerClient(clientRegistration);
- } finally {
- clientRegistration.close();
- }
+ authCreateClients();
+ registerClient();
}
@Test
public void registerClientAsAdminWithNoAccess() throws ClientRegistrationException {
- ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build();
+ authNoAccess();
try {
- registerClient(clientRegistration);
+ registerClient();
fail("Expected 403");
} catch (ClientRegistrationException e) {
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
- } finally {
- clientRegistration.close();
}
}
@Test
+ public void getClientAsAdmin() throws ClientRegistrationException {
+ registerClientAsAdmin();
+ ClientRepresentation rep = reg.get(CLIENT_ID);
+ assertNotNull(rep);
+ }
+
+ @Test
public void getClientAsAdminWithCreateOnly() throws ClientRegistrationException {
- registerClient(clientRegistrationAsAdmin);
- ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build();
+ registerClientAsAdmin();
+ authCreateClients();
try {
- clientRegistration.get(CLIENT_ID);
+ reg.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());
-
+ public void getClientAsAdminWithNoAccess() throws ClientRegistrationException {
+ registerClientAsAdmin();
+ authNoAccess();
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();
+ reg.get(CLIENT_ID);
+ fail("Expected 403");
+ } catch (ClientRegistrationException e) {
+ assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
}
}
@Test
- public void getClientAsAdminWithNoAccess() throws ClientRegistrationException {
- registerClient(clientRegistrationAsAdmin);
- ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build();
+ public void getClientNotFound() throws ClientRegistrationException {
+ authManageClients();
try {
- clientRegistration.get(CLIENT_ID);
+ reg.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);
+ private void updateClient() throws ClientRegistrationException {
+ ClientRepresentation client = reg.get(CLIENT_ID);
client.setRedirectUris(Collections.singletonList("http://localhost:8080/app"));
- clientRegistration.update(client);
+ reg.update(client);
- ClientRepresentation updatedClient = clientRegistration.get(CLIENT_ID);
+ ClientRepresentation updatedClient = reg.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);
+ registerClientAsAdmin();
+
+ authManageClients();
+ updateClient();
}
@Test
public void updateClientAsAdminWithCreateOnly() throws ClientRegistrationException {
- ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build();
+ authCreateClients();
try {
- updateClient(clientRegistration);
+ updateClient();
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();
+ authNoAccess();
try {
- updateClient(clientRegistration);
+ updateClient();
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);
+ public void updateClientNotFound() throws ClientRegistrationException {
+ authManageClients();
+ try {
+ updateClient();
+ fail("Expected 403");
+ } catch (ClientRegistrationException e) {
+ assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+ }
}
- 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);
+ private void deleteClient(ClientRepresentation client) throws ClientRegistrationException {
+ reg.delete(CLIENT_ID);
+ try {
+ adminClient.realm("test").clients().get(client.getId()).toRepresentation();
+ fail("Expected 403");
+ } catch (NotFoundException e) {
+ }
}
@Test
public void deleteClientAsAdmin() throws ClientRegistrationException {
- registerClient(clientRegistrationAsAdmin);
- deleteClient(clientRegistrationAsAdmin);
+ authCreateClients();
+ ClientRepresentation client = registerClient();
+
+ authManageClients();
+ deleteClient(client);
}
@Test
public void deleteClientAsAdminWithCreateOnly() throws ClientRegistrationException {
- ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build();
+ authManageClients();
+ ClientRepresentation client = registerClient();
try {
- deleteClient(clientRegistration);
+ authCreateClients();
+ deleteClient(client);
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();
+ authManageClients();
+ ClientRepresentation client = registerClient();
try {
- deleteClient(clientRegistration);
+ authNoAccess();
+ deleteClient(client);
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 void authCreateClients() {
+ reg.auth(Auth.token(getToken("create-clients", "password")));
+ }
+
+ private void authManageClients() {
+ reg.auth(Auth.token(getToken("manage-clients", "password")));
}
- private ClientRegistration.ClientRegistrationBuilder clientBuilder() {
- return ClientRegistration.create().realm("test").authServerUrl(testContext.getAuthServerContextRoot() + "/auth");
+ private void authNoAccess() {
+ reg.auth(Auth.token(getToken("no-access", "password")));
}
private String getToken(String username, String password) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/RegistrationAccessTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/RegistrationAccessTokenTest.java
new file mode 100644
index 0000000..d76da19
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/RegistrationAccessTokenTest.java
@@ -0,0 +1,94 @@
+package org.keycloak.testsuite.client;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.client.registration.Auth;
+import org.keycloak.client.registration.ClientRegistrationException;
+import org.keycloak.client.registration.HttpErrorException;
+import org.keycloak.representations.idm.ClientRepresentation;
+
+import javax.ws.rs.core.Response;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class RegistrationAccessTokenTest extends AbstractClientRegistrationTest {
+
+ private ClientRepresentation client;
+
+ @Before
+ public void before() throws Exception {
+ super.before();
+
+ client = new ClientRepresentation();
+ client.setEnabled(true);
+ client.setClientId("RegistrationAccessTokenTest");
+ client.setSecret("RegistrationAccessTokenTestClientSecret");
+ client.setRegistrationAccessToken("RegistrationAccessTokenTestRegistrationAccessToken");
+ client.setRootUrl("http://root");
+ client = createClient(client);
+
+ reg.auth(Auth.token(client.getRegistrationAccessToken()));
+ }
+
+ @Test
+ public void getClientWithRegistrationToken() throws ClientRegistrationException {
+ ClientRepresentation rep = reg.get(client.getClientId());
+ assertNotNull(rep);
+ }
+
+ @Test
+ public void getClientWithBadRegistrationToken() throws ClientRegistrationException {
+ reg.auth(Auth.token("invalid"));
+ try {
+ reg.get(client.getClientId());
+ fail("Expected 403");
+ } catch (ClientRegistrationException e) {
+ assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+ }
+ }
+
+ @Test
+ public void updateClientWithRegistrationToken() throws ClientRegistrationException {
+ client.setRootUrl("http://newroot");
+ reg.update(client);
+
+ assertEquals("http://newroot", getClient(client.getId()).getRootUrl());
+ }
+
+ @Test
+ public void updateClientWithBadRegistrationToken() throws ClientRegistrationException {
+ client.setRootUrl("http://newroot");
+
+ reg.auth(Auth.token("invalid"));
+ try {
+ reg.update(client);
+ fail("Expected 403");
+ } catch (ClientRegistrationException e) {
+ assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+ }
+
+ assertEquals("http://root", getClient(client.getId()).getRootUrl());
+ }
+
+ @Test
+ public void deleteClientWithRegistrationToken() throws ClientRegistrationException {
+ reg.delete(client);
+ assertNull(getClient(client.getId()));
+ }
+
+ @Test
+ public void deleteClientWithBadRegistrationToken() throws ClientRegistrationException {
+ reg.auth(Auth.token("invalid"));
+ try {
+ reg.delete(client);
+ fail("Expected 403");
+ } catch (ClientRegistrationException e) {
+ assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+ }
+ assertNotNull(getClient(client.getId()));
+ }
+
+}