Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
1c97b9c41d
32 changed files with 1079 additions and 247 deletions
|
@ -85,42 +85,38 @@ public class BasicAuthRequestAuthenticator extends BearerTokenRequestAuthenticat
|
|||
|
||||
private AccessTokenResponse getToken(String username, String password) throws Exception {
|
||||
AccessTokenResponse tokenResponse=null;
|
||||
HttpClient client = new HttpClientBuilder().disableTrustManager().build();
|
||||
HttpClient client = deployment.getClient();
|
||||
|
||||
try {
|
||||
HttpPost post = new HttpPost(
|
||||
KeycloakUriBuilder.fromUri(deployment.getAuthServerBaseUrl())
|
||||
.path(ServiceUrlConstants.TOKEN_PATH).build(deployment.getRealm()));
|
||||
java.util.List <NameValuePair> formparams = new java.util.ArrayList <NameValuePair>();
|
||||
formparams.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD));
|
||||
formparams.add(new BasicNameValuePair("username", username));
|
||||
formparams.add(new BasicNameValuePair("password", password));
|
||||
HttpPost post = new HttpPost(
|
||||
KeycloakUriBuilder.fromUri(deployment.getAuthServerBaseUrl())
|
||||
.path(ServiceUrlConstants.TOKEN_PATH).build(deployment.getRealm()));
|
||||
java.util.List <NameValuePair> formparams = new java.util.ArrayList <NameValuePair>();
|
||||
formparams.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD));
|
||||
formparams.add(new BasicNameValuePair("username", username));
|
||||
formparams.add(new BasicNameValuePair("password", password));
|
||||
|
||||
ClientCredentialsProviderUtils.setClientCredentials(deployment, post, formparams);
|
||||
ClientCredentialsProviderUtils.setClientCredentials(deployment, post, formparams);
|
||||
|
||||
UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
|
||||
post.setEntity(form);
|
||||
UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
|
||||
post.setEntity(form);
|
||||
|
||||
HttpResponse response = client.execute(post);
|
||||
int status = response.getStatusLine().getStatusCode();
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (status != 200) {
|
||||
throw new java.io.IOException("Bad status: " + status);
|
||||
}
|
||||
if (entity == null) {
|
||||
throw new java.io.IOException("No Entity");
|
||||
}
|
||||
java.io.InputStream is = entity.getContent();
|
||||
try {
|
||||
tokenResponse = JsonSerialization.readValue(is, AccessTokenResponse.class);
|
||||
} finally {
|
||||
try {
|
||||
is.close();
|
||||
} catch (java.io.IOException ignored) { }
|
||||
}
|
||||
} finally {
|
||||
client.getConnectionManager().shutdown();
|
||||
}
|
||||
HttpResponse response = client.execute(post);
|
||||
int status = response.getStatusLine().getStatusCode();
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (status != 200) {
|
||||
throw new java.io.IOException("Bad status: " + status);
|
||||
}
|
||||
if (entity == null) {
|
||||
throw new java.io.IOException("No Entity");
|
||||
}
|
||||
java.io.InputStream is = entity.getContent();
|
||||
try {
|
||||
tokenResponse = JsonSerialization.readValue(is, AccessTokenResponse.class);
|
||||
} finally {
|
||||
try {
|
||||
is.close();
|
||||
} catch (java.io.IOException ignored) { }
|
||||
}
|
||||
|
||||
return (tokenResponse);
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ public class KeycloakDeploymentBuilder {
|
|||
if (realmKeyPem == null && adapterConfig.isBearerOnly() && adapterConfig.getAuthServerUrl() == null) {
|
||||
throw new IllegalArgumentException("For bearer auth, you must set the realm-public-key or auth-server-url");
|
||||
}
|
||||
if (realmKeyPem == null || !deployment.isBearerOnly() || deployment.isRegisterNodeAtStartup() || deployment.getRegisterNodePeriod() != -1) {
|
||||
if (realmKeyPem == null || !deployment.isBearerOnly() || deployment.isEnableBasicAuth() || deployment.isRegisterNodeAtStartup() || deployment.getRegisterNodePeriod() != -1) {
|
||||
deployment.setClient(new HttpClientBuilder().build(adapterConfig));
|
||||
}
|
||||
if (adapterConfig.getAuthServerUrl() == null && (!deployment.isBearerOnly() || realmKeyPem == null)) {
|
||||
|
|
|
@ -45,14 +45,10 @@ public class Version {
|
|||
Version.VERSION = props.getProperty("version");
|
||||
Version.BUILD_TIME = props.getProperty("build-time");
|
||||
Version.RESOURCES_VERSION = Version.VERSION.toLowerCase();
|
||||
if (Version.RESOURCES_VERSION.endsWith("-snapshot")) {
|
||||
Version.RESOURCES_VERSION = Version.RESOURCES_VERSION.replace("-snapshot", "-" + Time.currentTime());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Version.VERSION= Version.UNKNOWN;
|
||||
Version.BUILD_TIME= Version.UNKNOWN;
|
||||
Version.VERSION = Version.UNKNOWN;
|
||||
Version.BUILD_TIME = Version.UNKNOWN;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -50,5 +50,9 @@
|
|||
<source>../../shared-cli/adapter-install.cli</source>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>cli/adapter-install-offline.cli</source>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
</file>
|
||||
</files>
|
||||
</assembly>
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
embed-server --server-config=standalone.xml
|
||||
/subsystem=security/security-domain=keycloak/:add
|
||||
/subsystem=security/security-domain=keycloak/authentication=classic/:add(login-modules=[{ "code" => "org.keycloak.adapters.jboss.KeycloakLoginModule","flag" => "required"}])
|
||||
/extension=org.keycloak.keycloak-adapter-subsystem/:add(module=org.keycloak.keycloak-adapter-subsystem)
|
||||
/subsystem=keycloak:add
|
|
@ -25,61 +25,46 @@
|
|||
<section>
|
||||
<title>Disabling Caches</title>
|
||||
<para>
|
||||
The realm and user caches can be disabled through configuration or through the management console. To
|
||||
manally disable the realm or user cache, you must edit the <literal>keycloak-server.json</literal> file
|
||||
The realm and user caches can be cleared through the management console. To
|
||||
disable the realm or user cache, you must edit the <literal>keycloak-server.json</literal> file
|
||||
in your distribution. Here's what the config looks like initially.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting><![CDATA[
|
||||
"realmCache": {
|
||||
"provider": "${keycloak.realm.cache.provider:mem}"
|
||||
"userCache": {
|
||||
"infinispan" : {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
|
||||
"userCache": {
|
||||
"provider": "${keycloak.user.cache.provider:mem}",
|
||||
"mem": {
|
||||
"maxSize": 20000
|
||||
"realmCache": {
|
||||
"infinispan" : {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
]]></programlisting>
|
||||
</para>
|
||||
<para>You must then change it to:
|
||||
<programlisting><![CDATA[
|
||||
"realmCache": {
|
||||
"provider": "${keycloak.realm.cache.provider:none}"
|
||||
"userCache": {
|
||||
"infinispan" : {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
|
||||
"userCache": {
|
||||
"provider": "${keycloak.user.cache.provider:none}"
|
||||
"realmCache": {
|
||||
"infinispan" : {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
You can also disable either of the caches at runtime through the Keycloak admin console Realm Settings page.
|
||||
This will not permanently disable the cache. If you reboot the server, the cache will be re-enabled unless
|
||||
you manualy disable the cache in the <literal>keycloak-server.json</literal> file.
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Clear Caches</title>
|
||||
<para>
|
||||
To clear the realm or user cache, go to the Keycloak admin console Realm Settings->Cache Config page. Disable the cache
|
||||
you want. Save the settings. Then re-enable the cache. This will cause the cache to be cleared.
|
||||
you want. This will cause the cache to be cleared.
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Cache Config</title>
|
||||
<para>
|
||||
Cache configuration is done within <literal>keycloak-server.json</literal>. Changes to this file will not
|
||||
be seen by the server until you reboot. Currently you can only configure the max size of the user cache.
|
||||
<programlisting><![CDATA[
|
||||
"userCache": {
|
||||
"provider": "${keycloak.user.cache.provider:mem}",
|
||||
"mem": {
|
||||
"maxSize": 20000
|
||||
}
|
||||
},
|
||||
]]></programlisting>
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
||||
</chapter>
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<section id="jboss-adapter-installation">
|
||||
<title>Adapter Installation</title>
|
||||
<para>
|
||||
Adapters are no longer included with the appliance or war distribution.Each adapter is a separate download on
|
||||
Adapters are no longer included with the appliance or war distribution. Each adapter is a separate download on
|
||||
the Keycloak download site. They are also available as a maven artifact.
|
||||
</para>
|
||||
<para>
|
||||
|
@ -74,6 +74,13 @@ $ jboss-cli.sh -c --file=adapter-install.cli
|
|||
</programlisting>
|
||||
The script will add the extension, subsystem, and optional security-domain as described below.
|
||||
</para>
|
||||
<para>
|
||||
For more recent versions of WildFly there's also a offline CLI script that can be used to install the adapter while the server is not running:
|
||||
<programlisting>
|
||||
$ cd $JBOSS_HOME/bin
|
||||
$ jboss-cli.sh -c --file=adapter-install-offline.cli
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
<programlisting><![CDATA[
|
||||
<server xmlns="urn:jboss:domain:1.4">
|
||||
|
|
|
@ -18,16 +18,20 @@
|
|||
<chapter id="saml">
|
||||
<title>SAML SSO</title>
|
||||
<para>
|
||||
Keycloak supports SAML 2.0 for registered applications. Both POST and Redirect bindings are supported. You can choose
|
||||
to require client signature validation and can have the server sign and/or encrypt responses as well. We do not
|
||||
yet support logout via redirects. All logouts happen via a background POST binding request to the application
|
||||
that will be logged out. We do not support SAML 1.1 either. If you want support for either of those, please
|
||||
Keycloak supports SAML 2.0 for registered applications. Both POST and Redirect bindings are supported. You can
|
||||
choose
|
||||
to require client signature validation and can have the server sign and/or encrypt responses as well. We do not
|
||||
yet support logout via redirects. All logouts happen via a background POST binding request to the application
|
||||
that will be logged out. We do not support SAML 1.1 either. If you want support for either of those, please
|
||||
log a JIRA request and we'll schedule it.
|
||||
</para>
|
||||
<para>
|
||||
When you create an application in the admin console, you can choose which protocol the application will log in with.
|
||||
In the application create screen, choose <literal>saml</literal> from the protocol list. After that there
|
||||
are a bunch of configuration options. Here is a description of each item:
|
||||
When you create an application in the admin console, you can choose which protocol the application will log in
|
||||
with.
|
||||
In the application create screen, choose
|
||||
<literal>saml</literal>
|
||||
from the protocol list. After that there
|
||||
are a bunch of configuration options. Here is a description of each item:
|
||||
</para>
|
||||
<para>
|
||||
<variablelist>
|
||||
|
@ -35,7 +39,7 @@
|
|||
<term>Client ID</term>
|
||||
<listitem>
|
||||
<para>
|
||||
This value must match the issuer value sent with AuthNRequests. Keycloak will pull the issuer
|
||||
This value must match the issuer value sent with AuthNRequests. Keycloak will pull the issuer
|
||||
from the Authn SAML request and match it to a client by this value.
|
||||
</para>
|
||||
</listitem>
|
||||
|
@ -45,7 +49,8 @@
|
|||
<listitem>
|
||||
<para>
|
||||
SAML login responses may specify the authentication method used (password, etc.) as well as
|
||||
a timestamp of the login. Setting this to on will include that statement in the response document.
|
||||
a timestamp of the login. Setting this to on will include that statement in the response
|
||||
document.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
@ -53,7 +58,8 @@
|
|||
<term>Multi-valued Roles</term>
|
||||
<listitem>
|
||||
<para>
|
||||
If this switch is off, any user role mappings will have a corresponding attribute created for it.
|
||||
If this switch is off, any user role mappings will have a corresponding attribute created for
|
||||
it.
|
||||
If this switch is turn on, only one role attribute will be created, but it will have
|
||||
multiple values within in.
|
||||
</para>
|
||||
|
@ -71,7 +77,9 @@
|
|||
<term>Sign Assertions</term>
|
||||
<listitem>
|
||||
<para>
|
||||
With the <literal>Sign Documents</literal> switch signs the whole document. With this setting
|
||||
With the
|
||||
<literal>Sign Documents</literal>
|
||||
switch signs the whole document. With this setting
|
||||
you just assign the assertions of the document.
|
||||
</para>
|
||||
</listitem>
|
||||
|
@ -88,7 +96,7 @@
|
|||
<term>Encrypt Assertions</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Encrypt assertions in SAML documents with the realm's private key. The AES algorithm is used
|
||||
Encrypt assertions in SAML documents with the realm's private key. The AES algorithm is used
|
||||
with a key size of 128 bits.
|
||||
</para>
|
||||
</listitem>
|
||||
|
@ -97,8 +105,10 @@
|
|||
<term>Client Signature Required</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Expect that documents coming from a client are signed. Keycloak will validate this signature
|
||||
using the client keys set up in the <literal>Application Keys</literal> submenu item.
|
||||
Expect that documents coming from a client are signed. Keycloak will validate this signature
|
||||
using the client keys set up in the
|
||||
<literal>Application Keys</literal>
|
||||
submenu item.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
@ -106,8 +116,10 @@
|
|||
<term>Force POST Binding</term>
|
||||
<listitem>
|
||||
<para>
|
||||
By default, Keycloak will respond using the initial SAML binding of the original request. By turning
|
||||
on this switch, you will force Keycloak to always respond using the SAML POST Binding even if the
|
||||
By default, Keycloak will respond using the initial SAML binding of the original request. By
|
||||
turning
|
||||
on this switch, you will force Keycloak to always respond using the SAML POST Binding even if
|
||||
the
|
||||
original request was the Redirect binding.
|
||||
</para>
|
||||
</listitem>
|
||||
|
@ -116,9 +128,12 @@
|
|||
<term>Front Channel Logout</term>
|
||||
<listitem>
|
||||
<para>
|
||||
If true, this application requires a browser redirect to be able to perform a logout. For example,
|
||||
the application may require a cookie to be reset which could only be done by a done via a redirect.
|
||||
If this switch is false, then Keycloak will invoke a background SAML request to logout the application.
|
||||
If true, this application requires a browser redirect to be able to perform a logout. For
|
||||
example,
|
||||
the application may require a cookie to be reset which could only be done by a done via a
|
||||
redirect.
|
||||
If this switch is false, then Keycloak will invoke a background SAML request to logout the
|
||||
application.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
@ -126,7 +141,8 @@
|
|||
<term>Force Name ID Format</term>
|
||||
<listitem>
|
||||
<para>
|
||||
If the request has a name ID policy, ignore it and used the value configured in the admin console
|
||||
If the request has a name ID policy, ignore it and used the value configured in the admin
|
||||
console
|
||||
under Name ID Format
|
||||
</para>
|
||||
</listitem>
|
||||
|
@ -135,18 +151,118 @@
|
|||
<term>Name ID Format</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Name ID Format for the subject. If no name ID policy is specified in the request or if the
|
||||
Force Name ID Format attribute is true, this value is used.
|
||||
Name ID Format for the subject. If no name ID policy is specified in the request or if the
|
||||
Force Name ID Format attribute is true, this value is used. Properties used for each of the
|
||||
respective formats are defined below.
|
||||
</para>
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>username</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Format:
|
||||
<literal>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</literal>
|
||||
</para>
|
||||
<para>
|
||||
Source:
|
||||
<literal>UserModel.userName</literal>
|
||||
property
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>email</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Format:
|
||||
<literal>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</literal>
|
||||
</para>
|
||||
<para>
|
||||
Source:
|
||||
<literal>UserModel.email</literal>
|
||||
property
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>transient</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Format:
|
||||
<literal>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</literal>
|
||||
</para>
|
||||
<para>
|
||||
Source:<literal>G-$randomUuid</literal>, I.E.
|
||||
<literal>G-5ef5b38f-864f-41ad-82a0-04ade9139500</literal>
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>persistent</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Format:
|
||||
<literal>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</literal>
|
||||
</para>
|
||||
<para>
|
||||
The persistent identifier will be evaluated in the following order. If one step
|
||||
does not yield a value, processing will continue on to the next until a value is
|
||||
found.
|
||||
</para>
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>
|
||||
1) saml.persistent.name.id.for.$clientId
|
||||
<literal>UserModel</literal> attribute
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
identifier unique to this client/user pair.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>
|
||||
2) saml.persistent.name.id.for.*
|
||||
<literal>UserModel</literal> attribute
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
user identifier for all clients, unless otherwise overridden by a
|
||||
$clientId attribute.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>
|
||||
3) G-$randomUuid
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
I.E.<literal>G-5ef5b38f-864f-41ad-82a0-04ade9139500</literal>. If
|
||||
neither a $clientId or wildcard user attribute is found, a persistent
|
||||
identifier will be generated for the given client. Note that once this
|
||||
identifier has been generated, a user attribute with the key
|
||||
<emphasis>saml.persistent.name.id.for.$clientId</emphasis>
|
||||
will be persisted and used on all subsequent requests against the given
|
||||
client.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Master SAML Processing URL</term>
|
||||
<listitem>
|
||||
<para>
|
||||
This URL will be used for all SAML requests and responsed directed to the SP. It will be used
|
||||
as the Assertion Consumer Service URL and the Single Logout Service URL. If a login request
|
||||
contains the Assertion Consumer Service URL, that will take precedence, but this URL must be valided
|
||||
This URL will be used for all SAML requests and responsed directed to the SP. It will be used
|
||||
as the Assertion Consumer Service URL and the Single Logout Service URL. If a login request
|
||||
contains the Assertion Consumer Service URL, that will take precedence, but this URL must be
|
||||
valided
|
||||
by a registered Valid Redirect URI pattern
|
||||
</para>
|
||||
</listitem>
|
||||
|
@ -186,9 +302,10 @@
|
|||
</variablelist>
|
||||
</para>
|
||||
<para>
|
||||
For login to work, Keycloak needs to be able to resolve the URL for the Assertion Consumer Service of the SP. If
|
||||
you are relying on the SP to provide this URL in the login request, then you must register valid redirect uri patterns
|
||||
so that this URL can be validated. You can set the Master SAML Processing URL as well, or alternatively, you can
|
||||
For login to work, Keycloak needs to be able to resolve the URL for the Assertion Consumer Service of the SP. If
|
||||
you are relying on the SP to provide this URL in the login request, then you must register valid redirect uri
|
||||
patterns
|
||||
so that this URL can be validated. You can set the Master SAML Processing URL as well, or alternatively, you can
|
||||
specify the Assertion Consumer Service URL per binding.
|
||||
</para>
|
||||
<para>
|
||||
|
@ -196,32 +313,43 @@
|
|||
you want Keycloak to use.
|
||||
</para>
|
||||
<para>
|
||||
One thing to note is that roles are not treated as a hierarchy. So, any role mappings will just be added
|
||||
to the role attributes in the SAML document using their basic name. So, if you have multiple application roles
|
||||
you might have name collisions. You can use the Scope Mapping menu item to control which role mappings are set
|
||||
One thing to note is that roles are not treated as a hierarchy. So, any role mappings will just be added
|
||||
to the role attributes in the SAML document using their basic name. So, if you have multiple application roles
|
||||
you might have name collisions. You can use the Scope Mapping menu item to control which role mappings are set
|
||||
in the response.
|
||||
</para>
|
||||
<section>
|
||||
<title>SAML Entity Descriptor</title>
|
||||
<para>
|
||||
If you go into the admin console in the application list menu page you will see an <literal>Import</literal>
|
||||
button. If you click on that you can import SAML Service Provider definitions using the <ulink url="http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf">Entity Descriptor</ulink>
|
||||
format described in SAML 2.0. You should review all the information there to make sure everything is set up correctly.
|
||||
If you go into the admin console in the application list menu page you will see an
|
||||
<literal>Import</literal>
|
||||
button. If you click on that you can import SAML Service Provider definitions using the
|
||||
<ulink url="http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf">Entity Descriptor
|
||||
</ulink>
|
||||
format described in SAML 2.0. You should review all the information there to make sure everything is set up
|
||||
correctly.
|
||||
</para>
|
||||
<para>
|
||||
Each realm has a URL where you can view the XML entity descriptor for the IDP. <literal>root/auth/realms/{realm}/protocol/saml/descriptor</literal>
|
||||
Each realm has a URL where you can view the XML entity descriptor for the IDP.
|
||||
<literal>root/auth/realms/{realm}/protocol/saml/descriptor</literal>
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>IDP Initiated Login</title>
|
||||
<para>
|
||||
IDP Initiated Login is a feature that where you can set up a URL on the Keycloak server that will log you into a specific application/client. To set this up
|
||||
go to the client page in the admin console of the client you want to set this up for. Specify the <literal>IDP Initiated SSO URL Name</literal>. This is a simple string
|
||||
with no whitespace in it. After this you can reference your client at the following URL: <literal>root/auth/realms/{realm}/protocol/saml/clients/{url-name}</literal>
|
||||
IDP Initiated Login is a feature that where you can set up a URL on the Keycloak server that will log you
|
||||
into a specific application/client. To set this up
|
||||
go to the client page in the admin console of the client you want to set this up for. Specify the<literal>
|
||||
IDP Initiated SSO URL Name</literal>. This is a simple string
|
||||
with no whitespace in it. After this you can reference your client at the following URL:
|
||||
<literal>root/auth/realms/{realm}/protocol/saml/clients/{url-name}</literal>
|
||||
</para>
|
||||
<para>
|
||||
If your client requires a special relay state, you can also configure this in the admin console. Alternatively, you can specify the relay state in a
|
||||
<literal>RelayState</literal> query parameter, i.e. : <literal>root/auth/realms/{realm}/protocol/saml/clients/{url-name}?RelayState=thestate</literal>
|
||||
If your client requires a special relay state, you can also configure this in the admin console.
|
||||
Alternatively, you can specify the relay state in a
|
||||
<literal>RelayState</literal>
|
||||
query parameter, i.e. :
|
||||
<literal>root/auth/realms/{realm}/protocol/saml/clients/{url-name}?RelayState=thestate</literal>
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
||||
|
|
|
@ -53,7 +53,7 @@ public class ClientTemplateAdapter implements ClientTemplateModel {
|
|||
|
||||
private void getDelegateForUpdate() {
|
||||
if (updated == null) {
|
||||
cacheSession.registerApplicationInvalidation(getId());
|
||||
cacheSession.registerClientTemplateInvalidation(getId());
|
||||
updated = cacheSession.getDelegate().getClientTemplateById(getId(), cachedRealm);
|
||||
if (updated == null) throw new IllegalStateException("Not found in database");
|
||||
}
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -43,7 +43,7 @@
|
|||
<apacheds.version>2.0.0-M17</apacheds.version>
|
||||
<apacheds.codec.version>1.0.0-M23</apacheds.codec.version>
|
||||
<org.apache.james.apache-mime4j.version>0.6</org.apache.james.apache-mime4j.version>
|
||||
<bouncycastle.crypto.version>1.50</bouncycastle.crypto.version>
|
||||
<bouncycastle.crypto.version>1.52</bouncycastle.crypto.version>
|
||||
<jackson.version>2.5.4</jackson.version>
|
||||
<apache.httpcomponents.version>4.3.6</apache.httpcomponents.version>
|
||||
<apache.httpcomponents.httpcore.version>4.3.3</apache.httpcomponents.httpcore.version>
|
||||
|
|
|
@ -98,7 +98,7 @@ public abstract class AuthorizationEndpointBase {
|
|||
|
||||
List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
|
||||
for (IdentityProviderModel identityProvider : identityProviders) {
|
||||
if (identityProvider.isAuthenticateByDefault()) {
|
||||
if (identityProvider.isEnabled() && identityProvider.isAuthenticateByDefault()) {
|
||||
// TODO if we are isPassive we should propagate this flag to default identity provider also if possible
|
||||
return buildRedirectToIdentityProvider(identityProvider.getAlias(), new ClientSessionCode(realm, clientSession).getCode());
|
||||
}
|
||||
|
|
|
@ -281,16 +281,7 @@ public class SamlProtocol implements LoginProtocol {
|
|||
// "G-" stands for "generated" Add this for the slight possibility of collisions.
|
||||
return "G-" + UUID.randomUUID().toString();
|
||||
} else if (nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get())) {
|
||||
// generate a persistent user id specifically for each client.
|
||||
UserModel user = userSession.getUser();
|
||||
String name = SAML_PERSISTENT_NAME_ID_FOR + "." + clientSession.getClient().getClientId();
|
||||
String samlPersistentId = user.getFirstAttribute(name);
|
||||
if (samlPersistentId != null)
|
||||
return samlPersistentId;
|
||||
// "G-" stands for "generated"
|
||||
samlPersistentId = "G-" + UUID.randomUUID().toString();
|
||||
user.setSingleAttribute(name, samlPersistentId);
|
||||
return samlPersistentId;
|
||||
return getPersistentNameId(clientSession, userSession);
|
||||
} else if (nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get())) {
|
||||
// TODO: Support for persistent NameID (pseudo-random identifier persisted in user object)
|
||||
return userSession.getUser().getUsername();
|
||||
|
@ -299,6 +290,43 @@ public class SamlProtocol implements LoginProtocol {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to retrieve the persistent type NameId as follows:
|
||||
*
|
||||
* <ol>
|
||||
* <li>saml.persistent.name.id.for.$clientId user attribute</li>
|
||||
* <li>saml.persistent.name.id.for.* user attribute</li>
|
||||
* <li>G-$randomUuid</li>
|
||||
* </ol>
|
||||
*
|
||||
* If a randomUuid is generated, an attribute for the given saml.persistent.name.id.for.$clientId will be generated,
|
||||
* otherwise no state change will occur with respect to the user's attributes.
|
||||
*
|
||||
* @return the user's persistent NameId
|
||||
*/
|
||||
protected String getPersistentNameId(final ClientSessionModel clientSession, final UserSessionModel userSession) {
|
||||
// attempt to retrieve the UserID for the client-specific attribute
|
||||
final UserModel user = userSession.getUser();
|
||||
final String clientNameId = String.format("%s.%s", SAML_PERSISTENT_NAME_ID_FOR,
|
||||
clientSession.getClient().getClientId());
|
||||
String samlPersistentNameId = user.getFirstAttribute(clientNameId);
|
||||
if (samlPersistentNameId != null) {
|
||||
return samlPersistentNameId;
|
||||
}
|
||||
|
||||
// check for a wildcard attribute
|
||||
final String wildcardNameId = String.format("%s.*", SAML_PERSISTENT_NAME_ID_FOR);
|
||||
samlPersistentNameId = user.getFirstAttribute(wildcardNameId);
|
||||
if (samlPersistentNameId != null) {
|
||||
return samlPersistentNameId;
|
||||
}
|
||||
|
||||
// default to generated. "G-" stands for "generated"
|
||||
samlPersistentNameId = "G-" + UUID.randomUUID().toString();
|
||||
user.setSingleAttribute(clientNameId, samlPersistentNameId);
|
||||
return samlPersistentNameId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode) {
|
||||
ClientSessionModel clientSession = accessCode.getClientSession();
|
||||
|
|
|
@ -40,6 +40,14 @@
|
|||
<module>wildfly</module>
|
||||
</modules>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>auth-server-wildfly-cluster</id>
|
||||
<modules>
|
||||
<module>wildfly</module>
|
||||
<module>wildfly-balancer</module>
|
||||
</modules>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>auth-server-eap7</id>
|
||||
<modules>
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<assembly>
|
||||
|
||||
<id>wildfly-balancer</id>
|
||||
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
</formats>
|
||||
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${wildfly.balancer.home}</directory>
|
||||
<outputDirectory>wildfly-balancer-${project.version}</outputDirectory>
|
||||
<excludes>
|
||||
<exclude>**/*.sh</exclude>
|
||||
</excludes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${wildfly.balancer.home}</directory>
|
||||
<outputDirectory>wildfly-balancer-${project.version}</outputDirectory>
|
||||
<includes>
|
||||
<include>**/*.sh</include>
|
||||
</includes>
|
||||
<fileMode>0755</fileMode>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
|
||||
</assembly>
|
|
@ -0,0 +1,118 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<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-servers</artifactId>
|
||||
<version>1.9.0.Final-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>integration-arquillian-server-wildfly-balancer</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>Wildfly Load Balancer</name>
|
||||
|
||||
<properties>
|
||||
<wildfly.balancer.home>${project.build.directory}/unpacked/wildfly-${wildfly.version}</wildfly.balancer.home>
|
||||
</properties>
|
||||
|
||||
<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-wildfly</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.wildfly</groupId>
|
||||
<artifactId>wildfly-dist</artifactId>
|
||||
<version>${wildfly.version}</version>
|
||||
<type>zip</type>
|
||||
<outputDirectory>${project.build.directory}/unpacked</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>xml-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>configure-mod-cluster</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>transform</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformationSets>
|
||||
<transformationSet>
|
||||
<dir>${wildfly.balancer.home}/standalone/configuration</dir>
|
||||
<includes>
|
||||
<include>standalone.xml</include>
|
||||
</includes>
|
||||
<stylesheet>src/main/xslt/mod_cluster.xsl</stylesheet>
|
||||
<outputDir>${wildfly.balancer.home}/standalone/configuration</outputDir>
|
||||
</transformationSet>
|
||||
</transformationSets>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>create-zip</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>assembly.xml</descriptor>
|
||||
</descriptors>
|
||||
<appendAssemblyId>false</appendAssemblyId>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,73 @@
|
|||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:xalan="http://xml.apache.org/xalan"
|
||||
xmlns:s="urn:jboss:domain:4.0"
|
||||
xmlns:u="urn:jboss:domain:undertow:3.0"
|
||||
version="2.0"
|
||||
exclude-result-prefixes="xalan j u">
|
||||
|
||||
<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="*"/>
|
||||
|
||||
<!--enable mod_cluster extension-->
|
||||
<xsl:template match="//s:extensions">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="node()|@*"/>
|
||||
<extension module="org.jboss.as.modcluster"/>
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
<!--add filter-ref-->
|
||||
<xsl:template match="//u:server[@name='default-server']/u:host[@name='default-host']">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="node()|@*"/>
|
||||
<filter-ref name="modcluster"/>
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
<!--add filter-->
|
||||
<xsl:template match="//u:filters">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="node()|@*"/>
|
||||
<mod-cluster
|
||||
name="modcluster"
|
||||
advertise-socket-binding="modcluster"
|
||||
management-socket-binding="http"
|
||||
enable-http2="true"
|
||||
/>
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
<!--add socket binding-->
|
||||
<xsl:template match="//s:socket-binding-group[@name='standard-sockets']">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="node()|@*"/>
|
||||
<socket-binding name="modcluster" port="23364" multicast-address="224.0.1.105"/>
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="@*|node()">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="@*|node()" />
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
|
@ -1,20 +1,20 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<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">
|
||||
|
@ -254,16 +254,16 @@
|
|||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${keycloak.server.home}/standalone/configuration</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/keystore</directory>
|
||||
<includes>
|
||||
<include>keycloak.jks</include>
|
||||
<include>keycloak.truststore</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<outputDirectory>${keycloak.server.home}/standalone/configuration</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/keystore</directory>
|
||||
<includes>
|
||||
<include>keycloak.jks</include>
|
||||
<include>keycloak.truststore</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
@ -412,5 +412,50 @@
|
|||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>auth-server-wildfly-cluster</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>xml-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>configure-wildfly-datasource</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>transform</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformationSets>
|
||||
<!-- point KeycloakDS datasource to H2 running on TCP port -->
|
||||
<transformationSet>
|
||||
<dir>${keycloak.server.home}/standalone/configuration</dir>
|
||||
<includes>
|
||||
<include>standalone-ha.xml</include>
|
||||
</includes>
|
||||
<stylesheet>src/main/xslt/datasource-jdbc-url.xsl</stylesheet>
|
||||
<outputDir>${keycloak.server.home}/standalone/configuration</outputDir>
|
||||
<parameters>
|
||||
<parameter>
|
||||
<name>pool.name</name>
|
||||
<value>KeycloakDS</value>
|
||||
</parameter>
|
||||
<parameter>
|
||||
<name>jdbc.url</name>
|
||||
<value>jdbc:h2:tcp://${jboss.bind.address:localhost}:9092/mem:keycloak;DB_CLOSE_DELAY=-1</value>
|
||||
</parameter>
|
||||
</parameters>
|
||||
</transformationSet>
|
||||
</transformationSets>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
</profiles>
|
||||
</project>
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:xalan="http://xml.apache.org/xalan"
|
||||
xmlns:j="urn:jboss:domain:4.0"
|
||||
xmlns:ds="urn:jboss:domain:datasources:4.0"
|
||||
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:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
|
||||
<xsl:strip-space elements="*"/>
|
||||
|
||||
|
||||
<xsl:variable name="nsDS" select="'urn:jboss:domain:datasources:'"/>
|
||||
|
||||
<xsl:param name="pool.name" select="'KeycloakDS'"/>
|
||||
<xsl:param name="jdbc.url" />
|
||||
|
||||
<!-- replace JDBC URL -->
|
||||
<xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
|
||||
/*[local-name()='datasources' and starts-with(namespace-uri(), $nsDS)]
|
||||
/*[local-name()='datasource' and starts-with(namespace-uri(), $nsDS) and @pool-name=$pool.name]
|
||||
/*[local-name()='connection-url' and starts-with(namespace-uri(), $nsDS)]">
|
||||
<connection-url>
|
||||
<xsl:value-of select="$jdbc.url"/>
|
||||
</connection-url>
|
||||
</xsl:template>
|
||||
|
||||
<!-- Copy everything else. -->
|
||||
<xsl:template match="@*|node()">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="@*|node()" />
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
|
@ -35,7 +35,10 @@
|
|||
<exclude.console>-</exclude.console>
|
||||
<exclude.account>-</exclude.account>
|
||||
<exclude.client>-</exclude.client>
|
||||
<exclude.migration>-</exclude.migration>
|
||||
<!--exclude migration tests by default, enabled by 'migration' profile in tests/pom.xml-->
|
||||
<exclude.migration>**/migration/**/*Test.java</exclude.migration>
|
||||
<!--exclude cluster tests by default, enabled by 'auth-server-*-cluster' profiles in tests/pom.xml-->
|
||||
<exclude.cluster>**/cluster/**/*Test.java</exclude.cluster>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -75,6 +78,7 @@
|
|||
<exclude>${exclude.account}</exclude>
|
||||
<exclude>${exclude.client}</exclude>
|
||||
<exclude>${exclude.migration}</exclude>
|
||||
<exclude>${exclude.cluster}</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
@ -108,29 +112,4 @@
|
|||
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>no-account</id>
|
||||
<properties>
|
||||
<!-- Exclude all account management tests. -->
|
||||
<exclude.account>**/account/**/*Test.java</exclude.account>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>no-client</id>
|
||||
<properties>
|
||||
<!-- Exclude all client tests. -->
|
||||
<exclude.client>**/client/**/*Test.java</exclude.client>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>adapters-only</id>
|
||||
<properties>
|
||||
<exclude.account>**/account/**/*Test.java</exclude.account>
|
||||
<exclude.client>**/client/**/*Test.java</exclude.client>
|
||||
<exclude.migration>**/migration/**/*Test.java</exclude.migration>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -40,9 +40,10 @@ public class AppServerTestEnricher {
|
|||
String appServerQ = (annotatedClass == null ? null
|
||||
: annotatedClass.getAnnotation(AppServerContainer.class).value());
|
||||
|
||||
return appServerQ == null || appServerQ.isEmpty()
|
||||
? getAuthServerQualifier() // app server == auth server
|
||||
: appServerQ;
|
||||
return annotatedClass == null ? null // no @AppServerContainer annotation --> no adapter test
|
||||
: (appServerQ == null || appServerQ.isEmpty() // @AppServerContainer annotation present but qualifier not set --> relative adapter test
|
||||
? getAuthServerQualifier() // app server == auth server
|
||||
: appServerQ);
|
||||
}
|
||||
|
||||
public static String getAppServerContextRoot() {
|
||||
|
@ -129,7 +130,7 @@ public class AppServerTestEnricher {
|
|||
String jbossHomePath = appServerInfo.getProperties().get("jbossHome");
|
||||
|
||||
File bin = new File(jbossHomePath + "/bin");
|
||||
|
||||
|
||||
File clientJar = new File(jbossHomePath + "/bin/client/jboss-cli-client.jar");
|
||||
if (!clientJar.exists()) {
|
||||
clientJar = new File(jbossHomePath + "/bin/client/jboss-client.jar"); // AS7
|
||||
|
@ -137,7 +138,7 @@ public class AppServerTestEnricher {
|
|||
if (!clientJar.exists()) {
|
||||
throw new IOException("JBoss CLI client JAR not found.");
|
||||
}
|
||||
|
||||
|
||||
String command = "java -jar " + clientJar.getAbsolutePath();
|
||||
String adapterScript = "adapter-install.cli";
|
||||
String samlAdapterScript = "adapter-install-saml.cli";
|
||||
|
|
|
@ -110,7 +110,10 @@ public class AuthServerTestEnricher {
|
|||
|
||||
boolean authServerCluster = authServerQualifier.endsWith("-cluster");
|
||||
|
||||
String authServerType = authServerQualifier.replaceAll("^auth-server-", "").replaceAll("-cluster$", "");
|
||||
String authServerType = authServerQualifier.replaceAll("auth-server-", "").replaceAll("-cluster", "");
|
||||
|
||||
log.info("authServerType:" + authServerType);
|
||||
|
||||
String authServerFrontend = authServerCluster
|
||||
? "auth-server-" + authServerType + "-balancer" // in cluster mode the load-balancer container serves as auth server frontend
|
||||
: authServerQualifier; // single-node mode
|
||||
|
@ -133,7 +136,7 @@ public class AuthServerTestEnricher {
|
|||
if (suiteContext.getAuthServerInfo() == null) {
|
||||
throw new RuntimeException(String.format("No auth server activated. A container matching '%s' needs to be enabled in arquillian.xml.", authServerFrontend));
|
||||
}
|
||||
if (authServerCluster && !suiteContext.getAuthServerBackendsInfo().isEmpty()) {
|
||||
if (authServerCluster && suiteContext.getAuthServerBackendsInfo().isEmpty()) {
|
||||
throw new RuntimeException(String.format("No cluster backend nodes activated. Containers matching '%sN' need to be enabled in arquillian.xml.", authServerBackend));
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.jboss.arquillian.core.spi.LoadableExtension;
|
|||
import org.jboss.arquillian.graphene.location.CustomizableURLResourceProvider;
|
||||
import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider;
|
||||
import org.jboss.arquillian.test.spi.execution.TestExecutionDecider;
|
||||
import org.keycloak.testsuite.arquillian.h2.H2TestEnricher;
|
||||
import org.keycloak.testsuite.arquillian.migration.MigrationTestExecutionDecider;
|
||||
import org.keycloak.testsuite.arquillian.undertow.CustomUndertowContainer;
|
||||
|
||||
|
@ -48,7 +49,8 @@ public class KeycloakArquillianExtension implements LoadableExtension {
|
|||
.service(DeploymentScenarioGenerator.class, DeploymentTargetModifier.class)
|
||||
.service(ApplicationArchiveProcessor.class, DeploymentArchiveProcessor.class)
|
||||
.observer(AuthServerTestEnricher.class)
|
||||
.observer(AppServerTestEnricher.class);
|
||||
.observer(AppServerTestEnricher.class)
|
||||
.observer(H2TestEnricher.class);
|
||||
|
||||
builder
|
||||
.service(DeployableContainer.class, CustomUndertowContainer.class);
|
||||
|
|
|
@ -93,9 +93,13 @@ public final class SuiteContext {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
String containers = "Auth server: " + (isAuthServerCluster() ? "\nFrontend: " : "")
|
||||
+ authServerInfo.getQualifier() + "\n";
|
||||
for (ContainerInfo bInfo : getAuthServerBackendsInfo()) {
|
||||
containers += "Backend: " + bInfo + "\n";
|
||||
}
|
||||
return "SUITE CONTEXT:\n"
|
||||
+ "Auth server: " + authServerInfo.getQualifier() + "\n"
|
||||
+(isAuthServerCluster() ? "Auth server cluster: " + getAuthServerBackendsInfo().size() + " nodes+\n" : "");
|
||||
+ containers;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package org.keycloak.testsuite.arquillian.h2;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import org.jboss.arquillian.core.api.annotation.Observes;
|
||||
import org.jboss.arquillian.test.spi.event.suite.AfterSuite;
|
||||
import org.jboss.arquillian.test.spi.event.suite.BeforeSuite;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.h2.tools.Server;
|
||||
|
||||
/**
|
||||
* Starts H2 before suite and stops it after.
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public class H2TestEnricher {
|
||||
|
||||
protected final Logger log = Logger.getLogger(this.getClass());
|
||||
|
||||
boolean runH2 = Boolean.parseBoolean(System.getProperty("run.h2", "false"));
|
||||
|
||||
private Server server = null;
|
||||
|
||||
public void startH2(@Observes(precedence = 2) BeforeSuite event) throws SQLException {
|
||||
if (runH2) {
|
||||
log.info("Starting H2 database.");
|
||||
server = Server.createTcpServer();
|
||||
server.start();
|
||||
log.info(String.format("URL: %s", server.getURL()));
|
||||
}
|
||||
}
|
||||
|
||||
public void stopH2(@Observes(precedence = -2) AfterSuite event) {
|
||||
if (runH2 && server.isRunning(false)) {
|
||||
log.info("Stopping H2 database.");
|
||||
server.stop();
|
||||
assert !server.isRunning(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package org.keycloak.testsuite;
|
||||
|
||||
import java.util.List;
|
||||
import org.jboss.arquillian.container.test.api.ContainerController;
|
||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public class ContainersTest extends AbstractKeycloakTest {
|
||||
|
||||
@ArquillianResource
|
||||
ContainerController controller;
|
||||
|
||||
@Override
|
||||
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testAuthServer() {
|
||||
|
||||
log.info("AUTH SERVER should be started.");
|
||||
assertTrue(controller.isStarted(AuthServerTestEnricher.getAuthServerQualifier()));
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package org.keycloak.testsuite.cluster;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.jboss.arquillian.container.test.api.ContainerController;
|
||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||
import org.keycloak.testsuite.arquillian.ContainerInfo;
|
||||
import static org.keycloak.testsuite.auth.page.AuthRealm.ADMIN;
|
||||
import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public abstract class AbstractClusterTest extends AbstractKeycloakTest {
|
||||
|
||||
@ArquillianResource
|
||||
protected ContainerController controller;
|
||||
|
||||
protected List<Keycloak> backendAdminClients = new ArrayList<>();
|
||||
|
||||
public void startBackendNodes(int count) {
|
||||
if (count < 0 || count > 10) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
assertTrue(suiteContext.getAuthServerBackendsInfo().size() >= count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
|
||||
ContainerInfo backendNode = suiteContext.getAuthServerBackendsInfo().get(i);
|
||||
|
||||
controller.start(backendNode.getQualifier());
|
||||
assertTrue(controller.isStarted(backendNode.getQualifier()));
|
||||
|
||||
log.info("Initializing admin client for: '" + backendNode.getContextRoot() + "/auth'");
|
||||
backendAdminClients.add(Keycloak.getInstance(backendNode.getContextRoot() + "/auth",
|
||||
MASTER, ADMIN, ADMIN, Constants.ADMIN_CLI_CLIENT_ID));
|
||||
}
|
||||
}
|
||||
|
||||
protected ContainerInfo backendInfo(int i) {
|
||||
return suiteContext.getAuthServerBackendsInfo().get(i);
|
||||
}
|
||||
|
||||
protected void startBackendNode(int i) {
|
||||
String container = backendInfo(i).getQualifier();
|
||||
if (!controller.isStarted(container)) {
|
||||
controller.start(container);
|
||||
}
|
||||
}
|
||||
|
||||
protected void stopBackendNode(int i) {
|
||||
controller.kill(backendInfo(i).getQualifier());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
package org.keycloak.testsuite.cluster;
|
||||
|
||||
import java.util.List;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.arquillian.ContainerInfo;
|
||||
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
|
||||
import static org.keycloak.testsuite.util.WaitUtils.pause;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public class TwoNodeClusterTest extends AbstractClusterTest {
|
||||
|
||||
@Override
|
||||
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void beforeTwoNodeClusterTest() {
|
||||
startBackendNodes(2);
|
||||
pause(3000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRealm() {
|
||||
testRealm(TEST, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRealmWithFailover() {
|
||||
testRealm(TEST + "_fo", true);
|
||||
}
|
||||
|
||||
public void testRealm(String realm, boolean containerFailover) {
|
||||
RealmRepresentation testRealm = new RealmRepresentation();
|
||||
testRealm.setRealm(realm);
|
||||
testRealm.setEnabled(true);
|
||||
|
||||
// CREATE on node1
|
||||
log.info("Creating test realm via node1.");
|
||||
backend1AdminClient().realms().create(testRealm);
|
||||
log.info("Test realm created.");
|
||||
|
||||
// check if created on node1
|
||||
RealmRepresentation testRealmOnBackend1 = backend1AdminClient().realms().realm(realm).toRepresentation();
|
||||
assertEquals(testRealmOnBackend1.getRealm(), testRealm.getRealm());
|
||||
if (containerFailover) {
|
||||
stopBackend1();
|
||||
}
|
||||
|
||||
// check if created on node2
|
||||
RealmRepresentation testRealmOnBackend2 = backend2AdminClient().realms().realm(realm).toRepresentation();
|
||||
assertEquals(testRealmOnBackend1.getId(), testRealmOnBackend2.getId());
|
||||
assertEquals(testRealmOnBackend1.getRealm(), testRealmOnBackend2.getRealm());
|
||||
|
||||
failback();
|
||||
|
||||
// UPDATE on node2
|
||||
testRealmOnBackend2.setRealm(realm + "_updated");
|
||||
backend2AdminClient().realms().realm(realm).update(testRealmOnBackend2);
|
||||
if (containerFailover) {
|
||||
stopBackend2();
|
||||
}
|
||||
// check if updated on node1
|
||||
testRealmOnBackend1 = backend1AdminClient().realms().realm(realm).toRepresentation();
|
||||
assertEquals(testRealmOnBackend1.getId(), testRealmOnBackend2.getId());
|
||||
assertEquals(testRealmOnBackend1.getRealm(), testRealmOnBackend2.getRealm());
|
||||
|
||||
failback();
|
||||
|
||||
// DELETE on node1
|
||||
backend1AdminClient().realms().realm(realm).remove();
|
||||
if (containerFailover) {
|
||||
stopBackend1();
|
||||
}
|
||||
// check if deleted on node2
|
||||
boolean testRealmOnBackend2Exists = false;
|
||||
for (RealmRepresentation realmOnBackend2 : backend2AdminClient().realms().findAll()) {
|
||||
if (realm.equals(realmOnBackend2.getRealm())
|
||||
|| testRealmOnBackend1.getId().equals(realmOnBackend2.getId())) {
|
||||
testRealmOnBackend2Exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertFalse(testRealmOnBackend2Exists);
|
||||
}
|
||||
|
||||
protected ContainerInfo backend1Info() {
|
||||
return backendInfo(0);
|
||||
}
|
||||
|
||||
protected ContainerInfo backend2Info() {
|
||||
return backendInfo(1);
|
||||
}
|
||||
|
||||
protected Keycloak backend1AdminClient() {
|
||||
return backendAdminClients.get(0);
|
||||
}
|
||||
|
||||
protected Keycloak backend2AdminClient() {
|
||||
return backendAdminClients.get(1);
|
||||
}
|
||||
|
||||
protected void startBackend1() {
|
||||
startBackendNode(0);
|
||||
}
|
||||
|
||||
protected void startBackend2() {
|
||||
startBackendNode(1);
|
||||
}
|
||||
|
||||
protected void failback() {
|
||||
startBackend1();
|
||||
startBackend2();
|
||||
}
|
||||
|
||||
protected void stopBackend1() {
|
||||
stopBackendNode(0);
|
||||
}
|
||||
|
||||
protected void stopBackend2() {
|
||||
stopBackendNode(1);
|
||||
}
|
||||
|
||||
}
|
|
@ -71,7 +71,7 @@
|
|||
<configuration>
|
||||
<property name="enabled">${auth.server.wildfly.cluster}</property>
|
||||
<property name="adapterImplClass">org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</property>
|
||||
<property name="jbossHome">${wildfly.home}</property>
|
||||
<property name="jbossHome">${keycloak.balancer.home}</property>
|
||||
<property name="javaVmArguments">
|
||||
-Djboss.socket.binding.port-offset=${auth.server.port.offset}
|
||||
-Xms64m -Xmx512m -XX:MaxPermSize=256m
|
||||
|
@ -86,15 +86,35 @@
|
|||
<property name="enabled">${auth.server.wildfly.cluster}</property>
|
||||
<property name="adapterImplClass">org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</property>
|
||||
<property name="jbossHome">${keycloak.backend1.home}</property>
|
||||
<property name="serverConfig">standalone-ha.xml</property>
|
||||
<property name="javaVmArguments">
|
||||
-Djboss.socket.binding.port-offset=${auth.server.backend1.port.offset}
|
||||
-Xms64m -Xmx512m -XX:MaxPermSize=256m
|
||||
${adapter.test.props}
|
||||
-Djboss.node.name=node1
|
||||
</property>
|
||||
<!--<property name="outputToConsole">false</property>-->
|
||||
<property name="managementPort">${auth.server.backend1.management.port}</property>
|
||||
<property name="startupTimeoutInSeconds">${startup.timeout.sec}</property>
|
||||
</configuration>
|
||||
</container>
|
||||
<container qualifier="auth-server-wildfly-backend2" mode="manual" >
|
||||
<configuration>
|
||||
<property name="enabled">${auth.server.wildfly.cluster}</property>
|
||||
<property name="adapterImplClass">org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</property>
|
||||
<property name="jbossHome">${keycloak.backend2.home}</property>
|
||||
<property name="serverConfig">standalone-ha.xml</property>
|
||||
<property name="javaVmArguments">
|
||||
-Djboss.socket.binding.port-offset=${auth.server.backend2.port.offset}
|
||||
-Xms64m -Xmx512m -XX:MaxPermSize=256m
|
||||
${adapter.test.props}
|
||||
-Djboss.node.name=node2
|
||||
</property>
|
||||
<!--<property name="outputToConsole">false</property>-->
|
||||
<property name="managementPort">${auth.server.backend2.management.port}</property>
|
||||
<property name="startupTimeoutInSeconds">${startup.timeout.sec}</property>
|
||||
</configuration>
|
||||
</container>
|
||||
</group>
|
||||
|
||||
<container qualifier="auth-server-eap7" mode="suite" >
|
||||
|
|
|
@ -159,7 +159,27 @@
|
|||
</build>
|
||||
|
||||
<profiles>
|
||||
|
||||
|
||||
<profile>
|
||||
<id>no-account</id>
|
||||
<properties>
|
||||
<exclude.account>**/account/**/*Test.java</exclude.account>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>no-client</id>
|
||||
<properties>
|
||||
<exclude.client>**/client/**/*Test.java</exclude.client>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>no-base</id>
|
||||
<properties>
|
||||
<exclude.account>**/account/**/*Test.java</exclude.account>
|
||||
<exclude.client>**/client/**/*Test.java</exclude.client>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>common-test-dependencies</id>
|
||||
<activation>
|
||||
|
@ -348,6 +368,7 @@
|
|||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
|
@ -398,6 +419,7 @@
|
|||
<adapter.test.props/>
|
||||
<keycloak.home>${containers.home}/keycloak-${project.version}</keycloak.home>
|
||||
<jboss.server.config.dir>${keycloak.home}/standalone/configuration</jboss.server.config.dir>
|
||||
<h2.version>1.3.173</h2.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
@ -450,6 +472,111 @@
|
|||
</build>
|
||||
</profile>
|
||||
|
||||
|
||||
<profile>
|
||||
<id>auth-server-wildfly-cluster</id>
|
||||
<properties>
|
||||
<!--disable exclusion pattern for cluster test which is enabled by default in base/pom.xml-->
|
||||
<exclude.cluster>-</exclude.cluster>
|
||||
|
||||
<auth.server.container>auth-server-wildfly-cluster</auth.server.container>
|
||||
<startup.timeout.sec>300</startup.timeout.sec>
|
||||
<adapter.test.props/>
|
||||
<h2.version>1.3.173</h2.version>
|
||||
|
||||
<keycloak.balancer.home>${containers.home}/balancer/wildfly-balancer-${project.version}</keycloak.balancer.home>
|
||||
<keycloak.backend1.home>${containers.home}/node1/keycloak-${project.version}</keycloak.backend1.home>
|
||||
<keycloak.backend2.home>${containers.home}/node2/keycloak-${project.version}</keycloak.backend2.home>
|
||||
|
||||
<keycloak.home>${keycloak.backend1.home}</keycloak.home>
|
||||
<jboss.server.config.dir>${keycloak.home}/standalone/configuration</jboss.server.config.dir>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.wildfly</groupId>
|
||||
<artifactId>wildfly-arquillian-container-managed</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<run.h2>true</run.h2>
|
||||
|
||||
<auth.server.wildfly.cluster>true</auth.server.wildfly.cluster>
|
||||
<auth.server.undertow>false</auth.server.undertow>
|
||||
<adapter.test.props>${adapter.test.props}</adapter.test.props>
|
||||
|
||||
<keycloak.balancer.home>${keycloak.balancer.home}</keycloak.balancer.home>
|
||||
<keycloak.backend1.home>${keycloak.backend1.home}</keycloak.backend1.home>
|
||||
<keycloak.backend2.home>${keycloak.backend2.home}</keycloak.backend2.home>
|
||||
|
||||
<!--100-->
|
||||
<auth.server.backend1.port.offset>101</auth.server.backend1.port.offset>
|
||||
<auth.server.backend2.port.offset>102</auth.server.backend2.port.offset>
|
||||
<!--8180-->
|
||||
<auth.server.backend1.http.port>8181</auth.server.backend1.http.port>
|
||||
<auth.server.backend2.http.port>8182</auth.server.backend2.http.port>
|
||||
<!--8543-->
|
||||
<auth.server.backend1.https.port>8544</auth.server.backend1.https.port>
|
||||
<auth.server.backend2.https.port>8545</auth.server.backend2.https.port>
|
||||
<!--10090-->
|
||||
<auth.server.backend1.management.port>10091</auth.server.backend1.management.port>
|
||||
<auth.server.backend2.management.port>10092</auth.server.backend2.management.port>
|
||||
<!--10099-->
|
||||
<auth.server.backend1.management.port.jmx>10100</auth.server.backend1.management.port.jmx>
|
||||
<auth.server.backend2.management.port.jmx>10101</auth.server.backend2.management.port.jmx>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>unpack-auth-server-wildfly</id>
|
||||
<phase>generate-test-resources</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.keycloak.testsuite</groupId>
|
||||
<artifactId>integration-arquillian-server-wildfly-balancer</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>zip</type>
|
||||
<outputDirectory>${containers.home}/balancer</outputDirectory>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
<groupId>org.keycloak.testsuite</groupId>
|
||||
<artifactId>integration-arquillian-server-wildfly</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>zip</type>
|
||||
<outputDirectory>${containers.home}/node1</outputDirectory>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
<groupId>org.keycloak.testsuite</groupId>
|
||||
<artifactId>integration-arquillian-server-wildfly</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>zip</type>
|
||||
<outputDirectory>${containers.home}/node2</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
<overWriteIfNewer>true</overWriteIfNewer>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>auth-server-eap7</id>
|
||||
<properties>
|
||||
|
@ -538,6 +665,10 @@
|
|||
<name>migrated.auth.server.version</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<!--diable exclusion pattern for migration tests, which is enabled by default in the base/pom.xml-->
|
||||
<exclude.migration>-</exclude.migration>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
@ -743,7 +874,7 @@
|
|||
</pluginManagement>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -831,6 +831,7 @@ additional-grants=Additional Grants
|
|||
revoke=Revoke
|
||||
new-password=New Password
|
||||
password-confirmation=Password Confirmation
|
||||
reset-password=Reset Password
|
||||
credentials.temporary.tooltip=If enabled user is required to change password on next login
|
||||
remove-totp=Remove TOTP
|
||||
credentials.remove-totp.tooltip=Remove one time password generator for user.
|
||||
|
|
|
@ -6,6 +6,8 @@ consoleBaseUrl = consoleBaseUrl + "/console";
|
|||
var configUrl = consoleBaseUrl + "/config";
|
||||
|
||||
var auth = {};
|
||||
var resourceBundle;
|
||||
var locale = 'en';
|
||||
|
||||
var module = angular.module('keycloak', [ 'keycloak.services', 'keycloak.loaders', 'ui.bootstrap', 'ui.select2', 'angularFileUpload', 'angularTreeview', 'pascalprecht.translate', 'ngCookies', 'ngSanitize']);
|
||||
var resourceRequests = 0;
|
||||
|
@ -34,6 +36,25 @@ angular.element(document).ready(function () {
|
|||
req.send();
|
||||
}
|
||||
|
||||
function loadResourceBundle(success, error) {
|
||||
var req = new XMLHttpRequest();
|
||||
req.open('GET', consoleBaseUrl + '/messages.json?lang=' + locale, true);
|
||||
req.setRequestHeader('Accept', 'application/json');
|
||||
|
||||
req.onreadystatechange = function () {
|
||||
if (req.readyState == 4) {
|
||||
if (req.status == 200) {
|
||||
var data = JSON.parse(req.responseText);
|
||||
success && success(data);
|
||||
} else {
|
||||
error && error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
req.send();
|
||||
}
|
||||
|
||||
function hasAnyAccess(user) {
|
||||
return user && user['realm_access'];
|
||||
}
|
||||
|
@ -45,6 +66,10 @@ angular.element(document).ready(function () {
|
|||
keycloakAuth.init({ onLoad: 'login-required' }).success(function () {
|
||||
auth.authz = keycloakAuth;
|
||||
|
||||
if (auth.authz.idTokenParsed.locale) {
|
||||
locale = auth.authz.idTokenParsed.locale;
|
||||
}
|
||||
|
||||
auth.refreshPermissions = function(success, error) {
|
||||
whoAmI(function(data) {
|
||||
auth.user = data;
|
||||
|
@ -57,17 +82,19 @@ angular.element(document).ready(function () {
|
|||
});
|
||||
};
|
||||
|
||||
auth.refreshPermissions(function() {
|
||||
module.factory('Auth', function() {
|
||||
return auth;
|
||||
});
|
||||
var injector = angular.bootstrap(document, ["keycloak"]);
|
||||
loadResourceBundle(function(data) {
|
||||
resourceBundle = data;
|
||||
|
||||
injector.get('$translate')('consoleTitle').then(function(consoleTitle) {
|
||||
document.title=consoleTitle;
|
||||
auth.refreshPermissions(function () {
|
||||
module.factory('Auth', function () {
|
||||
return auth;
|
||||
});
|
||||
var injector = angular.bootstrap(document, ["keycloak"]);
|
||||
|
||||
injector.get('$translate')('consoleTitle').then(function (consoleTitle) {
|
||||
document.title = consoleTitle;
|
||||
});
|
||||
});
|
||||
}, function() {
|
||||
window.location.reload();
|
||||
});
|
||||
}).error(function () {
|
||||
window.location.reload();
|
||||
|
@ -99,15 +126,8 @@ module.factory('authInterceptor', function($q, Auth) {
|
|||
|
||||
module.config(['$translateProvider', function($translateProvider) {
|
||||
$translateProvider.useSanitizeValueStrategy('sanitizeParameters');
|
||||
|
||||
var locale = auth.authz.idTokenParsed.locale;
|
||||
if (locale !== undefined) {
|
||||
$translateProvider.preferredLanguage(locale);
|
||||
} else {
|
||||
$translateProvider.preferredLanguage('en');
|
||||
}
|
||||
|
||||
$translateProvider.useUrlLoader('messages.json');
|
||||
$translateProvider.preferredLanguage(locale);
|
||||
$translateProvider.translations(locale, resourceBundle);
|
||||
}]);
|
||||
|
||||
module.config([ '$routeProvider', function($routeProvider) {
|
||||
|
|
|
@ -25,19 +25,19 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location
|
|||
},
|
||||
|
||||
get viewRealm() {
|
||||
return getAccess('view-realm') || this.manageRealm;
|
||||
return getAccess('view-realm') || getAccess('manage-realm') || this.manageRealm;
|
||||
},
|
||||
|
||||
get viewClients() {
|
||||
return getAccess('view-clients') || this.manageClients;
|
||||
return getAccess('view-clients') || getAccess('manage-clients') || this.manageClients;
|
||||
},
|
||||
|
||||
get viewUsers() {
|
||||
return getAccess('view-users') || this.manageClients;
|
||||
return getAccess('view-users') || getAccess('manage-users') || this.manageClients;
|
||||
},
|
||||
|
||||
get viewEvents() {
|
||||
return getAccess('view-events') || this.manageClients;
|
||||
return getAccess('view-events') || getAccess('manage-events') || this.manageClients;
|
||||
},
|
||||
|
||||
get manageRealm() {
|
||||
|
|
Loading…
Reference in a new issue