keycloak-aplcache
Changes
.travis.yml 10(+2 -8)
client-registration/api/src/main/java/org/keycloak/client/registration/ClientRegistration.java 68(+61 -7)
client-registration/api/src/main/java/org/keycloak/client/registration/OIDCClientRepresentationMixIn.java 22(+22 -0)
client-registration/cli/src/main/java/org/keycloak/client/registration/cli/ClientRegistrationCLI.java 2(+2 -0)
forms/common-freemarker/src/main/java/org/keycloak/freemarker/ExtendingThemeManager.java 45(+31 -14)
forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties 2(+1 -1)
saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProvider.java 70(+21 -49)
services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java 17(+5 -12)
services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidatePassword.java 9(+0 -9)
services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java 12(+11 -1)
services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java 125(+125 -0)
services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java 2(+1 -1)
services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java 91(+14 -77)
services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java 18(+7 -11)
services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java 110(+56 -54)
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(+0 -77)
testsuite/integration-arquillian/tests/adapters/as7/src/test/java/org/keycloak/testsuite/adapter/servlet/AS7DemoServletsAdapterTest.java 2(+2 -0)
testsuite/integration-arquillian/tests/adapters/as7/src/test/java/org/keycloak/testsuite/adapter/servlet/AS7SessionServletAdapterTest.java 2(+2 -0)
testsuite/integration-arquillian/tests/adapters/eap6/src/test/java/org/keycloak/testsuite/adapter/servlet/EAP6DemoServletsAdapterTest.java 14(+14 -0)
testsuite/integration-arquillian/tests/adapters/eap6/src/test/java/org/keycloak/testsuite/adapter/servlet/EAP6SessionServletAdapterTest.java 14(+14 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/WaitUtils.java 36(+25 -11)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java 7(+4 -3)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java 85(+85 -0)
Details
.travis.yml 10(+2 -8)
diff --git a/.travis.yml b/.travis.yml
index 8146d6e..5dee839 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,14 +3,8 @@ language: java
jdk:
- oraclejdk8
-cache:
- directories:
- - $HOME/.m2
-
-before_cache:
- - rm -rf $HOME/.m2/repository/org/keycloak
-
-install: mvn install -Pdistribution -DskipTests=true -B -V
+install:
+ - travis_wait mvn install -Pdistribution -DskipTests=true -B -V -q
script:
- mvn test -B
diff --git a/client-registration/api/src/main/java/org/keycloak/client/registration/Auth.java b/client-registration/api/src/main/java/org/keycloak/client/registration/Auth.java
index 31ad4e8..c3dd313 100644
--- a/client-registration/api/src/main/java/org/keycloak/client/registration/Auth.java
+++ b/client-registration/api/src/main/java/org/keycloak/client/registration/Auth.java
@@ -5,6 +5,7 @@ import org.apache.http.HttpRequest;
import org.keycloak.common.util.Base64;
import org.keycloak.representations.idm.ClientInitialAccessPresentation;
import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.oidc.OIDCClientRepresentation;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -21,11 +22,14 @@ public abstract class Auth {
return new BearerTokenAuth(initialAccess.getToken());
}
-
public static Auth token(ClientRepresentation client) {
return new BearerTokenAuth(client.getRegistrationAccessToken());
}
+ public static Auth token(OIDCClientRepresentation client) {
+ return new BearerTokenAuth(client.getRegistrationAccessToken());
+ }
+
public static Auth client(String clientId, String clientSecret) {
return new BasicAuth(clientId, clientSecret);
}
diff --git a/client-registration/api/src/main/java/org/keycloak/client/registration/ClientRegistration.java b/client-registration/api/src/main/java/org/keycloak/client/registration/ClientRegistration.java
index 2b1a991..f2215f1 100644
--- a/client-registration/api/src/main/java/org/keycloak/client/registration/ClientRegistration.java
+++ b/client-registration/api/src/main/java/org/keycloak/client/registration/ClientRegistration.java
@@ -3,8 +3,10 @@ package org.keycloak.client.registration;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClients;
import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.oidc.OIDCClientRepresentation;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
@@ -18,10 +20,17 @@ public class ClientRegistration {
public static final ObjectMapper outputMapper = new ObjectMapper();
static {
outputMapper.getSerializationConfig().addMixInAnnotations(ClientRepresentation.class, ClientRepresentationMixIn.class);
+ outputMapper.getSerializationConfig().addMixInAnnotations(OIDCClientRepresentation.class, OIDCClientRepresentationMixIn.class);
+ outputMapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
}
+ private final String JSON = "application/json";
+ private final String XML = "application/xml";
+
private final String DEFAULT = "default";
private final String INSTALLATION = "install";
+ private final String OIDC = "openid-connect";
+ private final String SAML = "saml2-entity-descriptor";
private HttpUtil httpUtil;
@@ -47,23 +56,23 @@ public class ClientRegistration {
public ClientRepresentation create(ClientRepresentation client) throws ClientRegistrationException {
String content = serialize(client);
- InputStream resultStream = httpUtil.doPost(content, DEFAULT);
+ InputStream resultStream = httpUtil.doPost(content, JSON, JSON, DEFAULT);
return deserialize(resultStream, ClientRepresentation.class);
}
public ClientRepresentation get(String clientId) throws ClientRegistrationException {
- InputStream resultStream = httpUtil.doGet(DEFAULT, clientId);
+ InputStream resultStream = httpUtil.doGet(JSON, DEFAULT, clientId);
return resultStream != null ? deserialize(resultStream, ClientRepresentation.class) : null;
}
public AdapterConfig getAdapterConfig(String clientId) throws ClientRegistrationException {
- InputStream resultStream = httpUtil.doGet(INSTALLATION, clientId);
+ InputStream resultStream = httpUtil.doGet(JSON, INSTALLATION, clientId);
return resultStream != null ? deserialize(resultStream, AdapterConfig.class) : null;
}
public ClientRepresentation update(ClientRepresentation client) throws ClientRegistrationException {
String content = serialize(client);
- InputStream resultStream = httpUtil.doPut(content, DEFAULT, client.getClientId());
+ InputStream resultStream = httpUtil.doPut(content, JSON, JSON, DEFAULT, client.getClientId());
return resultStream != null ? deserialize(resultStream, ClientRepresentation.class) : null;
}
@@ -75,10 +84,17 @@ public class ClientRegistration {
httpUtil.doDelete(DEFAULT, clientId);
}
- public static String serialize(ClientRepresentation client) throws ClientRegistrationException {
- try {
+ public OIDCClientRegistration oidc() {
+ return new OIDCClientRegistration();
+ }
- return outputMapper.writeValueAsString(client);
+ public SAMLClientRegistration saml() {
+ return new SAMLClientRegistration();
+ }
+
+ public static String serialize(Object obj) throws ClientRegistrationException {
+ try {
+ return outputMapper.writeValueAsString(obj);
} catch (IOException e) {
throw new ClientRegistrationException("Failed to write json object", e);
}
@@ -92,6 +108,44 @@ public class ClientRegistration {
}
}
+ public class OIDCClientRegistration {
+
+ public OIDCClientRepresentation create(OIDCClientRepresentation client) throws ClientRegistrationException {
+ String content = serialize(client);
+ InputStream resultStream = httpUtil.doPost(content, JSON, JSON, OIDC);
+ return deserialize(resultStream, OIDCClientRepresentation.class);
+ }
+
+ public OIDCClientRepresentation get(String clientId) throws ClientRegistrationException {
+ InputStream resultStream = httpUtil.doGet(JSON, OIDC, clientId);
+ return resultStream != null ? deserialize(resultStream, OIDCClientRepresentation.class) : null;
+ }
+
+ public OIDCClientRepresentation update(OIDCClientRepresentation client) throws ClientRegistrationException {
+ String content = serialize(client);
+ InputStream resultStream = httpUtil.doPut(content, JSON, JSON, OIDC, client.getClientId());
+ return resultStream != null ? deserialize(resultStream, OIDCClientRepresentation.class) : null;
+ }
+
+ public void delete(OIDCClientRepresentation client) throws ClientRegistrationException {
+ delete(client.getClientId());
+ }
+
+ public void delete(String clientId) throws ClientRegistrationException {
+ httpUtil.doDelete(OIDC, clientId);
+ }
+
+ }
+
+ public class SAMLClientRegistration {
+
+ public ClientRepresentation create(String entityDescriptor) throws ClientRegistrationException {
+ InputStream resultStream = httpUtil.doPost(entityDescriptor, XML, JSON, SAML);
+ return deserialize(resultStream, ClientRepresentation.class);
+ }
+
+ }
+
public static class ClientRegistrationBuilder {
private String url;
diff --git a/client-registration/api/src/main/java/org/keycloak/client/registration/HttpUtil.java b/client-registration/api/src/main/java/org/keycloak/client/registration/HttpUtil.java
index 6444749..4d44710 100644
--- a/client-registration/api/src/main/java/org/keycloak/client/registration/HttpUtil.java
+++ b/client-registration/api/src/main/java/org/keycloak/client/registration/HttpUtil.java
@@ -33,12 +33,12 @@ class HttpUtil {
this.auth = auth;
}
- InputStream doPost(String content, String... path) throws ClientRegistrationException {
+ InputStream doPost(String content, String contentType, String acceptType, 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.setHeader(HttpHeaders.CONTENT_TYPE, contentType);
+ request.setHeader(HttpHeaders.ACCEPT, acceptType);
request.setEntity(new StringEntity(content));
addAuth(request);
@@ -60,11 +60,11 @@ class HttpUtil {
}
}
- InputStream doGet(String... path) throws ClientRegistrationException {
+ InputStream doGet(String acceptType, String... path) throws ClientRegistrationException {
try {
HttpGet request = new HttpGet(getUrl(baseUri, path));
- request.setHeader(HttpHeaders.ACCEPT, "application/json");
+ request.setHeader(HttpHeaders.ACCEPT, acceptType);
addAuth(request);
@@ -90,12 +90,12 @@ class HttpUtil {
}
}
- InputStream doPut(String content, String... path) throws ClientRegistrationException {
+ InputStream doPut(String content, String contentType, String acceptType, 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.setHeader(HttpHeaders.CONTENT_TYPE, contentType);
+ request.setHeader(HttpHeaders.ACCEPT, acceptType);
request.setEntity(new StringEntity(content));
addAuth(request);
@@ -134,7 +134,7 @@ class HttpUtil {
response.getEntity().getContent().close();
}
- if (response.getStatusLine().getStatusCode() != 200) {
+ if (response.getStatusLine().getStatusCode() != 204) {
throw new HttpErrorException(response.getStatusLine());
}
} catch (IOException e) {
diff --git a/client-registration/api/src/main/java/org/keycloak/client/registration/OIDCClientRepresentationMixIn.java b/client-registration/api/src/main/java/org/keycloak/client/registration/OIDCClientRepresentationMixIn.java
new file mode 100644
index 0000000..b0dfed6
--- /dev/null
+++ b/client-registration/api/src/main/java/org/keycloak/client/registration/OIDCClientRepresentationMixIn.java
@@ -0,0 +1,22 @@
+package org.keycloak.client.registration;
+
+import org.codehaus.jackson.annotate.JsonIgnore;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+abstract class OIDCClientRepresentationMixIn {
+
+ @JsonIgnore
+ private Integer client_id_issued_at;
+
+ @JsonIgnore
+ private Integer client_secret_expires_at;
+
+ @JsonIgnore
+ private String registration_client_uri;
+
+ @JsonIgnore
+ private String registration_access_token;
+
+}
diff --git a/client-registration/cli/src/main/java/org/keycloak/client/registration/cli/ClientRegistrationCLI.java b/client-registration/cli/src/main/java/org/keycloak/client/registration/cli/ClientRegistrationCLI.java
index f0b0857..986faac 100644
--- a/client-registration/cli/src/main/java/org/keycloak/client/registration/cli/ClientRegistrationCLI.java
+++ b/client-registration/cli/src/main/java/org/keycloak/client/registration/cli/ClientRegistrationCLI.java
@@ -62,6 +62,8 @@ public class ClientRegistrationCLI {
CommandContainer command = registry.getCommand(args[0], null);
ParserGenerator.parseAndPopulate(command, args[0], Arrays.copyOfRange(args, 1, args.length));
}*/
+
+ //commandInvocation.getCommandRegistry().getAllCommandNames()
}
}
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 db8b8d0..c81b062 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
@@ -73,5 +73,7 @@
<column name="REGISTRATION_TOKEN" type="VARCHAR(255)"/>
</addColumn>
+ <modifyDataType tableName="REALM" columnName="PASSWORD_POLICY" newDataType="VARCHAR(2550)"/>
+
</changeSet>
</databaseChangeLog>
\ No newline at end of file
diff --git a/docbook/auth-server-docs/reference/en/en-US/modules/client-registration.xml b/docbook/auth-server-docs/reference/en/en-US/modules/client-registration.xml
index 0070cc0..fab7119 100755
--- a/docbook/auth-server-docs/reference/en/en-US/modules/client-registration.xml
+++ b/docbook/auth-server-docs/reference/en/en-US/modules/client-registration.xml
@@ -10,15 +10,15 @@
<para>
The Client Registration Service provides built-in support for Keycloak Client Representations, OpenID Connect
Client Meta Data and SAML Entity Descriptors. It's also possible to plugin custom client registration providers
- if required. The Client Registration Service endpoint is <literal><KEYCLOAK URL>/clients/<provider></literal>.
+ if required. The Client Registration Service endpoint is <literal><KEYCLOAK URL>/realms/<realm>/clients/<provider></literal>.
</para>
<para>
The built-in supported <literal>providers</literal> are:
<itemizedlist>
<listitem><literal>default</literal> Keycloak Representations</listitem>
<listitem><literal>install</literal> Keycloak Adapter Configuration</listitem>
- <!--<listitem><literal>openid-connect</literal> OpenID Connect Dynamic Client Registration</listitem>-->
- <!--<listitem><literal>saml-ed</literal> SAML Entity Descriptors</listitem>-->
+ <listitem><literal>openid-connect</literal> OpenID Connect Dynamic Client Registration</listitem>
+ <listitem><literal>saml2-entity-descriptor</literal> SAML Entity Descriptors</listitem>
</itemizedlist>
The following sections will describe how to use the different providers.
</para>
@@ -106,30 +106,30 @@ Authorization: bearer eyJhbGciOiJSUzI1NiJ9.eyJqdGkiOiJmMjJmNzQyYy04ZjNlLTQ2M....
</para>
<para>
To create a client create a Client Representation (JSON) then do a HTTP POST to:
- <literal><KEYCLOAK URL>/clients/<provider>/default</literal>. It will return a Client Representation
+ <literal><KEYCLOAK URL>/realms/<realm>/clients/<provider>/default</literal>. It will return a Client Representation
that also includes the registration access token. You should save the registration access token somewhere
if you want to retrieve the config, update or delete the client later.
</para>
<para>
To retrieve the Client Representation then do a HTTP GET to:
- <literal><KEYCLOAK URL>/clients/<provider>/default/<client id></literal>. It will also
+ <literal><KEYCLOAK URL>/realms/<realm>clients/<provider>/default/<client id></literal>. It will also
return a new registration access token.
</para>
<para>
To update the Client Representation then do a HTTP PUT to with the updated Client Representation to:
- <literal><KEYCLOAK URL>/clients/<provider>/default/<client id></literal>. It will also
+ <literal><KEYCLOAK URL>/realms/<realm>/clients/<provider>/default/<client id></literal>. It will also
return a new registration access token.
</para>
<para>
To delete the Client Representation then do a HTTP DELETE to:
- <literal><KEYCLOAK URL>/clients/<provider>/default/<client id></literal>
+ <literal><KEYCLOAK URL>/realms/<realm>/clients/<provider>/default/<client id></literal>
</para>
</section>
<section>
<title>Keycloak Adapter Configuration</title>
<para>
- The <default>installation</default> client registration provider can be used to retrieve the adapter configuration
+ The <literal>installation</literal> client registration provider can be used to retrieve the adapter configuration
for a client. In addition to token authentication you can also authenticate with client credentials using
HTTP basic authentication. To do this include the following header in the request:
<programlisting><![CDATA[
@@ -138,7 +138,7 @@ Authorization: basic BASE64(client-id + ':' + client-secret)
</para>
<para>
To retrieve the Adapter Configuration then do a HTTP GET to:
- <literal><KEYCLOAK URL>/clients/<provider>/installation/<client id></literal>
+ <literal><KEYCLOAK URL>//realms/<realm>clients/<provider>/installation/<client id></literal>
</para>
<para>
No authentication is required for public clients. This means that for the JavaScript adapter you can
@@ -146,23 +146,36 @@ Authorization: basic BASE64(client-id + ':' + client-secret)
</para>
</section>
- <!--
<section>
<title>OpenID Connect Dynamic Client Registration</title>
<para>
- TODO
+ Keycloak implements <ulink url="https://openid.net/specs/openid-connect-registration-1_0.html">OpenID Connect Dynamic Client Registration</ulink>,
+ which extends <ulink url="https://tools.ietf.org/html/rfc7591">OAuth 2.0 Dynamic Client Registration Protocol</ulink> and
+ <ulink url="https://tools.ietf.org/html/rfc7592">OAuth 2.0 Dynamic Client Registration Management Protocol</ulink>.
+ </para>
+ <para>
+ The endpoint to use these specifications to register clients in Keycloak is:
+ <literal><KEYCLOAK URL>/realms/<realm>/clients/<provider>/oidc[/<client id>]</literal>.
+ </para>
+ <para>
+ This endpoints can also be found in the OpenID Connect Discovery endpoint for the realm:
+ <literal><KEYCLOAK URL>/realms/<realm>/.well-known/openid-configuration</literal>.
</para>
</section>
- -->
- <!--
<section>
<title>SAML Entity Descriptors</title>
<para>
- TODO
+ The SAML Entity Descriptor endpoint only supports using SAML v2 Entity Descriptors to create clients. It
+ doesn't support retrieving, updating or deleting clients. For those operations the Keycloak representation
+ endpoints should be used. When creating a client a Keycloak Client Representation is returned with details
+ about the created client, including a registration access token.
+ </para>
+ <para>
+ To create a client do a HTTP POST with the SAML Entity Descriptor to:
+ <literal><KEYCLOAK URL>/realms/<realm>/clients/<provider>/saml2-entity-descriptor</literal>.
</para>
</section>
- -->
<section>
<title>Client Registration Java API</title>
diff --git a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ExtendingThemeManager.java b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ExtendingThemeManager.java
index 020d349..0a253d1 100644
--- a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ExtendingThemeManager.java
+++ b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ExtendingThemeManager.java
@@ -153,6 +153,10 @@ public class ExtendingThemeManager implements ThemeProvider {
private List<Theme> themes;
+ private Properties properties;
+
+ private ConcurrentHashMap<String, ConcurrentHashMap<Locale, Properties>> messages = new ConcurrentHashMap<>();
+
public ExtendingTheme(List<Theme> themes) {
this.themes = themes;
}
@@ -229,28 +233,41 @@ public class ExtendingThemeManager implements ThemeProvider {
@Override
public Properties getMessages(String baseBundlename, Locale locale) throws IOException {
- Properties messages = new Properties();
- ListIterator<Theme> itr = themes.listIterator(themes.size());
- while (itr.hasPrevious()) {
- Properties m = itr.previous().getMessages(baseBundlename, locale);
- if (m != null) {
- messages.putAll(m);
+ if (messages.get(baseBundlename) == null || messages.get(baseBundlename).get(locale) == null) {
+ Properties messages = new Properties();
+ ListIterator<Theme> itr = themes.listIterator(themes.size());
+ while (itr.hasPrevious()) {
+ Properties m = itr.previous().getMessages(baseBundlename, locale);
+ if (m != null) {
+ messages.putAll(m);
+ }
}
+
+ this.messages.putIfAbsent(baseBundlename, new ConcurrentHashMap<Locale, Properties>());
+ this.messages.get(baseBundlename).putIfAbsent(locale, messages);
+
+ return messages;
+ } else {
+ return messages.get(baseBundlename).get(locale);
}
- return messages;
}
@Override
public Properties getProperties() throws IOException {
- Properties properties = new Properties();
- ListIterator<Theme> itr = themes.listIterator(themes.size());
- while (itr.hasPrevious()) {
- Properties p = itr.previous().getProperties();
- if (p != null) {
- properties.putAll(p);
+ if (properties == null) {
+ Properties properties = new Properties();
+ ListIterator<Theme> itr = themes.listIterator(themes.size());
+ while (itr.hasPrevious()) {
+ Properties p = itr.previous().getProperties();
+ if (p != null) {
+ properties.putAll(p);
+ }
}
+ this.properties = properties;
+ return properties;
+ } else {
+ return properties;
}
- return properties;
}
}
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
index 09192ae..e274ca7 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
+++ b/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
@@ -271,7 +271,7 @@ client-certificate-import=Client Certificate Import
import-client-certificate=Import Client Certificate
jwt-import.key-alias.tooltip=Archive alias for your certificate.
secret=Secret
-regenerate-secret=Regenerate Secretsecret=Secret
+regenerate-secret=Regenerate Secret
registrationAccessToken=Registration access token
registrationAccessToken.regenerate=Regenerate registration access token
registrationAccessToken.tooltip=The registration access token provides access for clients to the client registration service.
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/otp-policy.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/otp-policy.html
index 1f8890e..eaaf811 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/otp-policy.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/otp-policy.html
@@ -51,18 +51,18 @@
<kc-tooltip>How far ahead should the server look just in case the token generator and server are out of time sync or counter sync?</kc-tooltip>
</div>
- <div class="form-group" data-ng-show="realm.otpPolicyType == 'hotp'">
+ <div class="form-group" data-ng-if="realm.otpPolicyType == 'hotp'">
<label class="col-md-2 control-label" for="counter">Initial Counter</label>
<div class="col-md-6">
- <input class="form-control" type="number" required min="1" max="120" id="counter" name="counter" data-ng-model="realm.otpPolicyInitialCounter" autofocus>
+ <input class="form-control" type="number" data-ng-required="realm.otpPolicyType == 'hotp'" min="1" max="120" id="counter" name="counter" data-ng-model="realm.otpPolicyInitialCounter" autofocus>
</div>
<kc-tooltip>What should the initial counter value be?</kc-tooltip>
</div>
- <div class="form-group" data-ng-show="realm.otpPolicyType == 'totp'">
+ <div class="form-group" data-ng-if="realm.otpPolicyType == 'totp'">
<label class="col-md-2 control-label" for="counter">OTP Token Period</label>
<div class="col-md-6">
- <input class="form-control" type="number" required min="1" max="120" id="period" name="period" data-ng-model="realm.otpPolicyPeriod">
+ <input class="form-control" type="number" data-ng-required="realm.otpPolicyType == 'totp'" min="1" max="120" id="period" name="period" data-ng-model="realm.otpPolicyPeriod">
</div>
<kc-tooltip>How many seconds should an OTP token be valid? Defaults to 30 seconds.</kc-tooltip>
</div>
diff --git a/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java b/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java
index aff7d37..3674334 100755
--- a/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java
+++ b/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java
@@ -76,6 +76,8 @@ public class PasswordPolicy implements Serializable {
list.add(new PasswordHistory(arg));
} else if (name.equals(ForceExpiredPasswordChange.NAME)) {
list.add(new ForceExpiredPasswordChange(arg));
+ } else {
+ throw new IllegalArgumentException("Unsupported policy");
}
}
return list;
diff --git a/model/api/src/main/java/org/keycloak/models/utils/CredentialValidation.java b/model/api/src/main/java/org/keycloak/models/utils/CredentialValidation.java
index 7ce15d8..af9b6b5 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/CredentialValidation.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/CredentialValidation.java
@@ -51,6 +51,10 @@ public class CredentialValidation {
}
public static boolean validateHashedCredential(RealmModel realm, UserModel user, String unhashedCredValue, UserCredentialValueModel credential) {
+ if(unhashedCredValue == null){
+ return false;
+ }
+
boolean validated = new Pbkdf2PasswordEncoder(credential.getSalt()).verify(unhashedCredValue, credential.getValue(), credential.getHashIterations());
if (validated) {
int iterations = hashIterations(realm);
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 dde4462..3916fd1 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
@@ -69,12 +69,12 @@ public class RepresentationToModel {
private static Logger logger = Logger.getLogger(RepresentationToModel.class);
public static OTPPolicy toPolicy(RealmRepresentation rep) {
OTPPolicy policy = new OTPPolicy();
- policy.setType(rep.getOtpPolicyType());
- policy.setLookAheadWindow(rep.getOtpPolicyLookAheadWindow());
- policy.setInitialCounter(rep.getOtpPolicyInitialCounter());
- policy.setAlgorithm(rep.getOtpPolicyAlgorithm());
- policy.setDigits(rep.getOtpPolicyDigits());
- policy.setPeriod(rep.getOtpPolicyPeriod());
+ if (rep.getOtpPolicyType() != null) policy.setType(rep.getOtpPolicyType());
+ if (rep.getOtpPolicyLookAheadWindow() != null) policy.setLookAheadWindow(rep.getOtpPolicyLookAheadWindow());
+ if (rep.getOtpPolicyInitialCounter() != null) policy.setInitialCounter(rep.getOtpPolicyInitialCounter());
+ if (rep.getOtpPolicyAlgorithm() != null) policy.setAlgorithm(rep.getOtpPolicyAlgorithm());
+ if (rep.getOtpPolicyDigits() != null) policy.setDigits(rep.getOtpPolicyDigits());
+ if (rep.getOtpPolicyPeriod() != null) policy.setPeriod(rep.getOtpPolicyPeriod());
return policy;
}
diff --git a/model/api/src/test/java/org/keycloak/models/PasswordPolicyTest.java b/model/api/src/test/java/org/keycloak/models/PasswordPolicyTest.java
index df76588..8c662fb 100755
--- a/model/api/src/test/java/org/keycloak/models/PasswordPolicyTest.java
+++ b/model/api/src/test/java/org/keycloak/models/PasswordPolicyTest.java
@@ -83,6 +83,15 @@ public class PasswordPolicyTest {
Assert.assertEquals("invalidPasswordNotUsernameMessage", policy.validate("jdoe", "jdoe").getMessage());
Assert.assertNull(policy.validate("jdoe", "ab&d1234"));
}
+
+ @Test
+ public void testInvalidPolicyName() {
+ try {
+ PasswordPolicy policy = new PasswordPolicy("noSuchPolicy");
+ Assert.fail("Expected exception");
+ } catch (IllegalArgumentException e) {
+ }
+ }
@Test
public void testRegexPatterns() {
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
index 81c2df4..1ee3cd2 100644
--- 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
@@ -1,63 +1,35 @@
package org.keycloak.protocol.saml.clientregistration;
-import org.jboss.logging.Logger;
-import org.keycloak.events.EventBuilder;
+import org.keycloak.exportimport.ClientDescriptionConverter;
import org.keycloak.models.KeycloakSession;
-import org.keycloak.services.clientregistration.ClientRegistrationAuth;
-import org.keycloak.services.clientregistration.ClientRegistrationProvider;
+import org.keycloak.protocol.saml.EntityDescriptorDescriptionConverter;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.services.clientregistration.AbstractClientRegistrationProvider;
+
+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 ClientRegistrationAuth auth;
+public class EntityDescriptorClientRegistrationProvider extends AbstractClientRegistrationProvider {
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(ClientRegistrationAuth auth) {
- this.auth = auth;
+ super(session);
}
- @Override
- public void setEvent(EventBuilder event) {
- this.event = event;
+ @POST
+ @Consumes(MediaType.APPLICATION_XML)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response createSaml(String descriptor) {
+ ClientRepresentation client = session.getProvider(ClientDescriptionConverter.class, EntityDescriptorDescriptionConverter.ID).convertToInternal(descriptor);
+ client = create(client);
+ URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(client.getClientId()).build();
+ return Response.created(uri).entity(client).build();
}
}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java
index 0506f21..5afaa3d 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java
@@ -148,24 +148,17 @@ public abstract class AbstractUsernameFormAuthenticator extends AbstractFormAuth
public boolean validatePassword(AuthenticationFlowContext context, UserModel user, MultivaluedMap<String, String> inputData) {
List<UserCredentialModel> credentials = new LinkedList<>();
String password = inputData.getFirst(CredentialRepresentation.PASSWORD);
- if (password == null || password.isEmpty()) {
- invalidPassword(context, user);
- return false;
- }
credentials.add(UserCredentialModel.password(password));
boolean valid = context.getSession().users().validCredentials(context.getRealm(), user, credentials);
if (!valid) {
- invalidPassword(context, user);
+ context.getEvent().user(user);
+ context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
+ Response challengeResponse = invalidCredentials(context);
+ context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse);
+ context.clearUser();
return false;
}
return true;
}
- private void invalidPassword(AuthenticationFlowContext context, UserModel user) {
- context.getEvent().user(user);
- context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
- Response challengeResponse = invalidCredentials(context);
- context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse);
- context.clearUser();
- }
}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidatePassword.java b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidatePassword.java
index cff7f37..21aa18d 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidatePassword.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidatePassword.java
@@ -31,15 +31,6 @@ public class ValidatePassword extends AbstractDirectGrantAuthenticator {
MultivaluedMap<String, String> inputData = context.getHttpRequest().getDecodedFormParameters();
List<UserCredentialModel> credentials = new LinkedList<>();
String password = inputData.getFirst(CredentialRepresentation.PASSWORD);
- if (password == null || password.isEmpty()) {
- if (context.getUser() != null) {
- context.getEvent().user(context.getUser());
- }
- context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
- Response challengeResponse = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_grant", "Invalid user credentials");
- context.failure(AuthenticationFlowError.INVALID_USER, challengeResponse);
- return;
- }
credentials.add(UserCredentialModel.password(password));
boolean valid = context.getSession().users().validCredentials(context.getRealm(), context.getUser(), credentials);
if (!valid) {
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 955dfe4..6caacd6 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverter.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverter.java
@@ -5,8 +5,7 @@ import org.keycloak.exportimport.ClientDescriptionConverter;
import org.keycloak.exportimport.ClientDescriptionConverterFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
-import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.protocol.oidc.representations.OIDCClientRepresentation;
+import org.keycloak.representations.oidc.OIDCClientRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.services.clientregistration.oidc.DescriptionConverter;
import org.keycloak.util.JsonSerialization;
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
index 714c0d1..60e9f9d 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
@@ -4,6 +4,8 @@ import org.keycloak.OAuth2Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
+import org.keycloak.services.clientregistration.ClientRegistrationService;
+import org.keycloak.services.clientregistration.oidc.OIDCClientRegistrationProviderFactory;
import org.keycloak.services.resources.RealmsResource;
import org.keycloak.services.Urls;
import org.keycloak.wellknown.WellKnownProvider;
@@ -48,6 +50,7 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
config.setUserinfoEndpoint(uriBuilder.clone().path(OIDCLoginProtocolService.class, "issueUserInfo").build(realm.getName(), OIDCLoginProtocol.LOGIN_PROTOCOL).toString());
config.setLogoutEndpoint(uriBuilder.clone().path(OIDCLoginProtocolService.class, "logout").build(realm.getName(), OIDCLoginProtocol.LOGIN_PROTOCOL).toString());
config.setJwksUri(uriBuilder.clone().path(OIDCLoginProtocolService.class, "certs").build(realm.getName(), OIDCLoginProtocol.LOGIN_PROTOCOL).toString());
+ config.setRegistrationEndpoint(RealmsResource.clientRegistrationUrl(uriInfo).path(ClientRegistrationService.class, "provider").build(realm.getName(), OIDCClientRegistrationProviderFactory.ID).toString());
config.setIdTokenSigningAlgValuesSupported(DEFAULT_ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED);
config.setResponseTypesSupported(DEFAULT_RESPONSE_TYPES_SUPPORTED);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java b/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java
index 9245e58..0226331 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java
@@ -7,7 +7,6 @@ import org.codehaus.jackson.annotate.JsonProperty;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -48,6 +47,9 @@ public class OIDCConfigurationRepresentation {
@JsonProperty("response_modes_supported")
private List<String> responseModesSupported;
+ @JsonProperty("registration_endpoint")
+ private String registrationEndpoint;
+
protected Map<String, Object> otherClaims = new HashMap<String, Object>();
public String getIssuer() {
@@ -138,6 +140,14 @@ public class OIDCConfigurationRepresentation {
this.responseModesSupported = responseModesSupported;
}
+ public String getRegistrationEndpoint() {
+ return registrationEndpoint;
+ }
+
+ public void setRegistrationEndpoint(String registrationEndpoint) {
+ this.registrationEndpoint = registrationEndpoint;
+ }
+
@JsonAnyGetter
public Map<String, Object> getOtherClaims() {
return otherClaims;
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java
new file mode 100644
index 0000000..0c95a37
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java
@@ -0,0 +1,125 @@
+package org.keycloak.services.clientregistration;
+
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
+import org.keycloak.models.ClientInitialAccessModel;
+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.representations.idm.ClientRepresentation;
+import org.keycloak.services.ErrorResponseException;
+import org.keycloak.services.ForbiddenException;
+
+import javax.ws.rs.core.Response;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public abstract class AbstractClientRegistrationProvider implements ClientRegistrationProvider {
+
+ protected KeycloakSession session;
+ protected EventBuilder event;
+ protected ClientRegistrationAuth auth;
+
+ public AbstractClientRegistrationProvider(KeycloakSession session) {
+ this.session = session;
+ }
+
+ public ClientRepresentation create(ClientRepresentation client) {
+ event.event(EventType.CLIENT_REGISTER);
+
+ auth.requireCreate();
+
+ try {
+ ClientModel clientModel = RepresentationToModel.createClient(session, session.getContext().getRealm(), client, true);
+ if (client.getClientId() == null) {
+ clientModel.setClientId(clientModel.getId());
+ }
+
+ client = ModelToRepresentation.toRepresentation(clientModel);
+
+ String registrationAccessToken = ClientRegistrationTokenUtils.updateRegistrationAccessToken(session, clientModel);
+
+ client.setRegistrationAccessToken(registrationAccessToken);
+
+ if (auth.isInitialAccessToken()) {
+ ClientInitialAccessModel initialAccessModel = auth.getInitialAccessModel();
+ initialAccessModel.decreaseRemainingCount();
+ }
+
+ event.client(client.getClientId()).success();
+ return client;
+ } catch (ModelDuplicateException e) {
+ throw new ErrorResponseException(ErrorCodes.INVALID_CLIENT_METADATA, "Client Identifier in use", Response.Status.BAD_REQUEST);
+ }
+ }
+
+ public ClientRepresentation get(String clientId) {
+ event.event(EventType.CLIENT_INFO);
+
+ ClientModel client = session.getContext().getRealm().getClientByClientId(clientId);
+ auth.requireView(client);
+
+ ClientRepresentation rep = ModelToRepresentation.toRepresentation(client);
+
+ if (auth.isRegistrationAccessToken()) {
+ String registrationAccessToken = ClientRegistrationTokenUtils.updateRegistrationAccessToken(session, client);
+ rep.setRegistrationAccessToken(registrationAccessToken);
+ }
+
+ event.client(client.getClientId()).success();
+ return rep;
+ }
+
+ public ClientRepresentation update(String clientId, ClientRepresentation rep) {
+ event.event(EventType.CLIENT_UPDATE).client(clientId);
+
+ ClientModel client = session.getContext().getRealm().getClientByClientId(clientId);
+ auth.requireUpdate(client);
+
+ if (!client.getClientId().equals(rep.getClientId())) {
+ throw new ErrorResponseException(ErrorCodes.INVALID_CLIENT_METADATA, "Client Identifier modified", Response.Status.BAD_REQUEST);
+ }
+
+ RepresentationToModel.updateClient(rep, client);
+ rep = ModelToRepresentation.toRepresentation(client);
+
+ if (auth.isRegistrationAccessToken()) {
+ String registrationAccessToken = ClientRegistrationTokenUtils.updateRegistrationAccessToken(session, client);
+ rep.setRegistrationAccessToken(registrationAccessToken);
+ }
+
+ event.client(client.getClientId()).success();
+ return rep;
+ }
+
+ public void delete(String clientId) {
+ event.event(EventType.CLIENT_DELETE).client(clientId);
+
+ ClientModel client = session.getContext().getRealm().getClientByClientId(clientId);
+ auth.requireUpdate(client);
+
+ if (session.getContext().getRealm().removeClient(client.getId())) {
+ event.client(client.getClientId()).success();
+ } else {
+ throw new ForbiddenException();
+ }
+ }
+
+ @Override
+ public void setAuth(ClientRegistrationAuth auth) {
+ this.auth = auth;
+ }
+
+ @Override
+ public void setEvent(EventBuilder event) {
+ this.event = event;
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
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 0581388..621d5fb 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java
@@ -25,7 +25,7 @@ public class ClientRegistrationService {
}
@Path("{provider}")
- public Object getProvider(@PathParam("provider") String providerId) {
+ public Object provider(@PathParam("provider") String providerId) {
checkSsl();
ClientRegistrationProvider provider = session.getProvider(ClientRegistrationProvider.class, providerId);
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 38d71f2..126d00a 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java
@@ -2,14 +2,11 @@ package org.keycloak.services.clientregistration;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
-import org.keycloak.models.ClientInitialAccessModel;
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.representations.idm.ClientRepresentation;
-import org.keycloak.services.ErrorResponse;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
@@ -19,101 +16,41 @@ import java.net.URI;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
-public class DefaultClientRegistrationProvider implements ClientRegistrationProvider {
-
- private KeycloakSession session;
- private EventBuilder event;
- private ClientRegistrationAuth auth;
+public class DefaultClientRegistrationProvider extends AbstractClientRegistrationProvider {
public DefaultClientRegistrationProvider(KeycloakSession session) {
- this.session = session;
+ super(session);
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
- public Response create(ClientRepresentation client) {
- event.event(EventType.CLIENT_REGISTER);
-
- auth.requireCreate();
-
- try {
- ClientModel clientModel = RepresentationToModel.createClient(session, session.getContext().getRealm(), client, true);
- client = ModelToRepresentation.toRepresentation(clientModel);
-
- String registrationAccessToken = ClientRegistrationTokenUtils.updateRegistrationAccessToken(session, clientModel);
-
- client.setRegistrationAccessToken(registrationAccessToken);
-
- URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(clientModel.getId()).build();
-
- if (auth.isInitialAccessToken()) {
- ClientInitialAccessModel initialAccessModel = auth.getInitialAccessModel();
- initialAccessModel.decreaseRemainingCount();
- }
-
- event.client(client.getClientId()).success();
- return Response.created(uri).entity(client).build();
- } catch (ModelDuplicateException e) {
- return ErrorResponse.exists("Client " + client.getClientId() + " already exists");
- }
+ public Response createDefault(ClientRepresentation client) {
+ client = create(client);
+ URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(client.getClientId()).build();
+ return Response.created(uri).entity(client).build();
}
@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);
- auth.requireView(client);
-
- ClientRepresentation rep = ModelToRepresentation.toRepresentation(client);
-
- if (auth.isRegistrationAccessToken()) {
- String registrationAccessToken = ClientRegistrationTokenUtils.updateRegistrationAccessToken(session, client);
- rep.setRegistrationAccessToken(registrationAccessToken);
- }
-
- event.client(client.getClientId()).success();
- return Response.ok(rep).build();
+ public Response getDefault(@PathParam("clientId") String clientId) {
+ ClientRepresentation client = get(clientId);
+ return Response.ok(client).build();
}
@PUT
@Path("{clientId}")
@Consumes(MediaType.APPLICATION_JSON)
- public Response update(@PathParam("clientId") String clientId, ClientRepresentation rep) {
- event.event(EventType.CLIENT_UPDATE).client(clientId);
-
- ClientModel client = session.getContext().getRealm().getClientByClientId(clientId);
- auth.requireUpdate(client);
-
- RepresentationToModel.updateClient(rep, client);
- rep = ModelToRepresentation.toRepresentation(client);
-
- if (auth.isRegistrationAccessToken()) {
- String registrationAccessToken = ClientRegistrationTokenUtils.updateRegistrationAccessToken(session, client);
- rep.setRegistrationAccessToken(registrationAccessToken);
- }
-
- event.client(client.getClientId()).success();
- return Response.ok(rep).build();
+ public Response updateDefault(@PathParam("clientId") String clientId, ClientRepresentation client) {
+ client = update(clientId, client);
+ return Response.ok(client).build();
}
@DELETE
@Path("{clientId}")
- public Response delete(@PathParam("clientId") String clientId) {
- event.event(EventType.CLIENT_DELETE).client(clientId);
-
- 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();
- }
+ public void deleteDefault(@PathParam("clientId") String clientId) {
+ delete(clientId);
}
@Override
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ErrorCodes.java b/services/src/main/java/org/keycloak/services/clientregistration/ErrorCodes.java
new file mode 100644
index 0000000..ed491de
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ErrorCodes.java
@@ -0,0 +1,12 @@
+package org.keycloak.services.clientregistration;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface ErrorCodes {
+
+ String INVALID_REDIRECT_URI = "invalid_redirect_uri";
+
+ String INVALID_CLIENT_METADATA = "invalid_client_metadata";
+
+}
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
index 1e0784c..a7f9f2c 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java
@@ -1,8 +1,9 @@
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;
+import org.keycloak.representations.oidc.OIDCClientRepresentation;
+
+import java.net.URI;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -11,27 +12,22 @@ public class DescriptionConverter {
public static ClientRepresentation toInternal(OIDCClientRepresentation clientOIDC) {
ClientRepresentation client = new ClientRepresentation();
- client.setClientId(KeycloakModelUtils.generateId());
+ client.setClientId(clientOIDC.getClientId());
client.setName(clientOIDC.getClientName());
client.setRedirectUris(clientOIDC.getRedirectUris());
client.setBaseUrl(clientOIDC.getClientUri());
return client;
}
- public static OIDCClientResponseRepresentation toExternalResponse(ClientRepresentation client) {
- OIDCClientResponseRepresentation response = new OIDCClientResponseRepresentation();
+ public static OIDCClientRepresentation toExternalResponse(ClientRepresentation client, URI uri) {
+ OIDCClientRepresentation response = new OIDCClientRepresentation();
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());
-
+ response.setRegistrationClientUri(uri.toString());
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
index 961f28d..e60720b 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java
@@ -1,72 +1,70 @@
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.models.KeycloakSession;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.representations.oidc.OIDCClientRepresentation;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.services.ErrorResponseException;
+import org.keycloak.services.clientregistration.AbstractClientRegistrationProvider;
import org.keycloak.services.clientregistration.ClientRegistrationAuth;
-import org.keycloak.services.clientregistration.ClientRegistrationProvider;
+import org.keycloak.services.clientregistration.ErrorCodes;
+
+import javax.ws.rs.*;
+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 {
+public class OIDCClientRegistrationProvider extends AbstractClientRegistrationProvider {
- private static final Logger logger = Logger.getLogger(OIDCClientRegistrationProvider.class);
+ public OIDCClientRegistrationProvider(KeycloakSession session) {
+ super(session);
+ }
- private KeycloakSession session;
- private EventBuilder event;
- private ClientRegistrationAuth auth;
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response createOIDC(OIDCClientRepresentation clientOIDC) {
+ if (clientOIDC.getClientId() != null) {
+ throw new ErrorResponseException(ErrorCodes.INVALID_CLIENT_METADATA, "Client Identifier included", Response.Status.BAD_REQUEST);
+ }
- public OIDCClientRegistrationProvider(KeycloakSession session) {
- this.session = session;
+ ClientRepresentation client = DescriptionConverter.toInternal(clientOIDC);
+ client = create(client);
+ URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(client.getClientId()).build();
+ clientOIDC = DescriptionConverter.toExternalResponse(client, uri);
+ clientOIDC.setClientIdIssuedAt(Time.currentTime());
+ return Response.created(uri).entity(clientOIDC).build();
}
-// @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.setRegistrationToken(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");
-// }
-// }
+ @GET
+ @Path("{clientId}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getOIDC(@PathParam("clientId") String clientId) {
+ ClientRepresentation client = get(clientId);
+ OIDCClientRepresentation clientOIDC = DescriptionConverter.toExternalResponse(client, session.getContext().getUri().getRequestUri());
+ return Response.ok(clientOIDC).build();
+ }
- @Override
- public void close() {
+ @PUT
+ @Path("{clientId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response updateOIDC(@PathParam("clientId") String clientId, OIDCClientRepresentation clientOIDC) {
+ ClientRepresentation client = DescriptionConverter.toInternal(clientOIDC);
+ client = update(clientId, client);
+ URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(client.getClientId()).build();
+ clientOIDC = DescriptionConverter.toExternalResponse(client, uri);
+ return Response.ok(clientOIDC).build();
+ }
+
+ @DELETE
+ @Path("{clientId}")
+ public void deleteOIDC(@PathParam("clientId") String clientId) {
+ delete(clientId);
}
@Override
@@ -79,4 +77,8 @@ public class OIDCClientRegistrationProvider implements ClientRegistrationProvide
this.event = event;
}
+ @Override
+ public void close() {
+ }
+
}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProviderFactory.java b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProviderFactory.java
index 6f112f8..0144e79 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProviderFactory.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProviderFactory.java
@@ -11,6 +11,8 @@ import org.keycloak.services.clientregistration.ClientRegistrationProviderFactor
*/
public class OIDCClientRegistrationProviderFactory implements ClientRegistrationProviderFactory {
+ public static final String ID = "openid-connect";
+
@Override
public ClientRegistrationProvider create(KeycloakSession session) {
return new OIDCClientRegistrationProvider(session);
@@ -30,7 +32,7 @@ public class OIDCClientRegistrationProviderFactory implements ClientRegistration
@Override
public String getId() {
- return "openid-connect";
+ return ID;
}
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index d874d1e..90eab4a 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -236,7 +236,7 @@ public class RealmAdminResource {
} catch (ModelDuplicateException e) {
throw e;
} catch (Exception e) {
- logger.error(e);
+ logger.error(e.getMessage(), e);
return ErrorResponse.error("Failed to update " + rep.getRealm() + " Realm.", Response.Status.INTERNAL_SERVER_ERROR);
}
}
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 4b5e4f2..cc1e49a 100755
--- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
@@ -59,6 +59,10 @@ public class RealmsResource {
return uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(RealmsResource.class, "getProtocol");
}
+ public static UriBuilder clientRegistrationUrl(UriInfo uriInfo) {
+ return uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(RealmsResource.class, "getClientsService");
+ }
+
public static UriBuilder brokerUrl(UriInfo uriInfo) {
return uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(RealmsResource.class, "getBrokerService");
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java
index bb33515..42870a6 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java
@@ -180,7 +180,6 @@ public class RealmTest extends AbstractClientTest {
String description = IOUtils.toString(getClass().getResourceAsStream("/client-descriptions/client-oidc.json"));
ClientRepresentation converted = realm.convertClientDescription(description);
- assertEquals(36, converted.getClientId().length());
assertEquals(1, converted.getRedirectUris().size());
assertEquals("http://localhost", converted.getRedirectUris().get(0));
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java
index 126d0e5..28f0915 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java
@@ -170,6 +170,10 @@ public class KeycloakServer {
System.setProperty("keycloak.theme.cacheTemplates", "false");
}
+ if (!System.getProperties().containsKey("keycloak.theme.cacheThemes")) {
+ System.setProperty("keycloak.theme.cacheThemes", "false");
+ }
+
if (!System.getProperties().containsKey("keycloak.theme.staticMaxAge")) {
System.setProperty("keycloak.theme.staticMaxAge", "-1");
}
diff --git a/testsuite/integration-arquillian/tests/adapters/as7/pom.xml b/testsuite/integration-arquillian/tests/adapters/as7/pom.xml
index 313c154..15b575c 100644
--- a/testsuite/integration-arquillian/tests/adapters/as7/pom.xml
+++ b/testsuite/integration-arquillian/tests/adapters/as7/pom.xml
@@ -25,14 +25,13 @@
<type>zip</type>
</dependency>
<dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-as7-adapter-dist</artifactId>
- <type>zip</type>
+ <groupId>org.wildfly</groupId>
+ <artifactId>wildfly-arquillian-container-managed</artifactId>
</dependency>
<dependency>
- <groupId>org.jboss.as</groupId>
- <artifactId>jboss-as-arquillian-container-managed</artifactId>
- <version>7.2.0.Final</version>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-eap6-adapter-dist</artifactId>
+ <type>zip</type>
</dependency>
</dependencies>
@@ -67,18 +66,20 @@
<artifactItem>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-as7-adapter-dist</artifactId>
+ <version>${project.version}</version>
<type>zip</type>
<outputDirectory>${adapter.libs.as7}</outputDirectory>
</artifactItem>
</artifactItems>
- <overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
</executions>
</plugin>
+
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
+ <version>2.18.1</version>
<configuration>
<systemPropertyVariables>
<app.server.as7>true</app.server.as7>
@@ -90,7 +91,6 @@
</plugins>
</build>
-
<profiles>
<profile>
<id>adapter-libs-provided</id>
@@ -133,5 +133,4 @@
</build>
</profile>
</profiles>
-
</project>
diff --git a/testsuite/integration-arquillian/tests/adapters/as7/src/main/xslt/arquillian.xsl b/testsuite/integration-arquillian/tests/adapters/as7/src/main/xslt/arquillian.xsl
index 8970850..9ba1e94 100644
--- a/testsuite/integration-arquillian/tests/adapters/as7/src/main/xslt/arquillian.xsl
+++ b/testsuite/integration-arquillian/tests/adapters/as7/src/main/xslt/arquillian.xsl
@@ -18,6 +18,7 @@
<property name="jbossHome">${app.server.as7.home}</property>
<property name="javaVmArguments">-Djboss.socket.binding.port-offset=${app.server.port.offset} -Xms64m -Xmx512m -XX:MaxPermSize=256m ${adapter.test.props}</property>
<property name="managementAddress">localhost</property>
+ <property name="managementProtocol">remote</property>
<property name="managementPort">${app.server.management.port.jmx}</property>
</configuration>
</container>
diff --git a/testsuite/integration-arquillian/tests/adapters/as7/src/test/java/org/keycloak/testsuite/adapter/servlet/AS7DemoServletsAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/as7/src/test/java/org/keycloak/testsuite/adapter/servlet/AS7DemoServletsAdapterTest.java
index f0258b2..1a4a68c 100644
--- a/testsuite/integration-arquillian/tests/adapters/as7/src/test/java/org/keycloak/testsuite/adapter/servlet/AS7DemoServletsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/adapters/as7/src/test/java/org/keycloak/testsuite/adapter/servlet/AS7DemoServletsAdapterTest.java
@@ -1,5 +1,6 @@
package org.keycloak.testsuite.adapter.servlet;
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
/**
@@ -7,6 +8,7 @@ import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
* @author tkyjovsk
*/
@AppServerContainer("app-server-as7")
+@AdapterLibsLocationProperty("adapter.libs.as7")
public class AS7DemoServletsAdapterTest extends AbstractDemoServletsAdapterTest {
}
diff --git a/testsuite/integration-arquillian/tests/adapters/as7/src/test/java/org/keycloak/testsuite/adapter/servlet/AS7SessionServletAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/as7/src/test/java/org/keycloak/testsuite/adapter/servlet/AS7SessionServletAdapterTest.java
index 9362ecd..4b88033 100644
--- a/testsuite/integration-arquillian/tests/adapters/as7/src/test/java/org/keycloak/testsuite/adapter/servlet/AS7SessionServletAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/adapters/as7/src/test/java/org/keycloak/testsuite/adapter/servlet/AS7SessionServletAdapterTest.java
@@ -1,5 +1,6 @@
package org.keycloak.testsuite.adapter.servlet;
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
/**
@@ -7,6 +8,7 @@ import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
* @author tkyjovsk
*/
@AppServerContainer("app-server-as7")
+@AdapterLibsLocationProperty("adapter.libs.as7")
public class AS7SessionServletAdapterTest extends AbstractSessionServletAdapterTest {
}
diff --git a/testsuite/integration-arquillian/tests/adapters/eap6/pom.xml b/testsuite/integration-arquillian/tests/adapters/eap6/pom.xml
new file mode 100644
index 0000000..433ca63
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/eap6/pom.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-tests-adapters</artifactId>
+ <version>1.7.0.Final-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>integration-arquillian-adapters-eap6</artifactId>
+ <name>Adapter Tests on EAP 6</name>
+
+ <properties>
+ <app.server.eap6.home>${containers.home}/jboss-eap-6.4</app.server.eap6.home>
+ <adapter.libs.eap6>${containers.home}/keycloak-eap6-adapter-dist</adapter.libs.eap6>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.as</groupId>
+ <artifactId>jboss-as-dist</artifactId>
+ <version>${jboss.version}</version>
+ <type>zip</type>
+ </dependency>
+ <dependency>
+ <groupId>org.wildfly</groupId>
+ <artifactId>wildfly-arquillian-container-managed</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-eap6-adapter-dist</artifactId>
+ <type>zip</type>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack-as7-and-adapter</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.jboss.as</groupId>
+ <artifactId>jboss-as-dist</artifactId>
+ <version>${jboss.version}</version>
+ <type>zip</type>
+ <outputDirectory>${containers.home}</outputDirectory>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-eap6-adapter-dist</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ <outputDirectory>${adapter.libs.eap6}</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.18.1</version>
+ <configuration>
+ <systemPropertyVariables>
+ <app.server.eap6>true</app.server.eap6>
+ <app.server.eap6.home>${app.server.eap6.home}</app.server.eap6.home>
+ <adapter.libs.eap6>${adapter.libs.eap6}</adapter.libs.eap6>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>adapter-libs-provided</id>
+ <activation>
+ <property>
+ <name>!adapter.libs.bundled</name>
+ </property>
+ </activation>
+ <properties>
+ <adapter.libs.eap6>${app.server.eap6.home}</adapter.libs.eap6>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>xml-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>configure-adapter-subsystem</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>transform</goal>
+ </goals>
+ <configuration>
+ <transformationSets>
+ <transformationSet>
+ <dir>${app.server.eap6.home}/standalone/configuration</dir>
+ <includes>
+ <include>standalone.xml</include>
+ </includes>
+ <stylesheet>src/main/xslt/standalone.xsl</stylesheet>
+ <outputDir>${app.server.eap6.home}/standalone/configuration</outputDir>
+ </transformationSet>
+ </transformationSets>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git a/testsuite/integration-arquillian/tests/adapters/eap6/src/main/xslt/arquillian.xsl b/testsuite/integration-arquillian/tests/adapters/eap6/src/main/xslt/arquillian.xsl
new file mode 100644
index 0000000..fbfd50d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/eap6/src/main/xslt/arquillian.xsl
@@ -0,0 +1,37 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:a="http://jboss.org/schema/arquillian"
+ version="2.0"
+ exclude-result-prefixes="xalan a">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+ <xsl:template match="/a:arquillian">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+
+ <container qualifier="app-server-eap6" mode="manual" >
+ <configuration>
+ <property name="enabled">${app.server.eap6}</property>
+ <property name="adapterImplClass">org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</property>
+ <property name="jbossHome">${app.server.eap6.home}</property>
+ <property name="javaVmArguments">-Djboss.socket.binding.port-offset=${app.server.port.offset} -Xms64m -Xmx512m -XX:MaxPermSize=256m ${adapter.test.props}</property>
+ <property name="managementAddress">localhost</property>
+ <property name="managementProtocol">remote</property>
+ <property name="managementPort">${app.server.management.port.jmx}</property>
+ </configuration>
+ </container>
+
+ </xsl:copy>
+ </xsl:template>
+
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/adapters/eap6/src/main/xslt/standalone.xsl b/testsuite/integration-arquillian/tests/adapters/eap6/src/main/xslt/standalone.xsl
new file mode 100644
index 0000000..fb3612b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/eap6/src/main/xslt/standalone.xsl
@@ -0,0 +1,51 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:j="urn:jboss:domain:1.7"
+ xmlns:ds="urn:jboss:domain:datasources:1.2"
+ xmlns:k="urn:jboss:domain:keycloak:1.1"
+ xmlns:sec="urn:jboss:domain:security:1.2"
+ version="2.0"
+ exclude-result-prefixes="xalan j ds k sec">
+
+ <xsl:param name="config"/>
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+ <xsl:template match="//j:extensions">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ <extension module="org.keycloak.keycloak-adapter-subsystem"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//j:profile">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ <subsystem xmlns="urn:jboss:domain:keycloak:1.1"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//sec:security-domains">
+ <xsl:copy>
+ <xsl:apply-templates select="node()[name(.)='security-domain']"/>
+ <security-domain name="keycloak">
+ <authentication>
+ <login-module code="org.keycloak.adapters.jboss.KeycloakLoginModule" flag="required"/>
+ </authentication>
+ </security-domain>
+ <security-domain name="sp" cache-type="default">
+ <authentication>
+ <login-module code="org.picketlink.identity.federation.bindings.wildfly.SAML2LoginModule" flag="required"/>
+ </authentication>
+ </security-domain>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/adapters/eap6/src/test/java/org/keycloak/testsuite/adapter/servlet/EAP6DemoServletsAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/eap6/src/test/java/org/keycloak/testsuite/adapter/servlet/EAP6DemoServletsAdapterTest.java
new file mode 100644
index 0000000..5eb363e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/eap6/src/test/java/org/keycloak/testsuite/adapter/servlet/EAP6DemoServletsAdapterTest.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer("app-server-eap6")
+@AdapterLibsLocationProperty("adapter.libs.eap6")
+public class EAP6DemoServletsAdapterTest extends AbstractDemoServletsAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/adapters/eap6/src/test/java/org/keycloak/testsuite/adapter/servlet/EAP6SessionServletAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/eap6/src/test/java/org/keycloak/testsuite/adapter/servlet/EAP6SessionServletAdapterTest.java
new file mode 100644
index 0000000..c187910
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/eap6/src/test/java/org/keycloak/testsuite/adapter/servlet/EAP6SessionServletAdapterTest.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer("app-server-eap6")
+@AdapterLibsLocationProperty("adapter.libs.eap6")
+public class EAP6SessionServletAdapterTest extends AbstractSessionServletAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/WaitUtils.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/WaitUtils.java
index 7b0fdb1..00aaed3 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/WaitUtils.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/WaitUtils.java
@@ -17,6 +17,7 @@
*/
package org.keycloak.testsuite.util;
+import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.jboss.arquillian.graphene.Graphene.waitAjax;
@@ -31,24 +32,35 @@ import org.openqa.selenium.WebElement;
*/
public final class WaitUtils {
+ public static final String PAGELOAD_TIMEOUT_PROP = "pageload.timeout";
+ public static final String IMPLICIT_TIMEOUT_PROP = "implicit.timeout";
+ public static final String SCRIPT_TIMEOUT_PROP = "script.timeout";
+ public static final String POLLING_INTERVAL_PROP = "polling.interval";
+
+ public static final Integer PAGELOAD_TIMEOUT = Integer.parseInt(System.getProperty(PAGELOAD_TIMEOUT_PROP, "5000"));
+ public static final Integer IMPLICIT_TIMEOUT = Integer.parseInt(System.getProperty(IMPLICIT_TIMEOUT_PROP, "3000"));
+ public static final Integer SCRIPT_TIMEOUT = Integer.parseInt(System.getProperty(SCRIPT_TIMEOUT_PROP, "3000"));
+
+ public static final Integer POLLING_INTERVAL = Integer.parseInt(System.getProperty(POLLING_INTERVAL_PROP, "1000"));
+
public static void waitAjaxForElement(WebElement element) {
- waitAjax().until()
- .element(element).is().present();
+ waitAjax().withTimeout(SCRIPT_TIMEOUT, TimeUnit.MILLISECONDS).pollingEvery(POLLING_INTERVAL, TimeUnit.MILLISECONDS)
+ .until().element(element).is().present();
}
public static void waitAjaxForElementNotPresent(WebElement element) {
- waitAjax().until()
- .element(element).is().not().present();
+ waitAjax().withTimeout(SCRIPT_TIMEOUT, TimeUnit.MILLISECONDS).pollingEvery(POLLING_INTERVAL, TimeUnit.MILLISECONDS)
+ .until().element(element).is().not().present();
}
public static void waitAjaxForElementNotVisible(WebElement element) {
- waitAjax().until()
- .element(element).is().not().visible();
+ waitAjax().withTimeout(SCRIPT_TIMEOUT, TimeUnit.MILLISECONDS).pollingEvery(POLLING_INTERVAL, TimeUnit.MILLISECONDS)
+ .until().element(element).is().not().visible();
}
public static void waitGuiForElement(By element, String message) {
- waitGui().until(message)
- .element(element).is().present();
+ waitGui().withTimeout(IMPLICIT_TIMEOUT, TimeUnit.MILLISECONDS).pollingEvery(POLLING_INTERVAL, TimeUnit.MILLISECONDS)
+ .until(message).element(element).is().present();
}
public static void waitGuiForElement(By element) {
@@ -60,11 +72,13 @@ public final class WaitUtils {
}
public static void waitGuiForElementPresent(WebElement element, String message) {
- waitGui().until(message).element(element).is().present();
+ waitGui().withTimeout(IMPLICIT_TIMEOUT, TimeUnit.MILLISECONDS).pollingEvery(POLLING_INTERVAL, TimeUnit.MILLISECONDS)
+ .until(message).element(element).is().present();
}
public static void waitGuiForElementNotPresent(WebElement element) {
- waitGui().until().element(element).is().not().present();
+ waitGui().withTimeout(IMPLICIT_TIMEOUT, TimeUnit.MILLISECONDS).pollingEvery(POLLING_INTERVAL, TimeUnit.MILLISECONDS)
+ .until().element(element).is().not().present();
}
public static void pause(long millis) {
@@ -74,5 +88,5 @@ public final class WaitUtils {
Logger.getLogger(WaitUtils.class.getName()).log(Level.SEVERE, null, ex);
}
}
-
+
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
index 8ed6dcd..e553b3d 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
@@ -32,6 +32,7 @@ import org.keycloak.testsuite.auth.page.account.Account;
import org.keycloak.testsuite.auth.page.login.OIDCLogin;
import org.keycloak.testsuite.auth.page.login.UpdatePassword;
import org.keycloak.testsuite.util.Timer;
+import org.keycloak.testsuite.util.WaitUtils;
/**
*
@@ -116,9 +117,9 @@ public abstract class AbstractKeycloakTest {
}
protected void driverSettings() {
- driver.manage().timeouts().pageLoadTimeout(5, TimeUnit.SECONDS);
- driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
- driver.manage().timeouts().setScriptTimeout(3, TimeUnit.SECONDS);
+ driver.manage().timeouts().pageLoadTimeout(WaitUtils.PAGELOAD_TIMEOUT, TimeUnit.MILLISECONDS);
+ driver.manage().timeouts().implicitlyWait(WaitUtils.IMPLICIT_TIMEOUT, TimeUnit.MILLISECONDS);
+ driver.manage().timeouts().setScriptTimeout(WaitUtils.SCRIPT_TIMEOUT, TimeUnit.MILLISECONDS);
driver.manage().window().maximize();
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java
new file mode 100644
index 0000000..9696274
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java
@@ -0,0 +1,85 @@
+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.representations.idm.ClientInitialAccessCreatePresentation;
+import org.keycloak.representations.idm.ClientInitialAccessPresentation;
+import org.keycloak.representations.oidc.OIDCClientRepresentation;
+
+import java.util.Collections;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
+
+ @Before
+ public void before() throws Exception {
+ super.before();
+
+ ClientInitialAccessPresentation token = adminClient.realm(REALM_NAME).clientInitialAccess().create(new ClientInitialAccessCreatePresentation(0, 10));
+ reg.auth(Auth.token(token));
+ }
+
+ public OIDCClientRepresentation create() throws ClientRegistrationException {
+ OIDCClientRepresentation client = new OIDCClientRepresentation();
+ client.setClientName("RegistrationAccessTokenTest");
+ client.setClientUri("http://root");
+ client.setRedirectUris(Collections.singletonList("http://redirect"));
+
+ OIDCClientRepresentation response = reg.oidc().create(client);
+
+ return response;
+ }
+
+ @Test
+ public void createClient() throws ClientRegistrationException {
+ OIDCClientRepresentation response = create();
+
+ assertNotNull(response.getRegistrationAccessToken());
+ assertNotNull(response.getClientIdIssuedAt());
+ assertNotNull(response.getClientId());
+ assertNull(response.getClientSecretExpiresAt());
+ assertNotNull(response.getRegistrationClientUri());
+ assertEquals("RegistrationAccessTokenTest", response.getClientName());
+ assertEquals("http://root", response.getClientUri());
+ assertEquals(1, response.getRedirectUris().size());
+ assertEquals("http://redirect", response.getRedirectUris().get(0));
+ }
+
+ @Test
+ public void getClient() throws ClientRegistrationException {
+ OIDCClientRepresentation response = create();
+ reg.auth(Auth.token(response));
+
+ OIDCClientRepresentation rep = reg.oidc().get(response.getClientId());
+ assertNotNull(rep);
+ assertNotEquals(response.getRegistrationAccessToken(), rep.getRegistrationAccessToken());
+ }
+
+ @Test
+ public void updateClient() throws ClientRegistrationException {
+ OIDCClientRepresentation response = create();
+ reg.auth(Auth.token(response));
+
+ response.setRedirectUris(Collections.singletonList("http://newredirect"));
+
+ OIDCClientRepresentation updated = reg.oidc().update(response);
+
+ assertEquals(1, updated.getRedirectUris().size());
+ assertEquals("http://newredirect", updated.getRedirectUris().get(0));
+ }
+
+ @Test
+ public void deleteClient() throws ClientRegistrationException {
+ OIDCClientRepresentation response = create();
+ reg.auth(Auth.token(response));
+
+ reg.oidc().delete(response);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/SAMLClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/SAMLClientRegistrationTest.java
new file mode 100644
index 0000000..1f5eab8
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/SAMLClientRegistrationTest.java
@@ -0,0 +1,42 @@
+package org.keycloak.testsuite.client;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.client.registration.Auth;
+import org.keycloak.client.registration.ClientRegistrationException;
+import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation;
+import org.keycloak.representations.idm.ClientInitialAccessPresentation;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.oidc.OIDCClientRepresentation;
+
+import java.io.IOException;
+import java.util.Collections;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class SAMLClientRegistrationTest extends AbstractClientRegistrationTest {
+
+ @Before
+ public void before() throws Exception {
+ super.before();
+
+ ClientInitialAccessPresentation token = adminClient.realm(REALM_NAME).clientInitialAccess().create(new ClientInitialAccessCreatePresentation(0, 10));
+ reg.auth(Auth.token(token));
+ }
+
+ @Test
+ public void createClient() throws ClientRegistrationException, IOException {
+ String entityDescriptor = IOUtils.toString(getClass().getResourceAsStream("/clientreg-test/saml-entity-descriptor.xml"));
+ ClientRepresentation response = reg.saml().create(entityDescriptor);
+
+ assertNotNull(response.getRegistrationAccessToken());
+ assertEquals("loadbalancer-9.siroe.com", response.getClientId());
+ assertEquals(1, response.getRedirectUris().size());
+ assertEquals("https://LoadBalancer-9.siroe.com:3443/federation/Consumer/metaAlias/sp", response.getRedirectUris().get(0));
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/clientreg-test/saml-entity-descriptor.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/clientreg-test/saml-entity-descriptor.xml
new file mode 100644
index 0000000..b00ab25
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/clientreg-test/saml-entity-descriptor.xml
@@ -0,0 +1,82 @@
+<EntityDescriptor
+ xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+ entityID="loadbalancer-9.siroe.com">
+ <SPSSODescriptor
+ AuthnRequestsSigned="false"
+ WantAssertionsSigned="false"
+ protocolSupportEnumeration=
+ "urn:oasis:names:tc:SAML:2.0:protocol">
+ <KeyDescriptor use="signing">
+ <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
+ <X509Data>
+ <X509Certificate>
+MIICYDCCAgqgAwIBAgICBoowDQYJKoZIhvcNAQEEBQAwgZIxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
+EwpDYWxpZm9ybmlhMRQwEgYDVQQHEwtTYW50YSBDbGFyYTEeMBwGA1UEChMVU3VuIE1pY3Jvc3lz
+dGVtcyBJbmMuMRowGAYDVQQLExFJZGVudGl0eSBTZXJ2aWNlczEcMBoGA1UEAxMTQ2VydGlmaWNh
+dGUgTWFuYWdlcjAeFw0wNjExMDIxOTExMzRaFw0xMDA3MjkxOTExMzRaMDcxEjAQBgNVBAoTCXNp
+cm9lLmNvbTEhMB8GA1UEAxMYbG9hZGJhbGFuY2VyLTkuc2lyb2UuY29tMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQCjOwa5qoaUuVnknqf5pdgAJSEoWlvx/jnUYbkSDpXLzraEiy2UhvwpoBgB
+EeTSUaPPBvboCItchakPI6Z/aFdH3Wmjuij9XD8r1C+q//7sUO0IGn0ORycddHhoo0aSdnnxGf9V
+tREaqKm9dJ7Yn7kQHjo2eryMgYxtr/Z5Il5F+wIDAQABo2AwXjARBglghkgBhvhCAQEEBAMCBkAw
+DgYDVR0PAQH/BAQDAgTwMB8GA1UdIwQYMBaAFDugITflTCfsWyNLTXDl7cMDUKuuMBgGA1UdEQQR
+MA+BDW1hbGxhQHN1bi5jb20wDQYJKoZIhvcNAQEEBQADQQB/6DOB6sRqCZu2OenM9eQR0gube85e
+nTTxU4a7x1naFxzYXK1iQ1vMARKMjDb19QEJIEJKZlDK4uS7yMlf1nFS
+ </X509Certificate>
+ </X509Data>
+ </KeyInfo>
+ </KeyDescriptor>
+ <KeyDescriptor use="encryption">
+ <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
+ <X509Data>
+ <X509Certificate>
+MIICTDCCAfagAwIBAgICBo8wDQYJKoZIhvcNAQEEBQAwgZIxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
+EwpDYWxpZm9ybmlhMRQwEgYDVQQHEwtTYW50YSBDbGFyYTEeMBwGA1UEChMVU3VuIE1pY3Jvc3lz
+dGVtcyBJbmMuMRowGAYDVQQLExFJZGVudGl0eSBTZXJ2aWNlczEcMBoGA1UEAxMTQ2VydGlmaWNh
+dGUgTWFuYWdlcjAeFw0wNjExMDcyMzU2MTdaFw0xMDA4MDMyMzU2MTdaMCMxITAfBgNVBAMTGGxv
+YWRiYWxhbmNlci05LnNpcm9lLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAw574iRU6
+HsSO4LXW/OGTXyfsbGv6XRVOoy3v+J1pZ51KKejcDjDJXNkKGn3/356AwIaqbcymWd59T0zSqYfR
+Hn+45uyjYxRBmVJseLpVnOXLub9jsjULfGx0yjH4w+KsZSZCXatoCHbj/RJtkzuZY6V9to/hkH3S
+InQB4a3UAgMCAwEAAaNgMF4wEQYJYIZIAYb4QgEBBAQDAgZAMA4GA1UdDwEB/wQEAwIE8DAfBgNV
+HSMEGDAWgBQ7oCE35Uwn7FsjS01w5e3DA1CrrjAYBgNVHREEETAPgQ1tYWxsYUBzdW4uY29tMA0G
+CSqGSIb3DQEBBAUAA0EAMlbfBg/ff0Xkv4DOR5LEqmfTZKqgdlD81cXynfzlF7XfnOqI6hPIA90I
+x5Ql0ejivIJAYcMGUyA+/YwJg2FGoA==
+ </X509Certificate>
+ </X509Data>
+ </KeyInfo>
+ <EncryptionMethod Algorithm=
+ "https://www.w3.org/2001/04/xmlenc#aes128-cbc">
+ <KeySize xmlns="https://www.w3.org/2001/04/xmlenc#">128</KeySize>
+ </EncryptionMethod>
+ </KeyDescriptor>
+ <SingleLogoutService
+ Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
+ Location="https://LoadBalancer-9.siroe.com:3443/federation/SPSloRedirect/metaAlias/sp"
+ ResponseLocation="https://LoadBalancer-9.siroe.com:3443/federation/SPSloRedirect/metaAlias/sp"/>
+ <SingleLogoutService
+ Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP"
+ Location="https://LoadBalancer-9.siroe.com:3443/federation/SPSloSoap/metaAlias/sp"/>
+ <ManageNameIDService
+ Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
+ Location="https://LoadBalancer-9.siroe.com:3443/federation/SPMniRedirect/metaAlias/sp"
+ ResponseLocation="https://LoadBalancer-9.siroe.com:3443/federation/SPMniRedirect/metaAlias/sp"/>
+ <ManageNameIDService
+ Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP"
+ Location="https://LoadBalancer-9.siroe.com:3443/federation/SPMniSoap/metaAlias/sp"
+ ResponseLocation="https://LoadBalancer-9.siroe.com:3443/federation/SPMniSoap/metaAlias/sp"/>
+ <NameIDFormat>
+ urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
+ </NameIDFormat>
+ <NameIDFormat>
+ urn:oasis:names:tc:SAML:2.0:nameid-format:transient
+ </NameIDFormat>
+ <AssertionConsumerService
+ isDefault="true"
+ index="0"
+ Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
+ Location="https://LoadBalancer-9.siroe.com:3443/federation/Consumer/metaAlias/sp"/>
+ <AssertionConsumerService
+ index="1"
+ Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
+ Location="https://LoadBalancer-9.siroe.com:3443/federation/Consumer/metaAlias/sp"/>
+ </SPSSODescriptor>
+</EntityDescriptor>