KEYCLOAK-15697 Make the Service Provider Entity ID user configurable
This commit is contained in:
parent
59ef7d258f
commit
f274ec447b
7 changed files with 130 additions and 2 deletions
|
@ -370,7 +370,12 @@ public class SAMLEndpoint {
|
|||
}
|
||||
|
||||
private String getEntityId(UriInfo uriInfo, RealmModel realm) {
|
||||
return UriBuilder.fromUri(uriInfo.getBaseUri()).path("realms").path(realm.getName()).build().toString();
|
||||
String configEntityId = config.getEntityId();
|
||||
|
||||
if (configEntityId == null || configEntityId.isEmpty())
|
||||
return UriBuilder.fromUri(uriInfo.getBaseUri()).path("realms").path(realm.getName()).build().toString();
|
||||
else
|
||||
return configEntityId;
|
||||
}
|
||||
|
||||
protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder holder, ResponseType responseType, String relayState, String clientId) {
|
||||
|
|
|
@ -165,7 +165,12 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
|||
}
|
||||
|
||||
private String getEntityId(UriInfo uriInfo, RealmModel realm) {
|
||||
return UriBuilder.fromUri(uriInfo.getBaseUri()).path("realms").path(realm.getName()).build().toString();
|
||||
String configEntityId = getConfig().getEntityId();
|
||||
|
||||
if (configEntityId == null || configEntityId.isEmpty())
|
||||
return UriBuilder.fromUri(uriInfo.getBaseUri()).path("realms").path(realm.getName()).build().toString();
|
||||
else
|
||||
return configEntityId;
|
||||
}
|
||||
|
||||
private List<String> getAuthnContextClassRefUris() {
|
||||
|
|
|
@ -33,6 +33,7 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
|
|||
|
||||
public static final XmlKeyInfoKeyNameTransformer DEFAULT_XML_KEY_INFO_KEY_NAME_TRANSFORMER = XmlKeyInfoKeyNameTransformer.NONE;
|
||||
|
||||
public static final String ENTITY_ID = "entityId";
|
||||
public static final String ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO = "addExtensionsElementWithKeyInfo";
|
||||
public static final String BACKCHANNEL_SUPPORTED = "backchannelSupported";
|
||||
public static final String ENCRYPTION_PUBLIC_KEY = "encryptionPublicKey";
|
||||
|
@ -65,6 +66,14 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
|
|||
super(identityProviderModel);
|
||||
}
|
||||
|
||||
public String getEntityId() {
|
||||
return getConfig().get(ENTITY_ID);
|
||||
}
|
||||
|
||||
public void setEntityId(String entityId) {
|
||||
getConfig().put(ENTITY_ID, entityId);
|
||||
}
|
||||
|
||||
public String getSingleSignOnServiceUrl() {
|
||||
return getConfig().get(SINGLE_SIGN_ON_SERVICE_URL);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.keycloak.broker.saml.SAMLIdentityProviderConfig;
|
||||
import org.keycloak.dom.saml.v2.protocol.AuthnRequestType;
|
||||
import org.keycloak.saml.common.util.DocumentUtil;
|
||||
import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request;
|
||||
import org.keycloak.testsuite.saml.AbstractSamlTest;
|
||||
import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater;
|
||||
import org.keycloak.testsuite.util.SamlClient;
|
||||
import org.keycloak.testsuite.util.SamlClient.Binding;
|
||||
import org.keycloak.testsuite.util.SamlClientBuilder;
|
||||
import java.io.Closeable;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.ASSERTION_NSURI;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot;
|
||||
|
||||
/**
|
||||
* Final class as it's not intended to be overriden.
|
||||
*/
|
||||
public final class KcSamlCustomEntityIdBrokerTest extends AbstractBrokerTest {
|
||||
|
||||
@Override
|
||||
protected BrokerConfiguration getBrokerConfiguration() {
|
||||
return KcSamlBrokerConfiguration.INSTANCE;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomEntityNotSet() throws Exception {
|
||||
// No comparison type, no classrefs, no declrefs -> No RequestedAuthnContext
|
||||
try (Closeable idpUpdater = new IdentityProviderAttributeUpdater(identityProviderResource)
|
||||
.update())
|
||||
{
|
||||
// Build the login request document
|
||||
AuthnRequestType loginRep = SamlClient.createLoginRequestDocument(AbstractSamlTest.SAML_CLIENT_ID_SALES_POST + ".dot/ted", getConsumerRoot() + "/sales-post/saml", null);
|
||||
Document doc = SAML2Request.convert(loginRep);
|
||||
new SamlClientBuilder()
|
||||
.authnRequest(getConsumerSamlEndpoint(bc.consumerRealmName()), doc, Binding.POST)
|
||||
.build() // Request to consumer IdP
|
||||
.login().idp(bc.getIDPAlias()).build()
|
||||
.processSamlResponse(Binding.POST) // AuthnRequest to producer IdP
|
||||
.targetAttributeSamlRequest()
|
||||
.transformDocument((document) -> {
|
||||
try
|
||||
{
|
||||
log.infof("Document: %s", DocumentUtil.asString(document));
|
||||
|
||||
// Find the Issuer element
|
||||
Element issuerElement = DocumentUtil.getDirectChildElement(document.getDocumentElement(), ASSERTION_NSURI.get(), "Issuer");
|
||||
Assert.assertEquals("Unexpected Issuer element value", "https://localhost:8543/auth/realms/consumer", issuerElement.getTextContent());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
})
|
||||
.build()
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomEntityIdSet() throws Exception {
|
||||
// Comparison type set, no classrefs, no declrefs -> No RequestedAuthnContext
|
||||
try (Closeable idpUpdater = new IdentityProviderAttributeUpdater(identityProviderResource)
|
||||
.setAttribute(SAMLIdentityProviderConfig.ENTITY_ID, "http://my.custom.entity.id")
|
||||
.update())
|
||||
{
|
||||
// Build the login request document
|
||||
AuthnRequestType loginRep = SamlClient.createLoginRequestDocument(AbstractSamlTest.SAML_CLIENT_ID_SALES_POST + ".dot/ted", getConsumerRoot() + "/sales-post/saml", null);
|
||||
Document doc = SAML2Request.convert(loginRep);
|
||||
new SamlClientBuilder()
|
||||
.authnRequest(getConsumerSamlEndpoint(bc.consumerRealmName()), doc, Binding.POST)
|
||||
.build() // Request to consumer IdP
|
||||
.login().idp(bc.getIDPAlias()).build()
|
||||
.processSamlResponse(Binding.POST) // AuthnRequest to producer IdP
|
||||
.targetAttributeSamlRequest()
|
||||
.transformDocument((document) -> {
|
||||
try
|
||||
{
|
||||
log.infof("Document: %s", DocumentUtil.asString(document));
|
||||
|
||||
// Find the Issuer element
|
||||
Element issuerElement = DocumentUtil.getDirectChildElement(document.getDocumentElement(), ASSERTION_NSURI.get(), "Issuer");
|
||||
Assert.assertEquals("Unexpected Issuer element value", "http://my.custom.entity.id", issuerElement.getTextContent());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
})
|
||||
.build()
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -664,6 +664,8 @@ import-from-url=Import from URL
|
|||
identity-provider.import-from-url.tooltip=Import metadata from a remote IDP discovery descriptor.
|
||||
import-from-file=Import from file
|
||||
identity-provider.import-from-file.tooltip=Import metadata from a downloaded IDP discovery descriptor.
|
||||
identity-provider.saml.entity-id=Service Provider Entity ID
|
||||
identity-provider.saml.entity-id.tooltip=The Entity ID that will be used to uniquely identify this SAML Service Provider
|
||||
identity-provider.saml.protocol-endpoints.saml=SAML 2.0 Service Provider Metadata
|
||||
identity-provider.saml.protocol-endpoints.saml.tooltip=Shows the configuration of the Service Provider endpoint
|
||||
saml-config=SAML Config
|
||||
|
|
|
@ -873,6 +873,7 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
|
|||
$scope.identityProvider.config.signatureAlgorithm = $scope.signatureAlgorithms[1];
|
||||
$scope.identityProvider.config.xmlSigKeyInfoKeyNameTransformer = $scope.xmlKeyNameTranformers[1];
|
||||
}
|
||||
$scope.identityProvider.config.entityId = $scope.identityProvider.config.entityId || (authUrl + '/realms/' + realm.realm);
|
||||
}
|
||||
|
||||
$scope.hidePassword = true;
|
||||
|
|
|
@ -133,6 +133,13 @@
|
|||
<fieldset>
|
||||
<legend uncollapsed><span class="text">{{:: 'saml-config' | translate}}</span> <kc-tooltip>{{:: 'identity-provider.saml-config.tooltip' | translate}}</kc-tooltip></legend>
|
||||
|
||||
<div class="form-group clearfix">
|
||||
<label class="col-md-2 control-label" for="entityId"><span class="required">*</span> {{:: 'identity-provider.saml.entity-id' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
<input kc-no-reserved-chars class="form-control" id="entityId" type="text" ng-model="identityProvider.config.entityId" required>
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'identityProvider.config.entity-id.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group clearfix">
|
||||
<label class="col-md-2 control-label" for="singleSignOnServiceUrl"><span class="required">*</span> {{:: 'single-signon-service-url' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
|
|
Loading…
Reference in a new issue