keycloak-aplcache
Changes
docbook/reference/en/en-US/modules/saml.xml 72(+69 -3)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html 8(+4 -4)
saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterService.java 21(+13 -8)
testsuite/integration/pom.xml 8(+8 -0)
Details
docbook/reference/en/en-US/modules/saml.xml 72(+69 -3)
diff --git a/docbook/reference/en/en-US/modules/saml.xml b/docbook/reference/en/en-US/modules/saml.xml
index 1de1dc6..912fc63 100755
--- a/docbook/reference/en/en-US/modules/saml.xml
+++ b/docbook/reference/en/en-US/modules/saml.xml
@@ -96,12 +96,78 @@
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <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
+ under Name ID Format
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <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.
+ </para>
+ </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
+ by a registered Valid Redirect URI pattern
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Assertion Consumer Service POST Binding URL</term>
+ <listitem>
+ <para>
+ POST Binding URL for the Assertion Consumer Service.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Assertion Consumer Service Redirect Binding URL</term>
+ <listitem>
+ <para>
+ Redirect Binding URL for the Assertion Consumer Service.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Logout Service POST Binding URL</term>
+ <listitem>
+ <para>
+ POST Binding URL for the Logout Service.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Logout Service Redirect Binding URL</term>
+ <listitem>
+ <para>
+ Redirect Binding URL for the Logout Service.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
<para>
- You have to specify an admin URL if you want logout to work. This should be a URL that will except single logout
- requests from the Keycloak server. You should also specify a default redirect url. Keycloak will redirect to this
- url after single logout is complete.
+ 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>
+ For logout to work, you must specify a Master SAML Processing URL, or the Loging Service URL for the binding
+ 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
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html
index 1ad2bc3..1ce6d80 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html
@@ -221,16 +221,16 @@
<fieldset>
<legend collapsed><span class="text">Fine Grain SAML Endpoint Configuration</span> <span tooltip-placement="right" tooltip="Expand this section to configure exact URLs for Assertion Consumer and Single Logout Service." class="fa fa-info-circle"></span></legend>
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
- <label class="col-sm-2 control-label" for="logoutPostBinding">Assertion Consumer Service POST Binding URL</label>
+ <label class="col-sm-2 control-label" for="consumerServicePost">Assertion Consumer Service POST Binding URL</label>
<div class="col-sm-6">
- <input ng-model="application.attributes.saml_assertion_consumer_service_url_post" class="form-control" type="text" name="logoutPostBinding" id="logoutPostBinding" />
+ <input ng-model="application.attributes.saml_assertion_consumer_url_post" class="form-control" type="text" name="consumerServicePost" id="consumerServicePost" />
</div>
<span tooltip-placement="right" tooltip="SAML POST Binding URL for the application's assertion consumer service (login responses). You can leave this blank if you do not have a URL for this binding." class="fa fa-info-circle"></span>
</div>
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
- <label class="col-sm-2 control-label" for="logoutPostBinding">Assertion Consumer Service Redirect Binding URL</label>
+ <label class="col-sm-2 control-label" for="consumerServiceRedirect">Assertion Consumer Service Redirect Binding URL</label>
<div class="col-sm-6">
- <input ng-model="application.attributes.saml_assertion_consumer_service_url_redirect" class="form-control" type="text" name="logoutRedirectBinding" id="logoutRedirectBinding" />
+ <input ng-model="application.attributes.saml_assertion_consumer_url_redirect" class="form-control" type="text" name="consumerServiceRedirect" id="consumerServiceRedirect" />
</div>
<span tooltip-placement="right" tooltip="SAML Redirect Binding URL for the application's assertion consumer service (login responses). You can leave this blank if you do not have a URL for this binding." class="fa fa-info-circle"></span>
</div>
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterService.java
index 833fa2d..39d6f37 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterService.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterService.java
@@ -89,15 +89,20 @@ public class EntityDescriptorImporterService {
if (spDescriptorType.isWantAssertionsSigned()) {
app.setAttribute(SamlProtocol.SAML_ASSERTION_SIGNATURE, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
}
- String adminUrl = getLogoutLocation(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get());
- if (adminUrl != null) app.setManagementUrl(adminUrl);
-
- String urlPattern = CoreConfigUtil.getServiceURL(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get());
- if (urlPattern == null) {
- urlPattern = CoreConfigUtil.getServiceURL(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get());
+ String logoutPost = getLogoutLocation(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get());
+ if (logoutPost != null) app.setAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE, logoutPost);
+ String logoutRedirect = getLogoutLocation(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get());
+ if (logoutPost != null) app.setAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE, logoutRedirect);
+
+ String assertionConsumerServicePostBinding = CoreConfigUtil.getServiceURL(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get());
+ if (assertionConsumerServicePostBinding != null) {
+ app.setAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE, assertionConsumerServicePostBinding);
+ app.addRedirectUri(assertionConsumerServicePostBinding);
}
- if (urlPattern != null) {
- app.addRedirectUri(urlPattern);
+ String assertionConsumerServiceRedirectBinding = CoreConfigUtil.getServiceURL(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get());
+ if (assertionConsumerServiceRedirectBinding != null) {
+ app.setAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE, assertionConsumerServiceRedirectBinding);
+ app.addRedirectUri(assertionConsumerServiceRedirectBinding);
}
for (KeyDescriptorType keyDescriptor : spDescriptorType.getKeyDescriptor()) {
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
index a83bfd8..ffdb39d 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
@@ -51,6 +51,8 @@ public class SamlProtocol implements LoginProtocol {
public static final String SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE = "saml_assertion_consumer_url_redirect";
public static final String SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE = "saml_single_logout_service_url_post";
public static final String SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE = "saml_single_logout_service_url_redirect";
+ public static final String SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE = "saml_force_name_id_format";
+ public static final String SAML_NAME_ID_FORMAT_ATTRIBUTE = "saml_name_id_format";
public static final String LOGIN_PROTOCOL = "saml";
public static final String SAML_BINDING = "saml_binding";
public static final String SAML_POST_BINDING = "post";
@@ -175,10 +177,30 @@ public class SamlProtocol implements LoginProtocol {
protected String getNameIdFormat(ClientSessionModel clientSession) {
String nameIdFormat = clientSession.getNote(GeneralConstants.NAMEID_FORMAT);
+ ClientModel client = clientSession.getClient();
+ boolean forceFormat = forceNameIdFormat(client);
+ String configuredNameIdFormat = client.getAttribute(SAML_NAME_ID_FORMAT_ATTRIBUTE);
+ if ((nameIdFormat == null || forceFormat) && configuredNameIdFormat != null) {
+ if (configuredNameIdFormat.equals("email")) {
+ nameIdFormat = JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get();
+ } else if (configuredNameIdFormat.equals("persistent")) {
+ nameIdFormat = JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get();
+ } else if (configuredNameIdFormat.equals("transient")) {
+ nameIdFormat = JBossSAMLURIConstants.NAMEID_FORMAT_TRANSIENT.get();
+ } else if (configuredNameIdFormat.equals("username")) {
+ nameIdFormat = JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get();
+ } else {
+ nameIdFormat = JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get();
+ }
+ }
if(nameIdFormat == null) return SAML_DEFAULT_NAMEID_FORMAT;
return nameIdFormat;
}
+ public static boolean forceNameIdFormat(ClientModel client) {
+ return "true".equals(client.getAttribute(SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE));
+ }
+
protected String getNameId(String nameIdFormat, ClientSessionModel clientSession, UserSessionModel userSession) {
if (nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get())) {
return userSession.getUser().getEmail();
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
index 44808d6..b21af78 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
@@ -245,7 +245,7 @@ public class SamlService {
// Handle NameIDPolicy from SP
NameIDPolicyType nameIdPolicy = requestAbstractType.getNameIDPolicy();
- if(nameIdPolicy != null) {
+ if(nameIdPolicy != null && !SamlProtocol.forceNameIdFormat(client)) {
String nameIdFormat = nameIdPolicy.getFormat().toString();
// TODO: Handle AllowCreate too, relevant for persistent NameID.
if(isSupportedNameIdFormat(nameIdFormat)) {
@@ -254,8 +254,6 @@ public class SamlService {
event.error(Errors.INVALID_TOKEN);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unsupported NameIDFormat.");
}
- } else {
- clientSession.setNote(GeneralConstants.NAMEID_FORMAT, JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get());
}
Response response = authManager.checkNonFormAuthentication(session, clientSession, realm, uriInfo, request, clientConnection, headers, event);
testsuite/integration/pom.xml 8(+8 -0)
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index cae67d1..5800d98 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -25,6 +25,14 @@
<dependencies>
<dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ </dependency>
+ <dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-dependencies-server-all</artifactId>
<version>${project.version}</version>
diff --git a/testsuite/integration/src/test/resources/saml/signed-post-email/WEB-INF/picketlink.xml b/testsuite/integration/src/test/resources/saml/signed-post-email/WEB-INF/picketlink.xml
index d036b07..b5cd01c 100755
--- a/testsuite/integration/src/test/resources/saml/signed-post-email/WEB-INF/picketlink.xml
+++ b/testsuite/integration/src/test/resources/saml/signed-post-email/WEB-INF/picketlink.xml
@@ -21,7 +21,7 @@
class="org.picketlink.identity.federation.web.handlers.saml2.SAML2LogOutHandler"/>
<Handler
class="org.picketlink.identity.federation.web.handlers.saml2.SAML2AuthenticationHandler">
- <Option Key="NAMEID_FORMAT" Value="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"/>
+ <!-- <Option Key="NAMEID_FORMAT" Value="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"/> -->
</Handler>
<Handler
class="org.picketlink.identity.federation.web.handlers.saml2.RolesGenerationHandler"/>
diff --git a/testsuite/integration/src/test/resources/saml/testsaml.json b/testsuite/integration/src/test/resources/saml/testsaml.json
index 3b9931a..9635c0c 100755
--- a/testsuite/integration/src/test/resources/saml/testsaml.json
+++ b/testsuite/integration/src/test/resources/saml/testsaml.json
@@ -123,6 +123,8 @@
"http://localhost:8081/sales-post-sig-email/*"
],
"attributes": {
+ "saml_force_name_id_format": "true",
+ "saml_name_id_format": "email",
"saml_assertion_consumer_url_post": "http://localhost:8081/sales-post-sig-email",
"saml_assertion_consumer_url_redirect": "http://localhost:8081/sales-post-sig-email",
"saml_single_logout_service_url_post": "http://localhost:8081/sales-post-sig-email",