Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
8533d49dfb
41 changed files with 1049 additions and 206 deletions
|
@ -73,13 +73,14 @@
|
|||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<createTable tableName="CLIENT_ALLOWED_IDENTITY_PROVIDER">
|
||||
<createTable tableName="CLIENT_IDENTITY_PROVIDER_MAPPING">
|
||||
<column name="CLIENT_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="INTERNAL_ID" type="VARCHAR(36)">
|
||||
<column name="IDENTITY_PROVIDER_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="RETRIEVE_TOKEN" type="BOOLEAN(1)"/>
|
||||
</createTable>
|
||||
<createTable tableName="CLIENT_PROTOCOL_MAPPER">
|
||||
<column name="CLIENT_ID" type="VARCHAR(36)">
|
||||
|
@ -104,10 +105,11 @@
|
|||
<addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="FEDERATED_IDENTITY" constraintName="FK404288B92EF007A6" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
|
||||
<addForeignKeyConstraint baseColumnNames="IDENTITY_PROVIDER_ID" baseTableName="IDENTITY_PROVIDER_CONFIG" constraintName="FKDC4897CF864C4E43" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="INTERNAL_ID" referencedTableName="IDENTITY_PROVIDER"/>
|
||||
<addForeignKeyConstraint baseColumnNames="PROTOCOL_MAPPER_ID" baseTableName="PROTOCOL_MAPPER_CONFIG" constraintName="FK_PMConfig" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="PROTOCOL_MAPPER"/>
|
||||
<addForeignKeyConstraint baseColumnNames="INTERNAL_ID" baseTableName="CLIENT_ALLOWED_IDENTITY_PROVIDER" constraintName="FK_7CELWNIBJI49AVXSRTUF6XJ12" referencedColumnNames="INTERNAL_ID" referencedTableName="IDENTITY_PROVIDER"/>
|
||||
<addUniqueConstraint columnNames="INTERNAL_ID,CLIENT_ID" constraintName="UK_7CAELWNIBJI49AVXSRTUF6XJ12" tableName="CLIENT_ALLOWED_IDENTITY_PROVIDER"/>
|
||||
<addForeignKeyConstraint baseColumnNames="IDENTITY_PROVIDER_ID" baseTableName="CLIENT_IDENTITY_PROVIDER_MAPPING" constraintName="FK_7CELWNIBJI49AVXSRTUF6XJ12" referencedColumnNames="INTERNAL_ID" referencedTableName="IDENTITY_PROVIDER"/>
|
||||
<addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="CLIENT_IDENTITY_PROVIDER_MAPPING" constraintName="FK_56ELWNIBJI49AVXSRTUF6XJ23" referencedColumnNames="ID" referencedTableName="CLIENT"/>
|
||||
<addForeignKeyConstraint baseColumnNames="MAPPING_ID" baseTableName="CLIENT_PROTOCOL_MAPPER" constraintName="FK_CPCM" referencedColumnNames="ID" referencedTableName="PROTOCOL_MAPPER"/>
|
||||
<addUniqueConstraint columnNames="CLIENT_ID,MAPPING_ID" constraintName="UK_CPCM" tableName="CLIENT_PROTOCOL_MAPPER"/>
|
||||
<addUniqueConstraint columnNames="PROVIDER_NONIMAL_ID" constraintName="UK_2DAELWNIBJI49AVXSRTUF6XJ33" tableName="IDENTITY_PROVIDER"/>
|
||||
<addUniqueConstraint columnNames="IDENTITY_PROVIDER_ID,CLIENT_ID" constraintName="UK_7CAELWNIBJI49AVXSRTUF6XJ12" tableName="CLIENT_IDENTITY_PROVIDER_MAPPING"/>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
<class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.ScopeMappingEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.IdentityProviderEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.ClientIdentityProviderMappingEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.ClaimTypeEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.ProtocolMapperEntity</class>
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package org.keycloak.representations.idm;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -29,7 +28,7 @@ public class ApplicationRepresentation {
|
|||
protected Boolean fullScopeAllowed;
|
||||
protected Integer nodeReRegistrationTimeout;
|
||||
protected Map<String, Integer> registeredNodes;
|
||||
protected List<String> allowedIdentityProviders;
|
||||
protected List<ClientIdentityProviderMappingRepresentation> identityProviders;
|
||||
protected List<ClientProtocolMappingRepresentation> protocolMappers;
|
||||
|
||||
public String getId() {
|
||||
|
@ -192,12 +191,12 @@ public class ApplicationRepresentation {
|
|||
this.frontchannelLogout = frontchannelLogout;
|
||||
}
|
||||
|
||||
public List<String> getAllowedIdentityProviders() {
|
||||
return this.allowedIdentityProviders;
|
||||
public List<ClientIdentityProviderMappingRepresentation> getIdentityProviders() {
|
||||
return this.identityProviders;
|
||||
}
|
||||
|
||||
public void setAllowedIdentityProviders(List<String> allowedIdentityProviders) {
|
||||
this.allowedIdentityProviders = allowedIdentityProviders;
|
||||
public void setIdentityProviders(List<ClientIdentityProviderMappingRepresentation> identityProviders) {
|
||||
this.identityProviders = identityProviders;
|
||||
}
|
||||
|
||||
public List<ClientProtocolMappingRepresentation> getProtocolMappers() {
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source
|
||||
*
|
||||
* Copyright 2013 Red Hat, Inc. and/or its affiliates.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package org.keycloak.representations.idm;
|
||||
|
||||
/**
|
||||
* @author pedroigor
|
||||
*/
|
||||
public class ClientIdentityProviderMappingRepresentation {
|
||||
|
||||
protected String id;
|
||||
protected boolean retrieveToken;
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(String identityProviderId) {
|
||||
this.id = identityProviderId;
|
||||
}
|
||||
|
||||
public boolean isRetrieveToken() {
|
||||
return this.retrieveToken;
|
||||
}
|
||||
|
||||
public void setRetrieveToken(boolean retrieveToken) {
|
||||
this.retrieveToken = retrieveToken;
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@ package org.keycloak.representations.idm;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -23,8 +22,8 @@ public class OAuthClientRepresentation {
|
|||
protected Boolean directGrantsOnly;
|
||||
protected Boolean fullScopeAllowed;
|
||||
protected Boolean frontchannelLogout;
|
||||
protected List<String> allowedIdentityProviders;
|
||||
protected List<ClientProtocolMappingRepresentation> protocolMappers;
|
||||
private List<ClientIdentityProviderMappingRepresentation> identityProviders;
|
||||
|
||||
|
||||
public String getId() {
|
||||
|
@ -139,12 +138,12 @@ public class OAuthClientRepresentation {
|
|||
this.frontchannelLogout = frontchannelLogout;
|
||||
}
|
||||
|
||||
public List<String> getAllowedIdentityProviders() {
|
||||
return this.allowedIdentityProviders;
|
||||
public List<ClientIdentityProviderMappingRepresentation> getIdentityProviders() {
|
||||
return this.identityProviders;
|
||||
}
|
||||
|
||||
public void setAllowedIdentityProviders(List<String> allowedIdentityProviders) {
|
||||
this.allowedIdentityProviders = allowedIdentityProviders;
|
||||
public void setIdentityProviders(List<ClientIdentityProviderMappingRepresentation> identityProviders) {
|
||||
this.identityProviders = identityProviders;
|
||||
}
|
||||
|
||||
public List<ClientProtocolMappingRepresentation> getProtocolMappers() {
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<!ENTITY Events SYSTEM "modules/events.xml">
|
||||
<!ENTITY AdminApi SYSTEM "modules/admin-rest-api.xml">
|
||||
<!ENTITY UserFederation SYSTEM "modules/user-federation.xml">
|
||||
<!ENTITY Kerberos SYSTEM "modules/kerberos.xml">
|
||||
<!ENTITY ExportImport SYSTEM "modules/export-import.xml">
|
||||
<!ENTITY ServerCache SYSTEM "modules/cache.xml">
|
||||
<!ENTITY SecurityVulnerabilities SYSTEM "modules/security-vulnerabilities.xml">
|
||||
|
@ -119,6 +120,7 @@ This one is short
|
|||
&AdminApi;
|
||||
&Events;
|
||||
&UserFederation;
|
||||
&Kerberos;
|
||||
&ExportImport;
|
||||
&ServerCache;
|
||||
&SAML;
|
||||
|
|
|
@ -962,7 +962,7 @@ Authorization: Bearer {keycloak_access_token}]]></programlisting>
|
|||
</section>
|
||||
|
||||
<section>
|
||||
<title>Enabling/Disabling Identity Providers for Service Providers</title>
|
||||
<title>Configuring Identity Providers for Applications</title>
|
||||
<para>
|
||||
By default, all identity providers enabled for a particular realm are also available to all its applications.
|
||||
However, you can also specify which identity providers should be available when
|
||||
|
@ -993,6 +993,10 @@ Authorization: Bearer {keycloak_access_token}]]></programlisting>
|
|||
</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
<para>
|
||||
From this page you can also configure if an application is allowed to retrieve tokens from an specific identity provider. For that,
|
||||
just click on the <emphasis>Can Retrieve Token</emphasis> button.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
|
231
docbook/reference/en/en-US/modules/kerberos.xml
Normal file
231
docbook/reference/en/en-US/modules/kerberos.xml
Normal file
|
@ -0,0 +1,231 @@
|
|||
<chapter id="kerberos">
|
||||
<title>Kerberos brokering</title>
|
||||
<para>
|
||||
Keycloak supports login with Kerberos ticket through SPNEGO. SPNEGO (Simple and Protected GSSAPI Negotiation Mechanism) is used
|
||||
to authenticate transparently through the web browser after the user has been authenticated when logging-in his session.
|
||||
For non-web cases or when ticket is not available during login, Keycloak also supports login with Kerberos username/password.
|
||||
</para>
|
||||
<para>
|
||||
A typical use case for web authentication is the following:
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
User logs into his desktop (Such as a Windows machine in Active Directory domain or Linux machine with Kerberos integration enabled).
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
User then uses his browser (IE/Firefox/Chrome) to access a web application secured by Keycloak.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Application redirects to Keycloak login.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Keycloak sends HTML login screen together with status 401 and HTTP header <literal>WWW-Authenticate: Negotiate</literal>
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
In case that browser has Kerberos ticket from desktop login, it transfers the desktop sign on information to the
|
||||
Keycloak in header <literal>Authorization: Negotiate 'spnego-token'</literal> . Otherwise it just displays login screen.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Keycloak validates token from browser and authenticate user. It provisions user data from LDAP (in case of
|
||||
LDAPFederationProvider with Kerberos authentication support) or let user to update his profile and prefill data
|
||||
(in case of KerberosFederationProvider).
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Keycloak returns back to the application. Communication between Keycloak and application happens through OpenID
|
||||
Connect or SAML messages. The fact that Keycloak was authenticated through Kerberos is hidden from the application.
|
||||
So Keycloak acts as broker to Kerberos/SPNEGO login.
|
||||
</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
</para>
|
||||
<para>
|
||||
For setup there are 3 main parts:
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
Setup and configuration of Kerberos server (KDC)
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Setup and configuration of Keycloak server
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Setup and configuration of client machines
|
||||
</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
</para>
|
||||
<section>
|
||||
<title>Setup of Kerberos server</title>
|
||||
<para>
|
||||
This is platform dependent. Exact steps depend on your OS and the Kerberos vendor you're going to use.
|
||||
Consult Windows Active Directory, MIT Kerberos and your OS documentation for how exactly to setup and configure Kerberos server.
|
||||
</para>
|
||||
<para>
|
||||
At least you will need to:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
Add some user principals to your Kerberos database. You can also integrate your Kerberos with LDAP,
|
||||
which means that user accounts will be provisioned from LDAP server.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Add service principal for "HTTP" service. For example if your Keycloak server will be running on
|
||||
<literal>www.mydomain.org</literal> you may need to add principal <literal>HTTP/www.mydomain.org@MYDOMAIN.ORG</literal>
|
||||
assuming that MYDOMAIN.ORG will be your Kerberos realm.
|
||||
</para>
|
||||
<para>
|
||||
For example on MIT Kerberos you can run "kadmin" session. If you are on same machine where is MIT Kerberos, you can simply use command:
|
||||
<programlisting><![CDATA[
|
||||
sudo kadmin.local
|
||||
]]></programlisting>
|
||||
Then add HTTP principal and export his key to keytab file with the commands like:
|
||||
<programlisting><![CDATA[
|
||||
addprinc -randkey HTTP/www.mydomain.org@MYDOMAIN.ORG
|
||||
ktadd -k /tmp/http.keytab HTTP/www.mydomain.org@MYDOMAIN.ORG
|
||||
]]></programlisting>
|
||||
Keytab file <literal>/tmp/http.keytab</literal> will need to be accessible on the host where keycloak server will be running.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Setup and configuration of Keycloak server</title>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
Install kerberos client. This is again platform dependent. If you are on Fedora, Ubuntu or RHEL, you can install package <literal>freeipa-client</literal>,
|
||||
which contains Kerberos client and bunch of other stuff.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Configure kerberos client (on linux it's in file <literal>/etc/krb5.conf</literal> ). You need to put your Kerberos realm and at least
|
||||
configure the Http domains your server will be running on. For the example realm MYDOMAIN.ORG you may configure <literal>domain_realm</literal> section like this:
|
||||
<programlisting><![CDATA[
|
||||
[domain_realm]
|
||||
.mydomain.org = MYDOMAIN.ORG
|
||||
mydomain.org = MYDOMAIN.ORG
|
||||
]]></programlisting>
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Export keytab file with HTTP principal and make sure the file is accessible to the process under which Keycloak
|
||||
server is running. For production, it's ideal if it's readable just by this process and not by someone else.
|
||||
For MIT Kerberos example above, we already exported keytab to <literal>/tmp/http.keytab</literal> . If your KDC and Keycloak
|
||||
are running on same host, you have file already available.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Finally run Keycloak server and configure SPNEGO/Kerberos authentication in Keycloak admin console. Keycloak supports Kerberos authentication
|
||||
through <link linkend='user_federation'>Federation provider SPI</link> . We have 2 federation providers with Kerberos authentication support:
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>Kerberos</term>
|
||||
<listitem>
|
||||
<para>
|
||||
This provider is useful if you want to authenticate with Kerberos <literal>NOT</literal> backed by LDAP server.
|
||||
In this case, users are usually created to Keycloak database after first successful SPNEGO/Kerberos login
|
||||
and they may need to update profile after first login, as Kerberos protocol itself doesn't provision
|
||||
any data like first name, last name or email.
|
||||
</para>
|
||||
<para>
|
||||
You can also choose if users can authenticate with classic username/password. In this case, if user doesn't have SPNEGO ticket available,
|
||||
Keycloak will display login screen and user can fill his Kerberos username and password on login screen. Username/password works also for non-web flows like
|
||||
<link linkend='direct-access-grants'>Direct Access grants</link>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>LDAP</term>
|
||||
<listitem>
|
||||
<para>
|
||||
This provider is useful if you want to authenticate with Kerberos backed by LDAP server.
|
||||
In this case, data about users are provisioned from LDAP server after successful Kerberos authentication.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
<section>
|
||||
<title>Setup and configuration of client machines</title>
|
||||
<para>
|
||||
Clients need to install kerberos client and setup krb5.conf as described above. Additionally they need to enable SPNEGO login support in their browser.
|
||||
See for example <ulink url="http://www.microhowto.info/howto/configure_firefox_to_authenticate_using_spnego_and_kerberos.html">this</ulink>
|
||||
for more info about Firefox. URI <literal>.mydomain.org</literal> must be allowed in <literal>network.negotiate-auth.trusted-uris</literal> config option.
|
||||
</para>
|
||||
<para>
|
||||
In windows domain, clients usually don't need to configure anything special as IE is already able to participate in SPNEGO authentication for the windows domain.
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Example setups</title>
|
||||
<para>
|
||||
For easier testing with Kerberos, we provided some example setups to test.
|
||||
</para>
|
||||
<section>
|
||||
<title>Keycloak and FreeIPA docker image</title>
|
||||
<para>
|
||||
Once you install <ulink url="https://www.docker.com/">docker</ulink>, you can run docker image with <ulink url="http://www.freeipa.org/">FreeIPA</ulink>
|
||||
server installed. FreeIPA provides integrated security solution with MIT Kerberos and 389 LDAP server among other things . The image provides
|
||||
also Keycloak server configured with LDAP Federation provider and enabled SPNEGO/Kerberos authentication against the FreeIPA server.
|
||||
See details <ulink url="https://github.com/mposolda/keycloak-freeipa-docker/blob/master/README.md">here</ulink> .
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>ApacheDS testing Kerberos server</title>
|
||||
<para>
|
||||
For quick testing and unit tests, we use very simple <ulink url="http://directory.apache.org/apacheds/">ApacheDS</ulink> Kerberos server.
|
||||
You need to build Keycloak from sources and then run Kerberos server with maven-exec-plugin from our testsuite. See details
|
||||
<ulink url="https://github.com/keycloak/keycloak/blob/master/testsuite/integration/README.md#kerberos-server">here</ulink> .
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
<section>
|
||||
<title>Troubleshooting</title>
|
||||
<para>
|
||||
If you have issues, we recommend to enable more logging by:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
Enable <literal>Debug</literal> flag in admin console for Kerberos or LDAP federation providers
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Enable TRACE logging for category <literal>org.keycloak</literal> in logging section of <literal>$WILDFLY_HOME/standalone/configuration/standalone.xml</literal>
|
||||
to receive more info <literal>$WILDFLY_HOME/standalone/log/server.log</literal>
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Add system properties <literal>-Dsun.security.krb5.debug=true</literal> and <literal>-Dsun.security.spnego.debug=true</literal>
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
|
@ -97,6 +97,14 @@
|
|||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Allow Kerberos authentication</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Enable Kerberos/SPNEGO authentication in realm with users data provisioned from LDAP. More info in <link linkend="kerberos">Kerberos section</link>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Other options</term>
|
||||
<listitem>
|
||||
|
|
|
@ -39,6 +39,9 @@
|
|||
"baseUrl": "/facebook-authentication",
|
||||
"redirectUris": [
|
||||
"/facebook-authentication/*"
|
||||
],
|
||||
"webOrigins": [
|
||||
"http://localhost:8080"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
|
|
@ -39,6 +39,9 @@
|
|||
"baseUrl": "/google-authentication",
|
||||
"redirectUris": [
|
||||
"/google-authentication/*"
|
||||
],
|
||||
"webOrigins": [
|
||||
"http://localhost:8080"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
|
|
@ -38,8 +38,7 @@
|
|||
"adminUrl": "/saml-broker-authentication",
|
||||
"baseUrl": "/saml-broker-authentication",
|
||||
"redirectUris": [
|
||||
"/saml-broker-authentication/*",
|
||||
"http://localhost:8080/saml-broker-authentication/*"
|
||||
"/saml-broker-authentication/*"
|
||||
],
|
||||
"webOrigins": [
|
||||
"http://localhost:8080"
|
||||
|
@ -55,7 +54,7 @@
|
|||
"updateProfileFirstLogin" : "true",
|
||||
"storeToken" : "true",
|
||||
"config": {
|
||||
"singleSignOnServiceUrl": "http://localhost:8081/auth/realms/saml-broker-realm/protocol/saml",
|
||||
"singleSignOnServiceUrl": "http://localhost:8080/auth/realms/saml-broker-realm/protocol/saml",
|
||||
"nameIDPolicyFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
|
||||
"signingCertificate": "MIIDdzCCAl+gAwIBAgIEbySuqTANBgkqhkiG9w0BAQsFADBsMRAwDgYDVQQGEwdVbmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYDVQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRAwDgYDVQQDEwdVbmtub3duMB4XDTE1MDEyODIyMTYyMFoXDTE3MTAyNDIyMTYyMFowbDEQMA4GA1UEBhMHVW5rbm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4GA1UEChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEAxMHVW5rbm93bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAII/K9NNvXi9IySl7+l2zY/kKrGTtuR4WdCI0xLW/Jn4dLY7v1/HOnV4CC4ecFOzhdNFPtJkmEhP/q62CpmOYOKApXk3tfmm2rwEz9bWprVxgFGKnbrWlz61Z/cjLAlhD3IUj2ZRBquYgSXQPsYfXo1JmSWF5pZ9uh1FVqu9f4wvRqY20ZhUN+39F+1iaBsoqsrbXypCn1HgZkW1/9D9GZug1c3vB4wg1TwZZWRNGtxwoEhdK6dPrNcZ+6PdanVilWrbQFbBjY4wz8/7IMBzssoQ7Usmo8F1Piv0FGfaVeJqBrcAvbiBMpk8pT+27u6p8VyIX6LhGvnxIwM07NByeSUCAwEAAaMhMB8wHQYDVR0OBBYEFFlcNuTYwI9W0tQ224K1gFJlMam0MA0GCSqGSIb3DQEBCwUAA4IBAQB5snl1KWOJALtAjLqD0mLPg1iElmZP82Lq1htLBt3XagwzU9CaeVeCQ7lTp+DXWzPa9nCLhsC3QyrV3/+oqNli8C6NpeqI8FqN2yQW/QMWN1m5jWDbmrWwtQzRUn/rh5KEb5m3zPB+tOC6e/2bV3QeQebxeW7lVMD0tSCviUg1MQf1l2gzuXQo60411YwqrXwk6GMkDOhFDQKDlMchO3oRbQkGbcP8UeiKAXjMeHfzbiBr+cWz8NYZEtxUEDYDjTpKrYCSMJBXpmgVJCZ00BswbksxJwaGqGMPpUKmCV671pf3m8nq3xyiHMDGuGwtbU+GE8kVx85menmp8+964nin",
|
||||
"wantAuthnRequestsSigned": true,
|
||||
|
|
|
@ -28,10 +28,10 @@
|
|||
},
|
||||
"applications": [
|
||||
{
|
||||
"name": "http://localhost:8081/auth/",
|
||||
"name": "http://localhost:8080/auth/",
|
||||
"enabled": true,
|
||||
"redirectUris": [
|
||||
"http://localhost:8081/auth/realms/saml-broker-authentication-realm/broker/saml-identity-provider"
|
||||
"http://localhost:8080/auth/realms/saml-broker-authentication-realm/broker/saml-identity-provider"
|
||||
],
|
||||
"attributes": {
|
||||
"saml.assertion.signature": "true",
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"realm" : "saml-broker-authentication-realm",
|
||||
"resource" : "saml-broker-authentication",
|
||||
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||
"auth-server-url": "http://localhost:8081/auth",
|
||||
"auth-server-url": "/auth",
|
||||
"ssl-required" : "external",
|
||||
"public-client" : true
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"realm" : "twitter-identity-provider-realm",
|
||||
"resource" : "twitter-authentication",
|
||||
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||
"auth-server-url": "/auth",
|
||||
"ssl-required" : "external",
|
||||
"public-client" : true
|
||||
}
|
|
@ -40,6 +40,9 @@
|
|||
"baseUrl": "/twitter-authentication",
|
||||
"redirectUris": [
|
||||
"/twitter-authentication/*"
|
||||
],
|
||||
"webOrigins": [
|
||||
"http://localhost:8080"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -171,7 +171,7 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
controller : 'RealmIdentityProviderCtrl'
|
||||
})
|
||||
.when('/realms/:realm/identity-provider-settings/provider/:provider_id/:id', {
|
||||
templateUrl : function(params){ return 'partials/realm-identity-provider-' + params.provider_id + '.html'; },
|
||||
templateUrl : function(params){ return resourceUrl + '/partials/realm-identity-provider-' + params.provider_id + '.html'; },
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
|
|
|
@ -49,75 +49,95 @@ module.controller('ApplicationCredentialsCtrl', function($scope, $location, real
|
|||
});
|
||||
});
|
||||
|
||||
module.controller('ApplicationIdentityProviderCtrl', function($scope, $location, realm, application, Application, $location, Notifications) {
|
||||
module.controller('ApplicationIdentityProviderCtrl', function($scope, $location, $route, realm, application, Application, $location, Notifications) {
|
||||
$scope.realm = realm;
|
||||
$scope.application = angular.copy(application);
|
||||
var length = 0;
|
||||
|
||||
if ($scope.application.identityProviders) {
|
||||
length = $scope.application.identityProviders.length;
|
||||
} else {
|
||||
$scope.application.identityProviders = new Array(realm.identityProviders.length);
|
||||
}
|
||||
|
||||
for (j = length; j < realm.identityProviders.length; j++) {
|
||||
$scope.application.identityProviders[j] = {};
|
||||
}
|
||||
|
||||
$scope.identityProviders = [];
|
||||
|
||||
if (!$scope.application.allowedIdentityProviders) {
|
||||
$scope.application.allowedIdentityProviders = [];
|
||||
}
|
||||
|
||||
for (j = 0; j < realm.identityProviders.length; j++) {
|
||||
var identityProvider = realm.identityProviders[j];
|
||||
var match = false;
|
||||
var applicationProvider;
|
||||
|
||||
for (i = 0; i < $scope.application.allowedIdentityProviders.length; i++) {
|
||||
var appProvider = $scope.application.allowedIdentityProviders[i];
|
||||
for (i = 0; i < $scope.application.identityProviders.length; i++) {
|
||||
applicationProvider = $scope.application.identityProviders[i];
|
||||
|
||||
if (appProvider == identityProvider.id) {
|
||||
$scope.identityProviders[i] = identityProvider;
|
||||
match = true;
|
||||
if (applicationProvider) {
|
||||
if (applicationProvider.retrieveToken) {
|
||||
applicationProvider.retrieveToken = applicationProvider.retrieveToken.toString();
|
||||
} else {
|
||||
applicationProvider.retrieveToken = false.toString();
|
||||
}
|
||||
|
||||
if (applicationProvider.id == identityProvider.id) {
|
||||
$scope.identityProviders[i] = {};
|
||||
$scope.identityProviders[i].identityProvider = identityProvider;
|
||||
$scope.identityProviders[i].retrieveToken = applicationProvider.retrieveToken.toString();
|
||||
break;
|
||||
}
|
||||
|
||||
applicationProvider = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
var length = $scope.identityProviders.length;
|
||||
if (applicationProvider == null) {
|
||||
var length = $scope.identityProviders.length + $scope.application.identityProviders.length;
|
||||
|
||||
length = length + $scope.application.allowedIdentityProviders.length;
|
||||
|
||||
$scope.identityProviders[length] = identityProvider;
|
||||
$scope.identityProviders[length] = {};
|
||||
$scope.identityProviders[length].identityProvider = identityProvider;
|
||||
$scope.identityProviders[length].retrieveToken = false.toString();
|
||||
}
|
||||
}
|
||||
|
||||
$scope.identityProviders = $scope.identityProviders.filter(function(n){ return n != undefined });
|
||||
|
||||
var oldCopy = angular.copy($scope.application);
|
||||
|
||||
$scope.save = function() {
|
||||
var selectedProviders = [];
|
||||
|
||||
for (i = 0; i < $scope.application.allowedIdentityProviders.length; i++) {
|
||||
var appProvider = $scope.application.allowedIdentityProviders[i];
|
||||
for (i = 0; i < $scope.application.identityProviders.length; i++) {
|
||||
var appProvider = $scope.application.identityProviders[i];
|
||||
|
||||
if (appProvider) {
|
||||
if (appProvider.id != null && appProvider.id != false) {
|
||||
selectedProviders[selectedProviders.length] = appProvider;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.allowedIdentityProviders = $scope.application.allowedIdentityProviders;
|
||||
$scope.application.allowedIdentityProviders = selectedProviders;
|
||||
$scope.application.identityProviders = selectedProviders;
|
||||
|
||||
Application.update({
|
||||
realm : realm.realm,
|
||||
application : application.id
|
||||
}, $scope.application, function() {
|
||||
$scope.changed = false;
|
||||
$scope.application.allowedIdentityProviders = $scope.allowedIdentityProviders;
|
||||
$location.url("/realms/" + realm.realm + "/applications/" + application.id + "/identity-provider");
|
||||
$route.reload();
|
||||
Notifications.success("Your changes have been saved to the application.");
|
||||
});
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.application = angular.copy(application);
|
||||
$scope.application = angular.copy(oldCopy);
|
||||
$scope.changed = false;
|
||||
};
|
||||
|
||||
$scope.$watch(function() {
|
||||
return $location.path();
|
||||
}, function() {
|
||||
$scope.path = $location.path().substring(1).split("/");
|
||||
});
|
||||
$scope.$watch('application', function() {
|
||||
if (!angular.equals($scope.application, oldCopy)) {
|
||||
$scope.changed = true;
|
||||
}
|
||||
}, true);
|
||||
});
|
||||
|
||||
module.controller('ApplicationSamlKeyCtrl', function($scope, $location, $http, $upload, realm, application,
|
||||
|
|
|
@ -324,74 +324,94 @@ module.controller('OAuthClientRevocationCtrl', function($scope, realm, oauth, OA
|
|||
}
|
||||
});
|
||||
|
||||
module.controller('OAuthClientIdentityProviderCtrl', function($scope, realm, oauth, OAuthClient, $location, Notifications) {
|
||||
module.controller('OAuthClientIdentityProviderCtrl', function($scope, $route, realm, oauth, OAuthClient, $location, Notifications) {
|
||||
$scope.realm = realm;
|
||||
$scope.oauth = angular.copy(oauth);
|
||||
var length = 0;
|
||||
|
||||
if ($scope.oauth.identityProviders) {
|
||||
length = $scope.oauth.identityProviders.length;
|
||||
} else {
|
||||
$scope.oauth.identityProviders = new Array(realm.identityProviders.length);
|
||||
}
|
||||
|
||||
for (j = length; j < realm.identityProviders.length; j++) {
|
||||
$scope.oauth.identityProviders[j] = {};
|
||||
}
|
||||
|
||||
$scope.identityProviders = [];
|
||||
|
||||
if (!$scope.oauth.allowedIdentityProviders) {
|
||||
$scope.oauth.allowedIdentityProviders = [];
|
||||
}
|
||||
|
||||
for (j = 0; j < realm.identityProviders.length; j++) {
|
||||
var identityProvider = realm.identityProviders[j];
|
||||
var match = false;
|
||||
var applicationProvider;
|
||||
|
||||
for (i = 0; i < $scope.oauth.allowedIdentityProviders.length; i++) {
|
||||
var appProvider = $scope.oauth.allowedIdentityProviders[i];
|
||||
for (i = 0; i < $scope.oauth.identityProviders.length; i++) {
|
||||
applicationProvider = $scope.oauth.identityProviders[i];
|
||||
|
||||
if (appProvider == identityProvider.id) {
|
||||
$scope.identityProviders[i] = identityProvider;
|
||||
match = true;
|
||||
if (applicationProvider) {
|
||||
if (applicationProvider.retrieveToken) {
|
||||
applicationProvider.retrieveToken = applicationProvider.retrieveToken.toString();
|
||||
} else {
|
||||
applicationProvider.retrieveToken = false.toString();
|
||||
}
|
||||
|
||||
if (applicationProvider.id == identityProvider.id) {
|
||||
$scope.identityProviders[i] = {};
|
||||
$scope.identityProviders[i].identityProvider = identityProvider;
|
||||
$scope.identityProviders[i].retrieveToken = applicationProvider.retrieveToken.toString();
|
||||
break;
|
||||
}
|
||||
|
||||
applicationProvider = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
var length = $scope.identityProviders.length;
|
||||
if (applicationProvider == null) {
|
||||
var length = $scope.identityProviders.length + $scope.oauth.identityProviders.length;
|
||||
|
||||
length = length + $scope.oauth.allowedIdentityProviders.length;
|
||||
|
||||
$scope.identityProviders[length] = identityProvider;
|
||||
$scope.identityProviders[length] = {};
|
||||
$scope.identityProviders[length].identityProvider = identityProvider;
|
||||
$scope.identityProviders[length].retrieveToken = false.toString();
|
||||
}
|
||||
}
|
||||
|
||||
$scope.identityProviders = $scope.identityProviders.filter(function(n){ return n != undefined });
|
||||
|
||||
var oldCopy = angular.copy($scope.oauth);
|
||||
|
||||
$scope.save = function() {
|
||||
var selectedProviders = [];
|
||||
|
||||
for (i = 0; i < $scope.oauth.allowedIdentityProviders.length; i++) {
|
||||
var appProvider = $scope.oauth.allowedIdentityProviders[i];
|
||||
for (i = 0; i < $scope.oauth.identityProviders.length; i++) {
|
||||
var appProvider = $scope.oauth.identityProviders[i];
|
||||
|
||||
if (appProvider) {
|
||||
if (appProvider.id != null && appProvider.id != false) {
|
||||
selectedProviders[selectedProviders.length] = appProvider;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.allowedIdentityProviders = $scope.oauth.allowedIdentityProviders;
|
||||
$scope.oauth.allowedIdentityProviders = selectedProviders;
|
||||
$scope.oauth.identityProviders = selectedProviders;
|
||||
|
||||
OAuthClient.update({
|
||||
realm : realm.realm,
|
||||
oauth : oauth.id
|
||||
}, $scope.oauth, function() {
|
||||
$scope.changed = false;
|
||||
$scope.oauth.allowedIdentityProviders = $scope.allowedIdentityProviders;
|
||||
$location.url("/realms/" + realm.realm + "/oauth-clients/" + oauth.id + "/identity-provider");
|
||||
$route.reload();
|
||||
Notifications.success("Your changes have been saved to the application.");
|
||||
});
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.oauth = angular.copy(oauth);
|
||||
$scope.oauth = angular.copy(oldCopy);
|
||||
$scope.changed = false;
|
||||
};
|
||||
|
||||
$scope.$watch(function() {
|
||||
return $location.path();
|
||||
}, function() {
|
||||
$scope.path = $location.path().substring(1).split("/");
|
||||
});
|
||||
$scope.$watch('oauth', function() {
|
||||
if (!angular.equals($scope.oauth, oldCopy)) {
|
||||
$scope.changed = true;
|
||||
}
|
||||
}, true);
|
||||
});
|
||||
|
||||
|
|
|
@ -7,17 +7,24 @@
|
|||
<li><a href="#/realms/{{realm.realm}}/applications/{{application.id}}">{{application.name}}</a></li>
|
||||
<li class="active">Identity Provider</li>
|
||||
</ol>
|
||||
<h2 data-ng-hide="create"><span>{{application.name}}</span> Identity Provider Settings<span tooltip-placement="right" tooltip="Select which identity providers can be used for this application. By default, all identity providers are enabled. Select one or more to restrict." class="fa fa-info-circle"></span></h2>
|
||||
<h2 data-ng-hide="create"><span>{{application.name}}</span> Identity Provider Settings</h2>
|
||||
<form class="form-horizontal" name="identityProviderForm" novalidate>
|
||||
<div class="form-group" ng-repeat="identityProvider in identityProviders">
|
||||
<legend><span class="text">{{identityProvider.name}}</span></legend>
|
||||
<label class="col-sm-2 control-label" for="{{identityProvider.id}}">Enable</label>
|
||||
<legend><span class="text">{{identityProvider.identityProvider.name}}</span></legend>
|
||||
<label class="col-sm-2 control-label" for="{{identityProvider.identityProvider.id}}">Enable <span tooltip-placement="right" tooltip="If disabled, users can not login to the application using this identity provider." class="fa fa-info-circle"></span></label>
|
||||
<div class="col-sm-4">
|
||||
<input ng-model="application.allowedIdentityProviders[$index]" name="identityProvider.id" id="identityProvider.id" value="identityProvider.id" onoffswitchmodel />
|
||||
<input ng-model="application.identityProviders[$index].id" name="identityProvider.identityProvider.id" id="identityProvider.identityProvider.id" value="identityProvider.identityProvider.id" onoffswitchmodel />
|
||||
</div>
|
||||
<div data-ng-show="application.identityProviders[$index].id">
|
||||
<label class="col-sm-2 control-label" for="{{identityProvider.identityProvider.id}}retrieveToken">Can Retrieve Token <span tooltip-placement="right" tooltip="If disabled, the application can not retrieve tokens from the identity provider." class="fa fa-info-circle"></span></label>
|
||||
<div class="col-sm-4">
|
||||
<input ng-model="application.identityProviders[$index].retrieveToken" name="identityProvider.identityProvider.id + 'retrieveToken'" id="identityProvider.identityProvider.id + 'retrieveToken'" value="true" onoffswitchmodel />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-right form-actions">
|
||||
<button kc-save>Save</button>
|
||||
<button kc-reset data-ng-show="changed">Clear changes</button>
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -10,14 +10,21 @@
|
|||
<h2 data-ng-hide="create"><span>{{oauth.name}}</span> Identity Provider Settings</h2>
|
||||
<form class="form-horizontal" name="identityProviderForm" novalidate>
|
||||
<div class="form-group" ng-repeat="identityProvider in identityProviders">
|
||||
<legend><span class="text">{{identityProvider.name}}</span></legend>
|
||||
<label class="col-sm-2 control-label" for="{{identityProvider.id}}">Enable</label>
|
||||
<legend><span class="text">{{identityProvider.identityProvider.name}}</span></legend>
|
||||
<label class="col-sm-2 control-label" for="{{identityProvider.identityProvider.id}}">Enable <span tooltip-placement="right" tooltip="If disabled, users can not login to the application using this identity provider." class="fa fa-info-circle"></span></label>
|
||||
<div class="col-sm-4">
|
||||
<input ng-model="oauth.allowedIdentityProviders[$index]" name="identityProvider.id" id="identityProvider.id" value="identityProvider.id" kc-onoffswitch-model />
|
||||
<input ng-model="oauth.identityProviders[$index].id" name="identityProvider.identityProvider.id" id="identityProvider.identityProvider.id" value="identityProvider.identityProvider.id" onoffswitchmodel />
|
||||
</div>
|
||||
<div data-ng-show="oauth.identityProviders[$index].id">
|
||||
<label class="col-sm-2 control-label" for="{{identityProvider.identityProvider.id}}retrieveToken">Can Retrieve Token <span tooltip-placement="right" tooltip="If disabled, the application can not retrieve tokens from the identity provider." class="fa fa-info-circle"></span></label>
|
||||
<div class="col-sm-4">
|
||||
<input ng-model="oauth.identityProviders[$index].retrieveToken" name="identityProvider.identityProvider.id + 'retrieveToken'" id="identityProvider.identityProvider.id + 'retrieveToken'" value="true" onoffswitchmodel />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-right form-actions">
|
||||
<button kc-save>Save</button>
|
||||
<button kc-reset data-ng-show="changed">Clear changes</button>
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source
|
||||
*
|
||||
* Copyright 2013 Red Hat, Inc. and/or its affiliates.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package org.keycloak.models;
|
||||
|
||||
/**
|
||||
* @author pedroigor
|
||||
*/
|
||||
public class ClientIdentityProviderMappingModel {
|
||||
|
||||
private String identityProvider;
|
||||
private boolean retrieveToken;
|
||||
|
||||
public String getIdentityProvider() {
|
||||
return this.identityProvider;
|
||||
}
|
||||
|
||||
public void setIdentityProvider(String identityProviderModel) {
|
||||
this.identityProvider = identityProviderModel;
|
||||
}
|
||||
|
||||
public boolean isRetrieveToken() {
|
||||
return this.retrieveToken;
|
||||
}
|
||||
|
||||
public void setRetrieveToken(boolean retrieveToken) {
|
||||
this.retrieveToken = retrieveToken;
|
||||
}
|
||||
}
|
|
@ -98,11 +98,10 @@ public interface ClientModel {
|
|||
|
||||
void setNotBefore(int notBefore);
|
||||
|
||||
void updateAllowedIdentityProviders(List<String> identityProviders);
|
||||
|
||||
List<String> getAllowedIdentityProviders();
|
||||
|
||||
void updateAllowedIdentityProviders(List<ClientIdentityProviderMappingModel> identityProviders);
|
||||
List<ClientIdentityProviderMappingModel> getIdentityProviders();
|
||||
boolean hasIdentityProvider(String providerId);
|
||||
boolean isAllowedRetrieveTokenFromIdentityProvider(String providerId);
|
||||
|
||||
Set<ProtocolMapperModel> getProtocolMappers();
|
||||
void addProtocolMappers(Set<String> mapperIds);
|
||||
|
|
|
@ -29,7 +29,7 @@ public class ClientEntity extends AbstractIdentifiableEntity {
|
|||
private List<String> webOrigins = new ArrayList<String>();
|
||||
private List<String> redirectUris = new ArrayList<String>();
|
||||
private List<String> scopeIds = new ArrayList<String>();
|
||||
private List<String> allowedIdentityProviders = new ArrayList<String>();
|
||||
private List<ClientIdentityProviderMappingEntity> identityProviders = new ArrayList<ClientIdentityProviderMappingEntity>();
|
||||
private Set<String> protocolMappers = new HashSet<String>();
|
||||
|
||||
public String getName() {
|
||||
|
@ -144,12 +144,12 @@ public class ClientEntity extends AbstractIdentifiableEntity {
|
|||
this.frontchannelLogout = frontchannelLogout;
|
||||
}
|
||||
|
||||
public List<String> getAllowedIdentityProviders() {
|
||||
return this.allowedIdentityProviders;
|
||||
public List<ClientIdentityProviderMappingEntity> getIdentityProviders() {
|
||||
return this.identityProviders;
|
||||
}
|
||||
|
||||
public void setAllowedIdentityProviders(List<String> allowedIdentityProviders) {
|
||||
this.allowedIdentityProviders = allowedIdentityProviders;
|
||||
public void setIdentityProviders(List<ClientIdentityProviderMappingEntity> identityProviders) {
|
||||
this.identityProviders = identityProviders;
|
||||
}
|
||||
|
||||
public Set<String> getProtocolMappers() {
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source
|
||||
*
|
||||
* Copyright 2013 Red Hat, Inc. and/or its affiliates.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package org.keycloak.models.entities;
|
||||
|
||||
/**
|
||||
* @author pedroigor
|
||||
*/
|
||||
public class ClientIdentityProviderMappingEntity {
|
||||
|
||||
private String id;
|
||||
private Boolean retrieveToken;
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Boolean isRetrieveToken() {
|
||||
return this.retrieveToken;
|
||||
}
|
||||
|
||||
public void setRetrieveToken(Boolean retrieveToken) {
|
||||
this.retrieveToken = retrieveToken;
|
||||
}
|
||||
|
||||
}
|
|
@ -3,6 +3,7 @@ package org.keycloak.models.utils;
|
|||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.ClaimMask;
|
||||
import org.keycloak.models.ClaimTypeModel;
|
||||
import org.keycloak.models.ClientIdentityProviderMappingModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
|
@ -19,6 +20,7 @@ import org.keycloak.models.UserSessionModel;
|
|||
import org.keycloak.representations.idm.ApplicationRepresentation;
|
||||
import org.keycloak.representations.idm.ClaimRepresentation;
|
||||
import org.keycloak.representations.idm.ClaimTypeRepresentation;
|
||||
import org.keycloak.representations.idm.ClientIdentityProviderMappingRepresentation;
|
||||
import org.keycloak.representations.idm.ClientProtocolMappingRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
||||
|
@ -262,8 +264,8 @@ public class ModelToRepresentation {
|
|||
rep.setRegisteredNodes(new HashMap<String, Integer>(applicationModel.getRegisteredNodes()));
|
||||
}
|
||||
|
||||
if (!applicationModel.getAllowedIdentityProviders().isEmpty()) {
|
||||
rep.setAllowedIdentityProviders(applicationModel.getAllowedIdentityProviders());
|
||||
if (!applicationModel.getIdentityProviders().isEmpty()) {
|
||||
rep.setIdentityProviders(toRepresentation(applicationModel.getIdentityProviders()));
|
||||
}
|
||||
|
||||
if (!applicationModel.getProtocolMappers().isEmpty()) {
|
||||
|
@ -279,6 +281,21 @@ public class ModelToRepresentation {
|
|||
return rep;
|
||||
}
|
||||
|
||||
private static List<ClientIdentityProviderMappingRepresentation> toRepresentation(List<ClientIdentityProviderMappingModel> identityProviders) {
|
||||
ArrayList<ClientIdentityProviderMappingRepresentation> representations = new ArrayList<ClientIdentityProviderMappingRepresentation>();
|
||||
|
||||
for (ClientIdentityProviderMappingModel model : identityProviders) {
|
||||
ClientIdentityProviderMappingRepresentation representation = new ClientIdentityProviderMappingRepresentation();
|
||||
|
||||
representation.setId(model.getIdentityProvider());
|
||||
representation.setRetrieveToken(model.isRetrieveToken());
|
||||
|
||||
representations.add(representation);
|
||||
}
|
||||
|
||||
return representations;
|
||||
}
|
||||
|
||||
public static OAuthClientRepresentation toRepresentation(OAuthClientModel model) {
|
||||
OAuthClientRepresentation rep = new OAuthClientRepresentation();
|
||||
rep.setId(model.getId());
|
||||
|
@ -301,8 +318,8 @@ public class ModelToRepresentation {
|
|||
}
|
||||
rep.setNotBefore(model.getNotBefore());
|
||||
|
||||
if (!model.getAllowedIdentityProviders().isEmpty()) {
|
||||
rep.setAllowedIdentityProviders(model.getAllowedIdentityProviders());
|
||||
if (!model.getIdentityProviders().isEmpty()) {
|
||||
rep.setIdentityProviders(toRepresentation(model.getIdentityProviders()));
|
||||
}
|
||||
|
||||
if (!model.getProtocolMappers().isEmpty()) {
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.keycloak.models.ApplicationModel;
|
|||
import org.keycloak.models.BrowserSecurityHeaders;
|
||||
import org.keycloak.models.ClaimMask;
|
||||
import org.keycloak.models.ClaimTypeModel;
|
||||
import org.keycloak.models.ClientIdentityProviderMappingModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
|
@ -23,6 +24,7 @@ import org.keycloak.models.UserModel;
|
|||
import org.keycloak.representations.idm.ApplicationRepresentation;
|
||||
import org.keycloak.representations.idm.ClaimRepresentation;
|
||||
import org.keycloak.representations.idm.ClaimTypeRepresentation;
|
||||
import org.keycloak.representations.idm.ClientIdentityProviderMappingRepresentation;
|
||||
import org.keycloak.representations.idm.ClientProtocolMappingRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
||||
|
@ -473,17 +475,7 @@ public class RepresentationToModel {
|
|||
applicationModel.setProtocolMappers(ids);
|
||||
}
|
||||
|
||||
List<String> allowedIdentityProviders = resourceRep.getAllowedIdentityProviders();
|
||||
|
||||
if (allowedIdentityProviders == null || allowedIdentityProviders.isEmpty()) {
|
||||
allowedIdentityProviders = new ArrayList<String>();
|
||||
|
||||
for (IdentityProviderModel identityProvider : realm.getIdentityProviders()) {
|
||||
allowedIdentityProviders.add(identityProvider.getId());
|
||||
}
|
||||
}
|
||||
|
||||
applicationModel.updateAllowedIdentityProviders(allowedIdentityProviders);
|
||||
applicationModel.updateAllowedIdentityProviders(toModel(resourceRep.getIdentityProviders(), realm));
|
||||
|
||||
return applicationModel;
|
||||
}
|
||||
|
@ -536,9 +528,7 @@ public class RepresentationToModel {
|
|||
setClaims(resource, rep.getClaims());
|
||||
}
|
||||
|
||||
if (rep.getAllowedIdentityProviders() != null) {
|
||||
resource.updateAllowedIdentityProviders(rep.getAllowedIdentityProviders());
|
||||
}
|
||||
updateClientIdentityProvides(rep.getIdentityProviders(), resource);
|
||||
}
|
||||
|
||||
public static void setClaims(ClientModel model, ClaimRepresentation rep) {
|
||||
|
@ -613,17 +603,7 @@ public class RepresentationToModel {
|
|||
public static OAuthClientModel createOAuthClient(OAuthClientRepresentation rep, RealmModel realm) {
|
||||
OAuthClientModel model = createOAuthClient(rep.getId(), rep.getName(), realm);
|
||||
|
||||
List<String> allowedIdentityProviders = rep.getAllowedIdentityProviders();
|
||||
|
||||
if (allowedIdentityProviders == null || allowedIdentityProviders.isEmpty()) {
|
||||
allowedIdentityProviders = new ArrayList<String>();
|
||||
|
||||
for (IdentityProviderModel identityProvider : realm.getIdentityProviders()) {
|
||||
allowedIdentityProviders.add(identityProvider.getId());
|
||||
}
|
||||
}
|
||||
|
||||
model.updateAllowedIdentityProviders(allowedIdentityProviders);
|
||||
model.updateAllowedIdentityProviders(toModel(rep.getIdentityProviders(), realm));
|
||||
|
||||
updateOAuthClient(rep, model);
|
||||
return model;
|
||||
|
@ -667,9 +647,7 @@ public class RepresentationToModel {
|
|||
}
|
||||
}
|
||||
|
||||
if (rep.getAllowedIdentityProviders() != null) {
|
||||
model.updateAllowedIdentityProviders(rep.getAllowedIdentityProviders());
|
||||
}
|
||||
updateClientIdentityProvides(rep.getIdentityProviders(), model);
|
||||
|
||||
if (rep.getProtocolMappers() != null) {
|
||||
Set<String> ids = new HashSet<String>();
|
||||
|
@ -868,4 +846,48 @@ public class RepresentationToModel {
|
|||
model.setConfig(rep.getConfig());
|
||||
return model;
|
||||
}
|
||||
|
||||
private static List<ClientIdentityProviderMappingModel> toModel(List<ClientIdentityProviderMappingRepresentation> repIdentityProviders, RealmModel realm) {
|
||||
List<ClientIdentityProviderMappingModel> allowedIdentityProviders = new ArrayList<ClientIdentityProviderMappingModel>();
|
||||
|
||||
if (repIdentityProviders == null || repIdentityProviders.isEmpty()) {
|
||||
allowedIdentityProviders = new ArrayList<ClientIdentityProviderMappingModel>();
|
||||
|
||||
for (IdentityProviderModel identityProvider : realm.getIdentityProviders()) {
|
||||
ClientIdentityProviderMappingModel identityProviderMapping = new ClientIdentityProviderMappingModel();
|
||||
|
||||
identityProviderMapping.setIdentityProvider(identityProvider.getId());
|
||||
|
||||
allowedIdentityProviders.add(identityProviderMapping);
|
||||
}
|
||||
} else {
|
||||
for (ClientIdentityProviderMappingRepresentation rep : repIdentityProviders) {
|
||||
ClientIdentityProviderMappingModel identityProviderMapping = new ClientIdentityProviderMappingModel();
|
||||
|
||||
identityProviderMapping.setIdentityProvider(rep.getId());
|
||||
identityProviderMapping.setRetrieveToken(rep.isRetrieveToken());
|
||||
|
||||
allowedIdentityProviders.add(identityProviderMapping);
|
||||
}
|
||||
}
|
||||
|
||||
return allowedIdentityProviders;
|
||||
}
|
||||
|
||||
private static void updateClientIdentityProvides(List<ClientIdentityProviderMappingRepresentation> identityProviders, ClientModel resource) {
|
||||
if (identityProviders != null) {
|
||||
List<ClientIdentityProviderMappingModel> allowedIdentityProviders = new ArrayList<ClientIdentityProviderMappingModel>();
|
||||
|
||||
for (ClientIdentityProviderMappingRepresentation mappingRepresentation : identityProviders) {
|
||||
ClientIdentityProviderMappingModel identityProviderMapping = new ClientIdentityProviderMappingModel();
|
||||
|
||||
identityProviderMapping.setIdentityProvider(mappingRepresentation.getId());
|
||||
identityProviderMapping.setRetrieveToken(mappingRepresentation.isRetrieveToken());
|
||||
|
||||
allowedIdentityProviders.add(identityProviderMapping);
|
||||
}
|
||||
|
||||
resource.updateAllowedIdentityProviders(allowedIdentityProviders);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.keycloak.models.cache;
|
||||
|
||||
import org.keycloak.models.ClientIdentityProviderMappingModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
@ -263,15 +264,15 @@ public abstract class ClientAdapter implements ClientModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateAllowedIdentityProviders(List<String> identityProviders) {
|
||||
public void updateAllowedIdentityProviders(List<ClientIdentityProviderMappingModel> identityProviders) {
|
||||
getDelegateForUpdate();
|
||||
updatedClient.updateAllowedIdentityProviders(identityProviders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllowedIdentityProviders() {
|
||||
if (updatedClient != null) return updatedClient.getAllowedIdentityProviders();
|
||||
return cachedClient.getAllowedIdentityProviders();
|
||||
public List<ClientIdentityProviderMappingModel> getIdentityProviders() {
|
||||
if (updatedClient != null) return updatedClient.getIdentityProviders();
|
||||
return cachedClient.getIdentityProviders();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -280,6 +281,12 @@ public abstract class ClientAdapter implements ClientModel {
|
|||
return cachedClient.hasIdentityProvider(providerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowedRetrieveTokenFromIdentityProvider(String providerId) {
|
||||
if (updatedClient != null) return updatedClient.isAllowedRetrieveTokenFromIdentityProvider(providerId);
|
||||
return cachedClient.isAllowedRetrieveTokenFromIdentityProvider(providerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ProtocolMapperModel> getProtocolMappers() {
|
||||
if (updatedClient != null) return updatedClient.getProtocolMappers();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.keycloak.models.cache.entities;
|
||||
|
||||
import org.keycloak.models.ClientIdentityProviderMappingModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
@ -35,7 +36,7 @@ public class CachedClient {
|
|||
protected int notBefore;
|
||||
protected Set<String> scope = new HashSet<String>();
|
||||
protected Set<String> webOrigins = new HashSet<String>();
|
||||
private List<String> allowedIdentityProviders = new ArrayList<String>();
|
||||
private List<ClientIdentityProviderMappingModel> identityProviders = new ArrayList<ClientIdentityProviderMappingModel>();
|
||||
private Set<ProtocolMapperModel> protocolClaimMappings = new HashSet<ProtocolMapperModel>();
|
||||
|
||||
public CachedClient(RealmCache cache, RealmProvider delegate, RealmModel realm, ClientModel model) {
|
||||
|
@ -57,7 +58,7 @@ public class CachedClient {
|
|||
for (RoleModel role : model.getScopeMappings()) {
|
||||
scope.add(role.getId());
|
||||
}
|
||||
this.allowedIdentityProviders = model.getAllowedIdentityProviders();
|
||||
this.identityProviders = model.getIdentityProviders();
|
||||
protocolClaimMappings.addAll(model.getProtocolMappers());
|
||||
}
|
||||
|
||||
|
@ -125,15 +126,31 @@ public class CachedClient {
|
|||
return frontchannelLogout;
|
||||
}
|
||||
|
||||
public List<String> getAllowedIdentityProviders() {
|
||||
return this.allowedIdentityProviders;
|
||||
public List<ClientIdentityProviderMappingModel> getIdentityProviders() {
|
||||
return this.identityProviders;
|
||||
}
|
||||
|
||||
public boolean hasIdentityProvider(String providerId) {
|
||||
return this.allowedIdentityProviders.contains(providerId);
|
||||
for (ClientIdentityProviderMappingModel model : getIdentityProviders()) {
|
||||
if (model.getIdentityProvider().equals(providerId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Set<ProtocolMapperModel> getProtocolClaimMappings() {
|
||||
return protocolClaimMappings;
|
||||
}
|
||||
|
||||
public boolean isAllowedRetrieveTokenFromIdentityProvider(String providerId) {
|
||||
for (ClientIdentityProviderMappingModel model : getIdentityProviders()) {
|
||||
if (model.getIdentityProvider().equals(providerId)) {
|
||||
return model.isRetrieveToken();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
package org.keycloak.models.jpa;
|
||||
|
||||
import org.keycloak.models.ClientIdentityProviderMappingModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.jpa.entities.ClientEntity;
|
||||
import org.keycloak.models.jpa.entities.ClientIdentityProviderMappingEntity;
|
||||
import org.keycloak.models.jpa.entities.IdentityProviderEntity;
|
||||
import org.keycloak.models.jpa.entities.ProtocolMapperEntity;
|
||||
import org.keycloak.models.jpa.entities.RealmEntity;
|
||||
import org.keycloak.models.jpa.entities.RoleEntity;
|
||||
import org.keycloak.models.jpa.entities.ScopeMappingEntity;
|
||||
|
||||
|
@ -303,47 +304,96 @@ public abstract class ClientAdapter implements ClientModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateAllowedIdentityProviders(List<String> identityProviders) {
|
||||
Collection<IdentityProviderEntity> entities = entity.getAllowedIdentityProviders();
|
||||
public void updateAllowedIdentityProviders(List<ClientIdentityProviderMappingModel> identityProviders) {
|
||||
Collection<ClientIdentityProviderMappingEntity> entities = entity.getIdentityProviders();
|
||||
Set<String> already = new HashSet<String>();
|
||||
List<IdentityProviderEntity> remove = new ArrayList<IdentityProviderEntity>();
|
||||
for (IdentityProviderEntity rel : entities) {
|
||||
if (!contains(rel.getId(), identityProviders.toArray(new String[identityProviders.size()]))) {
|
||||
remove.add(rel);
|
||||
List<ClientIdentityProviderMappingEntity> remove = new ArrayList<ClientIdentityProviderMappingEntity>();
|
||||
|
||||
for (ClientIdentityProviderMappingEntity entity : entities) {
|
||||
IdentityProviderEntity identityProvider = entity.getIdentityProvider();
|
||||
boolean toRemove = true;
|
||||
|
||||
for (ClientIdentityProviderMappingModel model : identityProviders) {
|
||||
if (model.getIdentityProvider().equals(identityProvider.getId())) {
|
||||
toRemove = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (toRemove) {
|
||||
remove.add(entity);
|
||||
} else {
|
||||
already.add(rel.getId());
|
||||
already.add(entity.getIdentityProvider().getId());
|
||||
}
|
||||
}
|
||||
for (IdentityProviderEntity entity : remove) {
|
||||
for (ClientIdentityProviderMappingEntity entity : remove) {
|
||||
entities.remove(entity);
|
||||
em.remove(entity);
|
||||
}
|
||||
em.flush();
|
||||
for (String providerId : identityProviders) {
|
||||
if (!already.contains(providerId)) {
|
||||
TypedQuery<IdentityProviderEntity> query = em.createNamedQuery("findIdentityProviderById", IdentityProviderEntity.class).setParameter("id", providerId);
|
||||
IdentityProviderEntity providerEntity = query.getSingleResult();
|
||||
entities.add(providerEntity);
|
||||
for (ClientIdentityProviderMappingModel model : identityProviders) {
|
||||
ClientIdentityProviderMappingEntity mappingEntity = null;
|
||||
|
||||
if (!already.contains(model.getIdentityProvider())) {
|
||||
mappingEntity = new ClientIdentityProviderMappingEntity();
|
||||
entities.add(mappingEntity);
|
||||
} else {
|
||||
for (ClientIdentityProviderMappingEntity entity : entities) {
|
||||
if (entity.getIdentityProvider().getId().equals(model.getIdentityProvider())) {
|
||||
mappingEntity = entity;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TypedQuery<IdentityProviderEntity> query = em.createNamedQuery("findIdentityProviderById", IdentityProviderEntity.class).setParameter("id", model.getIdentityProvider());
|
||||
IdentityProviderEntity identityProviderEntity = query.getSingleResult();
|
||||
|
||||
mappingEntity.setIdentityProvider(identityProviderEntity);
|
||||
mappingEntity.setClient(this.entity);
|
||||
mappingEntity.setRetrieveToken(model.isRetrieveToken());
|
||||
|
||||
em.persist(mappingEntity);
|
||||
}
|
||||
em.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllowedIdentityProviders() {
|
||||
Collection<IdentityProviderEntity> entities = entity.getAllowedIdentityProviders();
|
||||
List<String> providers = new ArrayList<String>();
|
||||
public List<ClientIdentityProviderMappingModel> getIdentityProviders() {
|
||||
List<ClientIdentityProviderMappingModel> models = new ArrayList<ClientIdentityProviderMappingModel>();
|
||||
|
||||
for (IdentityProviderEntity entity : entities) {
|
||||
providers.add(entity.getId());
|
||||
for (ClientIdentityProviderMappingEntity entity : this.entity.getIdentityProviders()) {
|
||||
ClientIdentityProviderMappingModel model = new ClientIdentityProviderMappingModel();
|
||||
|
||||
model.setIdentityProvider(entity.getIdentityProvider().getId());
|
||||
model.setRetrieveToken(entity.isRetrieveToken());
|
||||
|
||||
models.add(model);
|
||||
}
|
||||
|
||||
return providers;
|
||||
return models;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasIdentityProvider(String providerId) {
|
||||
List<String> allowedIdentityProviders = getAllowedIdentityProviders();
|
||||
return allowedIdentityProviders.contains(providerId);
|
||||
for (ClientIdentityProviderMappingModel model : getIdentityProviders()) {
|
||||
if (model.getIdentityProvider().equals(providerId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowedRetrieveTokenFromIdentityProvider(String providerId) {
|
||||
for (ClientIdentityProviderMappingModel model : getIdentityProviders()) {
|
||||
if (model.getIdentityProvider().equals(providerId)) {
|
||||
return model.isRetrieveToken();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean contains(String str, String[] array) {
|
||||
|
|
|
@ -73,9 +73,8 @@ public abstract class ClientEntity {
|
|||
@CollectionTable(name="CLIENT_ATTRIBUTES", joinColumns={ @JoinColumn(name="CLIENT_ID") })
|
||||
protected Map<String, String> attributes = new HashMap<String, String>();
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY)
|
||||
@JoinTable(name="CLIENT_ALLOWED_IDENTITY_PROVIDER", joinColumns = { @JoinColumn(name="CLIENT_ID")}, inverseJoinColumns = { @JoinColumn(name="INTERNAL_ID")})
|
||||
Collection<IdentityProviderEntity> allowedIdentityProviders = new ArrayList<IdentityProviderEntity>();
|
||||
@OneToMany(fetch = FetchType.LAZY, mappedBy = "client", cascade = CascadeType.REMOVE)
|
||||
Collection<ClientIdentityProviderMappingEntity> identityProviders = new ArrayList<ClientIdentityProviderMappingEntity>();
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY)
|
||||
@JoinTable(name="CLIENT_PROTOCOL_MAPPER", joinColumns = { @JoinColumn(name="CLIENT_ID")}, inverseJoinColumns = { @JoinColumn(name="MAPPING_ID")})
|
||||
|
@ -193,12 +192,12 @@ public abstract class ClientEntity {
|
|||
this.frontchannelLogout = frontchannelLogout;
|
||||
}
|
||||
|
||||
public Collection<IdentityProviderEntity> getAllowedIdentityProviders() {
|
||||
return this.allowedIdentityProviders;
|
||||
public Collection<ClientIdentityProviderMappingEntity> getIdentityProviders() {
|
||||
return this.identityProviders;
|
||||
}
|
||||
|
||||
public void setAllowedIdentityProviders(Collection<IdentityProviderEntity> allowedIdentityProviders) {
|
||||
this.allowedIdentityProviders = allowedIdentityProviders;
|
||||
public void setIdentityProviders(Collection<ClientIdentityProviderMappingEntity> identityProviders) {
|
||||
this.identityProviders = identityProviders;
|
||||
}
|
||||
|
||||
public Collection<ProtocolMapperEntity> getProtocolMappers() {
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
package org.keycloak.models.jpa.entities;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author pedroigor
|
||||
*/
|
||||
@Table(name="CLIENT_IDENTITY_PROVIDER_MAPPING")
|
||||
@Entity
|
||||
@IdClass(ClientIdentityProviderMappingEntity.Key.class)
|
||||
public class ClientIdentityProviderMappingEntity {
|
||||
|
||||
@Id
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "CLIENT_ID")
|
||||
private ClientEntity client;
|
||||
|
||||
@Id
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "IDENTITY_PROVIDER_ID")
|
||||
private IdentityProviderEntity identityProvider;
|
||||
|
||||
@Column(name = "RETRIEVE_TOKEN")
|
||||
private boolean retrieveToken;
|
||||
|
||||
public ClientEntity getClient() {
|
||||
return this.client;
|
||||
}
|
||||
|
||||
public void setClient(ClientEntity client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public IdentityProviderEntity getIdentityProvider() {
|
||||
return this.identityProvider;
|
||||
}
|
||||
|
||||
public void setIdentityProvider(IdentityProviderEntity identityProvider) {
|
||||
this.identityProvider = identityProvider;
|
||||
}
|
||||
|
||||
public void setRetrieveToken(boolean retrieveToken) {
|
||||
this.retrieveToken = retrieveToken;
|
||||
}
|
||||
|
||||
public boolean isRetrieveToken() {
|
||||
return retrieveToken;
|
||||
}
|
||||
|
||||
public static class Key implements Serializable {
|
||||
|
||||
private ClientEntity client;
|
||||
private IdentityProviderEntity identityProvider;
|
||||
|
||||
public Key() {
|
||||
}
|
||||
|
||||
public Key(ClientEntity client, IdentityProviderEntity identityProvider) {
|
||||
this.client = client;
|
||||
this.identityProvider = identityProvider;
|
||||
}
|
||||
|
||||
public ClientEntity getUser() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public IdentityProviderEntity getIdentityProvider() {
|
||||
return identityProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Key key = (Key) o;
|
||||
|
||||
if (identityProvider != null ? !identityProvider.getId().equals(key.identityProvider.getId()) : key.identityProvider != null)
|
||||
return false;
|
||||
if (client != null ? !client.getId().equals(key.client != null ? key.client.getId() : null) : key.client != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = client != null ? client.getId().hashCode() : 0;
|
||||
result = 31 * result + (identityProvider != null ? identityProvider.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ClientIdentityProviderMappingEntity key = (ClientIdentityProviderMappingEntity) o;
|
||||
|
||||
if (identityProvider != null ? !identityProvider.getId().equals(key.identityProvider.getId()) : key.identityProvider != null)
|
||||
return false;
|
||||
if (client != null ? !client.getId().equals(key.client != null ? key.client.getId() : null) : key.client != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = client != null ? client.getId().hashCode() : 0;
|
||||
result = 31 * result + (identityProvider != null ? identityProvider.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package org.keycloak.models.mongo.keycloak.adapters;
|
|||
|
||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.ClientIdentityProviderMappingModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
|
@ -9,6 +10,7 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.entities.ClientEntity;
|
||||
import org.keycloak.models.entities.ClientIdentityProviderMappingEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
||||
import org.keycloak.models.mongo.utils.MongoModelUtils;
|
||||
|
||||
|
@ -323,24 +325,58 @@ public abstract class ClientAdapter<T extends MongoIdentifiableEntity> extends A
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateAllowedIdentityProviders(List<String> identityProviders) {
|
||||
List<String> providerIds = new ArrayList<String>();
|
||||
for (String providerId : identityProviders) {
|
||||
providerIds.add(providerId);
|
||||
public void updateAllowedIdentityProviders(List<ClientIdentityProviderMappingModel> identityProviders) {
|
||||
List<ClientIdentityProviderMappingEntity> stored = getMongoEntityAsClient().getIdentityProviders();
|
||||
|
||||
for (ClientIdentityProviderMappingModel model : identityProviders) {
|
||||
ClientIdentityProviderMappingEntity entity = new ClientIdentityProviderMappingEntity();
|
||||
|
||||
entity.setId(model.getIdentityProvider());
|
||||
entity.setRetrieveToken(model.isRetrieveToken());
|
||||
}
|
||||
|
||||
getMongoEntityAsClient().setAllowedIdentityProviders(identityProviders);
|
||||
getMongoEntityAsClient().setIdentityProviders(stored);
|
||||
updateMongoEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllowedIdentityProviders() {
|
||||
return getMongoEntityAsClient().getAllowedIdentityProviders();
|
||||
public List<ClientIdentityProviderMappingModel> getIdentityProviders() {
|
||||
List<ClientIdentityProviderMappingModel> models = new ArrayList<ClientIdentityProviderMappingModel>();
|
||||
|
||||
for (ClientIdentityProviderMappingEntity entity : getMongoEntityAsClient().getIdentityProviders()) {
|
||||
ClientIdentityProviderMappingModel model = new ClientIdentityProviderMappingModel();
|
||||
|
||||
model.setIdentityProvider(entity.getId());
|
||||
model.setRetrieveToken(entity.isRetrieveToken());
|
||||
|
||||
models.add(model);
|
||||
}
|
||||
|
||||
return models;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasIdentityProvider(String providerId) {
|
||||
List<String> allowedIdentityProviders = getMongoEntityAsClient().getAllowedIdentityProviders();
|
||||
return allowedIdentityProviders.contains(providerId);
|
||||
for (ClientIdentityProviderMappingEntity identityProviderMappingModel : getMongoEntityAsClient().getIdentityProviders()) {
|
||||
String identityProvider = identityProviderMappingModel.getId();
|
||||
|
||||
if (identityProvider.equals(providerId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowedRetrieveTokenFromIdentityProvider(String providerId) {
|
||||
for (ClientIdentityProviderMappingEntity identityProviderMappingModel : getMongoEntityAsClient().getIdentityProviders()) {
|
||||
if (identityProviderMappingModel.getId().equals(providerId)) {
|
||||
return identityProviderMappingModel.isRetrieveToken();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -186,6 +186,10 @@ public class IdentityBrokerService {
|
|||
return corsResponse(badRequest("Client [" + audience + "] not authorized."), clientModel);
|
||||
}
|
||||
|
||||
if (!clientModel.isAllowedRetrieveTokenFromIdentityProvider(providerId)) {
|
||||
return corsResponse(badRequest("Client [" + audience + "] not authorized to retrieve tokens from identity provider [" + providerId + "]."), clientModel);
|
||||
}
|
||||
|
||||
if (OAuthClientModel.class.isInstance(clientModel) && !forceRetrieval) {
|
||||
return corsResponse(Flows.forms(this.session, this.realmModel, clientModel, this.uriInfo)
|
||||
.setClientSessionCode(authManager.extractAuthorizationHeaderToken(this.request.getHttpHeaders()))
|
||||
|
@ -323,7 +327,7 @@ public class IdentityBrokerService {
|
|||
federatedUser.addRequiredAction(UPDATE_PROFILE);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return redirectToLoginPage(e.getMessage(), clientCode);
|
||||
return redirectToLoginPage(e, clientCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -439,16 +443,21 @@ public class IdentityBrokerService {
|
|||
}
|
||||
|
||||
private Response redirectToErrorPage(String message, Throwable throwable) {
|
||||
if (message == null) {
|
||||
message = "Unexpected error when authenticating with identity provider";
|
||||
}
|
||||
|
||||
fireErrorEvent(message, throwable);
|
||||
return Flows.forwardToSecurityFailurePage(this.session, this.realmModel, this.uriInfo, message);
|
||||
}
|
||||
|
||||
private Response badRequest(String message) {
|
||||
fireErrorEvent(message);
|
||||
return Flows.errors().error(message, Status.BAD_REQUEST);
|
||||
private Response redirectToLoginPage(Throwable t, ClientSessionCode clientCode) {
|
||||
String message = t.getMessage();
|
||||
|
||||
if (message == null) {
|
||||
message = "Unexpected error when authenticating with identity provider";
|
||||
}
|
||||
|
||||
private Response redirectToLoginPage(String message, ClientSessionCode clientCode) {
|
||||
fireErrorEvent(message);
|
||||
return Flows.forms(this.session, this.realmModel, clientCode.getClientSession().getClient(), this.uriInfo)
|
||||
.setClientSessionCode(clientCode.getCode())
|
||||
|
@ -456,6 +465,11 @@ public class IdentityBrokerService {
|
|||
.createLogin();
|
||||
}
|
||||
|
||||
private Response badRequest(String message) {
|
||||
fireErrorEvent(message);
|
||||
return Flows.errors().error(message, Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
private IdentityProvider getIdentityProvider(String providerId) {
|
||||
IdentityProviderModel identityProviderModel = this.realmModel.getIdentityProviderById(providerId);
|
||||
|
||||
|
@ -513,7 +527,11 @@ public class IdentityBrokerService {
|
|||
FederatedIdentityModel federatedIdentityModel = new FederatedIdentityModel(updatedIdentity.getIdentityProviderId(), updatedIdentity.getId(),
|
||||
updatedIdentity.getUsername(), updatedIdentity.getToken());
|
||||
// Check if no user already exists with this username or email
|
||||
UserModel existingUser = this.session.users().getUserByEmail(updatedIdentity.getEmail(), this.realmModel);
|
||||
UserModel existingUser = null;
|
||||
|
||||
if (updatedIdentity.getEmail() != null) {
|
||||
existingUser = this.session.users().getUserByEmail(updatedIdentity.getEmail(), this.realmModel);
|
||||
}
|
||||
|
||||
if (existingUser != null) {
|
||||
fireErrorEvent(Errors.FEDERATED_IDENTITY_EMAIL_EXISTS);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.keycloak.services.resources.admin;
|
||||
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.keycloak.models.ClientIdentityProviderMappingModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
@ -17,6 +18,7 @@ import javax.ws.rs.GET;
|
|||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -63,11 +65,15 @@ public class IdentityProviderResource {
|
|||
|
||||
private void removeClientIdentityProviders(List<? extends ClientModel> clients, IdentityProviderModel identityProvider) {
|
||||
for (ClientModel clientModel : clients) {
|
||||
List<String> allowedIdentityProviders = clientModel.getAllowedIdentityProviders();
|
||||
List<ClientIdentityProviderMappingModel> identityProviders = clientModel.getIdentityProviders();
|
||||
|
||||
allowedIdentityProviders.remove(identityProvider.getId());
|
||||
for (ClientIdentityProviderMappingModel providerMappingModel : new ArrayList<ClientIdentityProviderMappingModel>(identityProviders)) {
|
||||
if (providerMappingModel.getIdentityProvider().equals(identityProvider.getId())) {
|
||||
identityProviders.remove(providerMappingModel);
|
||||
}
|
||||
}
|
||||
|
||||
clientModel.updateAllowedIdentityProviders(allowedIdentityProviders);
|
||||
clientModel.updateAllowedIdentityProviders(identityProviders);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.jboss.resteasy.spi.NotFoundException;
|
|||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.provider.IdentityProviderFactory;
|
||||
import org.keycloak.models.ClientIdentityProviderMappingModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
@ -179,9 +180,12 @@ public class IdentityProvidersResource {
|
|||
|
||||
private void updateClientIdentityProviders(List<? extends ClientModel> clients, IdentityProviderRepresentation identityProvider) {
|
||||
for (ClientModel clientModel : clients) {
|
||||
List<String> allowedIdentityProviders = clientModel.getAllowedIdentityProviders();
|
||||
List<ClientIdentityProviderMappingModel> allowedIdentityProviders = clientModel.getIdentityProviders();
|
||||
ClientIdentityProviderMappingModel providerMappingModel = new ClientIdentityProviderMappingModel();
|
||||
|
||||
allowedIdentityProviders.add(identityProvider.getId());
|
||||
providerMappingModel.setIdentityProvider(identityProvider.getId());
|
||||
|
||||
allowedIdentityProviders.add(providerMappingModel);
|
||||
|
||||
clientModel.updateAllowedIdentityProviders(allowedIdentityProviders);
|
||||
}
|
||||
|
|
|
@ -100,12 +100,12 @@ To start a ApacheDS based LDAP server for testing LDAP sending run:
|
|||
|
||||
There are additional system properties you can use to configure (See EmbeddedServersFactory class for details). Once done, you can create LDAP Federation provider
|
||||
in Keycloak admin console with the settings like:
|
||||
Vendor: Other
|
||||
Connection URL: ldap://localhost:10389
|
||||
Base DN: dc=keycloak,dc=org
|
||||
User DN Suffix: ou=People,dc=keycloak,dc=org
|
||||
Bind DN: uid=admin,ou=system
|
||||
Bind credential: secret
|
||||
* Vendor: Other
|
||||
* Connection URL: ldap://localhost:10389
|
||||
* Base DN: dc=keycloak,dc=org
|
||||
* User DN Suffix: ou=People,dc=keycloak,dc=org
|
||||
* Bind DN: uid=admin,ou=system
|
||||
* Bind credential: secret
|
||||
|
||||
Kerberos server
|
||||
---------------
|
||||
|
@ -114,10 +114,40 @@ To start a ApacheDS based Kerberos server for testing Kerberos + LDAP sending ru
|
|||
|
||||
mvn exec:java -Pkerberos
|
||||
|
||||
There are additional system properties you can use to configure (See EmbeddedServersFactory class for details). Once done, you can create LDAP Federation provider
|
||||
in Keycloak admin console with same settings like mentioned in previous LDAP section. And you can enable Kerberos with the settings like:
|
||||
There are additional system properties you can use to configure (See EmbeddedServersFactory class for details) but for testing purposes default values should be good.
|
||||
By default ApacheDS LDAP server will be running on localhost:10389 and Kerberos KDC on localhost:6088 . LDAP will import initial data from [src/main/resources/kerberos/users-kerberos.ldif](src/main/resources/kerberos/users-kerberos.ldif) .
|
||||
|
||||
Server Principal: HTTP/localhost@KEYCLOAK.ORG
|
||||
KeyTab: $KEYCLOAK_SOURCES/testsuite/integration/src/main/resources/kerberos/http.keytab
|
||||
Once kerberos is running, you can create LDAP Federation provider in Keycloak admin console with same settings like mentioned in previous LDAP section.
|
||||
But additionally you can enable Kerberos authentication in LDAP provider with the settings like:
|
||||
|
||||
* Kerberos realm: KEYCLOAK.ORG
|
||||
* Server Principal: HTTP/localhost@KEYCLOAK.ORG
|
||||
* KeyTab: $KEYCLOAK_SOURCES/testsuite/integration/src/main/resources/kerberos/http.keytab (Replace $KEYCLOAK_SOURCES with correct absolute path of your sources)
|
||||
|
||||
Once you do this, you should also ensure that your Kerberos client configuration file is properly configured with KEYCLOAK.ORG domain.
|
||||
See [src/main/resources/kerberos/test-krb5.conf](src/main/resources/kerberos/test-krb5.conf) for inspiration. The location of Kerberos configuration file
|
||||
is platform dependent (In linux it's file `/etc/krb5.conf` )
|
||||
|
||||
Then you need to configure your browser to allow SPNEGO/Kerberos login from `localhost` .
|
||||
|
||||
Exact steps are again browser dependent. For Firefox see for example [http://www.microhowto.info/howto/configure_firefox_to_authenticate_using_spnego_and_kerberos.html](http://www.microhowto.info/howto/configure_firefox_to_authenticate_using_spnego_and_kerberos.html) .
|
||||
URI `localhost` must be allowed in `network.negotiate-auth.trusted-uris` config option.
|
||||
|
||||
For Chrome, you just need to run the browser with command similar to this (more details in Chrome documentation):
|
||||
|
||||
```
|
||||
/usr/bin/google-chrome-stable --auth-server-whitelist="localhost"
|
||||
```
|
||||
|
||||
|
||||
Finally test the integration by retrieve kerberos ticket. In many OS you can achieve this by running command from CMD like:
|
||||
|
||||
```
|
||||
kinit hnelson@KEYCLOAK.ORG
|
||||
```
|
||||
|
||||
and provide password `secret`
|
||||
|
||||
Now when you access `http://localhost:8081/auth/realms/master/account` you should be logged in automatically as user `hnelson` .
|
||||
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ public class KerberosEmbeddedServer extends LDAPEmbeddedServer {
|
|||
public void init() throws Exception {
|
||||
super.init();
|
||||
|
||||
log.info("Creating KDC server");
|
||||
log.info("Creating KDC server. kerberosRealm: " + kerberosRealm + ", kdcPort: " + kdcPort);
|
||||
createAndStartKdcServer();
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ import org.junit.Rule;
|
|||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.ClientIdentityProviderMappingModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
@ -156,11 +158,18 @@ public abstract class AbstractIdentityProviderTest {
|
|||
IdentityProviderModel identityProviderModel = getIdentityProviderModel();
|
||||
RealmModel realm = getRealm();
|
||||
ApplicationModel applicationModel = realm.getApplicationByName("test-app");
|
||||
List<String> allowedIdentityProviders = applicationModel.getAllowedIdentityProviders();
|
||||
List<ClientIdentityProviderMappingModel> allowedIdentityProviders = applicationModel.getIdentityProviders();
|
||||
ClientIdentityProviderMappingModel mapping = null;
|
||||
|
||||
assertTrue(allowedIdentityProviders.contains(identityProviderModel.getId()));
|
||||
for (ClientIdentityProviderMappingModel model : allowedIdentityProviders) {
|
||||
if (model.getIdentityProvider().equals(identityProviderModel.getId())) {
|
||||
mapping = model;
|
||||
}
|
||||
}
|
||||
|
||||
allowedIdentityProviders.remove(identityProviderModel.getId());
|
||||
assertNotNull(mapping);
|
||||
|
||||
allowedIdentityProviders.remove(mapping);
|
||||
|
||||
this.driver.navigate().to("http://localhost:8081/test-app/");
|
||||
|
||||
|
@ -173,7 +182,7 @@ public abstract class AbstractIdentityProviderTest {
|
|||
|
||||
}
|
||||
|
||||
allowedIdentityProviders.add(identityProviderModel.getId());
|
||||
allowedIdentityProviders.add(mapping);
|
||||
|
||||
applicationModel.updateAllowedIdentityProviders(allowedIdentityProviders);
|
||||
|
||||
|
@ -317,6 +326,18 @@ public abstract class AbstractIdentityProviderTest {
|
|||
|
||||
assertNotNull(identityModel.getToken());
|
||||
|
||||
ClientModel clientModel = realm.findClient("test-app");
|
||||
ClientIdentityProviderMappingModel providerMappingModel = null;
|
||||
|
||||
for (ClientIdentityProviderMappingModel identityProviderMappingModel : clientModel.getIdentityProviders()) {
|
||||
if (identityProviderMappingModel.getIdentityProvider().equals(getProviderId())) {
|
||||
providerMappingModel = identityProviderMappingModel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
providerMappingModel.setRetrieveToken(false);
|
||||
|
||||
UserSessionStatus userSessionStatus = retrieveSessionStatus();
|
||||
String accessToken = userSessionStatus.getAccessTokenString();
|
||||
URI tokenEndpointUrl = Urls.identityProviderRetrieveToken(BASE_URI, getProviderId(), realm.getName());
|
||||
|
@ -331,6 +352,14 @@ public abstract class AbstractIdentityProviderTest {
|
|||
WebTarget tokenEndpoint = client.target(tokenEndpointUrl);
|
||||
Response response = tokenEndpoint.request().get();
|
||||
|
||||
assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
||||
|
||||
providerMappingModel.setRetrieveToken(true);
|
||||
|
||||
client = ClientBuilder.newBuilder().register(authFilter).build();
|
||||
tokenEndpoint = client.target(tokenEndpointUrl);
|
||||
response = tokenEndpoint.request().get();
|
||||
|
||||
assertEquals(Status.OK.getStatusCode(), response.getStatus());
|
||||
assertNotNull(response.readEntity(String.class));
|
||||
|
||||
|
@ -375,6 +404,18 @@ public abstract class AbstractIdentityProviderTest {
|
|||
|
||||
assertTrue(oauth.getCurrentQuery().containsKey(OAuth2Constants.CODE));
|
||||
|
||||
ClientModel clientModel = getRealm().findClient("third-party");
|
||||
ClientIdentityProviderMappingModel providerMappingModel = null;
|
||||
|
||||
for (ClientIdentityProviderMappingModel identityProviderMappingModel : clientModel.getIdentityProviders()) {
|
||||
if (identityProviderMappingModel.getIdentityProvider().equals(getProviderId())) {
|
||||
providerMappingModel = identityProviderMappingModel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
providerMappingModel.setRetrieveToken(true);
|
||||
|
||||
AccessTokenResponse accessToken = oauth.doAccessTokenRequest(oauth.getCurrentQuery().get(OAuth2Constants.CODE), "password");
|
||||
URI tokenEndpointUrl = Urls.identityProviderRetrieveToken(BASE_URI, getProviderId(), getRealm().getName());
|
||||
String authHeader = "Bearer " + accessToken.getAccessToken();
|
||||
|
|
|
@ -25,6 +25,8 @@ import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
|
|||
import org.keycloak.broker.saml.SAMLIdentityProvider;
|
||||
import org.keycloak.broker.saml.SAMLIdentityProviderConfig;
|
||||
import org.keycloak.broker.saml.SAMLIdentityProviderFactory;
|
||||
import org.keycloak.models.ClientIdentityProviderMappingModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
|
@ -113,6 +115,31 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
|||
assertFalse(identityProviderModel.isAuthenticateByDefault());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplicationIdentityProviders() throws Exception {
|
||||
RealmModel realm = installTestRealm();
|
||||
|
||||
ClientModel client = realm.findClient("test-app-with-allowed-providers");
|
||||
List<ClientIdentityProviderMappingModel> identityProviders = client.getIdentityProviders();
|
||||
|
||||
assertEquals(1, identityProviders.size());
|
||||
|
||||
ClientIdentityProviderMappingModel identityProviderMappingModel = identityProviders.get(0);
|
||||
|
||||
assertEquals("kc-oidc-idp", identityProviderMappingModel.getIdentityProvider());
|
||||
assertEquals(false, identityProviderMappingModel.isRetrieveToken());
|
||||
|
||||
identityProviders.remove(identityProviderMappingModel);
|
||||
|
||||
client.updateAllowedIdentityProviders(identityProviders);
|
||||
|
||||
client = realm.findClientById(client.getId());
|
||||
identityProviders = client.getIdentityProviders();
|
||||
|
||||
assertEquals(0, identityProviders.size());
|
||||
}
|
||||
|
||||
|
||||
private void assertIdentityProviderConfig(List<IdentityProviderModel> identityProviders) {
|
||||
assertFalse(identityProviders.isEmpty());
|
||||
|
||||
|
|
|
@ -197,8 +197,11 @@
|
|||
"/test-app/*"
|
||||
],
|
||||
"webOrigins": [],
|
||||
"allowedIdentityProviders": [
|
||||
"kc-oidc-idp"
|
||||
"identityProviders": [
|
||||
{
|
||||
"id": "kc-oidc-idp",
|
||||
"retrieveToken": false
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
|
Loading…
Reference in a new issue