diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/ConfiguratorRelyingPartyRegistrationRepository.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/ConfiguratorRelyingPartyRegistrationRepository.java
index 9d077ae91d7..e8e06f5c811 100644
--- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/ConfiguratorRelyingPartyRegistrationRepository.java
+++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/ConfiguratorRelyingPartyRegistrationRepository.java
@@ -3,6 +3,8 @@
import lombok.extern.slf4j.Slf4j;
import org.cloudfoundry.identity.uaa.provider.SamlIdentityProviderDefinition;
import org.cloudfoundry.identity.uaa.util.KeyWithCert;
+import org.cloudfoundry.identity.uaa.zone.IdentityZone;
+import org.cloudfoundry.identity.uaa.zone.ZoneAware;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.saml2.core.Saml2X509Credential;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
@@ -15,7 +17,8 @@
import java.util.function.Function;
@Slf4j
-public class ConfiguratorRelyingPartyRegistrationRepository implements RelyingPartyRegistrationRepository {
+public class ConfiguratorRelyingPartyRegistrationRepository
+ implements RelyingPartyRegistrationRepository, ZoneAware {
private final SamlIdentityProviderConfigurator configurator;
private final KeyWithCert keyWithCert;
@@ -54,7 +57,7 @@ public RelyingPartyRegistration findByRegistrationId(String registrationId) {
return buildRelyingPartyRegistration(registrationId, identityProviderDefinition);
}
}
- return null;
+ return buildDefaultRelyingPartyRegistration();
}
private RelyingPartyRegistration buildRelyingPartyRegistration(String registrationId, SamlIdentityProviderDefinition def) {
@@ -78,4 +81,41 @@ private RelyingPartyRegistration buildRelyingPartyRegistration(String registrati
)
.build();
}
+
+ private RelyingPartyRegistration buildDefaultRelyingPartyRegistration() {
+ String samlEntityID, samlServiceUri;
+ IdentityZone zone = retrieveZone();
+ if (zone.isUaa()) {
+ samlEntityID = this.samlEntityID;
+ samlServiceUri = this.samlEntityID;
+ }
+ else if (zone.getConfig() != null && zone.getConfig().getSamlConfig() != null) {
+
+ samlEntityID = zone.getConfig().getSamlConfig().getEntityID();
+ samlServiceUri = zone.getSubdomain() + "." + this.samlEntityID;
+ }
+ else {
+ return null;
+ }
+
+ return RelyingPartyRegistrations
+ .fromMetadataLocation("dummy-saml-idp-metadata.xml")
+ .entityId(samlEntityID)
+// .nameIdFormat(def.getNameID())
+// .registrationId(registrationId)
+ .assertionConsumerServiceLocation(assertionConsumerServiceLocationFunction.apply(samlServiceUri))
+ .singleLogoutServiceResponseLocation(singleLogoutServiceResponseLocationFunction.apply(samlServiceUri))
+ // TODO: Verify that we should accept both POST and REDIRECT bindings
+ .singleLogoutServiceBindings(c -> {c.add(Saml2MessageBinding.REDIRECT); c.add(Saml2MessageBinding.POST);})
+ .assertingPartyDetails(details -> details
+ .wantAuthnRequestsSigned(samlSignRequest)
+ )
+ .signingX509Credentials(cred -> cred
+ .add(Saml2X509Credential.signing(keyWithCert.getPrivateKey(), keyWithCert.getCertificate()))
+ )
+ .decryptionX509Credentials(cred -> cred
+ .add(Saml2X509Credential.decryption(keyWithCert.getPrivateKey(), keyWithCert.getCertificate()))
+ )
+ .build();
+ }
}
diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/DelegatingRelyingPartyRegistrationRepository.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/DelegatingRelyingPartyRegistrationRepository.java
index 754ea1385fa..ec71e1c0e86 100644
--- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/DelegatingRelyingPartyRegistrationRepository.java
+++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/DelegatingRelyingPartyRegistrationRepository.java
@@ -1,5 +1,7 @@
package org.cloudfoundry.identity.uaa.provider.saml;
+import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
+import org.cloudfoundry.identity.uaa.zone.ZoneAware;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.util.Assert;
@@ -34,9 +36,10 @@ public DelegatingRelyingPartyRegistrationRepository(RelyingPartyRegistrationRepo
*/
@Override
public RelyingPartyRegistration findByRegistrationId(String registrationId) {
+ boolean isDefaultZone = IdentityZoneHolder.isUaa();
for (RelyingPartyRegistrationRepository repository : this.delegates) {
RelyingPartyRegistration registration = repository.findByRegistrationId(registrationId);
- if (registration != null) {
+ if (registration != null && (isDefaultZone || repository instanceof ZoneAware)) {
return registration;
}
}
diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/zone/ZoneAware.java b/server/src/main/java/org/cloudfoundry/identity/uaa/zone/ZoneAware.java
new file mode 100644
index 00000000000..d4b39605d8e
--- /dev/null
+++ b/server/src/main/java/org/cloudfoundry/identity/uaa/zone/ZoneAware.java
@@ -0,0 +1,7 @@
+package org.cloudfoundry.identity.uaa.zone;
+
+public interface ZoneAware {
+ default IdentityZone retrieveZone() {
+ return IdentityZoneHolder.get();
+ }
+}
diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/SamlLoginIT.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/SamlLoginIT.java
index 7d8d1a3d8d1..de5b69c08c2 100644
--- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/SamlLoginIT.java
+++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/SamlLoginIT.java
@@ -220,6 +220,47 @@ void samlSPMetadata() {
.contains("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified");
}
+ @Test
+ void samlSPMetadataForZone() {
+ String zoneId = "testzone1";
+ String zoneUrl = baseUrl.replace("localhost", zoneId + ".localhost");
+
+ //identity client token
+ RestTemplate identityClient = IntegrationTestUtils.getClientCredentialsTemplate(
+ IntegrationTestUtils.getClientCredentialsResource(baseUrl, new String[]{"zones.write", "zones.read", "scim.zones"}, "identity", "identitysecret")
+ );
+ RestTemplate adminClient = IntegrationTestUtils.getClientCredentialsTemplate(
+ IntegrationTestUtils.getClientCredentialsResource(baseUrl, new String[0], "admin", "adminsecret")
+ );
+
+ //create the zone
+ IdentityZoneConfiguration config = new IdentityZoneConfiguration();
+ config.getCorsPolicy().getDefaultConfiguration().setAllowedMethods(List.of(GET.toString(), POST.toString()));
+ config.getSamlConfig().setEntityID(zoneId + "-saml-login");
+ IntegrationTestUtils.createZoneOrUpdateSubdomain(identityClient, baseUrl, zoneId, zoneId, config);
+
+ RestTemplate request = new RestTemplate();
+ ResponseEntity response = request.getForEntity(
+ zoneUrl + "/saml/metadata", String.class);
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+ String metadataXml = response.getBody();
+
+ // The SAML SP metadata should match the following UAA configs:
+ // login.entityID
+ assertThat(metadataXml).contains("entityID=\"" + zoneId + "-saml-login\"")
+ // TODO: Are DigestMethod and SignatureMethod needed?
+ // login.saml.signatureAlgorithm
+ //.contains("")
+ //.contains("")
+ // login.saml.signRequest
+ .contains("AuthnRequestsSigned=\"true\"")
+ // login.saml.wantAssertionSigned
+ .contains("WantAssertionsSigned=\"true\"")
+ // login.saml.nameID
+// .contains("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified");
+ .contains("/saml/SSO/alias/" + zoneId + ".cloudfoundry-saml-login"); // TODO: Improve this check
+ }
+
@Test
void contentTypes() {
String loginUrl = baseUrl + "/login";