diff --git a/build.gradle b/build.gradle index b9d3badddd2..0073962eecf 100644 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,7 @@ buildscript { classpath(libraries.testRetryPlugin) classpath(libraries.gradleJcocoPlugin) classpath(libraries.sonarqubePlugin) + //classpath(libraries.shadowPlugin) } } @@ -66,6 +67,14 @@ subprojects { exclude(group: "com.vaadin.external.google", module: "android-json") exclude(group: "com.unboundid.components", module: "json") + // Exclude opensaml-security-api and non-FIPS bouncycastle libs, and use Shadow library for FIPS compliance + exclude(group: "org.bouncycastle", module: "bcpkix-jdk15on") + exclude(group: "org.bouncycastle", module: "bcprov-jdk15on") + exclude(group: "org.bouncycastle", module: "bcutil-jdk15on") + exclude(group: "org.bouncycastle", module: "bcprov-jdk18on") + exclude(group: "org.bouncycastle", module: "bcpkix-jdk18on") + exclude(group: "org.bouncycastle", module: "bcutil-jdk18on") + resolutionStrategy { resolutionStrategy.eachDependency { DependencyResolveDetails details -> if (details.requested.group == 'org.opensaml' && details.requested.name.startsWith("opensaml-")) { diff --git a/dependencies.gradle b/dependencies.gradle index 00730cc1d48..006bdc480f9 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -6,7 +6,9 @@ ext { // Versions shared between multiple dependencies versions.aspectJVersion = "1.9.4" versions.apacheDsVersion = "2.0.0.AM27" -versions.bouncyCastleVersion = "1.0.2.5" +versions.bouncyCastleFipsVersion = "1.0.2.5" +versions.bouncyCastlePkixFipsVersion = "1.0.7" +versions.bouncyCastleTlsFipsVersion = "1.0.19" versions.hamcrestVersion = "2.2" versions.springBootVersion = "2.7.18" versions.springFrameworkVersion = "5.3.36" @@ -43,8 +45,9 @@ libraries.apacheDsProtocolLdap = "org.apache.directory.server:apacheds-protocol- libraries.apacheLdapApi = "org.apache.directory.api:api-ldap-model:2.1.6" libraries.aspectJRt = "org.aspectj:aspectjrt" libraries.aspectJWeaver = "org.aspectj:aspectjweaver" -libraries.bouncyCastlePkix = "org.bouncycastle:bcpkix-fips:1.0.7" -libraries.bouncyCastleProv = "org.bouncycastle:bc-fips:${versions.bouncyCastleVersion}" +libraries.bouncyCastlePkixFips = "org.bouncycastle:bcpkix-fips:${versions.bouncyCastlePkixFipsVersion}" +libraries.bouncyCastleFipsProv = "org.bouncycastle:bc-fips:${versions.bouncyCastleFipsVersion}" +libraries.bouncyCastleTlsFips = "org.bouncycastle:bctls-fips:${versions.bouncyCastleTlsFipsVersion}" libraries.braveInstrumentationSpringWebmvc = "io.zipkin.brave:brave-instrumentation-spring-webmvc:${versions.braveVersion}" libraries.braveContextSlf4j = "io.zipkin.brave:brave-context-slf4j:${versions.braveVersion}" libraries.commonsCodec = "commons-codec:commons-codec:1.17.0" @@ -78,6 +81,7 @@ libraries.lombok = "org.projectlombok:lombok" libraries.mariaJdbcDriver = "org.mariadb.jdbc:mariadb-java-client" libraries.mockito = "org.mockito:mockito-core" libraries.mockitoJunit5 = "org.mockito:mockito-junit-jupiter" +libraries.openSamlApi = "org.opensaml:opensaml-saml-api:${versions.opensaml}" libraries.passay = "org.passay:passay:1.6.4" libraries.postgresql = "org.postgresql:postgresql:42.7.3" libraries.selenium = "org.seleniumhq.selenium:selenium-java:${versions.seleniumVersion}" @@ -127,6 +131,7 @@ libraries.velocity = "org.apache.velocity:velocity-engine-core:2.3" libraries.xerces = "xerces:xercesImpl:2.12.2" libraries.nimbusJwt = "com.nimbusds:nimbus-jose-jwt:9.40" libraries.xmlSecurity = "org.apache.santuario:xmlsec:4.0.2" +libraries.xmlUnit = "org.xmlunit:xmlunit-assertj:2.10.0" libraries.orgJson = "org.json:json:20240303" libraries.owaspEsapi = "org.owasp.esapi:esapi:2.5.4.0" libraries.jodaTime = "joda-time:joda-time:2.12.7" @@ -139,3 +144,4 @@ libraries.springBootGradlePlugin = "org.springframework.boot:spring-boot-gradle- libraries.springDependencyMangementGradlePlugin = "io.spring.gradle:dependency-management-plugin" libraries.gradleJcocoPlugin = "org.barfuin.gradle.jacocolog:gradle-jacoco-log:3.1.0" libraries.sonarqubePlugin = "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:5.0.0.4638" +//libraries.shadowPlugin = "com.github.johnrengelman:shadow:8.1.1" diff --git a/model/src/main/java/org/cloudfoundry/identity/uaa/provider/IdentityProvider.java b/model/src/main/java/org/cloudfoundry/identity/uaa/provider/IdentityProvider.java index 76a47d37e37..b53551e3cda 100644 --- a/model/src/main/java/org/cloudfoundry/identity/uaa/provider/IdentityProvider.java +++ b/model/src/main/java/org/cloudfoundry/identity/uaa/provider/IdentityProvider.java @@ -23,7 +23,7 @@ import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; - +import lombok.Getter; import org.cloudfoundry.identity.uaa.EntityWithAlias; import org.cloudfoundry.identity.uaa.util.JsonUtils; import org.springframework.util.StringUtils; @@ -44,6 +44,7 @@ import static org.cloudfoundry.identity.uaa.util.JsonUtils.getNodeAsInt; import static org.cloudfoundry.identity.uaa.util.JsonUtils.getNodeAsString; +@Getter @JsonSerialize(using = IdentityProvider.IdentityProviderSerializer.class) @JsonDeserialize(using = IdentityProvider.IdentityProviderDeserializer.class) public class IdentityProvider implements EntityWithAlias { @@ -78,45 +79,32 @@ public class IdentityProvider impl private String identityZoneId; private String aliasId; private String aliasZid; - public Date getCreated() { - return created; - } + @JsonIgnore + private boolean serializeConfigRaw; - public IdentityProvider setCreated(Date created) { + public IdentityProvider setCreated(Date created) { this.created = created; return this; } - public Date getLastModified() { - return lastModified; - } - - public IdentityProvider setLastModified(Date lastModified) { + public IdentityProvider setLastModified(Date lastModified) { this.lastModified = lastModified; return this; } - public IdentityProvider setVersion(int version) { + public IdentityProvider setVersion(int version) { this.version = version; return this; } - public int getVersion() { - return version; - } - - public String getName() { - return name; - } - - public IdentityProvider setName(String name) { + public IdentityProvider setName(String name) { this.name = name; return this; } - @Override - public String getId() { - return id; + public IdentityProvider setId(String id) { + this.id = id; + return this; } @Override @@ -124,16 +112,7 @@ public String getZoneId() { return getIdentityZoneId(); } - public IdentityProvider setId(String id) { - this.id = id; - return this; - } - - public T getConfig() { - return config; - } - - public IdentityProvider setConfig(T config) { + public IdentityProvider setConfig(T config) { if (config == null) { this.type = UNKNOWN; } else { @@ -166,11 +145,7 @@ public IdentityProvider setConfig(T config) { return this; } - public String getOriginKey() { - return originKey; - } - - public IdentityProvider setOriginKey(String originKey) { + public IdentityProvider setOriginKey(String originKey) { this.originKey = originKey; if (config != null && config instanceof SamlIdentityProviderDefinition) { ((SamlIdentityProviderDefinition) config).setIdpEntityAlias(originKey); @@ -179,29 +154,17 @@ public IdentityProvider setOriginKey(String originKey) { return this; } - public String getType() { - return type; - } - - public IdentityProvider setType(String type) { + public IdentityProvider setType(String type) { this.type = type; return this; } - public boolean isActive() { - return active; - } - - public IdentityProvider setActive(boolean active) { + public IdentityProvider setActive(boolean active) { this.active = active; return this; } - public String getIdentityZoneId() { - return identityZoneId; - } - - public IdentityProvider setIdentityZoneId(String identityZoneId) { + public IdentityProvider setIdentityZoneId(String identityZoneId) { this.identityZoneId = identityZoneId; if (config != null && config instanceof SamlIdentityProviderDefinition) { ((SamlIdentityProviderDefinition) config).setZoneId(identityZoneId); @@ -209,21 +172,11 @@ public IdentityProvider setIdentityZoneId(String identityZoneId) { return this; } - @Override - public String getAliasId() { - return aliasId; - } - @Override public void setAliasId(String aliasId) { this.aliasId = aliasId; } - @Override - public String getAliasZid() { - return aliasZid; - } - @Override public void setAliasZid(String aliasZid) { this.aliasZid = aliasZid; @@ -304,9 +257,7 @@ public boolean equals(Object obj) { } else if (!aliasZid.equals(other.aliasZid)) { return false; } - if (version != other.version) - return false; - return true; + return version == other.version; } @Override @@ -344,13 +295,6 @@ public String toString() { return sb.toString(); } - private boolean serializeConfigRaw; - - @JsonIgnore - public boolean isSerializeConfigRaw() { - return serializeConfigRaw; - } - @JsonIgnore public void setSerializeConfigRaw(boolean serializeConfigRaw) { this.serializeConfigRaw = serializeConfigRaw; @@ -446,8 +390,5 @@ public IdentityProvider deserialize(JsonParser jp, DeserializationContext ctxt) result.setAliasZid(getNodeAsString(node, FIELD_ALIAS_ZID, null)); return result; } - - } - } diff --git a/scripts/count-disabled-tests.sh b/scripts/count-disabled-tests.sh new file mode 100755 index 00000000000..647ceaf157b --- /dev/null +++ b/scripts/count-disabled-tests.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# +# Gives counts of Disabled/Ignored Unit/Integration tests in the project +# Usage: count-disabled-tests.sh [-l] +# -l: List the disabled/ignored tests + +function main() { + local tempFile + local searchFor + local disableCount + local ignoreCount + local total + local unitTestsCount + local integrationTestsCount + + tempFile=$(mktemp) + searchFor='Disabled' + find . -type f \( ! -wholename '*/target/*' ! -wholename './node_modules/*' ! -wholename '*/tmp/*' ! -wholename './out/*' ! -wholename '*/.gradle/*' ! -wholename '*/build/*' ! -wholename './.idea/*' ! -wholename './.git/*' \) -exec grep -H -A 1 "@$searchFor" {} \; | sed -e "s/^\.\///" | sed "/^--$/d; /\@${searchFor}/d" >"$tempFile" + disableCount=$(wc -l <"$tempFile") + + searchFor='Ignore' + find . -type f \( ! -wholename '*/target/*' ! -wholename './node_modules/*' ! -wholename '*/tmp/*' ! -wholename './out/*' ! -wholename '*/.gradle/*' ! -wholename '*/build/*' ! -wholename './.idea/*' ! -wholename './.git/*' \) -exec grep -H -A 1 "@$searchFor" {} \; | sed -e "s/^\.\///" | sed "/^--$/d; /\@${searchFor}/d" >>"$tempFile" + total=$(wc -l <"$tempFile") + ignoreCount=$(($total - $disableCount)) + + echo "Disabled: $disableCount" + echo "Ignored: $ignoreCount" + echo "Total: $total" + echo + + unitTestsCount=$(cat "$tempFile" | grep -v "IT.java" | wc -l) + integrationTestsCount=$(cat "$tempFile" | grep "IT.java" | wc -l) + echo "Unit Tests: $unitTestsCount" + echo "Integration Tests: $integrationTestsCount" + echo "Total: $total" + + if [[ "$1" -eq "-l" ]]; then + echo + echo Unit Tests: + echo + cat "$tempFile" | grep -v "IT.java" | sort + + echo + echo Integration Tests: + echo + cat "$tempFile" | grep "IT.java" | sort + + fi + + rm "$tempFile" +} + +main "$@" diff --git a/server/build.gradle b/server/build.gradle index 20ddae6f675..f9a2c289933 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -5,6 +5,8 @@ description = "CloudFoundry Identity Server JAR" dependencies { implementation(project(":cloudfoundry-identity-metrics-data")) implementation(project(":cloudfoundry-identity-model")) + // Shadow library is needed for FIPS compliance, as opensaml-security-api relies on non-FIPS compliant libraries + //implementation(project(path: ':cloudfoundry-identity-shadow-opensaml-security-api', configuration: 'shadow')) implementation(libraries.tomcatJdbc) providedCompile(libraries.tomcatEmbed) @@ -31,8 +33,9 @@ dependencies { implementation(libraries.xmlSecurity) implementation(libraries.springSessionJdbc) - implementation(libraries.bouncyCastleProv) - implementation(libraries.bouncyCastlePkix) + implementation(libraries.bouncyCastleFipsProv) + implementation(libraries.bouncyCastleTlsFips) + implementation(libraries.bouncyCastlePkixFips) implementation(libraries.guava) @@ -116,11 +119,18 @@ dependencies { configurations.all { exclude(group: "org.beanshell", module: "bsh-core") exclude(group: "org.apache-extras.beanshell", module: "bsh") - exclude(group: "org.bouncycastle", module: "bcpkix-jdk15on") - exclude(group: "org.bouncycastle", module: "bcprov-jdk15on") exclude(group: "com.fasterxml.woodstox", module: "woodstox-core") exclude(group: "commons-beanutils", module: "commons-beanutils") exclude(group: "commons-collections", module: "commons-collections") + + // Exclude opensaml-security-api and non-FIPS bouncycastle libs, and use Shadow library for FIPS compliance + //exclude(group: "org.opensaml", module: "opensaml-security-api") + exclude(group: "org.bouncycastle", module: "bcpkix-jdk15on") + exclude(group: "org.bouncycastle", module: "bcprov-jdk15on") + exclude(group: "org.bouncycastle", module: "bcutil-jdk15on") + exclude(group: "org.bouncycastle", module: "bcprov-jdk18on") + exclude(group: "org.bouncycastle", module: "bcpkix-jdk18on") + exclude(group: "org.bouncycastle", module: "bcutil-jdk18on") } jar { diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/UaaAuthentication.java b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/UaaAuthentication.java index 8d1b621ea0b..10205f24ca0 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/UaaAuthentication.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/UaaAuthentication.java @@ -14,6 +14,10 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.util.LinkedMultiValueMap; @@ -26,19 +30,21 @@ import java.util.Map; import java.util.Set; -import static java.util.Collections.EMPTY_MAP; +import static java.util.Collections.emptyMap; /** * Authentication token which represents a user. */ @JsonSerialize(using = UaaAuthenticationSerializer.class) @JsonDeserialize(using = UaaAuthenticationDeserializer.class) -public class UaaAuthentication implements Authentication, Serializable { - - private Collection authorities; - private Object credentials; - private UaaPrincipal principal; - private UaaAuthenticationDetails details; +@Getter +@Setter +@ToString +public class UaaAuthentication extends AbstractAuthenticationToken + implements Authentication, Serializable { + + private final Object credentials; + private final UaaPrincipal principal; private boolean authenticated; private long authenticatedTime = -1L; private long expiresAt = -1L; @@ -46,17 +52,7 @@ public class UaaAuthentication implements Authentication, Serializable { private Set authenticationMethods; private Set authContextClassRef; private Long lastLoginSuccessTime; - - private Map userAttributes; - - public Long getLastLoginSuccessTime() { - return lastLoginSuccessTime; - } - - public UaaAuthentication setLastLoginSuccessTime(Long lastLoginSuccessTime) { - this.lastLoginSuccessTime = lastLoginSuccessTime; - return this; - } + private Map> userAttributes; /** * Creates a token with the supplied array of authorities. @@ -86,12 +82,13 @@ public UaaAuthentication(UaaPrincipal principal, boolean authenticated, long authenticatedTime, long expiresAt) { + super(authorities); + if (principal == null || authorities == null) { throw new IllegalArgumentException("principal and authorities must not be null"); } + setDetails(details); this.principal = principal; - this.authorities = authorities; - this.details = details; this.credentials = credentials; this.authenticated = authenticated; this.authenticatedTime = authenticatedTime <= 0 ? -1 : authenticatedTime; @@ -112,18 +109,14 @@ public UaaAuthentication(UaaPrincipal uaaPrincipal, this.userAttributes = new HashMap<>(userAttributes); } - public UaaAuthentication(UaaAuthentication existing, UaaPrincipal principal) { - - this(principal, existing.getCredentials(), List.copyOf(existing.authorities), existing.getExternalGroups(), - existing.getUserAttributes(), existing.details, existing.isAuthenticated(), - existing.getAuthenticatedTime(), existing.getExpiresAt()); - this.authContextClassRef = existing.authContextClassRef; - this.authenticationMethods = existing.authenticationMethods; - this.lastLoginSuccessTime = existing.lastLoginSuccessTime; - } + public UaaAuthentication(UaaAuthentication existingAuthn, UaaPrincipal principal) { - public long getAuthenticatedTime() { - return authenticatedTime; + this(principal, existingAuthn.getCredentials(), List.copyOf(existingAuthn.getAuthorities()), existingAuthn.getExternalGroups(), + existingAuthn.getUserAttributes(), existingAuthn.getUaaAuthenticationDetails(), existingAuthn.isAuthenticated(), + existingAuthn.getAuthenticatedTime(), existingAuthn.getExpiresAt()); + this.authContextClassRef = existingAuthn.authContextClassRef; + this.authenticationMethods = existingAuthn.authenticationMethods; + this.lastLoginSuccessTime = existingAuthn.lastLoginSuccessTime; } @Override @@ -133,29 +126,13 @@ public String getName() { return principal.getName(); } - @Override - public Collection getAuthorities() { - return authorities; - } - - @Override - public Object getCredentials() { - return credentials; - } - - @Override - public Object getDetails() { - return details; - } - - @Override - public UaaPrincipal getPrincipal() { - return principal; + public UaaAuthenticationDetails getUaaAuthenticationDetails() { + return (UaaAuthenticationDetails) getDetails(); } @Override public boolean isAuthenticated() { - return authenticated && (expiresAt > 0 ? expiresAt > System.currentTimeMillis() : true); + return authenticated && (expiresAt <= 0 || expiresAt > System.currentTimeMillis()); } @Override @@ -163,10 +140,6 @@ public void setAuthenticated(boolean isAuthenticated) { authenticated = isAuthenticated; } - public long getExpiresAt() { - return expiresAt; - } - @Override public boolean equals(Object o) { if (this == o) { @@ -178,78 +151,33 @@ public boolean equals(Object o) { UaaAuthentication that = (UaaAuthentication) o; - if (!authorities.equals(that.authorities)) { + if (!getAuthorities().equals(that.getAuthorities())) { return false; } - if (!principal.equals(that.principal)) { - return false; - } - - return true; + return principal.equals(that.principal); } @Override public int hashCode() { - int result = authorities.hashCode(); + int result = getAuthorities().hashCode(); result = 31 * result + principal.hashCode(); return result; } - public Set getExternalGroups() { - return externalGroups; - } - - public void setExternalGroups(Set externalGroups) { - this.externalGroups = externalGroups; - } - public MultiValueMap getUserAttributes() { - return new LinkedMultiValueMap<>(userAttributes != null ? userAttributes : EMPTY_MAP); - } - - public Map> getUserAttributesAsMap() { - return userAttributes != null ? new HashMap<>(userAttributes) : EMPTY_MAP; + return new LinkedMultiValueMap<>(userAttributes != null ? userAttributes : emptyMap()); } public void setUserAttributes(MultiValueMap userAttributes) { this.userAttributes = new HashMap<>(); - for (Map.Entry> entry : userAttributes.entrySet()) { - this.userAttributes.put(entry.getKey(), entry.getValue()); - } - } -// -// @JsonIgnore -// public SAMLMessageContext getSamlMessageContext() { -// return samlMessageContext; -// } -// -// @JsonIgnore -// public void setSamlMessageContext(SAMLMessageContext samlMessageContext) { -// this.samlMessageContext = samlMessageContext; -// } - - public Set getAuthenticationMethods() { - return authenticationMethods; - } - - public void setAuthenticationMethods(Set authenticationMethods) { - - this.authenticationMethods = authenticationMethods; + this.userAttributes.putAll(userAttributes); } - public Set getAuthContextClassRef() { - return authContextClassRef; - } - - public void setAuthContextClassRef(Set authContextClassRef) { - this.authContextClassRef = authContextClassRef; - } - - public void setAuthenticatedTime(long authenticatedTime) { - this.authenticatedTime = authenticatedTime; + public Map> getUserAttributesAsMap() { + return userAttributes != null ? new HashMap<>(userAttributes) : emptyMap(); } public void setAuthenticationDetails(UaaAuthenticationDetails authenticationDetails) { - this.details = authenticationDetails; + setDetails(authenticationDetails); } } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/UaaPrincipal.java b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/UaaPrincipal.java index d67acf46b97..aa20dc4ca8c 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/UaaPrincipal.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/UaaPrincipal.java @@ -12,14 +12,15 @@ *******************************************************************************/ package org.cloudfoundry.identity.uaa.authentication; -import java.io.Serializable; -import java.security.Principal; - import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; import org.cloudfoundry.identity.uaa.user.UaaUser; import org.cloudfoundry.identity.uaa.user.UaaUserPrototype; +import java.io.Serializable; +import java.security.Principal; + /** * The principal object which should end up as the representation of an * authenticated user. @@ -27,6 +28,7 @@ * Contains the data required for an authenticated user within the UAA * application itself. */ +@Data public class UaaPrincipal implements Principal, Serializable { private final String id; private final String name; @@ -37,33 +39,34 @@ public class UaaPrincipal implements Principal, Serializable { public UaaPrincipal(UaaUser user) { this( - user.getId(), - user.getUsername(), - user.getEmail(), - user.getOrigin(), - user.getExternalId(), - user.getZoneId() + user.getId(), + user.getUsername(), + user.getEmail(), + user.getOrigin(), + user.getExternalId(), + user.getZoneId() ); } public UaaPrincipal(UaaUserPrototype userPrototype) { this( - userPrototype.getId(), - userPrototype.getUsername(), - userPrototype.getEmail(), - userPrototype.getOrigin(), - userPrototype.getExternalId(), - userPrototype.getZoneId() + userPrototype.getId(), + userPrototype.getUsername(), + userPrototype.getEmail(), + userPrototype.getOrigin(), + userPrototype.getExternalId(), + userPrototype.getZoneId() ); } + @JsonCreator public UaaPrincipal( - @JsonProperty("id") String id, - @JsonProperty("name") String username, - @JsonProperty("email") String email, - @JsonProperty("origin") String origin, - @JsonProperty("externalId") String externalId, - @JsonProperty("zoneId") String zoneId) { + @JsonProperty("id") String id, + @JsonProperty("name") String username, + @JsonProperty("email") String email, + @JsonProperty("origin") String origin, + @JsonProperty("externalId") String externalId, + @JsonProperty("zoneId") String zoneId) { this.id = id; this.name = username; this.email = email; @@ -72,25 +75,6 @@ public UaaPrincipal( this.zoneId = zoneId; } - public String getId() { - return id; - } - - @Override - public String getName() { - return name; - } - - public String getEmail() { - return email; - } - - public String getOrigin() { return origin; } - - public String getExternalId() { return externalId; } - - public String getZoneId() { return zoneId; } - /** * Returns {@code true} if the supplied object is a {@code UAAPrincipal} * instance with the @@ -101,8 +85,8 @@ public String getEmail() { */ @Override public boolean equals(Object rhs) { - if (rhs instanceof UaaPrincipal) { - return id.equals(((UaaPrincipal) rhs).id); + if (rhs instanceof UaaPrincipal uaaPrincipal) { + return id.equals(uaaPrincipal.id); } return false; } @@ -114,5 +98,4 @@ public boolean equals(Object rhs) { public int hashCode() { return id.hashCode(); } - } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/impl/config/IdentityProviderBootstrap.java b/server/src/main/java/org/cloudfoundry/identity/uaa/impl/config/IdentityProviderBootstrap.java index 8d00dc21484..23a946678b1 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/impl/config/IdentityProviderBootstrap.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/impl/config/IdentityProviderBootstrap.java @@ -12,11 +12,8 @@ *******************************************************************************/ package org.cloudfoundry.identity.uaa.impl.config; - -import org.cloudfoundry.identity.uaa.provider.JdbcIdentityProviderProvisioning; -import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.Getter; +import lombok.Setter; import org.cloudfoundry.identity.uaa.audit.event.EntityDeletedEvent; import org.cloudfoundry.identity.uaa.constants.OriginKeys; import org.cloudfoundry.identity.uaa.provider.AbstractIdentityProviderDefinition; @@ -32,6 +29,9 @@ import org.cloudfoundry.identity.uaa.util.LdapUtils; import org.cloudfoundry.identity.uaa.util.UaaMapUtils; import org.cloudfoundry.identity.uaa.zone.IdentityZone; +import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationEventPublisher; @@ -59,27 +59,33 @@ import static org.cloudfoundry.identity.uaa.provider.LdapIdentityProviderDefinition.LDAP_PROPERTY_TYPES; public class IdentityProviderBootstrap - implements InitializingBean, ApplicationListener, ApplicationEventPublisherAware { - private static Logger logger = LoggerFactory.getLogger(IdentityProviderBootstrap.class); + implements InitializingBean, ApplicationListener, ApplicationEventPublisherAware { + private static final Logger logger = LoggerFactory.getLogger(IdentityProviderBootstrap.class); - private IdentityProviderProvisioning provisioning; - private List providers = new LinkedList<>(); + private final IdentityProviderProvisioning provisioning; + private final List providers = new LinkedList<>(); + private final Environment environment; private BootstrapSamlIdentityProviderData configurator; private List oauthIdpDefintions; + @Setter private Map ldapConfig; private Map keystoneConfig; - private Environment environment; + @Setter private PasswordPolicy defaultPasswordPolicy; + @Setter private LockoutPolicy defaultLockoutPolicy; + @Getter + @Setter private boolean disableInternalUserManagement; + @Setter private List originsToDelete = null; private ApplicationEventPublisher publisher; public IdentityProviderBootstrap( final @Qualifier("identityProviderProvisioning") IdentityProviderProvisioning provisioning, Environment environment) { - if (provisioning==null) { + if (provisioning == null) { throw new NullPointerException("Constructor argument can't be null."); } this.provisioning = provisioning; @@ -98,7 +104,7 @@ private void addOauthProviders() { } public void validateDuplicateAlias(String originKey) { - for (IdentityProvider provider: providers.stream().map(IdentityProviderWrapper::getProvider).collect(toList())) { + for (IdentityProvider provider : providers.stream().map(IdentityProviderWrapper::getProvider).collect(toList())) { if (provider.getOriginKey().equals(originKey)) { throw new IllegalArgumentException("Provider alias " + originKey + " is not unique."); } @@ -108,8 +114,9 @@ public void validateDuplicateAlias(String originKey) { public void setSamlProviders(BootstrapSamlIdentityProviderData configurator) { this.configurator = configurator; } + protected void addSamlProviders() { - if (configurator==null) { + if (configurator == null) { return; } for (IdentityProviderWrapper wrapper : configurator.getSamlProviders()) { @@ -119,20 +126,16 @@ protected void addSamlProviders() { } - public void setLdapConfig(HashMap ldapConfig) { - this.ldapConfig = ldapConfig; - } - protected void addLdapProvider() { boolean ldapProfile = Arrays.asList(environment.getActiveProfiles()).contains(LDAP); //the LDAP provider has to be there //and we activate, deactivate based on the `ldap` profile presence - IdentityProvider provider = new IdentityProvider(); + IdentityProvider provider = new IdentityProvider<>(); provider.setActive(ldapProfile); provider.setOriginKey(LDAP); provider.setType(LDAP); provider.setName("UAA LDAP Provider"); - Map ldap = new HashMap<>(); + Map ldap = new HashMap<>(); ldap.put(LdapIdentityProviderDefinition.LDAP, ldapConfig); LdapIdentityProviderDefinition json = getLdapConfigAsDefinition(ldap); provider.setConfig(json); @@ -141,7 +144,7 @@ protected void addLdapProvider() { LDAP is a bit tricky. We have a Flyway conversion (2.0.2) that always adds an LDAP provider. So we have to assume that if LDAP config == null, then we should override it */ - boolean override = ldapConfig == null || ldapConfig.get("override") == null ? true : (boolean) ldapConfig.get("override"); + boolean override = ldapConfig == null || ldapConfig.get("override") == null || (boolean) ldapConfig.get("override"); if (!override) { IdentityProvider existing = getProviderByOriginIgnoreActiveFlag(LDAP, IdentityZone.getUaaZoneId()); override = existing == null || existing.getConfig() == null; @@ -151,8 +154,6 @@ LDAP is a bit tricky. We have a Flyway conversion (2.0.2) that always adds an LD providers.add(wrapper); } - - protected LdapIdentityProviderDefinition getLdapConfigAsDefinition(Map ldapConfig) { ldapConfig = UaaMapUtils.flatten(ldapConfig); populateLdapEnvironment(ldapConfig); @@ -164,16 +165,16 @@ protected LdapIdentityProviderDefinition getLdapConfigAsDefinition(Map ldapConfig) { //this method reads the environment and overwrites values (needed by LdapMockMvcTests that overrides properties through env) - AbstractEnvironment env = (AbstractEnvironment)environment; + AbstractEnvironment env = (AbstractEnvironment) environment; //these are our known complex data structures in the properties for (String property : LDAP_PROPERTY_NAMES) { - if (env.containsProperty(property) && LDAP_PROPERTY_TYPES.get(property)!=null) { + if (env.containsProperty(property) && LDAP_PROPERTY_TYPES.get(property) != null) { ldapConfig.put(property, env.getProperty(property, LDAP_PROPERTY_TYPES.get(property))); } } //but we can also have string properties like ldap.attributeMappings.user.attribute.mapToAttributeName=mapFromAttributeName - Map stringProperties = UaaMapUtils.getPropertiesStartingWith(env, "ldap."); + Map stringProperties = UaaMapUtils.getPropertiesStartingWith(env, "ldap."); for (Map.Entry entry : stringProperties.entrySet()) { if (!LDAP_PROPERTY_NAMES.contains(entry.getKey())) { ldapConfig.put(entry.getKey(), entry.getValue()); @@ -192,8 +193,8 @@ protected AbstractIdentityProviderDefinition getKeystoneDefinition(Map(); provider.setOriginKey(OriginKeys.KEYSTONE); provider.setType(OriginKeys.KEYSTONE); provider.setName("UAA Keystone Provider"); @@ -224,7 +225,7 @@ public void afterPropertiesSet() throws Exception { String zoneId = IdentityZone.getUaaZoneId(); - for (IdentityProviderWrapper wrapper: providers) { + for (IdentityProviderWrapper wrapper : providers) { IdentityProvider provider = wrapper.getProvider(); if (getOriginsToDelete().contains(provider.getOriginKey())) { //dont process origins slated for deletion @@ -232,7 +233,7 @@ public void afterPropertiesSet() throws Exception { } IdentityProvider existing = getProviderByOriginIgnoreActiveFlag(provider.getOriginKey(), zoneId); provider.setIdentityZoneId(zoneId); - if (existing==null) { + if (existing == null) { provisioning.create(provider, zoneId); } else if (wrapper.isOverride()) { provider.setId(existing.getId()); @@ -248,7 +249,7 @@ public void afterPropertiesSet() throws Exception { public IdentityProvider getProviderByOriginIgnoreActiveFlag(String origin, String zoneId) { try { return provisioning.retrieveByOriginIgnoreActiveFlag(origin, zoneId); - }catch (EmptyResultDataAccessException ignored){ + } catch (EmptyResultDataAccessException ignored) { } return null; @@ -257,19 +258,16 @@ public IdentityProvider getProviderByOriginIgnoreActiveFlag(String origin, Strin private void deleteIdentityProviders(String zoneId) { for (String origin : getOriginsToDelete()) { if (!UAA.equals(origin) && !LDAP.equals(origin)) { - logger.debug("Attempting to deactivating identity provider:"+origin); + logger.debug("Attempting to deactivating identity provider:" + origin); IdentityProvider provider = getProviderByOriginIgnoreActiveFlag(origin, zoneId); //delete provider if (provider != null) { EntityDeletedEvent event = new EntityDeletedEvent<>(provider, SYSTEM_AUTHENTICATION, IdentityZoneHolder.getCurrentZoneId()); if (this.publisher != null) { publisher.publishEvent(event); - logger.debug("Identity provider deactivated:" + origin); + logger.debug("Identity provider deactivated: {}", origin); } else { - logger.warn( - String.format("Unable to delete identity provider with origin '%s', no application publisher", - origin) - ); + logger.warn("Unable to delete identity provider with origin '{}', no application publisher", origin); } } } @@ -294,32 +292,11 @@ protected boolean getBooleanValue(String s, boolean defaultValue) { } } - public void setDefaultPasswordPolicy(PasswordPolicy defaultPasswordPolicy) { - this.defaultPasswordPolicy = defaultPasswordPolicy; - } - - public void setDefaultLockoutPolicy(LockoutPolicy defaultLockoutPolicy) { - this.defaultLockoutPolicy = defaultLockoutPolicy; - } - - public boolean isDisableInternalUserManagement() { - return disableInternalUserManagement; - } - - public void setDisableInternalUserManagement(boolean disableInternalUserManagement) { - this.disableInternalUserManagement = disableInternalUserManagement; - } - public void setOauthIdpDefinitions(List oauthIdpDefintions) { this.oauthIdpDefintions = oauthIdpDefintions; } - public void setOriginsToDelete(List originsToDelete) { - this.originsToDelete = originsToDelete; - } - public List getOriginsToDelete() { return ofNullable(originsToDelete).orElse(emptyList()); } - } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/BootstrapSamlIdentityProviderData.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/BootstrapSamlIdentityProviderData.java index af527c51354..8378dd4f35c 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/BootstrapSamlIdentityProviderData.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/BootstrapSamlIdentityProviderData.java @@ -12,13 +12,6 @@ *******************************************************************************/ package org.cloudfoundry.identity.uaa.provider.saml; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.cloudfoundry.identity.uaa.constants.OriginKeys; @@ -28,11 +21,15 @@ import org.cloudfoundry.identity.uaa.provider.SamlIdentityProviderDefinition.ExternalGroupMappingMode; import org.cloudfoundry.identity.uaa.util.JsonUtils; import org.cloudfoundry.identity.uaa.zone.IdentityZone; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + import static java.util.Collections.emptyList; import static java.util.Optional.ofNullable; import static org.cloudfoundry.identity.uaa.provider.AbstractIdentityProviderDefinition.EMAIL_DOMAIN_ATTR; @@ -54,7 +51,18 @@ public class BootstrapSamlIdentityProviderData implements InitializingBean { private List> samlProviders = new LinkedList<>(); private Map> providers = null; - public BootstrapSamlIdentityProviderData() { + public static IdentityProvider parseSamlProvider(SamlIdentityProviderDefinition def) { + IdentityProvider provider = new IdentityProvider(); + provider.setType(OriginKeys.SAML); + provider.setOriginKey(def.getIdpEntityAlias()); + provider.setName("UAA SAML Identity Provider[" + provider.getOriginKey() + "]"); + provider.setActive(true); + try { + provider.setConfig(def); + } catch (JsonUtils.JsonUtilException x) { + throw new RuntimeException("Non serializable SAML config"); + } + return provider; } public List getIdentityProviderDefinitions() { @@ -64,22 +72,22 @@ public List getIdentityProviderDefinitions() { } protected void parseIdentityProviderDefinitions() { - if (getLegacyIdpMetaData()!=null) { + if (getLegacyIdpMetaData() != null) { SamlIdentityProviderDefinition def = new SamlIdentityProviderDefinition(); def.setMetaDataLocation(getLegacyIdpMetaData()); def.setMetadataTrustCheck(isLegacyMetadataTrustCheck()); def.setNameID(getLegacyNameId()); def.setAssertionConsumerIndex(getLegacyAssertionConsumerIndex()); String alias = getLegacyIdpIdentityAlias(); - if (alias==null) { + if (alias == null) { throw new IllegalArgumentException("Invalid IDP - Alias must be not null for deprecated IDP."); } def.setIdpEntityAlias(alias); def.setShowSamlLink(isLegacyShowSamlLink()); def.setLinkText("Use your corporate credentials"); def.setZoneId(IdentityZone.getUaaZoneId()); //legacy only has UAA zone - log.debug("Legacy SAML provider configured with alias: "+alias); - IdentityProviderWrapper wrapper = new IdentityProviderWrapper(parseSamlProvider(def)); + log.debug("Legacy SAML provider configured with alias: " + alias); + IdentityProviderWrapper wrapper = new IdentityProviderWrapper<>(parseSamlProvider(def)); wrapper.setOverride(true); samlProviders.add(wrapper); } @@ -87,7 +95,7 @@ protected void parseIdentityProviderDefinitions() { for (IdentityProviderWrapper wrapper : samlProviders) { String alias = getUniqueAlias((SamlIdentityProviderDefinition) wrapper.getProvider().getConfig()); if (uniqueAlias.contains(alias)) { - throw new IllegalStateException("Duplicate IDP alias found:"+alias); + throw new IllegalStateException("Duplicate IDP alias found:" + alias); } uniqueAlias.add(alias); } @@ -101,32 +109,33 @@ public void setIdentityProviders(Map> providers) { if (providers == null) { return; } + this.providers = providers; for (Map.Entry entry : providers.entrySet()) { - String alias = (String)entry.getKey(); - Map saml = (Map)entry.getValue(); - String metaDataLocation = (String)saml.get("idpMetadata"); - String nameID = (String)saml.get("nameID"); - Integer assertionIndex = (Integer)saml.get("assertionConsumerIndex"); - Boolean trustCheck = (Boolean)saml.get("metadataTrustCheck"); - Boolean showLink = (Boolean)((Map)entry.getValue()).get("showSamlLoginLink"); - String socketFactoryClassName = (String)saml.get("socketFactoryClassName"); - String linkText = (String)((Map)entry.getValue()).get("linkText"); - String iconUrl = (String)((Map)entry.getValue()).get("iconUrl"); - String zoneId = (String)((Map)entry.getValue()).get("zoneId"); - String groupMappingMode = (String)((Map)entry.getValue()).get("groupMappingMode"); - String providerDescription = (String)((Map)entry.getValue()).get(PROVIDER_DESCRIPTION); - Boolean addShadowUserOnLogin = (Boolean)((Map)entry.getValue()).get("addShadowUserOnLogin"); - Boolean skipSslValidation = (Boolean)((Map)entry.getValue()).get("skipSslValidation"); - Boolean storeCustomAttributes = (Boolean)((Map)entry.getValue()).get(STORE_CUSTOM_ATTRIBUTES_NAME); - Boolean override = (Boolean)((Map)entry.getValue()).get("override"); + String alias = (String) entry.getKey(); + Map saml = (Map) entry.getValue(); + String metaDataLocation = (String) saml.get("idpMetadata"); + String nameID = (String) saml.get("nameID"); + Integer assertionIndex = (Integer) saml.get("assertionConsumerIndex"); + Boolean trustCheck = (Boolean) saml.get("metadataTrustCheck"); + Boolean showLink = (Boolean) ((Map) entry.getValue()).get("showSamlLoginLink"); + String socketFactoryClassName = (String) saml.get("socketFactoryClassName"); + String linkText = (String) ((Map) entry.getValue()).get("linkText"); + String iconUrl = (String) ((Map) entry.getValue()).get("iconUrl"); + String zoneId = (String) ((Map) entry.getValue()).get("zoneId"); + String groupMappingMode = (String) ((Map) entry.getValue()).get("groupMappingMode"); + String providerDescription = (String) ((Map) entry.getValue()).get(PROVIDER_DESCRIPTION); + Boolean addShadowUserOnLogin = (Boolean) ((Map) entry.getValue()).get("addShadowUserOnLogin"); + Boolean skipSslValidation = (Boolean) ((Map) entry.getValue()).get("skipSslValidation"); + Boolean storeCustomAttributes = (Boolean) ((Map) entry.getValue()).get(STORE_CUSTOM_ATTRIBUTES_NAME); + Boolean override = (Boolean) ((Map) entry.getValue()).get("override"); List authnContext = (List) saml.get("authnContext"); if (storeCustomAttributes == null) { storeCustomAttributes = true; //default value } - if (skipSslValidation==null) { + if (skipSslValidation == null) { skipSslValidation = socketFactoryClassName == null; } @@ -138,19 +147,21 @@ public void setIdentityProviders(Map> providers) { if (hasText(providerDescription)) { def.setProviderDescription(providerDescription); } - if (alias==null) { - throw new IllegalArgumentException("Invalid IDP - alias must not be null ["+metaDataLocation+"]"); + if (alias == null) { + throw new IllegalArgumentException("Invalid IDP - alias must not be null [" + metaDataLocation + "]"); } - if (metaDataLocation==null) { - throw new IllegalArgumentException("Invalid IDP - metaDataLocation must not be null ["+alias+"]"); + if (metaDataLocation == null) { + throw new IllegalArgumentException("Invalid IDP - metaDataLocation must not be null [" + alias + "]"); } def.setIdpEntityAlias(alias); - def.setAssertionConsumerIndex(assertionIndex== null ? 0 :assertionIndex); + def.setAssertionConsumerIndex(assertionIndex == null ? 0 : assertionIndex); def.setMetaDataLocation(metaDataLocation); def.setNameID(nameID); - def.setMetadataTrustCheck(trustCheck==null?true:trustCheck); - if(hasText(groupMappingMode)) { def.setGroupMappingMode(ExternalGroupMappingMode.valueOf(groupMappingMode)); } - def.setShowSamlLink(showLink==null?true: showLink); + def.setMetadataTrustCheck(trustCheck == null || trustCheck); + if (hasText(groupMappingMode)) { + def.setGroupMappingMode(ExternalGroupMappingMode.valueOf(groupMappingMode)); + } + def.setShowSamlLink(showLink == null || showLink); def.setSocketFactoryClassName(socketFactoryClassName); def.setLinkText(linkText); def.setIconUrl(iconUrl); @@ -158,33 +169,18 @@ public void setIdentityProviders(Map> providers) { def.setExternalGroupsWhitelist(externalGroupsWhitelist); def.setAttributeMappings(attributeMappings); def.setZoneId(hasText(zoneId) ? zoneId : IdentityZone.getUaaZoneId()); - def.setAddShadowUserOnLogin(addShadowUserOnLogin==null?true:addShadowUserOnLogin); + def.setAddShadowUserOnLogin(addShadowUserOnLogin == null || addShadowUserOnLogin); def.setSkipSslValidation(skipSslValidation); def.setAuthnContext(authnContext); - IdentityProvider provider = parseSamlProvider(def); IdentityProviderWrapper wrapper = new IdentityProviderWrapper(provider); - wrapper.setOverride(override == null ? true : override); + wrapper.setOverride(override == null || override); samlProviders.add(wrapper); } } - public static IdentityProvider parseSamlProvider(SamlIdentityProviderDefinition def) { - IdentityProvider provider = new IdentityProvider(); - provider.setType(OriginKeys.SAML); - provider.setOriginKey(def.getIdpEntityAlias()); - provider.setName("UAA SAML Identity Provider["+provider.getOriginKey()+"]"); - provider.setActive(true); - try { - provider.setConfig(def); - } catch (JsonUtils.JsonUtilException x) { - throw new RuntimeException("Non serializable SAML config"); - } - return provider; - } - public void setLegacyIdpIdentityAlias(String legacyIdpIdentityAlias) { if ("null".equals(legacyIdpIdentityAlias)) { this.legacyIdpIdentityAlias = null; 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 7384f906a5a..41f28d472e7 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 @@ -11,6 +11,7 @@ import org.springframework.util.Assert; import java.util.List; +import java.util.function.Function; @Slf4j public class ConfiguratorRelyingPartyRegistrationRepository implements RelyingPartyRegistrationRepository { @@ -19,16 +20,19 @@ public class ConfiguratorRelyingPartyRegistrationRepository implements RelyingPa private final KeyWithCert keyWithCert; private final Boolean samlSignRequest; private final String samlEntityID; + private final Function assertionConsumerServiceLocationFunction; public ConfiguratorRelyingPartyRegistrationRepository(Boolean samlSignRequest, @Qualifier("samlEntityID") String samlEntityID, KeyWithCert keyWithCert, - SamlIdentityProviderConfigurator configurator) { + SamlIdentityProviderConfigurator configurator, + Function assertionConsumerServiceLocationFunction) { Assert.notNull(configurator, "configurator cannot be null"); this.configurator = configurator; this.keyWithCert = keyWithCert; this.samlSignRequest = samlSignRequest; this.samlEntityID = samlEntityID; + this.assertionConsumerServiceLocationFunction = assertionConsumerServiceLocationFunction; } /** @@ -55,6 +59,7 @@ private RelyingPartyRegistration buildRelyingPartyRegistration(String registrati .entityId(samlEntityID) .nameIdFormat(def.getNameID()) .registrationId(registrationId) + .assertionConsumerServiceLocation(assertionConsumerServiceLocationFunction.apply(samlEntityID)) .assertingPartyDetails(details -> details .wantAuthnRequestsSigned(samlSignRequest) ) diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml40CompatibleAssertionValidators.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml40CompatibleAssertionValidators.java new file mode 100644 index 00000000000..b25c14568e3 --- /dev/null +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml40CompatibleAssertionValidators.java @@ -0,0 +1,247 @@ +package org.cloudfoundry.identity.uaa.provider.saml; + +import org.opensaml.core.config.ConfigurationService; +import org.opensaml.core.xml.config.XMLObjectProviderRegistry; +import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; +import org.opensaml.saml.common.assertion.ValidationContext; +import org.opensaml.saml.common.assertion.ValidationResult; +import org.opensaml.saml.saml2.assertion.ConditionValidator; +import org.opensaml.saml.saml2.assertion.SAML20AssertionValidator; +import org.opensaml.saml.saml2.assertion.SAML2AssertionValidationParameters; +import org.opensaml.saml.saml2.assertion.StatementValidator; +import org.opensaml.saml.saml2.assertion.SubjectConfirmationValidator; +import org.opensaml.saml.saml2.assertion.impl.AudienceRestrictionConditionValidator; +import org.opensaml.saml.saml2.assertion.impl.BearerSubjectConfirmationValidator; +import org.opensaml.saml.saml2.assertion.impl.DelegationRestrictionConditionValidator; +import org.opensaml.saml.saml2.core.Assertion; +import org.opensaml.saml.saml2.core.AuthnRequest; +import org.opensaml.saml.saml2.core.Condition; +import org.opensaml.saml.saml2.core.OneTimeUse; +import org.opensaml.saml.saml2.core.Response; +import org.opensaml.saml.saml2.core.SubjectConfirmation; +import org.opensaml.saml.saml2.core.SubjectConfirmationData; +import org.opensaml.saml.saml2.core.impl.AuthnRequestUnmarshaller; +import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator; +import org.opensaml.xmlsec.signature.support.SignaturePrevalidator; +import org.opensaml.xmlsec.signature.support.SignatureTrustEngine; +import org.springframework.core.convert.converter.Converter; +import org.springframework.security.saml2.core.Saml2Error; +import org.springframework.security.saml2.core.Saml2ErrorCodes; +import org.springframework.security.saml2.core.Saml2ResponseValidatorResult; +import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest; +import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider; +import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException; +import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; +import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; +import org.springframework.util.StringUtils; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.annotation.Nonnull; +import javax.xml.namespace.QName; +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +/** + * This class contains functions to Validate SAML assertions. It is based on the Spring-Security + * class SAML20AssertionValidators within: + * org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider + *

+ * But that class is not compatible with OpenSaml 4.0.x + */ +public class OpenSaml40CompatibleAssertionValidators { + + private static final AuthnRequestUnmarshaller authnRequestUnmarshaller; + private static final Collection conditions = new ArrayList<>(); + private static final Collection subjects = new ArrayList<>(); + private static final Collection statements = new ArrayList<>(); + private static final SignaturePrevalidator validator = new SAMLSignatureProfileValidator(); + private static final SAML20AssertionValidator attributeValidator = new SAML20AssertionValidator(conditions, + subjects, statements, null, null) { + @Nonnull + @Override + protected ValidationResult validateSignature(Assertion token, ValidationContext context) { + return ValidationResult.VALID; + } + }; + + static { + XMLObjectProviderRegistry registry = ConfigurationService.get(XMLObjectProviderRegistry.class); + authnRequestUnmarshaller = (AuthnRequestUnmarshaller) registry.getUnmarshallerFactory() + .getUnmarshaller(AuthnRequest.DEFAULT_ELEMENT_NAME); + } + + static { + conditions.add(new AudienceRestrictionConditionValidator()); + conditions.add(new DelegationRestrictionConditionValidator()); + conditions.add(new ConditionValidator() { + @Nonnull + @Override + public QName getServicedCondition() { + return OneTimeUse.DEFAULT_ELEMENT_NAME; + } + + @Nonnull + @Override + public ValidationResult validate(Condition condition, Assertion assertion, ValidationContext context) { + // applications should validate their own OneTimeUse conditions + return ValidationResult.VALID; + } + }); + subjects.add(new BearerSubjectConfirmationValidator() { + @Override + protected ValidationResult validateAddress(SubjectConfirmation confirmation, Assertion assertion, + ValidationContext context, boolean required) { + // applications should validate their own addresses - gh-7514 + return ValidationResult.VALID; + } + }); + } + + public static Converter createDefaultAssertionValidator() { + + return createDefaultAssertionValidatorWithParameters( + (params) -> params.put(SAML2AssertionValidationParameters.CLOCK_SKEW, Duration.ofMinutes(5))); + } + + public static Converter createDefaultAssertionValidatorWithParameters( + Consumer> validationContextParameters) { + return createAssertionValidator(Saml2ErrorCodes.INVALID_ASSERTION, + (assertionToken) -> OpenSaml40CompatibleAssertionValidators.attributeValidator, + (assertionToken) -> createValidationContext(assertionToken, validationContextParameters)); + } + + private static ValidationContext createValidationContext(OpenSaml4AuthenticationProvider.AssertionToken assertionToken, + Consumer> paramsConsumer) { + Saml2AuthenticationToken token = assertionToken.getToken(); + RelyingPartyRegistration relyingPartyRegistration = token.getRelyingPartyRegistration(); + String audience = relyingPartyRegistration.getEntityId(); + String recipient = relyingPartyRegistration.getAssertionConsumerServiceLocation(); + String assertingPartyEntityId = relyingPartyRegistration.getAssertingPartyDetails().getEntityId(); + Map params = new HashMap<>(); + Assertion assertion = assertionToken.getAssertion(); + if (assertionContainsInResponseTo(assertion)) { + String requestId = getAuthnRequestId(token.getAuthenticationRequest()); + params.put(SAML2AssertionValidationParameters.SC_VALID_IN_RESPONSE_TO, requestId); + } + params.put(SAML2AssertionValidationParameters.COND_VALID_AUDIENCES, Collections.singleton(audience)); + params.put(SAML2AssertionValidationParameters.SC_VALID_RECIPIENTS, Collections.singleton(recipient)); + params.put(SAML2AssertionValidationParameters.VALID_ISSUERS, Collections.singleton(assertingPartyEntityId)); + paramsConsumer.accept(params); + return new ValidationContext(params); + } + + private static boolean assertionContainsInResponseTo(Assertion assertion) { + if (assertion.getSubject() == null) { + return false; + } + for (SubjectConfirmation confirmation : assertion.getSubject().getSubjectConfirmations()) { + SubjectConfirmationData confirmationData = confirmation.getSubjectConfirmationData(); + if (confirmationData == null) { + continue; + } + if (StringUtils.hasText(confirmationData.getInResponseTo())) { + return true; + } + } + return false; + } + + private static String getAuthnRequestId(AbstractSaml2AuthenticationRequest serialized) { + AuthnRequest request = parseRequest(serialized); + if (request == null) { + return null; + } + return request.getID(); + } + + private static AuthnRequest parseRequest(AbstractSaml2AuthenticationRequest request) { + if (request == null) { + return null; + } + String samlRequest = request.getSamlRequest(); + if (!StringUtils.hasText(samlRequest)) { + return null; + } + if (request.getBinding() == Saml2MessageBinding.REDIRECT) { + samlRequest = Saml2Utils.samlInflate(Saml2Utils.samlDecode(samlRequest)); + } else { + samlRequest = new String(Saml2Utils.samlDecode(samlRequest), StandardCharsets.UTF_8); + } + try { + Document document = XMLObjectProviderRegistrySupport.getParserPool() + .parse(new ByteArrayInputStream(samlRequest.getBytes(StandardCharsets.UTF_8))); + Element element = document.getDocumentElement(); + return (AuthnRequest) authnRequestUnmarshaller.unmarshall(element); + } catch (Exception ex) { + String message = "Failed to deserialize associated authentication request [" + ex.getMessage() + "]"; + throw createAuthenticationException(Saml2ErrorCodes.MALFORMED_REQUEST_DATA, message, ex); + } + } + + private static Saml2AuthenticationException createAuthenticationException(String code, String message, + Exception cause) { + return new Saml2AuthenticationException(new Saml2Error(code, message), cause); + } + + private static Converter createAssertionValidator(String errorCode, + Converter validatorConverter, + Converter contextConverter) { + + return (assertionToken) -> { + Assertion assertion = assertionToken.getAssertion(); + SAML20AssertionValidator validator = validatorConverter.convert(assertionToken); + ValidationContext context = contextConverter.convert(assertionToken); + try { + ValidationResult result = validator.validate(assertion, context); + if (result == ValidationResult.VALID) { + return Saml2ResponseValidatorResult.success(); + } + } catch (Exception ex) { + String message = String.format("Invalid assertion [%s] for SAML response [%s]: %s", assertion.getID(), + ((Response) assertion.getParent()).getID(), ex.getMessage()); + return Saml2ResponseValidatorResult.failure(new Saml2Error(errorCode, message)); + } + String message = String.format("Invalid assertion [%s] for SAML response [%s]: %s", assertion.getID(), + ((Response) assertion.getParent()).getID(), context.getValidationFailureMessage()); + return Saml2ResponseValidatorResult.failure(new Saml2Error(errorCode, message)); + }; + } + + static SAML20AssertionValidator createSignatureValidator(SignatureTrustEngine engine) { + return new SAML20AssertionValidator(new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), engine, + validator) { + @Nonnull + @Override + protected ValidationResult validateConditions(Assertion assertion, ValidationContext context) { + return ValidationResult.VALID; + } + + @Nonnull + @Override + protected ValidationResult validateSubjectConfirmation(Assertion assertion, ValidationContext context) { + return ValidationResult.VALID; + } + + @Nonnull + @Override + protected ValidationResult validateStatements(Assertion assertion, ValidationContext context) { + return ValidationResult.VALID; + } + + @Override + protected ValidationResult validateIssuer(Assertion assertion, ValidationContext context) { + return ValidationResult.VALID; + } + }; + + } +} diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2Utils.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2Utils.java new file mode 100644 index 00000000000..fbca1d2ce7d --- /dev/null +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2Utils.java @@ -0,0 +1,92 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.cloudfoundry.identity.uaa.provider.saml; + +import org.springframework.security.saml2.Saml2Exception; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.Inflater; +import java.util.zip.InflaterOutputStream; + +/** + * This class contains functions to Encode, Decode, Deflate and Inflate SAML messages. + *

+ * It was copied from Spring-Security + * org.springframework.security.saml2.core.Saml2Utils + *

+ * There are multiple copies of this class in the Spring-Security code, this particular one exposes functionality publicly. + * Others are only used internally. + */ +public final class Saml2Utils { + + private Saml2Utils() { + } + + public static String samlEncode(byte[] b) { + return Base64.getEncoder().encodeToString(b); + } + + public static byte[] samlDecode(String s) { + return Base64.getMimeDecoder().decode(s); + } + + public static byte[] samlDeflate(String s) { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(out, + new Deflater(Deflater.DEFLATED, true)); + deflaterOutputStream.write(s.getBytes(StandardCharsets.UTF_8)); + deflaterOutputStream.finish(); + return out.toByteArray(); + } catch (IOException ex) { + throw new Saml2Exception("Unable to deflate string", ex); + } + } + + public static String samlInflate(byte[] b) { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + InflaterOutputStream inflaterOutputStream = new InflaterOutputStream(out, new Inflater(true)); + inflaterOutputStream.write(b); + inflaterOutputStream.finish(); + return out.toString(StandardCharsets.UTF_8); + } catch (IOException ex) { + throw new Saml2Exception("Unable to inflate string", ex); + } + } + + /***************************************************************************** + * Below are convenience methods not originally in the Spring-Security class + *****************************************************************************/ + + public static String samlEncode(String s) { + return samlEncode(s.getBytes(StandardCharsets.UTF_8)); + } + + public static String samlDeflateAndEncode(String s) { + return samlEncode(samlDeflate(s)); + } + + public static String samlDecodeAndInflate(String s) { + return samlInflate(samlDecode(s)); + } +} diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlAuthenticationFilterConfig.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlAuthenticationFilterConfig.java index 6bb2fb540df..688308c6b9e 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlAuthenticationFilterConfig.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlAuthenticationFilterConfig.java @@ -1,9 +1,14 @@ package org.cloudfoundry.identity.uaa.provider.saml; +import org.cloudfoundry.identity.uaa.provider.JdbcIdentityProviderProvisioning; +import org.cloudfoundry.identity.uaa.user.UaaUserDatabase; +import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.convert.converter.Converter; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.ProviderManager; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver; import org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter; @@ -17,6 +22,9 @@ import javax.servlet.Filter; import javax.servlet.http.HttpServletRequest; +/** + * Configuration for SAML Filters and Authentication Providers for SAML Authentication. + */ @Configuration public class SamlAuthenticationFilterConfig { @@ -29,8 +37,7 @@ Filter saml2WebSsoAuthenticationRequestFilter(RelyingPartyRegistrationRepository OpenSaml4AuthenticationRequestResolver openSaml4AuthenticationRequestResolver = new OpenSaml4AuthenticationRequestResolver(defaultRelyingPartyRegistrationResolver); openSaml4AuthenticationRequestResolver.setRelayStateResolver(relayStateResolver); - Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(openSaml4AuthenticationRequestResolver); - return filter; + return new Saml2WebSsoAuthenticationRequestFilter(openSaml4AuthenticationRequestResolver); } @Bean @@ -40,12 +47,32 @@ SecurityContextRepository securityContextRepository() { @Autowired @Bean - Filter saml2WebSsoAuthenticationFilter(SamlLoginAuthenticationProvider authenticationProvider, + AuthenticationProvider samlAuthenticationProvider(IdentityZoneManager identityZoneManager, + final UaaUserDatabase userDatabase, + final JdbcIdentityProviderProvisioning identityProviderProvisioning) { + +// SamlUaaResponseAuthenticationConverter samlResponseAuthenticationConverter = +// new SamlUaaResponseAuthenticationConverter(identityZoneManager, userDatabase, identityProviderProvisioning); +// +// OpenSaml4AuthenticationProvider authProvider = new OpenSaml4AuthenticationProvider(); +// //authProvider.setAssertionValidator(OpenSaml40CompatibleAssertionValidators.createDefaultAssertionValidator()); +// authProvider.setResponseAuthenticationConverter(samlResponseAuthenticationConverter); + + return new SamlLoginAuthenticationProvider(identityZoneManager, userDatabase, identityProviderProvisioning); + } + + @Autowired + @Bean + Filter saml2WebSsoAuthenticationFilter(AuthenticationProvider samlAuthenticationProvider, RelyingPartyRegistrationRepository relyingPartyRegistrationRepository, SecurityContextRepository securityContextRepository) { Saml2WebSsoAuthenticationFilter saml2WebSsoAuthenticationFilter = new Saml2WebSsoAuthenticationFilter(relyingPartyRegistrationRepository); - saml2WebSsoAuthenticationFilter.setAuthenticationManager(authenticationProvider); + + ProviderManager authenticationManager = new ProviderManager(samlAuthenticationProvider); + // TODO: set the publisher authenticationManager setAuthenticationEventPublisher(authenticationEventPublisher) + + saml2WebSsoAuthenticationFilter.setAuthenticationManager(authenticationManager); saml2WebSsoAuthenticationFilter.setSecurityContextRepository(securityContextRepository); return saml2WebSsoAuthenticationFilter; } @@ -63,4 +90,4 @@ public String convert(HttpServletRequest request) { return result.getVariables().get("registrationId"); } -} \ No newline at end of file +} diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlConfigProps.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlConfigProps.java index 2da27a834cf..774c38ecbea 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlConfigProps.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlConfigProps.java @@ -1,4 +1,3 @@ - package org.cloudfoundry.identity.uaa.provider.saml; import lombok.Data; @@ -8,9 +7,9 @@ import java.util.Map; @Data -@ConfigurationProperties(prefix="login.saml") +@ConfigurationProperties(prefix = "login.saml") public class SamlConfigProps { - private Map> providers; + private Map> providers; private String activeKeyId; diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlConfiguration.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlConfiguration.java index 881fab7fe24..4c62e5e4386 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlConfiguration.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlConfiguration.java @@ -12,30 +12,24 @@ public class SamlConfiguration { @Value("${login.entityID:unit-test-sp}") private String samlEntityID; - - @Bean - public String samlEntityID() { - return samlEntityID; - } - @Value("${login.idpMetadataURL:null}") private String metaDataUrl; - @Value("${login.idpEntityAlias:null}") private String legacyIdpIdentityAlias; - @Value("${login.saml.nameID:urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified}") private String legacyNameId; - @Value("${login.saml.assertionConsumerIndex:0}") private int legacyAssertionConsumerIndex; - @Value("${login.saml.metadataTrustCheck:true}") private boolean legacyMetadataTrustCheck; - @Value("${login.showSamlLoginLink:true}") private boolean legacyShowSamlLink; + @Bean + public String samlEntityID() { + return samlEntityID; + } + @Autowired @Bean public BootstrapSamlIdentityProviderData bootstrapMetaDataProviders(SamlConfigProps samlConfigProps) { @@ -53,6 +47,17 @@ public BootstrapSamlIdentityProviderData bootstrapMetaDataProviders(SamlConfigPr /* --- previous saml- XML configuration --- + @Value("${login.saml.signatureAlgorithm:SHA12}") + private String signatureAlgorithm; + + @Bean + public SamlConfigurationBean defaultSamlConfig(@Value("${login.saml.signatureAlgorithm:SHA12}") String signatureAlgorithm) { + SamlConfigurationBean samlConfigurationBean = new SamlConfigurationBean(); + SamlConfigurationBean.SignatureAlgorithm signatureAlgorithmEnum = SamlConfigurationBean.SignatureAlgorithm.valueOf(signatureAlgorithm); + samlConfigurationBean.setSignatureAlgorithm(signatureAlgorithmEnum); + return samlConfigurationBean; + } + @@ -329,10 +334,6 @@ public BootstrapSamlIdentityProviderData bootstrapMetaDataProviders(SamlConfigPr - - - - diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlConfigurationBean.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlConfigurationBean.java index 54eafd9c30e..29474d66070 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlConfigurationBean.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlConfigurationBean.java @@ -17,22 +17,23 @@ //import org.opensaml.xml.Configuration; //import org.opensaml.xml.security.BasicSecurityConfiguration; //import org.opensaml.xml.signature.SignatureConstants; + import org.springframework.beans.factory.InitializingBean; public class SamlConfigurationBean implements InitializingBean { - private SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.SHA1; + private SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.SHA1; - public void setSignatureAlgorithm(SignatureAlgorithm s) { - signatureAlgorithm = s; - } + public SignatureAlgorithm getSignatureAlgorithm() { + return signatureAlgorithm; + } - public SignatureAlgorithm getSignatureAlgorithm() { - return signatureAlgorithm; - } + public void setSignatureAlgorithm(SignatureAlgorithm s) { + signatureAlgorithm = s; + } - @Override - public void afterPropertiesSet() { + @Override + public void afterPropertiesSet() { // BasicSecurityConfiguration config = (BasicSecurityConfiguration) Configuration.getGlobalSecurityConfiguration(); // switch (signatureAlgorithm) { // case SHA1: @@ -48,11 +49,11 @@ public void afterPropertiesSet() { // config.setSignatureReferenceDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA512); // break; // } - } + } - public enum SignatureAlgorithm { - SHA1, - SHA256, - SHA512 - } + public enum SignatureAlgorithm { + SHA1, + SHA256, + SHA512 + } } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlLoginAuthenticationProvider.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlLoginAuthenticationProvider.java index 1917e49249a..e6476a32a4d 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlLoginAuthenticationProvider.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlLoginAuthenticationProvider.java @@ -61,7 +61,6 @@ import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; -import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.context.request.RequestAttributes; @@ -96,25 +95,14 @@ /** * SAML Authentication Provider responsible for validating of received SAML messages */ -@Component("samlAuthenticationProvider") @Slf4j public class SamlLoginAuthenticationProvider implements ApplicationEventPublisherAware, AuthenticationProvider, AuthenticationManager { public static final String AUTHENTICATION_CONTEXT_CLASS_REFERENCE = "acr"; - - private final IdentityZoneManager identityZoneManager; - private static final AuthnRequestUnmarshaller authnRequestUnmarshaller; - private final UaaUserDatabase userDatabase; - private final IdentityProviderProvisioning identityProviderProvisioning; - private static final ParserPool parserPool; - private static final ResponseUnmarshaller responseUnmarshaller; - // private final ScimGroupExternalMembershipManager externalMembershipManager; - private ApplicationEventPublisher eventPublisher; - static { XMLObjectProviderRegistry registry = ConfigurationService.get(XMLObjectProviderRegistry.class); authnRequestUnmarshaller = (AuthnRequestUnmarshaller) registry.getUnmarshallerFactory() @@ -126,6 +114,12 @@ public class SamlLoginAuthenticationProvider implements ApplicationEventPublishe parserPool = registry.getParserPool(); } + private final IdentityZoneManager identityZoneManager; + private final UaaUserDatabase userDatabase; + private final IdentityProviderProvisioning identityProviderProvisioning; + // private final ScimGroupExternalMembershipManager externalMembershipManager; + private ApplicationEventPublisher eventPublisher; + public SamlLoginAuthenticationProvider(IdentityZoneManager identityZoneManager, final UaaUserDatabase userDatabase, final JdbcIdentityProviderProvisioning identityProviderProvisioning) { @@ -163,7 +157,7 @@ public SamlLoginAuthenticationProvider(IdentityZoneManager identityZoneManager, * Authentication class will be tried. * @throws AuthenticationException if authentication fails. *

- * TODO: Move below into configuration of + * TODO: Move below into configuration of * @see OpenSaml4AuthenticationProvider * https://docs.spring.io/spring-security/reference/5.8/migration/servlet/saml2.html#_use_opensaml_4 */ @@ -174,29 +168,32 @@ public Authentication authenticate(Authentication authentication) throws Authent throw new IllegalArgumentException("Only SAMLAuthenticationToken is supported, " + authentication.getClass() + " was attempted"); } - Saml2AuthenticationToken originalToken = (Saml2AuthenticationToken) authentication; - String serializedResponse = originalToken.getSaml2Response(); + Saml2AuthenticationToken authenticationToken = (Saml2AuthenticationToken) authentication; + String serializedResponse = authenticationToken.getSaml2Response(); Response response = parseResponse(serializedResponse); List assertions = response.getAssertions(); - //Authentication openSamlAuth = openSaml4AuthenticationProvider.authenticate(authentication); + for (Assertion assertion : assertions) { + log.debug("Assertion: " + assertion); + } IdentityZone zone = identityZoneManager.getCurrentIdentityZone(); log.debug(String.format("Initiating SAML authentication in zone '%s' domain '%s'", zone.getId(), zone.getSubdomain())); - RelyingPartyRegistration relyingPartyRegistration = originalToken.getRelyingPartyRegistration(); - AbstractSaml2AuthenticationRequest authenticationRequest = originalToken.getAuthenticationRequest(); + RelyingPartyRegistration relyingPartyRegistration = authenticationToken.getRelyingPartyRegistration(); + AbstractSaml2AuthenticationRequest authenticationRequest = authenticationToken.getAuthenticationRequest(); String relayState; if (authenticationRequest != null) { relayState = authenticationRequest.getRelayState(); } - // TODO: remove hard coded marissa@test.org - UaaPrincipal principal = new UaaPrincipal(NotANumber, "marissa@test.org", originalToken.getName(), relyingPartyRegistration.getRegistrationId(), originalToken.getName(), zone.getId()); + String subjectName = assertions.get(0).getSubject().getNameID().getValue(); + UaaPrincipal initialPrincipal = new UaaPrincipal(NotANumber, subjectName, authenticationToken.getName(), + relyingPartyRegistration.getRegistrationId(), authenticationToken.getName(), zone.getId()); log.debug("Mapped SAML authentication to IDP with origin '{}' and username '{}'", - relyingPartyRegistration.getRegistrationId(), principal.getName()); + relyingPartyRegistration.getRegistrationId(), initialPrincipal.getName()); - List samlAuthorities = List.copyOf(originalToken.getAuthorities()); + List samlAuthorities = List.copyOf(authenticationToken.getAuthorities()); LinkedMultiValueMap customAttributes = new LinkedMultiValueMap<>(); // for (Map.Entry> entry : userAttributes.entrySet()) { @@ -210,8 +207,8 @@ public Authentication authenticate(Authentication authentication) throws Authent long authenticatedTime = System.currentTimeMillis(); long expiresAt = -1; - UaaAuthentication uaaAuthentication = new UaaAuthentication(principal, - originalToken.getCredentials(), samlAuthorities, externalGroups, customAttributes, null, + UaaAuthentication initialUaaAuthentication = new UaaAuthentication(initialPrincipal, + authenticationToken.getCredentials(), samlAuthorities, externalGroups, customAttributes, null, authenticated, authenticatedTime, expiresAt); @@ -235,7 +232,7 @@ public Authentication authenticate(Authentication authentication) throws Authent String.format( "Mapped SAML authentication to IDP with origin '%s' and username '%s'", idp.getOriginKey(), - principal.getName() + initialPrincipal.getName() ) ); @@ -254,11 +251,11 @@ public Authentication authenticate(Authentication authentication) throws Authent // } // // Set filteredExternalGroups = filterSamlAuthorities(samlConfig, samlAuthorities); - uaaAuthentication.setAuthenticationMethods(Set.of("ext")); + initialUaaAuthentication.setAuthenticationMethods(Set.of("ext")); MultiValueMap userAttributes = retrieveUserAttributes(samlConfig, response); List acrValues = userAttributes.get(AUTHENTICATION_CONTEXT_CLASS_REFERENCE); if (acrValues != null) { - uaaAuthentication.setAuthContextClassRef(Set.copyOf(acrValues)); + initialUaaAuthentication.setAuthContextClassRef(Set.copyOf(acrValues)); } // // if (samlConfig.getAuthnContext() != null) { @@ -267,9 +264,9 @@ public Authentication authenticate(Authentication authentication) throws Authent // } // } // - UaaUser user = createIfMissing(principal, addNew, samlAuthorities, userAttributes); + UaaUser user = createIfMissing(initialPrincipal, addNew, samlAuthorities, userAttributes); UaaPrincipal newPrincipal = new UaaPrincipal(user); - UaaAuthentication newAuthentication = new UaaAuthentication(uaaAuthentication, newPrincipal); + UaaAuthentication newAuthentication = new UaaAuthentication(initialUaaAuthentication, newPrincipal); publish(new IdentityProviderAuthenticationSuccessEvent(user, newAuthentication, OriginKeys.SAML, identityZoneManager.getCurrentIdentityZoneId())); // if (samlConfig.isStoreCustomAttributes()) { @@ -283,6 +280,64 @@ public Authentication authenticate(Authentication authentication) throws Authent // return newAuthentication; } +// +// private void process(Saml2AuthenticationToken token, Response response) { +// String issuer = response.getIssuer().getValue(); +// log.debug(LogMessage.format("Processing SAML response from %s", issuer)); +// boolean responseSigned = response.isSigned(); +// +// OpenSaml4AuthenticationProvider.ResponseToken responseToken = new OpenSaml4AuthenticationProvider.ResponseToken(response, token); +// Saml2ResponseValidatorResult result = this.responseSignatureValidator.convert(responseToken); +// if (responseSigned) { +// this.responseElementsDecrypter.accept(responseToken); +// } +// else if (!response.getEncryptedAssertions().isEmpty()) { +// result = result.concat(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, +// "Did not decrypt response [" + response.getID() + "] since it is not signed")); +// } +// result = result.concat(this.responseValidator.convert(responseToken)); +// boolean allAssertionsSigned = true; +// for (Assertion assertion : response.getAssertions()) { +// OpenSaml4AuthenticationProvider.AssertionToken assertionToken = new OpenSaml4AuthenticationProvider.AssertionToken(assertion, token); +// result = result.concat(this.assertionSignatureValidator.convert(assertionToken)); +// allAssertionsSigned = allAssertionsSigned && assertion.isSigned(); +// if (responseSigned || assertion.isSigned()) { +// this.assertionElementsDecrypter.accept(new OpenSaml4AuthenticationProvider.AssertionToken(assertion, token)); +// } +// result = result.concat(this.assertionValidator.convert(assertionToken)); +// } +// if (!responseSigned && !allAssertionsSigned) { +// String description = "Either the response or one of the assertions is unsigned. " +// + "Please either sign the response or all of the assertions."; +// result = result.concat(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, description)); +// } +// Assertion firstAssertion = CollectionUtils.firstElement(response.getAssertions()); +// if (firstAssertion != null && !hasName(firstAssertion)) { +// Saml2Error error = new Saml2Error(Saml2ErrorCodes.SUBJECT_NOT_FOUND, +// "Assertion [" + firstAssertion.getID() + "] is missing a subject"); +// result = result.concat(error); +// } +// +// if (result.hasErrors()) { +// Collection errors = result.getErrors(); +// if (this.logger.isTraceEnabled()) { +// this.logger.debug("Found " + errors.size() + " validation errors in SAML response [" + response.getID() +// + "]: " + errors); +// } +// else if (this.logger.isDebugEnabled()) { +// this.logger +// .debug("Found " + errors.size() + " validation errors in SAML response [" + response.getID() + "]"); +// } +// Saml2Error first = errors.iterator().next(); +// throw createAuthenticationException(first.getErrorCode(), first.getDescription(), null); +// } +// else { +// if (this.logger.isDebugEnabled()) { +// this.logger.debug("Successfully processed SAML Response [" + response.getID() + "]"); +// } +// } +// } + private Response parseResponse(String response) throws Saml2Exception, Saml2AuthenticationException { try { diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlRelyingPartyRegistrationRepositoryConfig.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlRelyingPartyRegistrationRepositoryConfig.java index f193682e09b..007adfd3ed0 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlRelyingPartyRegistrationRepositoryConfig.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlRelyingPartyRegistrationRepositoryConfig.java @@ -18,6 +18,7 @@ import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.List; +import java.util.function.Function; import static org.cloudfoundry.identity.uaa.provider.saml.SamlMetadataEndpoint.DEFAULT_REGISTRATION_ID; @@ -29,6 +30,7 @@ public class SamlRelyingPartyRegistrationRepositoryConfig { private final String samlEntityID; private final SamlConfigProps samlConfigProps; private final BootstrapSamlIdentityProviderData bootstrapSamlIdentityProviderData; + private final Function assertionConsumerServiceLocationFunction; @Value("${login.saml.nameID:urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified}") private String samlSpNameID; @@ -43,6 +45,7 @@ public SamlRelyingPartyRegistrationRepositoryConfig(@Qualifier("samlEntityID") S this.samlEntityID = samlEntityID; this.samlConfigProps = samlConfigProps; this.bootstrapSamlIdentityProviderData = bootstrapSamlIdentityProviderData; + this.assertionConsumerServiceLocationFunction = "{baseUrl}/saml/SSO/alias/%s"::formatted; } @Autowired @@ -76,7 +79,7 @@ RelyingPartyRegistrationRepository relyingPartyRegistrationRepository(SamlIdenti } InMemoryRelyingPartyRegistrationRepository bootstrapRepo = new InMemoryRelyingPartyRegistrationRepository(relyingPartyRegistrations); - ConfiguratorRelyingPartyRegistrationRepository configuratorRepo = new ConfiguratorRelyingPartyRegistrationRepository(samlSignRequest, samlEntityID, keyWithCert, samlIdentityProviderConfigurator); + ConfiguratorRelyingPartyRegistrationRepository configuratorRepo = new ConfiguratorRelyingPartyRegistrationRepository(samlSignRequest, samlEntityID, keyWithCert, samlIdentityProviderConfigurator, assertionConsumerServiceLocationFunction); return new DelegatingRelyingPartyRegistrationRepository(bootstrapRepo, configuratorRepo); } @@ -86,7 +89,7 @@ private RelyingPartyRegistration buildRelyingPartyRegistration(KeyWithCert keyWi .entityId(samlEntityID) .nameIdFormat(samlSpNameID) .registrationId(rpRegstrationId) - // TODO: assertionConsumerServiceUrlTemplate can be configured here. + .assertionConsumerServiceLocation(assertionConsumerServiceLocationFunction.apply(samlEntityID)) .assertingPartyDetails(details -> details .wantAuthnRequestsSigned(samlSignRequest) ) diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlUaaResponseAuthenticationConverter.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlUaaResponseAuthenticationConverter.java new file mode 100644 index 00000000000..087f171bdee --- /dev/null +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlUaaResponseAuthenticationConverter.java @@ -0,0 +1,428 @@ +package org.cloudfoundry.identity.uaa.provider.saml; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.cloudfoundry.identity.uaa.authentication.UaaAuthentication; +import org.cloudfoundry.identity.uaa.authentication.UaaPrincipal; +import org.cloudfoundry.identity.uaa.authentication.manager.ExternalGroupAuthorizationEvent; +import org.cloudfoundry.identity.uaa.authentication.manager.InvitedUserAuthenticatedEvent; +import org.cloudfoundry.identity.uaa.authentication.manager.NewUserAuthenticatedEvent; +import org.cloudfoundry.identity.uaa.constants.OriginKeys; +import org.cloudfoundry.identity.uaa.provider.IdentityProvider; +import org.cloudfoundry.identity.uaa.provider.IdentityProviderProvisioning; +import org.cloudfoundry.identity.uaa.provider.JdbcIdentityProviderProvisioning; +import org.cloudfoundry.identity.uaa.provider.SamlIdentityProviderDefinition; +import org.cloudfoundry.identity.uaa.user.UaaUser; +import org.cloudfoundry.identity.uaa.user.UaaUserDatabase; +import org.cloudfoundry.identity.uaa.user.UaaUserPrototype; +import org.cloudfoundry.identity.uaa.zone.IdentityZone; +import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; +import org.opensaml.core.xml.XMLObject; +import org.opensaml.core.xml.schema.XSAny; +import org.opensaml.core.xml.schema.XSBase64Binary; +import org.opensaml.core.xml.schema.XSBoolean; +import org.opensaml.core.xml.schema.XSBooleanValue; +import org.opensaml.core.xml.schema.XSDateTime; +import org.opensaml.core.xml.schema.XSInteger; +import org.opensaml.core.xml.schema.XSQName; +import org.opensaml.core.xml.schema.XSString; +import org.opensaml.core.xml.schema.XSURI; +import org.opensaml.saml.saml2.core.Assertion; +import org.opensaml.saml.saml2.core.Attribute; +import org.opensaml.saml.saml2.core.AttributeStatement; +import org.opensaml.saml.saml2.core.Response; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; +import org.springframework.core.convert.converter.Converter; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.ProviderNotFoundException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest; +import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider; +import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication; +import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; + +import javax.xml.namespace.QName; +import java.time.Instant; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.cloudfoundry.identity.uaa.constants.OriginKeys.NotANumber; +import static org.cloudfoundry.identity.uaa.provider.ExternalIdentityProviderDefinition.EMAIL_ATTRIBUTE_NAME; +import static org.cloudfoundry.identity.uaa.provider.ExternalIdentityProviderDefinition.EMAIL_VERIFIED_ATTRIBUTE_NAME; +import static org.cloudfoundry.identity.uaa.provider.ExternalIdentityProviderDefinition.FAMILY_NAME_ATTRIBUTE_NAME; +import static org.cloudfoundry.identity.uaa.provider.ExternalIdentityProviderDefinition.GIVEN_NAME_ATTRIBUTE_NAME; +import static org.cloudfoundry.identity.uaa.provider.ExternalIdentityProviderDefinition.PHONE_NUMBER_ATTRIBUTE_NAME; +import static org.cloudfoundry.identity.uaa.util.UaaHttpRequestUtils.isAcceptedInvitationAuthentication; + +/** + * + */ +@Slf4j +public class SamlUaaResponseAuthenticationConverter + implements Converter, + ApplicationEventPublisherAware { + + public static final String AUTHENTICATION_CONTEXT_CLASS_REFERENCE = "acr"; + + private final IdentityZoneManager identityZoneManager; + + //private static final AuthnRequestUnmarshaller authnRequestUnmarshaller; + private final UaaUserDatabase userDatabase; + private final IdentityProviderProvisioning identityProviderProvisioning; + + //private static final ParserPool parserPool; + + //private static final ResponseUnmarshaller responseUnmarshaller; + + // private final ScimGroupExternalMembershipManager externalMembershipManager; + private ApplicationEventPublisher eventPublisher; + + public SamlUaaResponseAuthenticationConverter(IdentityZoneManager identityZoneManager, + final UaaUserDatabase userDatabase, + final JdbcIdentityProviderProvisioning identityProviderProvisioning) { + this.identityZoneManager = identityZoneManager; + this.userDatabase = userDatabase; + this.identityProviderProvisioning = identityProviderProvisioning; + } + + @Override + public UaaAuthentication convert(OpenSaml4AuthenticationProvider.ResponseToken responseToken) { + // Do the default conversion + Saml2Authentication authentication = OpenSaml4AuthenticationProvider + .createDefaultResponseAuthenticationConverter() + .convert(responseToken); + + Saml2AuthenticationToken authenticationToken = responseToken.getToken(); + Response response = responseToken.getResponse(); + + IdentityZone zone = identityZoneManager.getCurrentIdentityZone(); + log.debug(String.format("Initiating SAML authentication in zone '%s' domain '%s'", + zone.getId(), zone.getSubdomain())); + RelyingPartyRegistration relyingPartyRegistration = authenticationToken.getRelyingPartyRegistration(); + AbstractSaml2AuthenticationRequest authenticationRequest = authenticationToken.getAuthenticationRequest(); + + Assertion assertion = responseToken.getResponse().getAssertions().get(0); + String username = assertion.getSubject().getNameID().getValue(); + //UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); + + List samlAuthorities = List.copyOf(authenticationToken.getAuthorities()); + + LinkedMultiValueMap customAttributes = new LinkedMultiValueMap<>(); +// for (Map.Entry> entry : userAttributes.entrySet()) { +// if (entry.getKey().startsWith(USER_ATTRIBUTE_PREFIX)) { +// customAttributes.put(entry.getKey().substring(USER_ATTRIBUTE_PREFIX.length()), entry.getValue()); +// } +// } + + Set externalGroups = Set.of(); + boolean authenticated = true; + long authenticatedTime = System.currentTimeMillis(); + long expiresAt = -1; + + UaaPrincipal initialPrincipal = new UaaPrincipal(NotANumber, "marissa@test.org", authenticationToken.getName(), + relyingPartyRegistration.getRegistrationId(), authenticationToken.getName(), zone.getId()); + UaaAuthentication initialUaaAuthentication = new UaaAuthentication(initialPrincipal, + authenticationToken.getCredentials(), samlAuthorities, externalGroups, customAttributes, null, + authenticated, authenticatedTime, + expiresAt); + + + String alias = relyingPartyRegistration.getRegistrationId(); +// String relayState = context.getRelayState(); + boolean addNew; + IdentityProvider idp; + SamlIdentityProviderDefinition samlConfig; + try { + idp = identityProviderProvisioning.retrieveByOrigin(alias, identityZoneManager.getCurrentIdentityZoneId()); + samlConfig = idp.getConfig(); + addNew = samlConfig.isAddShadowUserOnLogin(); + if (!idp.isActive()) { + throw new ProviderNotFoundException("Identity Provider has been disabled by administrator for alias:" + alias); + } + } catch (EmptyResultDataAccessException x) { + throw new ProviderNotFoundException("No SAML identity provider found in zone for alias:" + alias); + } +// + log.debug( + String.format( + "Mapped SAML authentication to IDP with origin '%s' and username '%s'", + idp.getOriginKey(), + initialPrincipal.getName() + ) + ); + + + //Collection samlAuthorities = retrieveSamlAuthorities(samlConfig, (SAMLCredential) result.getCredentials()); +// +// Collection authorities = + // Collection samlAuthoritinull; +// SamlIdentityProviderDefinition.ExternalGroupMappingMode groupMappingMode = idp.getConfig().getGroupMappingMode(); +// switch (groupMappingMode) { +// case EXPLICITLY_MAPPED: +// authorities = mapAuthorities(idp.getOriginKey(), samlAuthorities); +// break; +// case AS_SCOPES: +// authorities = new LinkedList<>(samlAuthorities); +// break; +// } +// +// Set filteredExternalGroups = filterSamlAuthorities(samlConfig, samlAuthorities); + initialUaaAuthentication.setAuthenticationMethods(Set.of("ext")); + MultiValueMap userAttributes = retrieveUserAttributes(samlConfig, response); + List acrValues = userAttributes.get(AUTHENTICATION_CONTEXT_CLASS_REFERENCE); + if (acrValues != null) { + initialUaaAuthentication.setAuthContextClassRef(Set.copyOf(acrValues)); + } + +// +// if (samlConfig.getAuthnContext() != null) { +// if (Collections.disjoint(userAttributes.get(AUTHENTICATION_CONTEXT_CLASS_REFERENCE), samlConfig.getAuthnContext())) { +// throw new BadCredentialsException("Identity Provider did not authenticate with the requested AuthnContext."); +// } +// } +// + UaaUser user = createIfMissing(initialPrincipal, addNew, samlAuthorities, userAttributes); + UaaPrincipal newPrincipal = new UaaPrincipal(user); + UaaAuthentication newAuthentication = new UaaAuthentication(initialUaaAuthentication, newPrincipal); + + // publish(new IdentityProviderAuthenticationSuccessEvent(user, newAuthentication, OriginKeys.SAML, identityZoneManager.getCurrentIdentityZoneId())); +// if (samlConfig.isStoreCustomAttributes()) { +// userDatabase.storeUserInfo(user.getId(), +// new UserInfo() +// .setUserAttributes(resultUaaAuthentication.getUserAttributes()) +// .setRoles(new LinkedList(resultUaaAuthentication.getExternalGroups())) +// ); +// } +// configureRelayRedirect(relayState); +// + return newAuthentication; + } + + /** + * Default conversion: + * Response response = responseToken.response; + * Saml2AuthenticationToken token = responseToken.token; + * Assertion assertion = CollectionUtils.firstElement(response.getAssertions()); + * String username = assertion.getSubject().getNameID().getValue(); + * Map> attributes = getAssertionAttributes(assertion); + * List sessionIndexes = getSessionIndexes(assertion); + * DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(username, attributes, + * sessionIndexes); + * String registrationId = responseToken.token.getRelyingPartyRegistration().getRegistrationId(); + * principal.setRelyingPartyRegistrationId(registrationId); + * return new Saml2Authentication(principal, token.getSaml2Response(), + * AuthorityUtils.createAuthorityList("ROLE_USER")); + */ + + /* + * TODO: Move User Attributes Stuff + */ + public MultiValueMap retrieveUserAttributes(SamlIdentityProviderDefinition definition, Response response) { + log.debug(String.format("Retrieving SAML user attributes [zone:%s, origin:%s]", definition.getZoneId(), definition.getIdpEntityAlias())); + MultiValueMap userAttributes = new LinkedMultiValueMap<>(); + List assertions = response.getAssertions(); + if (assertions.isEmpty()) { + return userAttributes; + } + for (Assertion assertion : assertions) { + if (assertion.getAttributeStatements() != null) { + for (AttributeStatement statement : assertion.getAttributeStatements()) { + for (Attribute attribute : statement.getAttributes()) { + if (attribute.getAttributeValues() != null) { + for (XMLObject xmlObject : attribute.getAttributeValues()) { + String key = attribute.getName(); + String value = getStringValue(key, definition, xmlObject); + if (value != null) { + userAttributes.add(key, value); + } + } + } + } + } + } + } + + if (definition != null && definition.getAttributeMappings() != null) { + for (Map.Entry attributeMapping : definition.getAttributeMappings().entrySet()) { + Object attributeKey = attributeMapping.getValue(); + if (attributeKey instanceof String) { + if (userAttributes.get(attributeKey) != null) { + String key = attributeMapping.getKey(); + userAttributes.addAll(key, userAttributes.get(attributeKey)); + } + } + } + } +// if (credential.getAuthenticationAssertion() != null && credential.getAuthenticationAssertion().getAuthnStatements() != null) { +// for (AuthnStatement statement : credential.getAuthenticationAssertion().getAuthnStatements()) { +// if (statement.getAuthnContext() != null && statement.getAuthnContext().getAuthnContextClassRef() != null) { +// userAttributes.add(AUTHENTICATION_CONTEXT_CLASS_REFERENCE, statement.getAuthnContext().getAuthnContextClassRef().getAuthnContextClassRef()); +// } +// } +// } + return userAttributes; + } + + protected String getStringValue(String key, SamlIdentityProviderDefinition definition, XMLObject xmlObject) { + String value = null; + if (xmlObject instanceof XSString) { + value = ((XSString) xmlObject).getValue(); + } else if (xmlObject instanceof XSAny) { + value = ((XSAny) xmlObject).getTextContent(); + } else if (xmlObject instanceof XSInteger) { + Integer i = ((XSInteger) xmlObject).getValue(); + value = i != null ? i.toString() : null; + } else if (xmlObject instanceof XSBoolean) { + XSBooleanValue b = ((XSBoolean) xmlObject).getValue(); + value = b != null && b.getValue() != null ? b.getValue().toString() : null; + } else if (xmlObject instanceof XSDateTime) { + Instant d = ((XSDateTime) xmlObject).getValue(); + value = d != null ? d.toString() : null; + } else if (xmlObject instanceof XSQName) { + QName name = ((XSQName) xmlObject).getValue(); + value = name != null ? name.toString() : null; + } else if (xmlObject instanceof XSURI) { + value = ((XSURI) xmlObject).getURI(); + } else if (xmlObject instanceof XSBase64Binary) { + value = ((XSBase64Binary) xmlObject).getValue(); + } + + if (value != null) { + log.debug(String.format("Found SAML user attribute %s of value %s [zone:%s, origin:%s]", key, value, definition.getZoneId(), definition.getIdpEntityAlias())); + return value; + } else if (xmlObject != null) { + log.debug(String.format("SAML user attribute %s at is not of type XSString or other recognizable type, %s [zone:%s, origin:%s]", key, xmlObject.getClass().getName(), definition.getZoneId(), definition.getIdpEntityAlias())); + } + return null; + } + + /* + * TODO: Move User Creation Stuff + */ + + protected UaaUser createIfMissing(UaaPrincipal samlPrincipal, boolean addNew, Collection authorities, MultiValueMap userAttributes) { + UaaUser user = null; + String invitedUserId = null; + boolean is_invitation_acceptance = isAcceptedInvitationAuthentication(); + if (is_invitation_acceptance) { + invitedUserId = (String) RequestContextHolder.currentRequestAttributes().getAttribute("user_id", RequestAttributes.SCOPE_SESSION); + user = userDatabase.retrieveUserById(invitedUserId); + if (userAttributes.getFirst(EMAIL_ATTRIBUTE_NAME) != null) { + if (!userAttributes.getFirst(EMAIL_ATTRIBUTE_NAME).equalsIgnoreCase(user.getEmail())) { + throw new BadCredentialsException("SAML User email mismatch. Authenticated email doesn't match invited email."); + } + } else { + userAttributes = new LinkedMultiValueMap<>(userAttributes); + userAttributes.add(EMAIL_ATTRIBUTE_NAME, user.getEmail()); + } + addNew = false; + if (user.getUsername().equals(user.getEmail()) && !user.getUsername().equals(samlPrincipal.getName())) { + user = user.modifyUsername(samlPrincipal.getName()); + } + publish(new InvitedUserAuthenticatedEvent(user)); + user = userDatabase.retrieveUserById(invitedUserId); + } + + boolean userModified = false; + UaaUser userWithSamlAttributes = getUser(samlPrincipal, userAttributes); + try { + if (user == null) { + user = userDatabase.retrieveUserByName(samlPrincipal.getName(), samlPrincipal.getOrigin()); + } + } catch (UsernameNotFoundException e) { + UaaUserPrototype uaaUser = userDatabase.retrieveUserPrototypeByEmail(userWithSamlAttributes.getEmail(), samlPrincipal.getOrigin()); + if (uaaUser != null) { + userModified = true; + user = new UaaUser(uaaUser.withUsername(samlPrincipal.getName())); + } else { + if (!addNew) { + throw new SamlLoginException("SAML user does not exist. " + + "You can correct this by creating a shadow user for the SAML user.", e); + } + publish(new NewUserAuthenticatedEvent(userWithSamlAttributes)); + try { + user = new UaaUser(userDatabase.retrieveUserPrototypeByName(samlPrincipal.getName(), samlPrincipal.getOrigin())); + } catch (UsernameNotFoundException ex) { + throw new BadCredentialsException("Unable to establish shadow user for SAML user:" + samlPrincipal.getName()); + } + } + } + if (haveUserAttributesChanged(user, userWithSamlAttributes)) { + userModified = true; + user = user.modifyAttributes(userWithSamlAttributes.getEmail(), + userWithSamlAttributes.getGivenName(), + userWithSamlAttributes.getFamilyName(), + userWithSamlAttributes.getPhoneNumber(), + userWithSamlAttributes.getExternalId(), + user.isVerified() || userWithSamlAttributes.isVerified()); + } + publish( + new ExternalGroupAuthorizationEvent( + user, + userModified, + authorities, + true + ) + ); + user = userDatabase.retrieveUserById(user.getId()); + return user; + } + + protected UaaUser getUser(UaaPrincipal principal, MultiValueMap userAttributes) { + if (principal.getName() == null && userAttributes.getFirst(EMAIL_ATTRIBUTE_NAME) == null) { + throw new BadCredentialsException("Cannot determine username from credentials supplied"); + } + + String name = principal.getName(); + return UaaUser.createWithDefaults(u -> + u.withId(OriginKeys.NotANumber) + .withUsername(name) + .withEmail(userAttributes.getFirst(EMAIL_ATTRIBUTE_NAME)) + .withPhoneNumber(userAttributes.getFirst(PHONE_NUMBER_ATTRIBUTE_NAME)) + .withPassword("") + .withGivenName(userAttributes.getFirst(GIVEN_NAME_ATTRIBUTE_NAME)) + .withFamilyName(userAttributes.getFirst(FAMILY_NAME_ATTRIBUTE_NAME)) + .withAuthorities(Collections.emptyList()) + .withVerified(Boolean.valueOf(userAttributes.getFirst(EMAIL_VERIFIED_ATTRIBUTE_NAME))) + .withOrigin(principal.getOrigin() != null ? principal.getOrigin() : OriginKeys.LOGIN_SERVER) + .withExternalId(name) + .withZoneId(principal.getZoneId()) + ); + } + + protected boolean haveUserAttributesChanged(UaaUser existingUser, UaaUser user) { + return existingUser.isVerified() != user.isVerified() || + !StringUtils.equals(existingUser.getGivenName(), user.getGivenName()) || + !StringUtils.equals(existingUser.getFamilyName(), user.getFamilyName()) || + !StringUtils.equals(existingUser.getPhoneNumber(), user.getPhoneNumber()) || + !StringUtils.equals(existingUser.getEmail(), user.getEmail()) || + !StringUtils.equals(existingUser.getExternalId(), user.getExternalId()); + } + + /* **************************************************** + ApplicationEventPublisherAware + **************************************************** */ + + @Override + public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { + this.eventPublisher = applicationEventPublisher; + } + + protected void publish(ApplicationEvent event) { + if (eventPublisher != null) { + eventPublisher.publishEvent(event); + } + } +} diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/user/UaaUser.java b/server/src/main/java/org/cloudfoundry/identity/uaa/user/UaaUser.java index 2b7120f61bb..69c199c0f51 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/user/UaaUser.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/user/UaaUser.java @@ -2,6 +2,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Getter; +import lombok.Setter; import org.cloudfoundry.identity.uaa.authentication.NonStringPassword; import org.springframework.security.core.GrantedAuthority; import org.springframework.util.Assert; @@ -20,6 +22,7 @@ * @author Dave Syer * @author Joel D'sa */ +@Getter @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public class UaaUser { @@ -95,22 +98,22 @@ public static UaaUser createWithDefaults(Consumer config) { private final String phoneNumber; + @Setter private Long lastLogonTime; + @Setter private Long previousLogonTime; - public String getZoneId() { - return zoneId; - } - private final String zoneId; private final List authorities; + @Setter private boolean verified = false; private boolean legacyVerificationBehavior = false; + @Setter private boolean passwordChangeRequired; public UaaUser(String username, String password, String email, String givenName, String familyName) { @@ -173,42 +176,10 @@ public UaaUser(UaaUserPrototype prototype) { this.previousLogonTime = prototype.getPreviousLogonTime(); } - public String getId() { - return id; - } - - public String getUsername() { - return username; - } - public String getPassword() { return password.getPassword(); } - public String getEmail() { - return email; - } - - public String getGivenName() { - return givenName; - } - - public String getFamilyName() { - return familyName; - } - - public String getOrigin() { - return origin; - } - - public String getExternalId() { - return externalId; - } - - public String getSalt() { - return salt; - } - public List getAuthorities() { return Optional.ofNullable(authorities).orElseThrow(); } @@ -238,121 +209,109 @@ public String toString() { + ", familyName=" + familyName + "}]"; } - public Date getModified() { - return modified; - } - - public Date getCreated() { - return created; - } - - public Date getPasswordLastModified() { - return passwordLastModified; - } - public UaaUser modifySource(String origin, String externalId) { return new UaaUser( - new UaaUserPrototype() - .withEmail(email) - .withGivenName(givenName) - .withFamilyName(familyName) - .withPhoneNumber(phoneNumber) - .withModified(modified) - .withId(id) - .withUsername(username) - .withPassword(password) - .withAuthorities(authorities) - .withCreated(created) - .withOrigin(origin) - .withExternalId(externalId) - .withVerified(verified) - .withZoneId(zoneId) - .withSalt(salt) - .withPasswordLastModified(passwordLastModified)); + new UaaUserPrototype() + .withEmail(email) + .withGivenName(givenName) + .withFamilyName(familyName) + .withPhoneNumber(phoneNumber) + .withModified(modified) + .withId(id) + .withUsername(username) + .withPassword(password) + .withAuthorities(authorities) + .withCreated(created) + .withOrigin(origin) + .withExternalId(externalId) + .withVerified(verified) + .withZoneId(zoneId) + .withSalt(salt) + .withPasswordLastModified(passwordLastModified)); } public UaaUser modifyEmail(String email) { return new UaaUser( - new UaaUserPrototype() - .withEmail(email) - .withGivenName(givenName) - .withFamilyName(familyName) - .withPhoneNumber(phoneNumber) - .withModified(modified) - .withId(id) - .withUsername(username) - .withPassword(password) - .withAuthorities(authorities) - .withCreated(created) - .withOrigin(origin) - .withExternalId(externalId) - .withVerified(verified) - .withZoneId(zoneId) - .withSalt(salt) - .withPasswordLastModified(passwordLastModified)); + new UaaUserPrototype() + .withEmail(email) + .withGivenName(givenName) + .withFamilyName(familyName) + .withPhoneNumber(phoneNumber) + .withModified(modified) + .withId(id) + .withUsername(username) + .withPassword(password) + .withAuthorities(authorities) + .withCreated(created) + .withOrigin(origin) + .withExternalId(externalId) + .withVerified(verified) + .withZoneId(zoneId) + .withSalt(salt) + .withPasswordLastModified(passwordLastModified)); } public UaaUser modifyOrigin(String origin) { return new UaaUser( - new UaaUserPrototype() - .withEmail(email) - .withGivenName(givenName) - .withFamilyName(familyName) - .withPhoneNumber(phoneNumber) - .withModified(modified) - .withId(id) - .withUsername(username) - .withPassword(password) - .withAuthorities(authorities) - .withCreated(created) - .withOrigin(origin) - .withExternalId(externalId) - .withVerified(verified) - .withZoneId(zoneId) - .withSalt(salt) - .withPasswordLastModified(passwordLastModified)); + new UaaUserPrototype() + .withEmail(email) + .withGivenName(givenName) + .withFamilyName(familyName) + .withPhoneNumber(phoneNumber) + .withModified(modified) + .withId(id) + .withUsername(username) + .withPassword(password) + .withAuthorities(authorities) + .withCreated(created) + .withOrigin(origin) + .withExternalId(externalId) + .withVerified(verified) + .withZoneId(zoneId) + .withSalt(salt) + .withPasswordLastModified(passwordLastModified)); } public UaaUser modifyId(String id) { return new UaaUser( - new UaaUserPrototype() - .withEmail(email) - .withGivenName(givenName) - .withFamilyName(familyName) - .withPhoneNumber(phoneNumber) - .withModified(modified) - .withId(id) - .withUsername(username) - .withPassword(password) - .withAuthorities(authorities) - .withCreated(created) - .withOrigin(origin) - .withExternalId(externalId) - .withVerified(verified) - .withZoneId(zoneId) - .withSalt(salt) - .withPasswordLastModified(passwordLastModified)); + new UaaUserPrototype() + .withEmail(email) + .withGivenName(givenName) + .withFamilyName(familyName) + .withPhoneNumber(phoneNumber) + .withModified(modified) + .withId(id) + .withUsername(username) + .withPassword(password) + .withAuthorities(authorities) + .withCreated(created) + .withOrigin(origin) + .withExternalId(externalId) + .withVerified(verified) + .withZoneId(zoneId) + .withSalt(salt) + .withPasswordLastModified(passwordLastModified)); } public UaaUser modifyUsername(String username) { return new UaaUser( - new UaaUserPrototype() - .withEmail(email) - .withGivenName(givenName) - .withFamilyName(familyName) - .withPhoneNumber(phoneNumber) - .withModified(modified) - .withId(id) - .withUsername(username) - .withPassword(password) - .withAuthorities(authorities) - .withCreated(created) - .withOrigin(origin) - .withExternalId(externalId) - .withVerified(verified) - .withZoneId(zoneId) - .withSalt(salt) - .withPasswordLastModified(passwordLastModified)); + new UaaUserPrototype() + .withEmail(email) + .withGivenName(givenName) + .withFamilyName(familyName) + .withPhoneNumber(phoneNumber) + .withModified(modified) + .withId(id) + .withUsername(username) + .withPassword(password) + .withAuthorities(authorities) + .withCreated(created) + .withOrigin(origin) + .withExternalId(externalId) + .withVerified(verified) + .withZoneId(zoneId) + .withSalt(salt) + .withPasswordLastModified(passwordLastModified)); } public UaaUser modifyAttributes(String email, @@ -379,44 +338,4 @@ public UaaUser modifyAttributes(String email, .withSalt(salt) .withPasswordLastModified(passwordLastModified)); } - - public boolean isVerified() { - return verified; - } - - public void setVerified(boolean verified) { - this.verified = verified; - } - - public String getPhoneNumber() { - return phoneNumber; - } - - public boolean isLegacyVerificationBehavior() { - return legacyVerificationBehavior; - } - - public boolean isPasswordChangeRequired() { - return passwordChangeRequired; - } - - public void setPasswordChangeRequired(boolean passwordChangeRequired) { - this.passwordChangeRequired = passwordChangeRequired; - } - - public Long getLastLogonTime() { - return lastLogonTime; - } - - public void setLastLogonTime(Long lastLogonTime) { - this.lastLogonTime = lastLogonTime; - } - - public Long getPreviousLogonTime() { - return previousLogonTime; - } - - public void setPreviousLogonTime(Long previousLogonTime) { - this.previousLogonTime = previousLogonTime; - } } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/user/UaaUserPrototype.java b/server/src/main/java/org/cloudfoundry/identity/uaa/user/UaaUserPrototype.java index 1c290631623..c343cc604d2 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/user/UaaUserPrototype.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/user/UaaUserPrototype.java @@ -12,12 +12,14 @@ *******************************************************************************/ package org.cloudfoundry.identity.uaa.user; +import lombok.Getter; import org.cloudfoundry.identity.uaa.authentication.NonStringPassword; import org.springframework.security.core.GrantedAuthority; import java.util.Date; import java.util.List; +@Getter public final class UaaUserPrototype { private String id = "NaN"; @@ -65,42 +67,32 @@ public UaaUserPrototype() { public UaaUserPrototype(UaaUser user) { withVerified(user.isVerified()) - .withLegacyVerificationBehavior(user.isLegacyVerificationBehavior()) - .withEmail(user.getEmail()) - .withUsername(user.getUsername()) - .withPhoneNumber(user.getPhoneNumber()) - .withId(user.getId()) - .withOrigin(user.getOrigin()) - .withZoneId(user.getZoneId()) - .withAuthorities(user.getAuthorities()) - .withPassword(user.getPassword()) - .withFamilyName(user.getFamilyName()) - .withGivenName(user.getGivenName()) - .withExternalId(user.getExternalId()) - .withPasswordLastModified(user.getPasswordLastModified()) - .withLastLogonSuccess(user.getLastLogonTime()) - .withPreviousLogonSuccess(user.getPreviousLogonTime()) - .withSalt(user.getSalt()) - .withCreated(user.getCreated()) - .withModified(user.getModified()) - .withPasswordChangeRequired(user.isPasswordChangeRequired()); - - } - - public String getId() { - return id; + .withLegacyVerificationBehavior(user.isLegacyVerificationBehavior()) + .withEmail(user.getEmail()) + .withUsername(user.getUsername()) + .withPhoneNumber(user.getPhoneNumber()) + .withId(user.getId()) + .withOrigin(user.getOrigin()) + .withZoneId(user.getZoneId()) + .withAuthorities(user.getAuthorities()) + .withPassword(user.getPassword()) + .withFamilyName(user.getFamilyName()) + .withGivenName(user.getGivenName()) + .withExternalId(user.getExternalId()) + .withPasswordLastModified(user.getPasswordLastModified()) + .withLastLogonSuccess(user.getLastLogonTime()) + .withPreviousLogonSuccess(user.getPreviousLogonTime()) + .withSalt(user.getSalt()) + .withCreated(user.getCreated()) + .withModified(user.getModified()) + .withPasswordChangeRequired(user.isPasswordChangeRequired()); } - public UaaUserPrototype withId(String id) { this.id = id; return this; } - public String getUsername() { - return username; - } - public UaaUserPrototype withUsername(String username) { this.username = username; return this; @@ -118,148 +110,87 @@ UaaUserPrototype withPassword(NonStringPassword password) { this.password = password; return this; } + public UaaUserPrototype withPassword(String password) { this.password = new NonStringPassword(password); return this; } - public String getEmail() { - return email; - } - public UaaUserPrototype withEmail(String email) { this.email = email; return this; } - public String getGivenName() { - return givenName; - } - public UaaUserPrototype withGivenName(String givenName) { this.givenName = givenName; return this; } - public String getFamilyName() { - return familyName; - } - public UaaUserPrototype withFamilyName(String familyName) { this.familyName = familyName; return this; } - public String getPhoneNumber() { - return phoneNumber; - } - public UaaUserPrototype withPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; return this; } - public Date getCreated() { - return created; - } - public UaaUserPrototype withCreated(Date created) { this.created = created; return this; } - public Date getModified() { - return modified; - } - public UaaUserPrototype withModified(Date modified) { this.modified = modified; return this; } - public String getOrigin() { - return origin; - } - public UaaUserPrototype withOrigin(String origin) { this.origin = origin; return this; } - public String getExternalId() { - return externalId; - } - public UaaUserPrototype withExternalId(String externalId) { this.externalId = externalId; return this; } - public String getSalt() { - return salt; - } - public UaaUserPrototype withSalt(String salt) { this.salt = salt; return this; } - public Date getPasswordLastModified() { - return passwordLastModified; - } - public UaaUserPrototype withPasswordLastModified(Date passwordLastModified) { this.passwordLastModified = passwordLastModified; return this; } - public String getZoneId() { - return zoneId; - } - public UaaUserPrototype withZoneId(String zoneId) { this.zoneId = zoneId; return this; } - public List getAuthorities() { - return authorities; - } - public UaaUserPrototype withAuthorities(List authorities) { this.authorities = authorities; return this; } - public boolean isVerified() { - return verified; - } - public UaaUserPrototype withVerified(boolean verified) { this.verified = verified; return this; } - public boolean isLegacyVerificationBehavior() { return legacyVerificationBehavior; } - public UaaUserPrototype withLegacyVerificationBehavior(boolean legacyVerificationBehavior) { this.legacyVerificationBehavior = legacyVerificationBehavior; return this; } - public boolean isPasswordChangeRequired() { - return passwordChangeRequired; - } - public UaaUserPrototype withPasswordChangeRequired(boolean requiresPasswordChange) { this.passwordChangeRequired = requiresPasswordChange; return this; } - public Long getLastLogonTime() { - return lastLogonTime; - } - public UaaUserPrototype withLastLogonSuccess(Long lastLogonTime) { this.lastLogonTime = lastLogonTime; return this; @@ -269,8 +200,4 @@ public UaaUserPrototype withPreviousLogonSuccess(Long previousLogonTime) { this.previousLogonTime = previousLogonTime; return this; } - - public Long getPreviousLogonTime() { - return previousLogonTime; - } } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/authentication/UaaAuthenticationSerializerDeserializerTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/authentication/UaaAuthenticationSerializerDeserializerTest.java index 75f12ac749d..212404c4ef8 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/authentication/UaaAuthenticationSerializerDeserializerTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/authentication/UaaAuthenticationSerializerDeserializerTest.java @@ -7,16 +7,16 @@ import org.junit.Test; import java.util.Collections; -import java.util.LinkedList; +import java.util.Objects; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class UaaAuthenticationSerializerDeserializerTest { @Test public void serializeUaaAuthentication() { UaaPrincipal p = new UaaPrincipal("user-id", "username", "user@example.com", OriginKeys.UAA, "", IdentityZoneHolder.get().getId()); - UaaAuthentication auth = new UaaAuthentication(p, UaaAuthority.USER_AUTHORITIES, new UaaAuthenticationDetails(false, "clientId", OriginKeys.ORIGIN,"sessionId")); + UaaAuthentication auth = new UaaAuthentication(p, UaaAuthority.USER_AUTHORITIES, new UaaAuthenticationDetails(false, "clientId", OriginKeys.ORIGIN, "sessionId")); auth.setAuthenticationMethods(Collections.singleton("pwd")); auth.setAuthContextClassRef(Collections.singleton("test:uri")); auth.setAuthenticatedTime(1485314434675L); @@ -24,16 +24,22 @@ public void serializeUaaAuthentication() { UaaAuthentication deserializedUaaAuthentication = JsonUtils.readValue(JsonUtils.writeValueAsString(auth), UaaAuthentication.class); - assertEquals(auth.getDetails(), deserializedUaaAuthentication.getDetails()); - assertEquals(auth.getPrincipal(), deserializedUaaAuthentication.getPrincipal()); - assertEquals("uaa.user", ((LinkedList) deserializedUaaAuthentication.getAuthorities()).get(0).toString()); - assertEquals(Collections.EMPTY_SET, deserializedUaaAuthentication.getExternalGroups()); - assertEquals(auth.getExpiresAt(), deserializedUaaAuthentication.getExpiresAt()); - assertEquals(auth.getAuthenticatedTime(), deserializedUaaAuthentication.getAuthenticatedTime()); - assertEquals(auth.isAuthenticated(), deserializedUaaAuthentication.isAuthenticated()); - assertEquals(auth.getUserAttributesAsMap(), deserializedUaaAuthentication.getUserAttributesAsMap()); - assertEquals(auth.getAuthenticationMethods(), deserializedUaaAuthentication.getAuthenticationMethods()); - assertEquals(auth.getAuthContextClassRef(), deserializedUaaAuthentication.getAuthContextClassRef()); - assertEquals(auth.getLastLoginSuccessTime(), deserializedUaaAuthentication.getLastLoginSuccessTime()); + assertThat(deserializedUaaAuthentication) + .returns(auth.getDetails(), UaaAuthentication::getDetails) + .returns(auth.getPrincipal(), UaaAuthentication::getPrincipal) + .returns(auth.getExpiresAt(), UaaAuthentication::getExpiresAt) + .returns(auth.getAuthenticatedTime(), UaaAuthentication::getAuthenticatedTime) + .returns(auth.isAuthenticated(), UaaAuthentication::isAuthenticated) + .returns(auth.getUserAttributesAsMap(), UaaAuthentication::getUserAttributesAsMap) + .returns(auth.getAuthenticationMethods(), UaaAuthentication::getAuthenticationMethods) + .returns(auth.getAuthContextClassRef(), UaaAuthentication::getAuthContextClassRef) + .returns(auth.getLastLoginSuccessTime(), UaaAuthentication::getLastLoginSuccessTime); + + assertThat(deserializedUaaAuthentication.getAuthorities()) + .extracting(Objects::toString) + .containsExactly("uaa.user"); + + assertThat(deserializedUaaAuthentication.getExternalGroups()) + .isEmpty(); } } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/ConfiguratorRelyingPartyRegistrationRepositoryTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/ConfiguratorRelyingPartyRegistrationRepositoryTest.java index 836fd2d8e34..a27c756e3d3 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/ConfiguratorRelyingPartyRegistrationRepositoryTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/ConfiguratorRelyingPartyRegistrationRepositoryTest.java @@ -10,6 +10,8 @@ import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.Arrays; +import java.util.List; +import java.util.function.Function; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -22,17 +24,19 @@ public class ConfiguratorRelyingPartyRegistrationRepositoryTest { private SamlIdentityProviderConfigurator mockConfigurator; private KeyWithCert mockKeyWithCert; private ConfiguratorRelyingPartyRegistrationRepository target; + private Function assertionConsumerServiceLocationFunction; @Before public void setup() { mockConfigurator = mock(SamlIdentityProviderConfigurator.class); mockKeyWithCert = mock(KeyWithCert.class); + assertionConsumerServiceLocationFunction = "{baseUrl}/saml/SSO/alias/%s"::formatted; } @Test public void constructor_nullConfigurator() { assertThrows(IllegalArgumentException.class, () -> { - target = new ConfiguratorRelyingPartyRegistrationRepository(true, ENTITY_ID, mockKeyWithCert, null); + target = new ConfiguratorRelyingPartyRegistrationRepository(true, ENTITY_ID, mockKeyWithCert, null, assertionConsumerServiceLocationFunction); }); } @@ -40,14 +44,14 @@ public void constructor_nullConfigurator() { public void testFindByRegistrationIdWhenNoneFound() throws IOException { when(mockKeyWithCert.getCertificate()).thenReturn(mock(X509Certificate.class)); when(mockKeyWithCert.getPrivateKey()).thenReturn(mock(PrivateKey.class)); - target = new ConfiguratorRelyingPartyRegistrationRepository(true, ENTITY_ID, mockKeyWithCert, mockConfigurator); + target = new ConfiguratorRelyingPartyRegistrationRepository(true, ENTITY_ID, mockKeyWithCert, mockConfigurator, assertionConsumerServiceLocationFunction); SamlIdentityProviderDefinition mockDefinition1 = mock(SamlIdentityProviderDefinition.class); when(mockDefinition1.getIdpEntityAlias()).thenReturn("registration1"); when(mockDefinition1.getNameID()).thenReturn("name1"); when(mockDefinition1.getMetaDataLocation()).thenReturn("saml-sample-metadata.xml"); - when(mockConfigurator.getIdentityProviderDefinitions()).thenReturn(Arrays.asList(mockDefinition1)); + when(mockConfigurator.getIdentityProviderDefinitions()).thenReturn(List.of(mockDefinition1)); assertNull(target.findByRegistrationId("registrationNotFound")); } @@ -55,7 +59,7 @@ public void testFindByRegistrationIdWhenNoneFound() throws IOException { public void testFindByRegistrationId() throws IOException { when(mockKeyWithCert.getCertificate()).thenReturn(mock(X509Certificate.class)); when(mockKeyWithCert.getPrivateKey()).thenReturn(mock(PrivateKey.class)); - target = new ConfiguratorRelyingPartyRegistrationRepository(true, ENTITY_ID, mockKeyWithCert, mockConfigurator); + target = new ConfiguratorRelyingPartyRegistrationRepository(true, ENTITY_ID, mockKeyWithCert, mockConfigurator, assertionConsumerServiceLocationFunction); //definition 1 SamlIdentityProviderDefinition mockDefinition1 = mock(SamlIdentityProviderDefinition.class); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2TestUtils.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2TestUtils.java new file mode 100644 index 00000000000..b01e7325f22 --- /dev/null +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2TestUtils.java @@ -0,0 +1,235 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.cloudfoundry.identity.uaa.provider.saml; + +import net.shibboleth.utilities.java.support.xml.SerializeSupport; +import org.opensaml.core.xml.XMLObject; +import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; +import org.opensaml.core.xml.io.Marshaller; +import org.opensaml.core.xml.io.MarshallingException; +import org.opensaml.core.xml.schema.XSDateTime; +import org.opensaml.core.xml.schema.impl.XSDateTimeBuilder; +import org.opensaml.saml.common.SignableSAMLObject; +import org.opensaml.saml.saml2.core.Assertion; +import org.opensaml.saml.saml2.core.Attribute; +import org.opensaml.saml.saml2.core.AttributeStatement; +import org.opensaml.saml.saml2.core.AttributeValue; +import org.opensaml.saml.saml2.core.AuthnRequest; +import org.opensaml.saml.saml2.core.Conditions; +import org.opensaml.saml.saml2.core.Response; +import org.opensaml.saml.saml2.core.SubjectConfirmation; +import org.opensaml.saml.saml2.core.SubjectConfirmationData; +import org.opensaml.saml.saml2.core.impl.AttributeBuilder; +import org.opensaml.xmlsec.signature.support.SignatureConstants; +import org.springframework.security.saml2.Saml2Exception; +import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest; +import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; +import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.time.Instant; +import java.util.List; +import java.util.Map; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * This class contains functions to create SAML Requests, Responses, Tokens and related objects for testing purposes. + * + * @see TestOpenSamlObjects + *

+ * The Functions in here were copied from Spring-Security Test Classes and made static: + * - spring-security/saml2/saml2-service-provider/src/opensaml4Test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProviderTests + */ +public final class Saml2TestUtils { + + private static final String DESTINATION = "https://localhost/login/saml2/sso/idp-alias"; + + private static final String RELYING_PARTY_ENTITY_ID = "https://localhost/saml2/service-provider-metadata/idp-alias"; + + private static final String ASSERTING_PARTY_ENTITY_ID = "https://some.idp.test/saml2/idp"; + + private Saml2TestUtils() { + } + + public static Saml2AuthenticationToken authenticationToken() { + Response response = responseWithAssertions(); + + AbstractSaml2AuthenticationRequest mockAuthenticationRequest = mockedStoredAuthenticationRequest("SAML2", + Saml2MessageBinding.POST, false); + Saml2AuthenticationToken token = token(response, verifying(registration()), mockAuthenticationRequest); + return token; + } + + public static Response responseWithAssertions() { + Response response = response(); + Assertion assertion = TestOpenSamlObjects.signed(assertion(), + TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID, + SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1); + response.getAssertions().add(assertion); + List attributeStatements = attributeStatements(); + assertion.getAttributeStatements().addAll(attributeStatements); + + return response; + } + + public static String serialize(XMLObject object) { + try { + Marshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object); + Element element = marshaller.marshall(object); + return SerializeSupport.nodeToString(element); + } catch (MarshallingException ex) { + throw new Saml2Exception(ex); + } + } + + public static Response response() { + Response response = TestOpenSamlObjects.response(); + response.setIssueInstant(Instant.now()); + return response; + } + + private static Response response(String destination, String issuerEntityId) { + Response response = TestOpenSamlObjects.response(destination, issuerEntityId); + response.setIssueInstant(Instant.now()); + return response; + } + + private static AuthnRequest request() { + AuthnRequest request = TestOpenSamlObjects.authnRequest(); + return request; + } + + private static String serializedRequest(AuthnRequest request, Saml2MessageBinding binding) { + String xml = serialize(request); + return (binding == Saml2MessageBinding.POST) ? Saml2Utils.samlEncode(xml.getBytes(StandardCharsets.UTF_8)) + : Saml2Utils.samlEncode(Saml2Utils.samlDeflate(xml)); + } + + public static String serializedResponse(Response response) { + String xml = serialize(response); + return Saml2Utils.samlEncode(xml.getBytes(StandardCharsets.UTF_8)); + } + + private static Assertion assertion(String inResponseTo) { + Assertion assertion = TestOpenSamlObjects.assertion(); + assertion.setIssueInstant(Instant.now()); + for (SubjectConfirmation confirmation : assertion.getSubject().getSubjectConfirmations()) { + SubjectConfirmationData data = confirmation.getSubjectConfirmationData(); + data.setNotBefore(Instant.now().minus(Duration.ofMillis(5 * 60 * 1000))); + data.setNotOnOrAfter(Instant.now().plus(Duration.ofMillis(5 * 60 * 1000))); + if (StringUtils.hasText(inResponseTo)) { + data.setInResponseTo(inResponseTo); + } + } + Conditions conditions = assertion.getConditions(); + conditions.setNotBefore(Instant.now().minus(Duration.ofMillis(5 * 60 * 1000))); + conditions.setNotOnOrAfter(Instant.now().plus(Duration.ofMillis(5 * 60 * 1000))); + return assertion; + } + + private static Assertion assertion() { + return assertion(null); + } + + private static T signed(T toSign) { + TestOpenSamlObjects.signed(toSign, TestSaml2X509Credentials.assertingPartySigningCredential(), + RELYING_PARTY_ENTITY_ID); + return toSign; + } + + private static List attributeStatements() { + List attributeStatements = TestOpenSamlObjects.attributeStatements(); + AttributeBuilder attributeBuilder = new AttributeBuilder(); + Attribute registeredDateAttr = attributeBuilder.buildObject(); + registeredDateAttr.setName("registeredDate"); + XSDateTime registeredDate = new XSDateTimeBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, + XSDateTime.TYPE_NAME); + registeredDate.setValue(Instant.parse("1970-01-01T00:00:00Z")); + registeredDateAttr.getAttributeValues().add(registeredDate); + attributeStatements.iterator().next().getAttributes().add(registeredDateAttr); + return attributeStatements; + } + + private static Saml2AuthenticationToken token() { + Response response = response(); + RelyingPartyRegistration registration = verifying(registration()).build(); + return new Saml2AuthenticationToken(registration, serialize(response)); + } + + private static Saml2AuthenticationToken token(Response response, RelyingPartyRegistration.Builder registration) { + return new Saml2AuthenticationToken(registration.build(), serialize(response)); + } + + private static Saml2AuthenticationToken token(Response response, RelyingPartyRegistration.Builder registration, + AbstractSaml2AuthenticationRequest authenticationRequest) { + return new Saml2AuthenticationToken(registration.build(), serialize(response), authenticationRequest); + } + + private static AbstractSaml2AuthenticationRequest mockedStoredAuthenticationRequest(String requestId, + Saml2MessageBinding binding, boolean corruptRequestString) { + AuthnRequest request = request(); + if (requestId != null) { + request.setID(requestId); + } + String serializedRequest = serializedRequest(request, binding); + if (corruptRequestString) { + serializedRequest = serializedRequest.substring(2, serializedRequest.length() - 2); + } + AbstractSaml2AuthenticationRequest mockAuthenticationRequest = mock(AbstractSaml2AuthenticationRequest.class); + given(mockAuthenticationRequest.getSamlRequest()).willReturn(serializedRequest); + given(mockAuthenticationRequest.getBinding()).willReturn(binding); + return mockAuthenticationRequest; + } + + private static RelyingPartyRegistration.Builder registration() { + return TestRelyingPartyRegistrations.noCredentials() + .entityId(RELYING_PARTY_ENTITY_ID) + .assertionConsumerServiceLocation(DESTINATION) + .assertingPartyDetails((party) -> party.entityId(ASSERTING_PARTY_ENTITY_ID)); + } + + private static RelyingPartyRegistration.Builder verifying(RelyingPartyRegistration.Builder builder) { + return builder.assertingPartyDetails((party) -> party + .verificationX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential()))); + } + + private static RelyingPartyRegistration.Builder decrypting(RelyingPartyRegistration.Builder builder) { + return builder + .decryptionX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyDecryptingCredential())); + } + + public static Map xmlNamespaces() { + return Map.of( + // Metadata + "md", "urn:oasis:names:tc:SAML:2.0:metadata", + "ds", "http://www.w3.org/2000/09/xmldsig#", + // Request + "saml2p", "urn:oasis:names:tc:SAML:2.0:protocol", + "saml2", "urn:oasis:names:tc:SAML:2.0:assertion", + // Response + "samlp", "urn:oasis:names:tc:SAML:2.0:protocol", + "saml", "urn:oasis:names:tc:SAML:2.0:assertion" + ); + } + +} diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/SamlLoginAuthenticationProviderTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/SamlLoginAuthenticationProviderTests.java index f7d5955e7cb..c7849e9cb31 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/SamlLoginAuthenticationProviderTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/SamlLoginAuthenticationProviderTests.java @@ -11,7 +11,6 @@ import org.cloudfoundry.identity.uaa.provider.IdentityProvider; import org.cloudfoundry.identity.uaa.provider.JdbcIdentityProviderProvisioning; import org.cloudfoundry.identity.uaa.provider.SamlIdentityProviderDefinition; -import org.cloudfoundry.identity.uaa.provider.saml.idp.SamlTestUtils; import org.cloudfoundry.identity.uaa.resources.jdbc.JdbcPagingListFactory; import org.cloudfoundry.identity.uaa.resources.jdbc.LimitSqlAdapter; import org.cloudfoundry.identity.uaa.scim.ScimGroup; @@ -40,6 +39,10 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EmptySource; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; import org.opensaml.core.config.InitializationException; import org.opensaml.core.config.InitializationService; import org.springframework.beans.factory.annotation.Autowired; @@ -50,13 +53,12 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.util.LinkedMultiValueMap; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; @@ -74,6 +76,7 @@ import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assumptions.assumeThat; import static org.cloudfoundry.identity.uaa.provider.ExternalIdentityProviderDefinition.EMAIL_ATTRIBUTE_NAME; import static org.cloudfoundry.identity.uaa.provider.ExternalIdentityProviderDefinition.EMAIL_VERIFIED_ATTRIBUTE_NAME; import static org.cloudfoundry.identity.uaa.provider.ExternalIdentityProviderDefinition.FAMILY_NAME_ATTRIBUTE_NAME; @@ -81,11 +84,13 @@ import static org.cloudfoundry.identity.uaa.provider.ExternalIdentityProviderDefinition.GROUP_ATTRIBUTE_NAME; import static org.cloudfoundry.identity.uaa.provider.ExternalIdentityProviderDefinition.PHONE_NUMBER_ATTRIBUTE_NAME; import static org.cloudfoundry.identity.uaa.provider.ExternalIdentityProviderDefinition.USER_ATTRIBUTE_PREFIX; +import static org.cloudfoundry.identity.uaa.provider.saml.Saml2TestUtils.authenticationToken; import static org.cloudfoundry.identity.uaa.test.ModelTestUtils.getResourceAsString; import static org.cloudfoundry.identity.uaa.util.AssertThrowsWithMessage.assertThrowsWithMessageThat; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -97,6 +102,7 @@ @WithDatabaseContext class SamlLoginAuthenticationProviderTests { + private static final String SAML_USER = "saml.user"; private static final String SAML_ADMIN = "saml.admin"; private static final String SAML_TEST = "saml.test"; @@ -110,13 +116,17 @@ class SamlLoginAuthenticationProviderTests { private static final String MANAGER = "manager"; private static final String JOHN_THE_SLOTH = "John the Sloth"; private static final String KARI_THE_ANT_EATER = "Kari the Ant Eater"; + private static final String IDP_META_DATA = getResourceAsString(SamlLoginAuthenticationProviderTests.class, "IDP_META_DATA.xml"); + private static final String TEST_EMAIL = "john.doe@example.com"; + private static final String TEST_USERNAME = "test@saml.user"; + private static final String TEST_PHONE_NUMBER = "123-456-7890"; + @Autowired + NamedParameterJdbcTemplate namedJdbcTemplate; private JdbcIdentityProviderProvisioning providerProvisioning; private CreateUserPublisher publisher; private JdbcUaaUserDatabase userDatabase; - private SamlLoginAuthenticationProvider authprovider; - // private WebSSOProfileConsumer consumer; -// private SAMLLogger samlLogger = mock(SAMLLogger.class); + private AuthenticationProvider authprovider; private SamlIdentityProviderDefinition providerDefinition; private IdentityProvider provider; private ScimUserProvisioning userProvisioning; @@ -124,19 +134,30 @@ class SamlLoginAuthenticationProviderTests { private ScimGroup uaaSamlUser; private ScimGroup uaaSamlAdmin; private IdentityZoneManager identityZoneManager; - @Autowired private JdbcTemplate jdbcTemplate; - - @Autowired - NamedParameterJdbcTemplate namedJdbcTemplate; - @Autowired private LimitSqlAdapter limitSqlAdapter; - @Autowired private PasswordEncoder passwordEncoder; + private static ScimUser createSamlUser(String username, String zoneId, ScimUserProvisioning userProvisioning) { + ScimUser user = new ScimUser("", username, "Marissa", "Bloggs"); + user.setPrimaryEmail("marissa.bloggs@test.com"); + user.setOrigin(OriginKeys.SAML); + return userProvisioning.createUser(user, "", zoneId); + } + + private UaaAuthentication authenticate() { + return authenticate(authenticationToken()); + } + + private UaaAuthentication authenticate(Authentication inAuthentication) { + Authentication authentication = authprovider.authenticate(inAuthentication); + assertThat(authentication).isInstanceOf(UaaAuthentication.class); + return (UaaAuthentication) authentication; + } + @BeforeEach void configureProvider() throws SecurityException, SQLException, InitializationException { identityZoneManager = new IdentityZoneManagerImpl(); @@ -174,7 +195,7 @@ void configureProvider() throws SecurityException, SQLException, InitializationE externalManager.mapExternalGroup(uaaSamlTest.getId(), SAML_TEST, OriginKeys.SAML, identityZoneManager.getCurrentIdentityZone().getId()); // consumer = mock(WebSSOProfileConsumer.class); -// SAMLCredential credential = getUserCredential("marissa-saml", "Marissa", "Bloggs", "marissa.bloggs@test.com", "1234567890"); +// SAMLCredential credential = getUserCredential("marissa-saml", "Marissa", "Bloggs", "marissa.bloggs@test.com", TEST_PHONE_NUMBER); // // when(consumer.processAuthenticationResponse(any())).thenReturn(credential); @@ -186,17 +207,20 @@ void configureProvider() throws SecurityException, SQLException, InitializationE providerProvisioning = new JdbcIdentityProviderProvisioning(jdbcTemplate); publisher = new CreateUserPublisher(bootstrap); - authprovider = new SamlLoginAuthenticationProvider( + SamlAuthenticationFilterConfig samlAuthenticationFilterConfig = new SamlAuthenticationFilterConfig(); + authprovider = samlAuthenticationFilterConfig.samlAuthenticationProvider( identityZoneManager, userDatabase, providerProvisioning); - authprovider.setApplicationEventPublisher(publisher); + if (authprovider instanceof SamlLoginAuthenticationProvider authProvider) { + authProvider.setApplicationEventPublisher(publisher); + } providerDefinition = new SamlIdentityProviderDefinition(); providerDefinition.setMetaDataLocation(String.format(IDP_META_DATA, OriginKeys.SAML)); providerDefinition.setIdpEntityAlias(OriginKeys.SAML); - provider = new IdentityProvider(); + provider = new IdentityProvider<>(); provider.setIdentityZoneId(IdentityZone.getUaaZoneId()); provider.setOriginKey(OriginKeys.SAML); provider.setName("saml-test"); @@ -214,31 +238,35 @@ void tearDown(@Autowired ApplicationContext applicationContext) throws SQLExcept @Test void testAuthenticateSimple() { - assertThat(authprovider.authenticate(mockSamlAuthentication())).isNotNull(); + assertThat(authprovider.authenticate(authenticationToken())).isNotNull(); + } + + @ParameterizedTest(name = "#{index} relayRedirectRejectsNonUrls - {0}") + @ValueSource(strings = {"test", "www.google.com"}) + @NullSource + @EmptySource + void relayRedirectRejectsNonUrls(String url) { + assumeThat(authprovider).isInstanceOf(SamlLoginAuthenticationProvider.class); + SamlLoginAuthenticationProvider authprovider = (SamlLoginAuthenticationProvider) this.authprovider; + authprovider.configureRelayRedirect(url); + assertThat(RequestContextHolder.currentRequestAttributes() + .getAttribute(UaaSavedRequestAwareAuthenticationSuccessHandler.URI_OVERRIDE_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST)) + .isNull(); } @Test void testAuthenticationEvents() { - authprovider.authenticate(mockSamlAuthentication()); + authprovider.authenticate(authenticationToken()); assertEquals(3, publisher.events.size()); - assertTrue(publisher.events.get(2) instanceof IdentityProviderAuthenticationSuccessEvent); - } - - @Test - void relay_sets_attribute() { - for (String url : Arrays.asList("test", "www.google.com", null)) { - authprovider.configureRelayRedirect(url); - assertThat(RequestContextHolder.currentRequestAttributes().getAttribute(UaaSavedRequestAwareAuthenticationSuccessHandler.URI_OVERRIDE_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST)) - .isNull(); - } + assertInstanceOf(IdentityProviderAuthenticationSuccessEvent.class, publisher.events.get(2)); } @Test - void test_relay_state_when_url() { + void relayRedirectIsSetForUrl() { String redirectUrl = "https://www.cloudfoundry.org"; - Saml2AuthenticationToken mockAuthenticationToken = mockSamlAuthentication(); + Saml2AuthenticationToken mockAuthenticationToken = authenticationToken(); when(mockAuthenticationToken.getAuthenticationRequest().getRelayState()).thenReturn(redirectUrl); - UaaAuthentication authentication = getAuthentication(mockAuthenticationToken); + UaaAuthentication authentication = authenticate(mockAuthenticationToken); //assertThat(uaaAuthentication.getAuthContextClassRef()).contains(AuthnContext.PASSWORD_AUTHN_CTX); verify(mockAuthenticationToken.getAuthenticationRequest(), times(1)).getRelayState(); //assertThat(RequestContextHolder.currentRequestAttributes().getAttribute(UaaSavedRequestAwareAuthenticationSuccessHandler.URI_OVERRIDE_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST)) @@ -247,8 +275,8 @@ void test_relay_state_when_url() { @Test void saml_authentication_contains_acr() { - Saml2AuthenticationToken mockAuthenticationToken = mockSamlAuthentication(); - UaaAuthentication authentication = getAuthentication(mockAuthenticationToken); + Saml2AuthenticationToken mockAuthenticationToken = authenticationToken(); + UaaAuthentication authentication = authenticate(mockAuthenticationToken); //assertThat(uaaAuthentication.getAuthContextClassRef()).contains(AuthnContext.PASSWORD_AUTHN_CTX); verify(mockAuthenticationToken.getAuthenticationRequest(), times(1)).getRelayState(); assertThat(RequestContextHolder.currentRequestAttributes().getAttribute(UaaSavedRequestAwareAuthenticationSuccessHandler.URI_OVERRIDE_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST)) @@ -275,7 +303,7 @@ void test_multiple_group_attributes() { @Test void authenticationContainsAmr() { - UaaAuthentication authentication = getAuthentication(mockSamlAuthentication()); + UaaAuthentication authentication = authenticate(); assertThat(authentication.getAuthenticationMethods()).contains("ext"); } @@ -375,7 +403,7 @@ void dontAdd_external_groups_to_authentication_without_whitelist() { provider.setConfig(providerDefinition); providerProvisioning.update(provider, identityZoneManager.getCurrentIdentityZone().getId()); - UaaAuthentication authentication = getAuthentication(mockSamlAuthentication()); + UaaAuthentication authentication = authenticate(); assertThat(authentication.getExternalGroups()).isEmpty(); } @@ -461,12 +489,13 @@ private ScimUser getInvitedUser() { @Disabled("SAML test doesn't compile") void update_existingUser_if_attributes_different() throws Exception { try { - userDatabase.retrieveUserByName("marissa-saml", OriginKeys.SAML); + userDatabase.retrieveUserByName(TEST_USERNAME, OriginKeys.SAML); fail("user should not exist"); } catch (UsernameNotFoundException ignored) { } -// getAuthentication(authprovider); - UaaUser user = userDatabase.retrieveUserByName("marissa-saml", OriginKeys.SAML); + authenticate(); + + UaaUser user = userDatabase.retrieveUserByName(TEST_USERNAME, OriginKeys.SAML); assertFalse(user.isVerified()); Map attributeMappings = new HashMap<>(); attributeMappings.put("given_name", "firstName"); @@ -478,18 +507,18 @@ void update_existingUser_if_attributes_different() throws Exception { // SAMLCredential credential = getUserCredential("marissa-saml", "Marissa-changed", null, "marissa.bloggs@change.org", null); // when(consumer.processAuthenticationResponse(any())).thenReturn(credential); -// getAuthentication(authprovider); + authenticate(); - user = userDatabase.retrieveUserByName("marissa-saml", OriginKeys.SAML); + user = userDatabase.retrieveUserByName(TEST_USERNAME, OriginKeys.SAML); assertEquals("Marissa-changed", user.getGivenName()); assertEquals("marissa.bloggs@change.org", user.getEmail()); assertFalse(user.isVerified()); // credential = getUserCredential("marissa-saml", "Marissa-changed", null, "marissa.bloggs@change.org", null, true); // when(consumer.processAuthenticationResponse(any())).thenReturn(credential); -// getAuthentication(authprovider); + authenticate(); - user = userDatabase.retrieveUserByName("marissa-saml", OriginKeys.SAML); + user = userDatabase.retrieveUserByName(TEST_USERNAME, OriginKeys.SAML); assertEquals("Marissa-changed", user.getGivenName()); assertEquals("marissa.bloggs@change.org", user.getEmail()); assertTrue(user.isVerified()); @@ -507,17 +536,17 @@ void update_existingUser_if_username_different() { provider.setConfig(providerDefinition); providerProvisioning.update(provider, identityZoneManager.getCurrentIdentityZone().getId()); -// getAuthentication(authprovider); + authenticate(); - UaaUser originalUser = userDatabase.retrieveUserByEmail("marissa.bloggs@test.com", OriginKeys.SAML); + UaaUser originalUser = userDatabase.retrieveUserByEmail(TEST_EMAIL, OriginKeys.SAML); assertNotNull(originalUser); - assertEquals("marissa-saml", originalUser.getUsername()); + assertEquals(TEST_USERNAME, originalUser.getUsername()); LinkedMultiValueMap attributes = new LinkedMultiValueMap<>(); attributes.add(GIVEN_NAME_ATTRIBUTE_NAME, "Marissa"); attributes.add(FAMILY_NAME_ATTRIBUTE_NAME, "Bloggs"); attributes.add(EMAIL_ATTRIBUTE_NAME, "marissa.bloggs@test.com"); - attributes.add(PHONE_NUMBER_ATTRIBUTE_NAME, "1234567890"); + attributes.add(PHONE_NUMBER_ATTRIBUTE_NAME, TEST_PHONE_NUMBER); UaaPrincipal samlPrincipal = new UaaPrincipal(OriginKeys.NotANumber, "marissa-saml-changed", "marissa.bloggs@test.com", OriginKeys.SAML, "marissa-saml-changed", identityZoneManager.getCurrentIdentityZone().getId()); // UaaUser user = authprovider.createIfMissing(samlPrincipal, false, new ArrayList(), attributes); @@ -528,22 +557,22 @@ void update_existingUser_if_username_different() { @Test void dont_update_existingUser_if_attributes_areTheSame() { - getAuthentication(mockSamlAuthentication()); - String email = "marissa@test.org"; - UaaUser user = userDatabase.retrieveUserByName(email, OriginKeys.SAML); + authenticate(); + UaaUser user = userDatabase.retrieveUserByName(TEST_USERNAME, OriginKeys.SAML); - getAuthentication(mockSamlAuthentication()); - UaaUser existingUser = userDatabase.retrieveUserByName(email, OriginKeys.SAML); + authenticate(); + UaaUser existingUser = userDatabase.retrieveUserByName(TEST_USERNAME, OriginKeys.SAML); assertThat(existingUser.getModified()).isEqualTo(user.getModified()); } @Test void have_attributes_changed() { - getAuthentication(mockSamlAuthentication()); - String email = "marissa@test.org"; + authenticate(); + assumeThat(authprovider).isInstanceOf(SamlLoginAuthenticationProvider.class); + SamlLoginAuthenticationProvider authprovider = (SamlLoginAuthenticationProvider) this.authprovider; - UaaUser existing = userDatabase.retrieveUserByName(email, OriginKeys.SAML); + UaaUser existing = userDatabase.retrieveUserByName(TEST_USERNAME, OriginKeys.SAML); UaaUser modified = new UaaUser(new UaaUserPrototype(existing)); assertThat(authprovider.haveUserAttributesChanged(existing, modified)).isFalse(); modified = new UaaUser(new UaaUserPrototype(existing).withEmail("other-email")); @@ -569,15 +598,14 @@ void shadowAccount_createdWith_MappedUserAttributes() { provider.setConfig(providerDefinition); providerProvisioning.update(provider, identityZoneManager.getCurrentIdentityZone().getId()); - getAuthentication(mockSamlAuthentication()); - String email = "marissa@test.org"; + authenticate(); - UaaUser user = userDatabase.retrieveUserByName(email, OriginKeys.SAML); + UaaUser user = userDatabase.retrieveUserByName(TEST_USERNAME, OriginKeys.SAML); assertThat(user) - .hasFieldOrPropertyWithValue("givenName", "Marissa") - .hasFieldOrPropertyWithValue("familyName", "Bloggs") - .hasFieldOrPropertyWithValue("email", email) - .hasFieldOrPropertyWithValue("phoneNumber", "1234567890"); + .returns("John", UaaUser::getGivenName) + .returns("Doe", UaaUser::getFamilyName) + .returns(TEST_EMAIL, UaaUser::getEmail) + .returns(TEST_PHONE_NUMBER, UaaUser::getPhoneNumber); } @Test @@ -594,14 +622,14 @@ void custom_user_attributes_stored_if_configured() { provider.setConfig(providerDefinition); provider = providerProvisioning.update(provider, identityZoneManager.getCurrentIdentityZone().getId()); - getAuthentication(mockSamlAuthentication()); + authenticate(); String email = "marissa@test.org"; UaaUser user = userDatabase.retrieveUserByName(email, OriginKeys.SAML); assertEquals("Marissa", user.getGivenName()); assertEquals("Bloggs", user.getFamilyName()); assertEquals(email, user.getEmail()); - assertEquals("1234567890", user.getPhoneNumber()); + assertEquals(TEST_PHONE_NUMBER, user.getPhoneNumber()); // assertEquals("marissa.bloggs@test.com", authentication.getUserAttributes().getFirst("secondary_email")); UserInfo userInfo = userDatabase.getUserInfo(user.getId()); @@ -613,7 +641,7 @@ void custom_user_attributes_stored_if_configured() { provider.setConfig(providerDefinition); provider = providerProvisioning.update(provider, identityZoneManager.getCurrentIdentityZone().getId()); - getAuthentication(mockSamlAuthentication()); + authenticate(); // assertEquals("marissa.bloggs@test.com", authentication.getUserAttributes().getFirst("secondary_email")); userInfo = userDatabase.getUserInfo(user.getId()); assertNotNull(userInfo); @@ -760,6 +788,9 @@ void user_authentication_contains_custom_attributes() { @Test @Disabled("SAML test fails") void getUserByDefaultUsesTheAvailableData() { + assumeThat(authprovider).isInstanceOf(SamlLoginAuthenticationProvider.class); + SamlLoginAuthenticationProvider authprovider = (SamlLoginAuthenticationProvider) this.authprovider; + UaaPrincipal principal = new UaaPrincipal( UUID.randomUUID().toString(), "user", @@ -777,7 +808,7 @@ void getUserByDefaultUsesTheAvailableData() { UaaUser user = authprovider.getUser(principal, attributes); assertThat(user) - .hasFieldOrPropertyWithValue("username", "user"); + .returns("user", UaaUser::getUsername); // .withEmail("user@example.com") // .withPhoneNumber("(415) 555-0111") // .withPassword("") @@ -793,6 +824,9 @@ void getUserByDefaultUsesTheAvailableData() { @Test @Disabled("SAML test fails") void getUserWithoutOriginSuppliesDefaultsToLoginServer() { + assumeThat(authprovider).isInstanceOf(SamlLoginAuthenticationProvider.class); + SamlLoginAuthenticationProvider authprovider = (SamlLoginAuthenticationProvider) this.authprovider; + UaaPrincipal principal = new UaaPrincipal( UUID.randomUUID().toString(), "user", @@ -805,12 +839,15 @@ void getUserWithoutOriginSuppliesDefaultsToLoginServer() { LinkedMultiValueMap attributes = new LinkedMultiValueMap<>(); UaaUser user = authprovider.getUser(principal, attributes); assertThat(user) - .hasFieldOrPropertyWithValue("origin", OriginKeys.LOGIN_SERVER); + .returns(OriginKeys.LOGIN_SERVER, UaaUser::getOrigin); } @Test @Disabled("SAML test fails") void getUserWithoutVerifiedDefaultsToFalse() { + assumeThat(authprovider).isInstanceOf(SamlLoginAuthenticationProvider.class); + SamlLoginAuthenticationProvider authprovider = (SamlLoginAuthenticationProvider) this.authprovider; + UaaPrincipal principal = new UaaPrincipal( UUID.randomUUID().toString(), "user", @@ -823,12 +860,15 @@ void getUserWithoutVerifiedDefaultsToFalse() { LinkedMultiValueMap attributes = new LinkedMultiValueMap<>(); UaaUser user = authprovider.getUser(principal, attributes); assertThat(user) - .hasFieldOrPropertyWithValue("verified", false); + .returns(false, UaaUser::isVerified); } @Test @Disabled("SAML test fails") void throwsIfUserNameAndEmailAreMissing() { + assumeThat(authprovider).isInstanceOf(SamlLoginAuthenticationProvider.class); + SamlLoginAuthenticationProvider authprovider = (SamlLoginAuthenticationProvider) this.authprovider; + UaaPrincipal principal = new UaaPrincipal( UUID.randomUUID().toString(), null, @@ -847,28 +887,6 @@ void throwsIfUserNameAndEmailAreMissing() { ); } - private static ScimUser createSamlUser(String username, String zoneId, ScimUserProvisioning userProvisioning) { - ScimUser user = new ScimUser("", username, "Marissa", "Bloggs"); - user.setPrimaryEmail("marissa.bloggs@test.com"); - user.setOrigin(OriginKeys.SAML); - return userProvisioning.createUser(user, "", zoneId); - } - - private UaaAuthentication getAuthentication(Authentication inAuthentication) { - Authentication authentication = authprovider.authenticate(inAuthentication); - assertThat(authentication).isInstanceOf(UaaAuthentication.class); - return (UaaAuthentication) authentication; - } - - private static Saml2AuthenticationToken mockSamlAuthentication() { - RelyingPartyRegistration mockRegistration = mock(RelyingPartyRegistration.class); - AbstractSaml2AuthenticationRequest authenticationRequest = mock(AbstractSaml2AuthenticationRequest.class); - when(mockRegistration.getRegistrationId()).thenReturn(OriginKeys.SAML); - String saml2Response = SamlTestUtils.getSamlResponseXml(); - - return new Saml2AuthenticationToken(mockRegistration, saml2Response, authenticationRequest); - } - public static class CreateUserPublisher implements ApplicationEventPublisher { final ScimUserBootstrap bootstrap; final List events = new ArrayList<>(); @@ -877,7 +895,6 @@ public static class CreateUserPublisher implements ApplicationEventPublisher { this.bootstrap = bootstrap; } - @Override public void publishEvent(ApplicationEvent event) { events.add(event); @@ -891,6 +908,4 @@ public void publishEvent(Object event) { throw new UnsupportedOperationException("not implemented"); } } - - private static final String IDP_META_DATA = getResourceAsString(SamlLoginAuthenticationProviderTests.class, "IDP_META_DATA.xml"); } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestOpenSamlObjects.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestOpenSamlObjects.java new file mode 100644 index 00000000000..c860240146b --- /dev/null +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestOpenSamlObjects.java @@ -0,0 +1,469 @@ +/* + * Copyright 2002-2022 the original author or authors. + * + * 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 + * + * https://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.cloudfoundry.identity.uaa.provider.saml; + +import org.apache.xml.security.encryption.XMLCipherParameters; +import org.opensaml.core.xml.XMLObject; +import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; +import org.opensaml.core.xml.io.MarshallingException; +import org.opensaml.core.xml.schema.XSAny; +import org.opensaml.core.xml.schema.XSBoolean; +import org.opensaml.core.xml.schema.XSBooleanValue; +import org.opensaml.core.xml.schema.XSInteger; +import org.opensaml.core.xml.schema.XSString; +import org.opensaml.core.xml.schema.XSURI; +import org.opensaml.core.xml.schema.impl.XSAnyBuilder; +import org.opensaml.core.xml.schema.impl.XSBooleanBuilder; +import org.opensaml.core.xml.schema.impl.XSIntegerBuilder; +import org.opensaml.core.xml.schema.impl.XSStringBuilder; +import org.opensaml.core.xml.schema.impl.XSURIBuilder; +import org.opensaml.saml.common.SAMLVersion; +import org.opensaml.saml.common.SignableSAMLObject; +import org.opensaml.saml.saml2.core.*; +import org.opensaml.saml.saml2.core.impl.AttributeBuilder; +import org.opensaml.saml.saml2.core.impl.AttributeStatementBuilder; +import org.opensaml.saml.saml2.core.impl.IssuerBuilder; +import org.opensaml.saml.saml2.core.impl.LogoutRequestBuilder; +import org.opensaml.saml.saml2.core.impl.LogoutResponseBuilder; +import org.opensaml.saml.saml2.core.impl.NameIDBuilder; +import org.opensaml.saml.saml2.core.impl.StatusBuilder; +import org.opensaml.saml.saml2.core.impl.StatusCodeBuilder; +import org.opensaml.saml.saml2.encryption.Encrypter; +import org.opensaml.security.SecurityException; +import org.opensaml.security.credential.BasicCredential; +import org.opensaml.security.credential.Credential; +import org.opensaml.security.credential.CredentialSupport; +import org.opensaml.security.credential.UsageType; +import org.opensaml.xmlsec.SignatureSigningParameters; +import org.opensaml.xmlsec.encryption.support.DataEncryptionParameters; +import org.opensaml.xmlsec.encryption.support.EncryptionException; +import org.opensaml.xmlsec.encryption.support.KeyEncryptionParameters; +import org.opensaml.xmlsec.signature.support.SignatureConstants; +import org.opensaml.xmlsec.signature.support.SignatureException; +import org.opensaml.xmlsec.signature.support.SignatureSupport; +import org.springframework.security.saml2.Saml2Exception; +import org.springframework.security.saml2.core.OpenSamlInitializationService; +import org.springframework.security.saml2.core.Saml2X509Credential; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; + +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import javax.xml.namespace.QName; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; + +/** + * This class contains functions to create SAML Requests, Responses, Tokens and related objects for testing purposes. + * These are building blocks, and most of the functionality here can be accessed via Saml2TestUtils, which does additional configuration. + *

+ * This was copied from Spring Security Test Classes + * Migrate to use the Spring Security class when it is made public + *

+ * Changes: + * - setValue on interface org.opensaml.core.xml.schema.XSURI + * - added to attributeStatements: firstName, lastName, phone + */ +public final class TestOpenSamlObjects { + + private static final String USERNAME = "test@saml.user"; + private static final String DESTINATION = "https://localhost/login/saml2/sso/idp-alias"; + private static final String ASSERTING_PARTY_ENTITY_ID = "https://some.idp.test/saml2/idp"; + private static final SecretKey SECRET_KEY = new SecretKeySpec( + Base64.getDecoder().decode("shOnwNMoCv88HKMEa91+FlYoD5RNvzMTAL5LGxZKIFk="), "AES"); + public static String RELYING_PARTY_ENTITY_ID = "https://localhost/saml2/service-provider-metadata/idp-alias"; + + static { + OpenSamlInitializationService.initialize(); + } + + private TestOpenSamlObjects() { + } + + public static Response response() { + return response(DESTINATION, ASSERTING_PARTY_ENTITY_ID); + } + + public static Response response(String destination, String issuerEntityId) { + Response response = build(Response.DEFAULT_ELEMENT_NAME); + response.setID("R" + UUID.randomUUID()); + response.setVersion(SAMLVersion.VERSION_20); + response.setID("_" + UUID.randomUUID()); + response.setDestination(destination); + response.setIssuer(issuer(issuerEntityId)); + return response; + } + + static Response signedResponseWithOneAssertion() { + return signedResponseWithOneAssertion((response) -> { + }); + } + + static Response signedResponseWithOneAssertion(Consumer responseConsumer) { + Response response = response(); + response.getAssertions().add(assertion()); + responseConsumer.accept(response); + return signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID); + } + + public static Assertion assertion() { + return assertion(USERNAME, ASSERTING_PARTY_ENTITY_ID, RELYING_PARTY_ENTITY_ID, DESTINATION); + } + + static Assertion assertion(String username, String issuerEntityId, String recipientEntityId, String recipientUri) { + Assertion assertion = build(Assertion.DEFAULT_ELEMENT_NAME); + assertion.setID("A" + UUID.randomUUID()); + assertion.setVersion(SAMLVersion.VERSION_20); + assertion.setIssuer(issuer(issuerEntityId)); + assertion.setSubject(subject(username)); + assertion.setConditions(conditions()); + SubjectConfirmation subjectConfirmation = subjectConfirmation(); + subjectConfirmation.setMethod(SubjectConfirmation.METHOD_BEARER); + SubjectConfirmationData confirmationData = subjectConfirmationData(recipientEntityId); + confirmationData.setRecipient(recipientUri); + subjectConfirmation.setSubjectConfirmationData(confirmationData); + assertion.getSubject().getSubjectConfirmations().add(subjectConfirmation); + AuthnStatement statement = build(AuthnStatement.DEFAULT_ELEMENT_NAME); + statement.setSessionIndex("session-index"); + assertion.getAuthnStatements().add(statement); + return assertion; + } + + static Issuer issuer(String entityId) { + Issuer issuer = build(Issuer.DEFAULT_ELEMENT_NAME); + issuer.setValue(entityId); + return issuer; + } + + static Subject subject(String principalName) { + Subject subject = build(Subject.DEFAULT_ELEMENT_NAME); + if (principalName != null) { + subject.setNameID(nameId(principalName)); + } + return subject; + } + + static NameID nameId(String principalName) { + NameID nameId = build(NameID.DEFAULT_ELEMENT_NAME); + nameId.setValue(principalName); + return nameId; + } + + static SubjectConfirmation subjectConfirmation() { + return build(SubjectConfirmation.DEFAULT_ELEMENT_NAME); + } + + static SubjectConfirmationData subjectConfirmationData(String recipient) { + SubjectConfirmationData subject = build(SubjectConfirmationData.DEFAULT_ELEMENT_NAME); + subject.setRecipient(recipient); + return subject; + } + + static Conditions conditions() { + return build(Conditions.DEFAULT_ELEMENT_NAME); + } + + public static AuthnRequest authnRequest() { + Issuer issuer = build(Issuer.DEFAULT_ELEMENT_NAME); + issuer.setValue(ASSERTING_PARTY_ENTITY_ID); + AuthnRequest authnRequest = build(AuthnRequest.DEFAULT_ELEMENT_NAME); + authnRequest.setIssuer(issuer); + authnRequest.setDestination(ASSERTING_PARTY_ENTITY_ID + "/SSO.saml2"); + authnRequest.setAssertionConsumerServiceURL(DESTINATION); + return authnRequest; + } + + static Credential getSigningCredential(Saml2X509Credential credential, String entityId) { + BasicCredential cred = getBasicCredential(credential); + cred.setEntityId(entityId); + cred.setUsageType(UsageType.SIGNING); + return cred; + } + + static BasicCredential getBasicCredential(Saml2X509Credential credential) { + return CredentialSupport.getSimpleCredential(credential.getCertificate(), credential.getPrivateKey()); + } + + static T signed(T signable, Saml2X509Credential credential, String entityId, + String signAlgorithmUri) { + SignatureSigningParameters parameters = new SignatureSigningParameters(); + Credential signingCredential = getSigningCredential(credential, entityId); + parameters.setSigningCredential(signingCredential); + parameters.setSignatureAlgorithm(signAlgorithmUri); + parameters.setSignatureReferenceDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA256); + parameters.setSignatureCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS); + try { + SignatureSupport.signObject(signable, parameters); + } catch (MarshallingException | SignatureException | SecurityException ex) { + throw new Saml2Exception(ex); + } + return signable; + } + + public static T signed(T signable, Saml2X509Credential credential, String entityId) { + return signed(signable, credential, entityId, SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256); + } + + static EncryptedAssertion encrypted(Assertion assertion, Saml2X509Credential credential) { + X509Certificate certificate = credential.getCertificate(); + Encrypter encrypter = getEncrypter(certificate); + try { + return encrypter.encrypt(assertion); + } catch (EncryptionException ex) { + throw new Saml2Exception("Unable to encrypt assertion.", ex); + } + } + + static EncryptedID encrypted(NameID nameId, Saml2X509Credential credential) { + X509Certificate certificate = credential.getCertificate(); + Encrypter encrypter = getEncrypter(certificate); + try { + return encrypter.encrypt(nameId); + } catch (EncryptionException ex) { + throw new Saml2Exception("Unable to encrypt nameID.", ex); + } + } + + static EncryptedAttribute encrypted(String name, String value, Saml2X509Credential credential) { + Attribute attribute = attribute(name, value); + X509Certificate certificate = credential.getCertificate(); + Encrypter encrypter = getEncrypter(certificate); + try { + return encrypter.encrypt(attribute); + } catch (EncryptionException ex) { + throw new Saml2Exception("Unable to encrypt nameID.", ex); + } + } + + private static Encrypter getEncrypter(X509Certificate certificate) { + String dataAlgorithm = XMLCipherParameters.AES_256; + String keyAlgorithm = XMLCipherParameters.RSA_1_5; + BasicCredential dataCredential = new BasicCredential(SECRET_KEY); + DataEncryptionParameters dataEncryptionParameters = new DataEncryptionParameters(); + dataEncryptionParameters.setEncryptionCredential(dataCredential); + dataEncryptionParameters.setAlgorithm(dataAlgorithm); + Credential credential = CredentialSupport.getSimpleCredential(certificate, null); + KeyEncryptionParameters keyEncryptionParameters = new KeyEncryptionParameters(); + keyEncryptionParameters.setEncryptionCredential(credential); + keyEncryptionParameters.setAlgorithm(keyAlgorithm); + Encrypter encrypter = new Encrypter(dataEncryptionParameters, keyEncryptionParameters); + Encrypter.KeyPlacement keyPlacement = Encrypter.KeyPlacement.valueOf("PEER"); + encrypter.setKeyPlacement(keyPlacement); + return encrypter; + } + + static Attribute attribute(String name, String value) { + Attribute attribute = build(Attribute.DEFAULT_ELEMENT_NAME); + attribute.setName(name); + XSString xsValue = new XSStringBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME); + xsValue.setValue(value); + attribute.getAttributeValues().add(xsValue); + return attribute; + } + + static AttributeStatement customAttributeStatement(String attributeName, XMLObject customAttributeValue) { + AttributeStatementBuilder attributeStatementBuilder = new AttributeStatementBuilder(); + AttributeBuilder attributeBuilder = new AttributeBuilder(); + Attribute attribute = attributeBuilder.buildObject(); + attribute.setName(attributeName); + attribute.getAttributeValues().add(customAttributeValue); + AttributeStatement attributeStatement = attributeStatementBuilder.buildObject(); + attributeStatement.getAttributes().add(attribute); + return attributeStatement; + } + + public static List attributeStatements() { + List attributeStatements = new ArrayList<>(); + AttributeStatementBuilder attributeStatementBuilder = new AttributeStatementBuilder(); + AttributeBuilder attributeBuilder = new AttributeBuilder(); + AttributeStatement attrStmt1 = attributeStatementBuilder.buildObject(); + + Attribute emailAttr = attributeBuilder.buildObject(); + emailAttr.setName("email"); + XSAny email1 = new XSAnyBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSAny.TYPE_NAME); // gh-8864 + email1.setTextContent("john.doe@example.com"); + emailAttr.getAttributeValues().add(email1); + + XSAny email2 = new XSAnyBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME); + email2.setTextContent("doe.john@example.com"); + emailAttr.getAttributeValues().add(email2); + attrStmt1.getAttributes().add(emailAttr); + + Attribute nameAttr = attributeBuilder.buildObject(); + nameAttr.setName("name"); + XSString name = new XSStringBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME); + name.setValue("John Doe"); + nameAttr.getAttributeValues().add(name); + attrStmt1.getAttributes().add(nameAttr); + + Attribute firstNameAttr = attributeBuilder.buildObject(); + firstNameAttr.setName("firstName"); + XSString firstName = new XSStringBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME); + firstName.setValue("John"); + firstNameAttr.getAttributeValues().add(firstName); + attrStmt1.getAttributes().add(firstNameAttr); + + Attribute lastNameAttr = attributeBuilder.buildObject(); + lastNameAttr.setName("lastName"); + XSString lastName = new XSStringBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME); + lastName.setValue("Doe"); + lastNameAttr.getAttributeValues().add(lastName); + attrStmt1.getAttributes().add(lastNameAttr); + + Attribute roleOneAttr = attributeBuilder.buildObject(); // gh-11042 + roleOneAttr.setName("role"); + XSString roleOne = new XSStringBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME); + roleOne.setValue("RoleOne"); + roleOneAttr.getAttributeValues().add(roleOne); + attrStmt1.getAttributes().add(roleOneAttr); + + Attribute roleTwoAttr = attributeBuilder.buildObject(); // gh-11042 + roleTwoAttr.setName("role"); + XSString roleTwo = new XSStringBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME); + roleTwo.setValue("RoleTwo"); + roleTwoAttr.getAttributeValues().add(roleTwo); + attrStmt1.getAttributes().add(roleTwoAttr); + + Attribute ageAttr = attributeBuilder.buildObject(); + ageAttr.setName("age"); + XSInteger age = new XSIntegerBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSInteger.TYPE_NAME); + age.setValue(21); + ageAttr.getAttributeValues().add(age); + attrStmt1.getAttributes().add(ageAttr); + + Attribute phoneAttr = attributeBuilder.buildObject(); + phoneAttr.setName("phone"); + XSString phone = new XSStringBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME); + phone.setValue("123-456-7890"); + phoneAttr.getAttributeValues().add(phone); + attrStmt1.getAttributes().add(phoneAttr); + + attributeStatements.add(attrStmt1); + AttributeStatement attrStmt2 = attributeStatementBuilder.buildObject(); + + Attribute websiteAttr = attributeBuilder.buildObject(); + websiteAttr.setName("website"); + XSURI uri = new XSURIBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSURI.TYPE_NAME); + uri.setURI("https://johndoe.com/"); + websiteAttr.getAttributeValues().add(uri); + attrStmt2.getAttributes().add(websiteAttr); + + Attribute registeredAttr = attributeBuilder.buildObject(); + registeredAttr.setName("registered"); + XSBoolean registered = new XSBooleanBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, + XSBoolean.TYPE_NAME); + registered.setValue(new XSBooleanValue(true, false)); + registeredAttr.getAttributeValues().add(registered); + attrStmt2.getAttributes().add(registeredAttr); + + attributeStatements.add(attrStmt2); + return attributeStatements; + } + + static Status successStatus() { + return status(StatusCode.SUCCESS); + } + + static Status status(String code) { + Status status = new StatusBuilder().buildObject(); + StatusCode statusCode = new StatusCodeBuilder().buildObject(); + statusCode.setValue(code); + status.setStatusCode(statusCode); + return status; + } + + public static LogoutRequest assertingPartyLogoutRequest(RelyingPartyRegistration registration) { + LogoutRequestBuilder logoutRequestBuilder = new LogoutRequestBuilder(); + LogoutRequest logoutRequest = logoutRequestBuilder.buildObject(); + logoutRequest.setID("id"); + NameIDBuilder nameIdBuilder = new NameIDBuilder(); + NameID nameId = nameIdBuilder.buildObject(); + nameId.setValue("user"); + logoutRequest.setNameID(nameId); + IssuerBuilder issuerBuilder = new IssuerBuilder(); + Issuer issuer = issuerBuilder.buildObject(); + issuer.setValue(registration.getAssertingPartyDetails().getEntityId()); + logoutRequest.setIssuer(issuer); + logoutRequest.setDestination(registration.getSingleLogoutServiceLocation()); + return logoutRequest; + } + + public static LogoutRequest assertingPartyLogoutRequestNameIdInEncryptedId(RelyingPartyRegistration registration) { + LogoutRequestBuilder logoutRequestBuilder = new LogoutRequestBuilder(); + LogoutRequest logoutRequest = logoutRequestBuilder.buildObject(); + logoutRequest.setID("id"); + NameIDBuilder nameIdBuilder = new NameIDBuilder(); + NameID nameId = nameIdBuilder.buildObject(); + nameId.setValue("user"); + logoutRequest.setNameID(null); + Saml2X509Credential credential = registration.getAssertingPartyDetails() + .getEncryptionX509Credentials() + .iterator() + .next(); + EncryptedID encrypted = encrypted(nameId, credential); + logoutRequest.setEncryptedID(encrypted); + IssuerBuilder issuerBuilder = new IssuerBuilder(); + Issuer issuer = issuerBuilder.buildObject(); + issuer.setValue(registration.getAssertingPartyDetails().getEntityId()); + logoutRequest.setIssuer(issuer); + logoutRequest.setDestination(registration.getSingleLogoutServiceLocation()); + return logoutRequest; + } + + public static LogoutResponse assertingPartyLogoutResponse(RelyingPartyRegistration registration) { + LogoutResponseBuilder logoutResponseBuilder = new LogoutResponseBuilder(); + LogoutResponse logoutResponse = logoutResponseBuilder.buildObject(); + logoutResponse.setID("id"); + StatusBuilder statusBuilder = new StatusBuilder(); + StatusCodeBuilder statusCodeBuilder = new StatusCodeBuilder(); + StatusCode code = statusCodeBuilder.buildObject(); + code.setValue(StatusCode.SUCCESS); + Status status = statusBuilder.buildObject(); + status.setStatusCode(code); + logoutResponse.setStatus(status); + IssuerBuilder issuerBuilder = new IssuerBuilder(); + Issuer issuer = issuerBuilder.buildObject(); + issuer.setValue(registration.getAssertingPartyDetails().getEntityId()); + logoutResponse.setIssuer(issuer); + logoutResponse.setDestination(registration.getSingleLogoutServiceResponseLocation()); + return logoutResponse; + } + + public static LogoutRequest relyingPartyLogoutRequest(RelyingPartyRegistration registration) { + LogoutRequestBuilder logoutRequestBuilder = new LogoutRequestBuilder(); + LogoutRequest logoutRequest = logoutRequestBuilder.buildObject(); + logoutRequest.setID("id"); + NameIDBuilder nameIdBuilder = new NameIDBuilder(); + NameID nameId = nameIdBuilder.buildObject(); + nameId.setValue("user"); + logoutRequest.setNameID(nameId); + IssuerBuilder issuerBuilder = new IssuerBuilder(); + Issuer issuer = issuerBuilder.buildObject(); + issuer.setValue(registration.getAssertingPartyDetails().getEntityId()); + logoutRequest.setIssuer(issuer); + logoutRequest.setDestination(registration.getAssertingPartyDetails().getSingleLogoutServiceLocation()); + return logoutRequest; + } + + static T build(QName qName) { + return (T) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(qName).buildObject(qName); + } + +} diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestRelyingPartyRegistrations.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestRelyingPartyRegistrations.java new file mode 100644 index 00000000000..d7d7dbd3997 --- /dev/null +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestRelyingPartyRegistrations.java @@ -0,0 +1,75 @@ +/* + * Copyright 2002-2023 the original author or authors. + * + * 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 + * + * https://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.cloudfoundry.identity.uaa.provider.saml; + +import org.springframework.security.saml2.core.Saml2X509Credential; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; +import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter; + +/** + * This was copied from Spring Security Test Classes + * Migrate to use the Spring Security class when it is made public + *

+ * Modified to work with org.springframework.security.saml2.core.Saml2X509Credential + * instead of now deprecated org.springframework.security.saml2.credentials.Saml2X509Credential; + */ +public final class TestRelyingPartyRegistrations { + + private TestRelyingPartyRegistrations() { + } + + public static RelyingPartyRegistration.Builder relyingPartyRegistration() { + String registrationId = "simplesamlphp"; + String rpEntityId = "{baseUrl}/saml2/service-provider-metadata/{registrationId}"; + Saml2X509Credential signingCredential = TestSaml2X509Credentials.relyingPartySigningCredential(); + String assertionConsumerServiceLocation = "{baseUrl}" + + Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI; + String apEntityId = "https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php"; + Saml2X509Credential verificationCertificate = TestSaml2X509Credentials.relyingPartyVerifyingCredential(); + String singleSignOnServiceLocation = "https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php"; + String singleLogoutServiceLocation = "{baseUrl}/logout/saml2/slo"; + return RelyingPartyRegistration.withRegistrationId(registrationId) + .entityId(rpEntityId) + .nameIdFormat("format") + .assertionConsumerServiceLocation(assertionConsumerServiceLocation) + .singleLogoutServiceLocation(singleLogoutServiceLocation) + .providerDetails((c) -> c.entityId(apEntityId).webSsoUrl(singleSignOnServiceLocation)) + .signingX509Credentials((c) -> c.add(signingCredential)) + .decryptionX509Credentials((c) -> c.add(verificationCertificate)); + } + + public static RelyingPartyRegistration.Builder noCredentials() { + return RelyingPartyRegistration.withRegistrationId("saml")//"registration-id") + .entityId("rp-entity-id") + .singleLogoutServiceLocation("https://rp.example.org/logout/saml2/request") + .singleLogoutServiceResponseLocation("https://rp.example.org/logout/saml2/response") + .assertionConsumerServiceLocation("https://rp.example.org/acs") + .assertingPartyDetails((party) -> party.entityId("ap-entity-id") + .singleSignOnServiceLocation("https://ap.example.org/sso") + .singleLogoutServiceLocation("https://ap.example.org/logout/saml2/request") + .singleLogoutServiceResponseLocation("https://ap.example.org/logout/saml2/response")); + } + + public static RelyingPartyRegistration.Builder full() { + return noCredentials() + .signingX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartySigningCredential())) + .decryptionX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyDecryptingCredential())) + .assertingPartyDetails((party) -> party.verificationX509Credentials( + (c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential()))); + } + +} diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestSaml2X509Credentials.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestSaml2X509Credentials.java new file mode 100644 index 00000000000..3ec8ccc717a --- /dev/null +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestSaml2X509Credentials.java @@ -0,0 +1,253 @@ +/* + * Copyright 2002-2022 the original author or authors. + * + * 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 + * + * https://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.cloudfoundry.identity.uaa.provider.saml; + +import org.bouncycastle.util.encoders.Base64; +import org.springframework.security.saml2.Saml2Exception; +import org.springframework.security.saml2.core.Saml2X509Credential; +import org.springframework.security.saml2.core.Saml2X509Credential.Saml2X509CredentialType; + +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; + +/** + * This was copied from Spring Security Test Classes + * Migrate to use the Spring Security class when it is made public + *

+ * Changed: + * privateKey/decodePrivateKey no longer uses bouncycastle and cryptacular with does not work with FIPS mode. + */ +public final class TestSaml2X509Credentials { + + private TestSaml2X509Credentials() { + } + + public static Saml2X509Credential assertingPartySigningCredential() { + return new Saml2X509Credential(idpPrivateKey(), idpCertificate(), Saml2X509CredentialType.SIGNING); + } + + public static Saml2X509Credential assertingPartyEncryptingCredential() { + return new Saml2X509Credential(spCertificate(), Saml2X509CredentialType.ENCRYPTION); + } + + public static Saml2X509Credential assertingPartyPrivateCredential() { + return new Saml2X509Credential(idpPrivateKey(), idpCertificate(), Saml2X509CredentialType.SIGNING, + Saml2X509CredentialType.DECRYPTION); + } + + public static Saml2X509Credential relyingPartyVerifyingCredential() { + return new Saml2X509Credential(idpCertificate(), Saml2X509CredentialType.VERIFICATION); + } + + public static Saml2X509Credential relyingPartyEncryptingCredential() { + return new Saml2X509Credential(idpCertificate(), Saml2X509CredentialType.ENCRYPTION); + } + + public static Saml2X509Credential relyingPartySigningCredential() { + return new Saml2X509Credential(spPrivateKey(), spCertificate(), Saml2X509CredentialType.SIGNING); + } + + public static Saml2X509Credential relyingPartyDecryptingCredential() { + return new Saml2X509Credential(spPrivateKey(), spCertificate(), Saml2X509CredentialType.DECRYPTION); + } + + public static Saml2X509Credential altPublicCredential() { + return new Saml2X509Credential(altCertificate(), Saml2X509CredentialType.VERIFICATION, + Saml2X509CredentialType.ENCRYPTION); + } + + public static Saml2X509Credential altPrivateCredential() { + return new Saml2X509Credential(altPrivateKey(), altCertificate(), Saml2X509CredentialType.SIGNING, + Saml2X509CredentialType.DECRYPTION); + } + + private static X509Certificate certificate(String cert) { + ByteArrayInputStream certBytes = new ByteArrayInputStream(cert.getBytes()); + try { + return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(certBytes); + } catch (CertificateException ex) { + throw new Saml2Exception(ex); + } + } + + private static PrivateKey privateKey(String key) { + key = key.replace("-----BEGIN PRIVATE KEY-----", ""); + key = key.replace("-----END PRIVATE KEY-----", ""); + key = key.replaceAll("\\s+", ""); + return decodePrivateKey(key.getBytes(StandardCharsets.UTF_8), new char[0]); + } + + private static PrivateKey decodePrivateKey(byte[] keyBytes, char[] password) { + try { + byte[] pkcs8EncodedBytes = Base64.decode(keyBytes); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8EncodedBytes); + KeyFactory kf = KeyFactory.getInstance("RSA"); + return kf.generatePrivate(keySpec); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new Saml2Exception(e); + } + } + + private static X509Certificate idpCertificate() { + return certificate( + """ + -----BEGIN CERTIFICATE----- + MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYD + VQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYD + VQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwX + c2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0Bw + aXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJ + BgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAa + BgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQD + DBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlr + QHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62 + E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz + 2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWW + RDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQ + nX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5 + cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gph + iJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5 + ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTAD + AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduO + nRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+v + ZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLu + xbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6z + V9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3 + lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk + -----END CERTIFICATE-----"""); + } + + private static PrivateKey idpPrivateKey() { + return privateKey(""" + -----BEGIN PRIVATE KEY----- + MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4cn62E1xLqpN3 + 4PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZX + W+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHE + fDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7h + Z6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/T + Xy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7 + I+J5lS8VAgMBAAECggEBAKyxBlIS7mcp3chvq0RF7B3PHFJMMzkwE+t3pLJcs4cZ + nezh/KbREfP70QjXzk/llnZCvxeIs5vRu24vbdBm79qLHqBuHp8XfHHtuo2AfoAQ + l4h047Xc/+TKMivnPQ0jX9qqndKDLqZDf5wnbslDmlskvF0a/MjsLU0TxtOfo+dB + t55FW11cGqxZwhS5Gnr+cbw3OkHz23b9gEOt9qfwPVepeysbmm9FjU+k4yVa7rAN + xcbzVb6Y7GCITe2tgvvEHmjB9BLmWrH3mZ3Af17YU/iN6TrpPd6Sj3QoS+2wGtAe + HbUs3CKJu7bIHcj4poal6Kh8519S+erJTtqQ8M0ZiEECgYEA43hLYAPaUueFkdfh + 9K/7ClH6436CUH3VdizwUXi26fdhhV/I/ot6zLfU2mgEHU22LBECWQGtAFm8kv0P + zPn+qjaR3e62l5PIlSYbnkIidzoDZ2ztu4jF5LgStlTJQPteFEGgZVl5o9DaSZOq + Yd7G3XqXuQ1VGMW58G5FYJPtA1cCgYEAz5TPUtK+R2KXHMjUwlGY9AefQYRYmyX2 + Tn/OFgKvY8lpAkMrhPKONq7SMYc8E9v9G7A0dIOXvW7QOYSapNhKU+np3lUafR5F + 4ZN0bxZ9qjHbn3AMYeraKjeutHvlLtbHdIc1j3sxe/EzltRsYmiqLdEBW0p6hwWg + tyGhYWVyaXMCgYAfDOKtHpmEy5nOCLwNXKBWDk7DExfSyPqEgSnk1SeS1HP5ctPK + +1st6sIhdiVpopwFc+TwJWxqKdW18tlfT5jVv1E2DEnccw3kXilS9xAhWkfwrEvf + V5I74GydewFl32o+NZ8hdo9GL1I8zO1rIq/et8dSOWGuWf9BtKu/vTGTTQKBgFxU + VjsCnbvmsEwPUAL2hE/WrBFaKocnxXx5AFNt8lEyHtDwy4Sg1nygGcIJ4sD6koQk + RdClT3LkvR04TAiSY80bN/i6ZcPNGUwSaDGZEWAIOSWbkwZijZNFnSGOEgxZX/IG + yd39766vREEMTwEeiMNEOZQ/dmxkJm4OOVe25cLdAoGACOtPnq1Fxay80UYBf4rQ + +bJ9yX1ulB8WIree1hD7OHSB2lRHxrVYWrglrTvkh63Lgx+EcsTV788OsvAVfPPz + BZrn8SdDlQqalMxUBYEFwnsYD3cQ8yOUnijFVC4xNcdDv8OIqVgSk4KKxU5AshaA + xk6Mox+u8Cc2eAK12H13i+8= + -----END PRIVATE KEY-----"""); + } + + private static X509Certificate spCertificate() { + return certificate(""" + -----BEGIN CERTIFICATE----- + MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMC + VVMxEzARBgNVBAgMCldhc2hpbmd0b24xEjAQBgNVBAcMCVZhbmNvdXZlcjEdMBsG + A1UECgwUU3ByaW5nIFNlY3VyaXR5IFNBTUwxCzAJBgNVBAsMAnNwMSAwHgYDVQQD + DBdzcC5zcHJpbmcuc2VjdXJpdHkuc2FtbDAeFw0xODA1MTQxNDMwNDRaFw0yODA1 + MTExNDMwNDRaMIGEMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjES + MBAGA1UEBwwJVmFuY291dmVyMR0wGwYDVQQKDBRTcHJpbmcgU2VjdXJpdHkgU0FN + TDELMAkGA1UECwwCc3AxIDAeBgNVBAMMF3NwLnNwcmluZy5zZWN1cml0eS5zYW1s + MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRu7/EI0BlNzMEBFVAcbx+lLos + vzIWU+01dGTY8gBdhMQNYKZ92lMceo2CuVJ66cUURPym3i7nGGzoSnAxAre+0YIM + +U0razrWtAUE735bkcqELZkOTZLelaoOztmWqRbe5OuEmpewH7cx+kNgcVjdctOG + y3Q6x+I4qakY/9qhBQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAeViTvHOyQopWEi + XOfI2Z9eukwrSknDwq/zscR0YxwwqDBMt/QdAODfSwAfnciiYLkmEjlozWRtOeN+ + qK7UFgP1bRl5qksrYX5S0z2iGJh0GvonLUt3e20Ssfl5tTEDDnAEUMLfBkyaxEHD + RZ/nbTJ7VTeZOSyRoVn5XHhpuJ0B + -----END CERTIFICATE-----"""); + } + + private static PrivateKey spPrivateKey() { + return privateKey(""" + -----BEGIN PRIVATE KEY----- + MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANG7v8QjQGU3MwQE + VUBxvH6Uuiy/MhZT7TV0ZNjyAF2ExA1gpn3aUxx6jYK5UnrpxRRE/KbeLucYbOhK + cDECt77Rggz5TStrOta0BQTvfluRyoQtmQ5Nkt6Vqg7O2ZapFt7k64Sal7AftzH6 + Q2BxWN1y04bLdDrH4jipqRj/2qEFAgMBAAECgYEAj4ExY1jjdN3iEDuOwXuRB+Nn + x7pC4TgntE2huzdKvLJdGvIouTArce8A6JM5NlTBvm69mMepvAHgcsiMH1zGr5J5 + wJz23mGOyhM1veON41/DJTVG+cxq4soUZhdYy3bpOuXGMAaJ8QLMbQQoivllNihd + vwH0rNSK8LTYWWPZYIECQQDxct+TFX1VsQ1eo41K0T4fu2rWUaxlvjUGhK6HxTmY + 8OMJptunGRJL1CUjIb45Uz7SP8TPz5FwhXWsLfS182kRAkEA3l+Qd9C9gdpUh1uX + oPSNIxn5hFUrSTW1EwP9QH9vhwb5Vr8Jrd5ei678WYDLjUcx648RjkjhU9jSMzIx + EGvYtQJBAMm/i9NR7IVyyNIgZUpz5q4LI21rl1r4gUQuD8vA36zM81i4ROeuCly0 + KkfdxR4PUfnKcQCX11YnHjk9uTFj75ECQEFY/gBnxDjzqyF35hAzrYIiMPQVfznt + YX/sDTE2AdVBVGaMj1Cb51bPHnNC6Q5kXKQnj/YrLqRQND09Q7ParX0CQQC5NxZr + 9jKqhHj8yQD6PlXTsY4Occ7DH6/IoDenfdEVD5qlet0zmd50HatN2Jiqm5ubN7CM + INrtuLp4YHbgk1mi + -----END PRIVATE KEY-----"""); + } + + private static X509Certificate altCertificate() { + return certificate(""" + -----BEGIN CERTIFICATE----- + MIICkDCCAfkCFEstVfmWSFQp/j88GaMUwqVK72adMA0GCSqGSIb3DQEBCwUAMIGG + MQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjESMBAGA1UEBwwJVmFu + Y291dmVyMR0wGwYDVQQKDBRTcHJpbmcgU2VjdXJpdHkgU0FNTDEMMAoGA1UECwwD + YWx0MSEwHwYDVQQDDBhhbHQuc3ByaW5nLnNlY3VyaXR5LnNhbWwwHhcNMjIwMjEw + MTY1ODA4WhcNMzIwMjEwMTY1ODA4WjCBhjELMAkGA1UEBhMCVVMxEzARBgNVBAgM + Cldhc2hpbmd0b24xEjAQBgNVBAcMCVZhbmNvdXZlcjEdMBsGA1UECgwUU3ByaW5n + IFNlY3VyaXR5IFNBTUwxDDAKBgNVBAsMA2FsdDEhMB8GA1UEAwwYYWx0LnNwcmlu + Zy5zZWN1cml0eS5zYW1sMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9ZGWj + TPDsymQCJL044py4xLsBI/S9RvzNeR9oD/tHyoxCE+YZzjf0PyBtwqKzkKWqCPf4 + XGUYHfEpkM5kJYwCW8TsOx5fnwLIQweiPqjYrBr/O0IjHMqYG9HlR/ros7iBt4ab + EGUu/B9yYg1YRYPxKQ6TNP3AD+9tBT8TsFFyjwIDAQABMA0GCSqGSIb3DQEBCwUA + A4GBAKJf2VHLjkCHRxlbWn63jGiquq3ENYgd1JS0DZ3ggFmuc6zQiqxzRGtArIDZ + 0jH5nrG0jcvO0fqDqBQh0iT8thfUnkViAQvACZ9a+0x0NzUicJ+Ra51c8Z2enqbg + pXy+ga67HcAXrDekm1MCGCgiEb/Cgl41lsideqhC8Efl7PRN + -----END CERTIFICATE-----"""); + } + + private static PrivateKey altPrivateKey() { + return privateKey(""" + -----BEGIN PRIVATE KEY----- + MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAL1kZaNM8OzKZAIk + vTjinLjEuwEj9L1G/M15H2gP+0fKjEIT5hnON/Q/IG3CorOQpaoI9/hcZRgd8SmQ + zmQljAJbxOw7Hl+fAshDB6I+qNisGv87QiMcypgb0eVH+uizuIG3hpsQZS78H3Ji + DVhFg/EpDpM0/cAP720FPxOwUXKPAgMBAAECgYEApYKslAZ0cer5dSoYNzNLFOnQ + J1H92r/Dw+k6+h0lUvr+keyD5T9jhM76DxHOUDBzpmIKGoDcVDQugk2rILfzXsQA + JtwvDRJk32Z02Vt0jb7t/WUOOQhjKCjQuv9/tOx90GCl0VxYG69UOjaMRWrlg/i9 + 6/zcTRIahIn5XxF0psECQQD7ivJCpDbOLJGsc8gNJR4cvjZ1q0mHIOrbKqJC0y1n + 5DrzGEflPeyCUwnOKNp9HJQP8gmZzXfj0JM9KsjpiUChAkEAwL+FmhDoTiqStIrH + h9Kdnsev//imMmRHxjwDhntYvqavUsISRmY3imd8inoYq5dzWQMzBtoTyMRmqeLT + DHV1LwJAW4xaV37Eo4z9B7Kr4Hzd1MA1ueW5QQDt+Q4vN/r7z4/1FHyFzh0Xcucd + 7nZX7qj0CkmgzOVG+Rb0P5LOxJA7gQJBAK1KQ2qNct375qPM9bEGSVGchH6k5X7+ + q4ztHdpFgTb/EzdbZiTG935GpjC1rwJuinTnrHOnkwv4j7iDRm24GF8CQQDqPvrQ + GcItR6UUy0q/B8UxLzlE6t+HiznfiJKfyGgCHU56Y4/ZhzSQz2MZHz9SK4DsUL9s + bOYrWq8VY2fyjV1t + -----END PRIVATE KEY-----"""); + } +} diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/idp/SamlTestUtils.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/idp/SamlTestUtils.java index 68d3340afed..52cb4626064 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/idp/SamlTestUtils.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/idp/SamlTestUtils.java @@ -1,89 +1,37 @@ package org.cloudfoundry.identity.uaa.provider.saml.idp; -import java.io.IOException; -import java.io.StringReader; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; -import java.util.Random; -import java.util.UUID; -import javax.xml.namespace.QName; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.xpath.XPath; -import javax.xml.xpath.XPathConstants; -import javax.xml.xpath.XPathExpression; -import javax.xml.xpath.XPathExpressionException; -import javax.xml.xpath.XPathFactory; - -import org.springframework.security.core.GrantedAuthority; -import org.cloudfoundry.identity.uaa.oauth.common.util.RandomValueStringGenerator; -//import org.springframework.security.saml.context.SAMLMessageContext; -//import org.springframework.security.saml.key.KeyManager; -//import org.springframework.security.saml.metadata.ExtendedMetadata; -//import org.springframework.security.saml.metadata.MetadataGenerator; - -import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; -import org.cloudfoundry.identity.uaa.authentication.UaaAuthentication; -import org.cloudfoundry.identity.uaa.authentication.UaaPrincipal; import org.cloudfoundry.identity.uaa.constants.OriginKeys; import org.cloudfoundry.identity.uaa.login.AddBcProvider; import org.cloudfoundry.identity.uaa.provider.SamlIdentityProviderDefinition; -import org.cloudfoundry.identity.uaa.provider.saml.SamlKeyManagerFactory; -import org.cloudfoundry.identity.uaa.saml.SamlKey; -import org.cloudfoundry.identity.uaa.user.UaaAuthority; import org.cloudfoundry.identity.uaa.zone.IdentityZone; -import org.cloudfoundry.identity.uaa.zone.SamlConfig; -import org.joda.time.DateTime; -//import org.opensaml.Configuration; -//import org.opensaml.DefaultBootstrap; -//import org.opensaml.common.SAMLObject; -//import org.opensaml.common.SAMLObjectBuilder; -//import org.opensaml.common.SAMLVersion; -//import org.opensaml.saml.saml2.core.Assertion; -//import org.opensaml.saml.saml2.core.Audience; -//import org.opensaml.saml.saml2.core.AudienceRestriction; -//import org.opensaml.saml.saml2.core.AuthnContext; -//import org.opensaml.saml.saml2.core.AuthnContextClassRef; -//import org.opensaml.saml.saml2.core.AuthnRequest; -//import org.opensaml.saml.saml2.core.AuthnStatement; -//import org.opensaml.saml.saml2.core.Conditions; -//import org.opensaml.saml.saml2.core.Issuer; -//import org.opensaml.saml.saml2.core.NameID; -//import org.opensaml.saml.saml2.core.Subject; -//import org.opensaml.saml.saml2.core.SubjectConfirmation; -//import org.opensaml.saml.saml2.core.SubjectConfirmationData; -//import org.opensaml.saml.saml2.core.impl.AssertionMarshaller; -//import org.opensaml.saml.saml2.metadata.EntityDescriptor; -//import org.opensaml.saml.saml2.metadata.SPSSODescriptor; -//import org.opensaml.xml.ConfigurationException; -//import org.opensaml.xml.XMLObjectBuilderFactory; -//import org.opensaml.xml.io.Marshaller; -//import org.opensaml.xml.security.SecurityHelper; -//import org.opensaml.xml.security.credential.Credential; -//import org.opensaml.xml.signature.Signature; -//import org.opensaml.xml.signature.Signer; -//import org.opensaml.xml.signature.impl.SignatureBuilder; -//import org.opensaml.xml.util.XMLHelper; import org.w3c.dom.Document; -import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import java.io.IOException; +import java.io.StringReader; +import java.util.LinkedList; +import java.util.List; + import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; //import static org.opensaml.common.xml.SAMLConstants.SAML20P_NS; -// TODO this class seems to be used more broadly than what its location indicates (uaa as saml idp); need to move it +// TODO: this class seems to be used more broadly than what its location indicates (uaa as saml idp); need to move it // also remove unused code in here +// Attempt to move usages to Saml2TestUtils style public class SamlTestUtils { + public static final String PROVIDER_PRIVATE_KEY_PASSWORD = "password"; + public static final String PROVIDER_PRIVATE_KEY = """ -----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQDHtC5gUXxBKpEqZTLkNvFwNGnNIkggNOwOQVNbpO0WVHIivig5 @@ -101,8 +49,6 @@ public class SamlTestUtils { qy45ptdwJLqLJCeNoR0JUcDNIRhOCuOPND7pcMtX6hI/ -----END RSA PRIVATE KEY-----"""; - public static final String PROVIDER_PRIVATE_KEY_PASSWORD = "password"; - public static final String PROVIDER_CERTIFICATE = """ -----BEGIN CERTIFICATE----- MIIDSTCCArKgAwIBAgIBADANBgkqhkiG9w0BAQQFADB8MQswCQYDVQQGEwJhdzEO @@ -125,161 +71,7 @@ public class SamlTestUtils { RpuRBwn3Ei+jCRouxTbzKPsuCVB+1sNyxMTXzf0= -----END CERTIFICATE-----"""; - static final String SP_ENTITY_ID = "unit-test-sp"; - static final String IDP_ENTITY_ID = "unit-test-idp"; - public static final String SAML_SP_METADATA_TESTZONE2_FOR_REDIRECT = """ - - Qi+CZaMVIemficNn/klUhpk/3QY=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urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddressurn:oasis:names:tc:SAML:2.0:nameid-format:transienturn:oasis:names:tc:SAML:2.0:nameid-format:persistenturn:oasis:names:tc:SAML:1.1:nameid-format:unspecifiedurn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName"""; - - public static final String SAML_IDP_METADATA_REDIRECT_ONLY = """ - - 8rJXCEVOlzN2dmhPBlxbYdTS1Dc=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\ - urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddressurn:oasis:names:tc:SAML:2.0:nameid-format:persistent\ - urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\ - \ - """; - - public static final String SAML_IDP_METADATA_POST_ONLY = """ - - 8rJXCEVOlzN2dmhPBlxbYdTS1Dc=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\ - urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddressurn:oasis:names:tc:SAML:2.0:nameid-format:persistent\ - urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\ - \ - """; - -// private XMLObjectBuilderFactory builderFactory; - -// public void initializeSimple() { -// builderFactory = Configuration.getBuilderFactory(); -// } - - public void initialize() /* throws ConfigurationException */ { + public static void initialize() /* throws ConfigurationException */ { IdentityZone.getUaa().getConfig().getSamlConfig().setPrivateKey(PROVIDER_PRIVATE_KEY); IdentityZone.getUaa().getConfig().getSamlConfig().setPrivateKeyPassword(PROVIDER_PRIVATE_KEY_PASSWORD); IdentityZone.getUaa().getConfig().getSamlConfig().setCertificate(PROVIDER_CERTIFICATE); @@ -288,13 +80,6 @@ public void initialize() /* throws ConfigurationException */ { // initializeSimple(); } - void setupZoneWithSamlConfig(IdentityZone zone) { - SamlConfig config = zone.getConfig().getSamlConfig(); - config.setPrivateKey(PROVIDER_PRIVATE_KEY); - config.setPrivateKeyPassword(PROVIDER_PRIVATE_KEY_PASSWORD); - config.setCertificate(PROVIDER_CERTIFICATE); - } - public static SamlIdentityProviderDefinition createLocalSamlIdpDefinition(String alias, String zoneId, String idpMetaData) { SamlIdentityProviderDefinition def = new SamlIdentityProviderDefinition(); def.setZoneId(zoneId); @@ -313,707 +98,6 @@ public static SamlIdentityProviderDefinition createLocalSamlIdpDefinition(String return def; } -// @SuppressWarnings("unchecked") -// SAMLMessageContext mockSamlMessageContext() { -// return mockSamlMessageContext(mockAuthnRequest()); -// } - -// @SuppressWarnings("unchecked") -// SAMLMessageContext mockSamlMessageContext(AuthnRequest authnRequest) { -// SAMLMessageContext context = new SAMLMessageContext(); -// -// context.setPeerEntityId(SP_ENTITY_ID); -// context.setPeerEntityRole(SPSSODescriptor.DEFAULT_ELEMENT_NAME); -// EntityDescriptor spMetadata = mockSpMetadata(); -// context.setPeerEntityMetadata(spMetadata); -// SPSSODescriptor spDescriptor = spMetadata.getSPSSODescriptor(SAML20P_NS); -// context.setPeerEntityRoleMetadata(spDescriptor); -// context.setInboundSAMLMessage(authnRequest); -// -// SamlConfig config = new SamlConfig(); -// config.setPrivateKey(PROVIDER_PRIVATE_KEY); -// config.setPrivateKeyPassword(PROVIDER_PRIVATE_KEY_PASSWORD); -// config.setCertificate(PROVIDER_CERTIFICATE); -// KeyManager keyManager = new SamlKeyManagerFactory().getKeyManager(config); -// context.setLocalSigningCredential(keyManager.getDefaultCredential()); -// return context; -// } - -// private EntityDescriptor mockSpMetadata() { -// ExtendedMetadata extendedMetadata = new ExtendedMetadata(); -// -// MetadataGenerator metadataGenerator = new MetadataGenerator(); -// metadataGenerator.setExtendedMetadata(extendedMetadata); -// metadataGenerator.setEntityId(SP_ENTITY_ID); -// metadataGenerator.setEntityBaseURL("http://localhost:8080/uaa/saml"); -// metadataGenerator.setWantAssertionSigned(false); -// -// KeyManager keyManager = mock(KeyManager.class); -// when(keyManager.getDefaultCredentialName()).thenReturn(null); -// metadataGenerator.setKeyManager(keyManager); -// return metadataGenerator.generateMetadata(); -// } - -// private AuthnRequest mockAuthnRequest() { -// return mockAuthnRequest(null); -// } - -// public String mockAssertionEncoded(Assertion assertion) throws Exception { -// AssertionMarshaller marshaller = new AssertionMarshaller(); -// Element plaintextElement = marshaller.marshall(assertion); -// String serializedElement = XMLHelper.nodeToString(plaintextElement); -// return Base64.encodeBase64URLSafeString(serializedElement.getBytes(StandardCharsets.UTF_8)); -// } - -// public String mockAssertionEncoded( -// String issuerEntityId, -// String format, -// String username, -// String spEndpoint, -// String audienceEntityID) throws Exception { -// final Assertion assertion = mockAssertion(issuerEntityId, format, username, spEndpoint, audienceEntityID); -// signAssertion(assertion, PROVIDER_PRIVATE_KEY, PROVIDER_PRIVATE_KEY_PASSWORD, PROVIDER_CERTIFICATE); -// return mockAssertionEncoded(assertion); -// } - -// private Assertion mockAssertion( -// String issuerEntityId, -// String format, -// String username, -// String spEndpoint, -// String audienceEntityID) { -// final DateTime now = new DateTime(); -// final DateTime until = now.plusHours(1); -// -// Assertion assertion = (Assertion) buildSamlObject(Assertion.DEFAULT_ELEMENT_NAME); -// -// { -// assertion.setIssueInstant(now); -// } -// -// { -// final Issuer issuer = (Issuer) buildSamlObject(Issuer.DEFAULT_ELEMENT_NAME); -// issuer.setValue(issuerEntityId); -// assertion.setIssuer(issuer); -// } -// -// { -// final NameID nameId = (NameID) buildSamlObject(NameID.DEFAULT_ELEMENT_NAME); -// nameId.setValue(username); -// nameId.setNameQualifier(NameID.UNSPECIFIED); -// nameId.setFormat(format); -// -// final SubjectConfirmationData confirmationMethod = (SubjectConfirmationData) buildSamlObject(SubjectConfirmationData.DEFAULT_ELEMENT_NAME); -// confirmationMethod.setNotOnOrAfter(until); -// confirmationMethod.setRecipient(spEndpoint); -// -// final SubjectConfirmation subjectConfirmation = (SubjectConfirmation) buildSamlObject(SubjectConfirmation.DEFAULT_ELEMENT_NAME); -// subjectConfirmation.setSubjectConfirmationData(confirmationMethod); -// subjectConfirmation.setMethod("urn:oasis:names:tc:SAML:2.0:cm:bearer"); -// -// final Subject subject = (Subject) buildSamlObject(Subject.DEFAULT_ELEMENT_NAME); -// subject.setNameID(nameId); -// subject.getSubjectConfirmations().add(subjectConfirmation); -// -// subject.getSubjectConfirmations().get(0).getSubjectConfirmationData().setInResponseTo(null); -// subject.getSubjectConfirmations().get(0).getSubjectConfirmationData().setNotOnOrAfter(until); -// -// assertion.setSubject(subject); -// } -// -// { -// final Audience audience = (Audience) buildSamlObject(Audience.DEFAULT_ELEMENT_NAME); -// audience.setAudienceURI(audienceEntityID); -// -// final AudienceRestriction audienceRestriction = (AudienceRestriction) buildSamlObject(AudienceRestriction.DEFAULT_ELEMENT_NAME); -// audienceRestriction.getAudiences().add(audience); -// -// final Conditions conditions = (Conditions) buildSamlObject(Conditions.DEFAULT_ELEMENT_NAME); -// conditions.getAudienceRestrictions().add(audienceRestriction); -// conditions.setNotBefore(new DateTime().minusSeconds(2)); -// conditions.setNotOnOrAfter(until); -// -// assertion.setConditions(conditions); -// } -// -// { -// final AuthnContextClassRef authnContextClassRef = (AuthnContextClassRef) buildSamlObject(AuthnContextClassRef.DEFAULT_ELEMENT_NAME); -// authnContextClassRef.setAuthnContextClassRef("urn:oasis:names:tc:SAML:2.0:ac:classes:Password"); -// -// final AuthnContext authnContext = (AuthnContext) buildSamlObject(AuthnContext.DEFAULT_ELEMENT_NAME); -// authnContext.setAuthnContextClassRef(authnContextClassRef); -// -// final AuthnStatement authnStatement = (AuthnStatement) buildSamlObject(AuthnStatement.DEFAULT_ELEMENT_NAME); -// authnStatement.setAuthnInstant(now); -// authnStatement.setSessionIndex("a358a06c15ja8d7a1idjaj07jb52gdi"); -// authnStatement.setSessionNotOnOrAfter(until); -// authnStatement.setAuthnContext(authnContext); -// -// assertion.getAuthnStatements().add(authnStatement); -// } -// -// return assertion; -// } - -// private SAMLObject buildSamlObject(QName elementName) { -// SAMLObjectBuilder issuerBuilder = (SAMLObjectBuilder) builderFactory.getBuilder(elementName); -// return issuerBuilder.buildObject(); -// } - -// public void signAssertion( -// Assertion assertion, -// String privateKey, -// String keyPassword, -// String certificate) -// throws Exception { -// -// final Signature signature = generateSignature(privateKey, keyPassword, certificate); -// assertion.setSignature(signature); -// Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(assertion); -// marshaller.marshall(assertion); -// Signer.signObject(signature); -// } - -// private Signature generateSignature(String privateKey, String keyPassword, String certificate) -// throws org.opensaml.xml.security.SecurityException { -// SamlConfig config = new SamlConfig(); -// config.addAndActivateKey("active-key", new SamlKey(privateKey, keyPassword, certificate)); -// KeyManager keyManager = new SamlKeyManagerFactory().getKeyManager(config); -// SignatureBuilder signatureBuilder = (SignatureBuilder) builderFactory.getBuilder(Signature.DEFAULT_ELEMENT_NAME); -// Signature signature = signatureBuilder.buildObject(); -// final Credential defaultCredential = keyManager.getDefaultCredential(); -// signature.setSigningCredential(defaultCredential); -// SecurityHelper.prepareSignatureParams(signature, defaultCredential, null, null); -// return signature; -// } - -// AuthnRequest mockAuthnRequest(String nameIDFormat) { -// @SuppressWarnings("unchecked") -// SAMLObjectBuilder builder = (SAMLObjectBuilder) builderFactory -// .getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME); -// AuthnRequest request = builder.buildObject(); -// request.setVersion(SAMLVersion.VERSION_20); -// request.setID(generateID()); -// request.setIssuer(getIssuer(SP_ENTITY_ID)); -// request.setVersion(SAMLVersion.VERSION_20); -// request.setIssueInstant(new DateTime()); -// if (null != nameIDFormat) { -// NameID nameID = ((SAMLObjectBuilder) builderFactory.getBuilder(NameID.DEFAULT_ELEMENT_NAME)) -// .buildObject(); -// nameID.setFormat(nameIDFormat); -// Subject subject = ((SAMLObjectBuilder) builderFactory.getBuilder(Subject.DEFAULT_ELEMENT_NAME)) -// .buildObject(); -// subject.setNameID(nameID); -// request.setSubject(subject); -// } -// return request; -// } - - private String generateID() { - Random r = new Random(); - return 'a' + Long.toString(Math.abs(r.nextLong()), 20) + Long.toString(Math.abs(r.nextLong()), 20); - } - -// public Issuer getIssuer(String localEntityId) { -// @SuppressWarnings("unchecked") -// SAMLObjectBuilder issuerBuilder = (SAMLObjectBuilder) builderFactory -// .getBuilder(Issuer.DEFAULT_ELEMENT_NAME); -// Issuer issuer = issuerBuilder.buildObject(); -// issuer.setValue(localEntityId); -// return issuer; -// } - - private UaaAuthentication mockUaaAuthentication() { - return mockUaaAuthentication(UUID.randomUUID().toString()); - } - - UaaAuthentication mockUaaAuthentication(String id) { - UaaAuthentication authentication = mock(UaaAuthentication.class); - when(authentication.getName()).thenReturn("marissa"); - - UaaPrincipal principal = new UaaPrincipal(id, "marissa", "marissa@testing.org", - OriginKeys.UAA, "marissa", "uaa"); - when(authentication.getPrincipal()).thenReturn(principal); - - Collection authorities = new ArrayList<>(); - authorities.add(UaaAuthority.UAA_USER); - doReturn(authorities).when(authentication).getAuthorities(); - - return authentication; - } - - static final String SAML_SP_METADATA = "" - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + "mPb/c/Gb/PN61JNRptMgHbK9L08=" - + "" - + "" - + "Ra6mE3hjN68Jwk6D3DktVrOu0BXJCSPTMr0YTgQyII8fv7j93BhuGMoZHw48tww6N9zkUDEuy+uRp9vd4gepxs8+XiL+kvoclMAStmzJ62/2fGuI3hCvht2lBXIuFBpZab3iuqxBhwceLnsvvsM5y4nfYDXuBS1XGRzrygLbldM=" - + "" - + "" - + "" - + "MIIDSTCCArKgAwIBAgIBADANBgkqhkiG9w0BAQQFADB8MQswCQYDVQQGEwJhdzEOMAwGA1UECBMF" - + "YXJ1YmExDjAMBgNVBAoTBWFydWJhMQ4wDAYDVQQHEwVhcnViYTEOMAwGA1UECxMFYXJ1YmExDjAM" - + "BgNVBAMTBWFydWJhMR0wGwYJKoZIhvcNAQkBFg5hcnViYUBhcnViYS5hcjAeFw0xNTExMjAyMjI2" - + "MjdaFw0xNjExMTkyMjI2MjdaMHwxCzAJBgNVBAYTAmF3MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UE" - + "ChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYDVQQLEwVhcnViYTEOMAwGA1UEAxMFYXJ1YmEx" - + "HTAbBgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB" - + "gQDHtC5gUXxBKpEqZTLkNvFwNGnNIkggNOwOQVNbpO0WVHIivig5L39WqS9u0hnA+O7MCA/KlrAR" - + "4bXaeVVhwfUPYBKIpaaTWFQR5cTR1UFZJL/OF9vAfpOwznoD66DDCnQVpbCjtDYWX+x6imxn8HCY" - + "xhMol6ZnTbSsFW6VZjFMjQIDAQABo4HaMIHXMB0GA1UdDgQWBBTx0lDzjH/iOBnOSQaSEWQLx1sy" - + "GDCBpwYDVR0jBIGfMIGcgBTx0lDzjH/iOBnOSQaSEWQLx1syGKGBgKR+MHwxCzAJBgNVBAYTAmF3" - + "MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UEChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYDVQQL" - + "EwVhcnViYTEOMAwGA1UEAxMFYXJ1YmExHTAbBgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyggEA" - + "MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAYvBJ0HOZbbHClXmGUjGs+GS+xC1FO/am" - + "2suCSYqNB9dyMXfOWiJ1+TLJk+o/YZt8vuxCKdcZYgl4l/L6PxJ982SRhc83ZW2dkAZI4M0/Ud3o" - + "ePe84k8jm3A7EvH5wi5hvCkKRpuRBwn3Ei+jCRouxTbzKPsuCVB+1sNyxMTXzf0=" - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + "MIIDSTCCArKgAwIBAgIBADANBgkqhkiG9w0BAQQFADB8MQswCQYDVQQGEwJhdzEOMAwGA1UECBMF" - + "YXJ1YmExDjAMBgNVBAoTBWFydWJhMQ4wDAYDVQQHEwVhcnViYTEOMAwGA1UECxMFYXJ1YmExDjAM" - + "BgNVBAMTBWFydWJhMR0wGwYJKoZIhvcNAQkBFg5hcnViYUBhcnViYS5hcjAeFw0xNTExMjAyMjI2" - + "MjdaFw0xNjExMTkyMjI2MjdaMHwxCzAJBgNVBAYTAmF3MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UE" - + "ChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYDVQQLEwVhcnViYTEOMAwGA1UEAxMFYXJ1YmEx" - + "HTAbBgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB" - + "gQDHtC5gUXxBKpEqZTLkNvFwNGnNIkggNOwOQVNbpO0WVHIivig5L39WqS9u0hnA+O7MCA/KlrAR" - + "4bXaeVVhwfUPYBKIpaaTWFQR5cTR1UFZJL/OF9vAfpOwznoD66DDCnQVpbCjtDYWX+x6imxn8HCY" - + "xhMol6ZnTbSsFW6VZjFMjQIDAQABo4HaMIHXMB0GA1UdDgQWBBTx0lDzjH/iOBnOSQaSEWQLx1sy" - + "GDCBpwYDVR0jBIGfMIGcgBTx0lDzjH/iOBnOSQaSEWQLx1syGKGBgKR+MHwxCzAJBgNVBAYTAmF3" - + "MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UEChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYDVQQL" - + "EwVhcnViYTEOMAwGA1UEAxMFYXJ1YmExHTAbBgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyggEA" - + "MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAYvBJ0HOZbbHClXmGUjGs+GS+xC1FO/am" - + "2suCSYqNB9dyMXfOWiJ1+TLJk+o/YZt8vuxCKdcZYgl4l/L6PxJ982SRhc83ZW2dkAZI4M0/Ud3o" - + "ePe84k8jm3A7EvH5wi5hvCkKRpuRBwn3Ei+jCRouxTbzKPsuCVB+1sNyxMTXzf0=" - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + "MIIDSTCCArKgAwIBAgIBADANBgkqhkiG9w0BAQQFADB8MQswCQYDVQQGEwJhdzEOMAwGA1UECBMF" - + "YXJ1YmExDjAMBgNVBAoTBWFydWJhMQ4wDAYDVQQHEwVhcnViYTEOMAwGA1UECxMFYXJ1YmExDjAM" - + "BgNVBAMTBWFydWJhMR0wGwYJKoZIhvcNAQkBFg5hcnViYUBhcnViYS5hcjAeFw0xNTExMjAyMjI2" - + "MjdaFw0xNjExMTkyMjI2MjdaMHwxCzAJBgNVBAYTAmF3MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UE" - + "ChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYDVQQLEwVhcnViYTEOMAwGA1UEAxMFYXJ1YmEx" - + "HTAbBgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB" - + "gQDHtC5gUXxBKpEqZTLkNvFwNGnNIkggNOwOQVNbpO0WVHIivig5L39WqS9u0hnA+O7MCA/KlrAR" - + "4bXaeVVhwfUPYBKIpaaTWFQR5cTR1UFZJL/OF9vAfpOwznoD66DDCnQVpbCjtDYWX+x6imxn8HCY" - + "xhMol6ZnTbSsFW6VZjFMjQIDAQABo4HaMIHXMB0GA1UdDgQWBBTx0lDzjH/iOBnOSQaSEWQLx1sy" - + "GDCBpwYDVR0jBIGfMIGcgBTx0lDzjH/iOBnOSQaSEWQLx1syGKGBgKR+MHwxCzAJBgNVBAYTAmF3" - + "MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UEChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYDVQQL" - + "EwVhcnViYTEOMAwGA1UEAxMFYXJ1YmExHTAbBgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyggEA" - + "MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAYvBJ0HOZbbHClXmGUjGs+GS+xC1FO/am" - + "2suCSYqNB9dyMXfOWiJ1+TLJk+o/YZt8vuxCKdcZYgl4l/L6PxJ982SRhc83ZW2dkAZI4M0/Ud3o" - + "ePe84k8jm3A7EvH5wi5hvCkKRpuRBwn3Ei+jCRouxTbzKPsuCVB+1sNyxMTXzf0=" - + "" - + "" - + "" - + "" - + "" - + "" - + "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" - + "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" - + "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" - + "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" - + "urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName" - + "" - + "" - + ""; - - static final String UNSIGNED_SAML_SP_METADATA = "" - + "" - + "" - + "" - + "" - + "" - + "" - + "MIIDSTCCArKgAwIBAgIBADANBgkqhkiG9w0BAQQFADB8MQswCQYDVQQGEwJhdzEOMAwGA1UECBMF" - + "YXJ1YmExDjAMBgNVBAoTBWFydWJhMQ4wDAYDVQQHEwVhcnViYTEOMAwGA1UECxMFYXJ1YmExDjAM" - + "BgNVBAMTBWFydWJhMR0wGwYJKoZIhvcNAQkBFg5hcnViYUBhcnViYS5hcjAeFw0xNTExMjAyMjI2" - + "MjdaFw0xNjExMTkyMjI2MjdaMHwxCzAJBgNVBAYTAmF3MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UE" - + "ChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYDVQQLEwVhcnViYTEOMAwGA1UEAxMFYXJ1YmEx" - + "HTAbBgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB" - + "gQDHtC5gUXxBKpEqZTLkNvFwNGnNIkggNOwOQVNbpO0WVHIivig5L39WqS9u0hnA+O7MCA/KlrAR" - + "4bXaeVVhwfUPYBKIpaaTWFQR5cTR1UFZJL/OF9vAfpOwznoD66DDCnQVpbCjtDYWX+x6imxn8HCY" - + "xhMol6ZnTbSsFW6VZjFMjQIDAQABo4HaMIHXMB0GA1UdDgQWBBTx0lDzjH/iOBnOSQaSEWQLx1sy" - + "GDCBpwYDVR0jBIGfMIGcgBTx0lDzjH/iOBnOSQaSEWQLx1syGKGBgKR+MHwxCzAJBgNVBAYTAmF3" - + "MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UEChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYDVQQL" - + "EwVhcnViYTEOMAwGA1UEAxMFYXJ1YmExHTAbBgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyggEA" - + "MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAYvBJ0HOZbbHClXmGUjGs+GS+xC1FO/am" - + "2suCSYqNB9dyMXfOWiJ1+TLJk+o/YZt8vuxCKdcZYgl4l/L6PxJ982SRhc83ZW2dkAZI4M0/Ud3o" - + "ePe84k8jm3A7EvH5wi5hvCkKRpuRBwn3Ei+jCRouxTbzKPsuCVB+1sNyxMTXzf0=" - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + "MIIDSTCCArKgAwIBAgIBADANBgkqhkiG9w0BAQQFADB8MQswCQYDVQQGEwJhdzEOMAwGA1UECBMF" - + "YXJ1YmExDjAMBgNVBAoTBWFydWJhMQ4wDAYDVQQHEwVhcnViYTEOMAwGA1UECxMFYXJ1YmExDjAM" - + "BgNVBAMTBWFydWJhMR0wGwYJKoZIhvcNAQkBFg5hcnViYUBhcnViYS5hcjAeFw0xNTExMjAyMjI2" - + "MjdaFw0xNjExMTkyMjI2MjdaMHwxCzAJBgNVBAYTAmF3MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UE" - + "ChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYDVQQLEwVhcnViYTEOMAwGA1UEAxMFYXJ1YmEx" - + "HTAbBgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB" - + "gQDHtC5gUXxBKpEqZTLkNvFwNGnNIkggNOwOQVNbpO0WVHIivig5L39WqS9u0hnA+O7MCA/KlrAR" - + "4bXaeVVhwfUPYBKIpaaTWFQR5cTR1UFZJL/OF9vAfpOwznoD66DDCnQVpbCjtDYWX+x6imxn8HCY" - + "xhMol6ZnTbSsFW6VZjFMjQIDAQABo4HaMIHXMB0GA1UdDgQWBBTx0lDzjH/iOBnOSQaSEWQLx1sy" - + "GDCBpwYDVR0jBIGfMIGcgBTx0lDzjH/iOBnOSQaSEWQLx1syGKGBgKR+MHwxCzAJBgNVBAYTAmF3" - + "MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UEChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYDVQQL" - + "EwVhcnViYTEOMAwGA1UEAxMFYXJ1YmExHTAbBgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyggEA" - + "MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAYvBJ0HOZbbHClXmGUjGs+GS+xC1FO/am" - + "2suCSYqNB9dyMXfOWiJ1+TLJk+o/YZt8vuxCKdcZYgl4l/L6PxJ982SRhc83ZW2dkAZI4M0/Ud3o" - + "ePe84k8jm3A7EvH5wi5hvCkKRpuRBwn3Ei+jCRouxTbzKPsuCVB+1sNyxMTXzf0=" - + "" - + "" - + "" - + "" - + "" - + "" - + "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" - + "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" - + "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" - + "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" - + "urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName" - + "" - + "" - + ""; - - static final String UNSIGNED_SAML_SP_METADATA_WITHOUT_ID = "" - + "" - + "" - + "" - + "" - + "" - + "" - + "MIIDSTCCArKgAwIBAgIBADANBgkqhkiG9w0BAQQFADB8MQswCQYDVQQGEwJhdzEOMAwGA1UECBMF" - + "YXJ1YmExDjAMBgNVBAoTBWFydWJhMQ4wDAYDVQQHEwVhcnViYTEOMAwGA1UECxMFYXJ1YmExDjAM" - + "BgNVBAMTBWFydWJhMR0wGwYJKoZIhvcNAQkBFg5hcnViYUBhcnViYS5hcjAeFw0xNTExMjAyMjI2" - + "MjdaFw0xNjExMTkyMjI2MjdaMHwxCzAJBgNVBAYTAmF3MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UE" - + "ChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYDVQQLEwVhcnViYTEOMAwGA1UEAxMFYXJ1YmEx" - + "HTAbBgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB" - + "gQDHtC5gUXxBKpEqZTLkNvFwNGnNIkggNOwOQVNbpO0WVHIivig5L39WqS9u0hnA+O7MCA/KlrAR" - + "4bXaeVVhwfUPYBKIpaaTWFQR5cTR1UFZJL/OF9vAfpOwznoD66DDCnQVpbCjtDYWX+x6imxn8HCY" - + "xhMol6ZnTbSsFW6VZjFMjQIDAQABo4HaMIHXMB0GA1UdDgQWBBTx0lDzjH/iOBnOSQaSEWQLx1sy" - + "GDCBpwYDVR0jBIGfMIGcgBTx0lDzjH/iOBnOSQaSEWQLx1syGKGBgKR+MHwxCzAJBgNVBAYTAmF3" - + "MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UEChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYDVQQL" - + "EwVhcnViYTEOMAwGA1UEAxMFYXJ1YmExHTAbBgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyggEA" - + "MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAYvBJ0HOZbbHClXmGUjGs+GS+xC1FO/am" - + "2suCSYqNB9dyMXfOWiJ1+TLJk+o/YZt8vuxCKdcZYgl4l/L6PxJ982SRhc83ZW2dkAZI4M0/Ud3o" - + "ePe84k8jm3A7EvH5wi5hvCkKRpuRBwn3Ei+jCRouxTbzKPsuCVB+1sNyxMTXzf0=" - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + "MIIDSTCCArKgAwIBAgIBADANBgkqhkiG9w0BAQQFADB8MQswCQYDVQQGEwJhdzEOMAwGA1UECBMF" - + "YXJ1YmExDjAMBgNVBAoTBWFydWJhMQ4wDAYDVQQHEwVhcnViYTEOMAwGA1UECxMFYXJ1YmExDjAM" - + "BgNVBAMTBWFydWJhMR0wGwYJKoZIhvcNAQkBFg5hcnViYUBhcnViYS5hcjAeFw0xNTExMjAyMjI2" - + "MjdaFw0xNjExMTkyMjI2MjdaMHwxCzAJBgNVBAYTAmF3MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UE" - + "ChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYDVQQLEwVhcnViYTEOMAwGA1UEAxMFYXJ1YmEx" - + "HTAbBgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB" - + "gQDHtC5gUXxBKpEqZTLkNvFwNGnNIkggNOwOQVNbpO0WVHIivig5L39WqS9u0hnA+O7MCA/KlrAR" - + "4bXaeVVhwfUPYBKIpaaTWFQR5cTR1UFZJL/OF9vAfpOwznoD66DDCnQVpbCjtDYWX+x6imxn8HCY" - + "xhMol6ZnTbSsFW6VZjFMjQIDAQABo4HaMIHXMB0GA1UdDgQWBBTx0lDzjH/iOBnOSQaSEWQLx1sy" - + "GDCBpwYDVR0jBIGfMIGcgBTx0lDzjH/iOBnOSQaSEWQLx1syGKGBgKR+MHwxCzAJBgNVBAYTAmF3" - + "MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UEChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYDVQQL" - + "EwVhcnViYTEOMAwGA1UEAxMFYXJ1YmExHTAbBgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyggEA" - + "MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAYvBJ0HOZbbHClXmGUjGs+GS+xC1FO/am" - + "2suCSYqNB9dyMXfOWiJ1+TLJk+o/YZt8vuxCKdcZYgl4l/L6PxJ982SRhc83ZW2dkAZI4M0/Ud3o" - + "ePe84k8jm3A7EvH5wi5hvCkKRpuRBwn3Ei+jCRouxTbzKPsuCVB+1sNyxMTXzf0=" - + "" - + "" - + "" - + "" - + "" - + "" - + "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" - + "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" - + "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" - + "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" - + "urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName" - + "" - + "" - + ""; - - private static final String UNSIGNED_SAML_SP_METADATA_ID_AND_ENTITY_ID = "" - + "" - + "" - + "" - + "" - + "" - + "" - + "MIIDSTCCArKgAwIBAgIBADANBgkqhkiG9w0BAQQFADB8MQswCQYDVQQGEwJhdzEOMAwGA1UECBMF" - + "YXJ1YmExDjAMBgNVBAoTBWFydWJhMQ4wDAYDVQQHEwVhcnViYTEOMAwGA1UECxMFYXJ1YmExDjAM" - + "BgNVBAMTBWFydWJhMR0wGwYJKoZIhvcNAQkBFg5hcnViYUBhcnViYS5hcjAeFw0xNTExMjAyMjI2" - + "MjdaFw0xNjExMTkyMjI2MjdaMHwxCzAJBgNVBAYTAmF3MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UE" - + "ChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYDVQQLEwVhcnViYTEOMAwGA1UEAxMFYXJ1YmEx" - + "HTAbBgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB" - + "gQDHtC5gUXxBKpEqZTLkNvFwNGnNIkggNOwOQVNbpO0WVHIivig5L39WqS9u0hnA+O7MCA/KlrAR" - + "4bXaeVVhwfUPYBKIpaaTWFQR5cTR1UFZJL/OF9vAfpOwznoD66DDCnQVpbCjtDYWX+x6imxn8HCY" - + "xhMol6ZnTbSsFW6VZjFMjQIDAQABo4HaMIHXMB0GA1UdDgQWBBTx0lDzjH/iOBnOSQaSEWQLx1sy" - + "GDCBpwYDVR0jBIGfMIGcgBTx0lDzjH/iOBnOSQaSEWQLx1syGKGBgKR+MHwxCzAJBgNVBAYTAmF3" - + "MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UEChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYDVQQL" - + "EwVhcnViYTEOMAwGA1UEAxMFYXJ1YmExHTAbBgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyggEA" - + "MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAYvBJ0HOZbbHClXmGUjGs+GS+xC1FO/am" - + "2suCSYqNB9dyMXfOWiJ1+TLJk+o/YZt8vuxCKdcZYgl4l/L6PxJ982SRhc83ZW2dkAZI4M0/Ud3o" - + "ePe84k8jm3A7EvH5wi5hvCkKRpuRBwn3Ei+jCRouxTbzKPsuCVB+1sNyxMTXzf0=" - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + "MIIDSTCCArKgAwIBAgIBADANBgkqhkiG9w0BAQQFADB8MQswCQYDVQQGEwJhdzEOMAwGA1UECBMF" - + "YXJ1YmExDjAMBgNVBAoTBWFydWJhMQ4wDAYDVQQHEwVhcnViYTEOMAwGA1UECxMFYXJ1YmExDjAM" - + "BgNVBAMTBWFydWJhMR0wGwYJKoZIhvcNAQkBFg5hcnViYUBhcnViYS5hcjAeFw0xNTExMjAyMjI2" - + "MjdaFw0xNjExMTkyMjI2MjdaMHwxCzAJBgNVBAYTAmF3MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UE" - + "ChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYDVQQLEwVhcnViYTEOMAwGA1UEAxMFYXJ1YmEx" - + "HTAbBgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB" - + "gQDHtC5gUXxBKpEqZTLkNvFwNGnNIkggNOwOQVNbpO0WVHIivig5L39WqS9u0hnA+O7MCA/KlrAR" - + "4bXaeVVhwfUPYBKIpaaTWFQR5cTR1UFZJL/OF9vAfpOwznoD66DDCnQVpbCjtDYWX+x6imxn8HCY" - + "xhMol6ZnTbSsFW6VZjFMjQIDAQABo4HaMIHXMB0GA1UdDgQWBBTx0lDzjH/iOBnOSQaSEWQLx1sy" - + "GDCBpwYDVR0jBIGfMIGcgBTx0lDzjH/iOBnOSQaSEWQLx1syGKGBgKR+MHwxCzAJBgNVBAYTAmF3" - + "MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UEChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYDVQQL" - + "EwVhcnViYTEOMAwGA1UEAxMFYXJ1YmExHTAbBgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyggEA" - + "MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAYvBJ0HOZbbHClXmGUjGs+GS+xC1FO/am" - + "2suCSYqNB9dyMXfOWiJ1+TLJk+o/YZt8vuxCKdcZYgl4l/L6PxJ982SRhc83ZW2dkAZI4M0/Ud3o" - + "ePe84k8jm3A7EvH5wi5hvCkKRpuRBwn3Ei+jCRouxTbzKPsuCVB+1sNyxMTXzf0=" - + "" - + "" - + "" - + "" - + "" - + "" - + "%s" - + "" - + "" - + ""; - - public static final String SAML_SP_METADATA_TESTZONE2 = """ - - Qi+CZaMVIemficNn/klUhpk/3QY=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urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddressurn:oasis:names:tc:SAML:2.0:nameid-format:transienturn:oasis:names:tc:SAML:2.0:nameid-format:persistenturn:oasis:names:tc:SAML:1.1:nameid-format:unspecifiedurn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName"""; - - public static final String SAML_IDP_METADATA_ARTIFACT_FIRST = """ - - 8rJXCEVOlzN2dmhPBlxbYdTS1Dc=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\ - urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddressurn:oasis:names:tc:SAML:2.0:nameid-format:persistent\ - urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\ - \ - \ - """; - - public static final String SAML_IDP_METADATA_ARTIFACT_ONLY = """ - - 8rJXCEVOlzN2dmhPBlxbYdTS1Dc=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\ - urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddressurn:oasis:names:tc:SAML:2.0:nameid-format:persistent\ - urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\ - """; - - - private static final String DEFAULT_NAME_ID_FORMATS = - "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" - + "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" - + "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" - + "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" - + "urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName"; - - static final String MOCK_SP_ENTITY_ID = "mock-saml-sp-entity-id"; - - static SamlServiceProvider mockSamlServiceProviderForZone(String zoneId) { - SamlServiceProviderDefinition singleAddDef = SamlServiceProviderDefinition.Builder.get() - .setMetaDataLocation(String.format(SamlTestUtils.UNSIGNED_SAML_SP_METADATA_ID_AND_ENTITY_ID, - new RandomValueStringGenerator().generate(), MOCK_SP_ENTITY_ID, DEFAULT_NAME_ID_FORMATS)) - .setNameID("sample-nameID").setSingleSignOnServiceIndex(1) - .setMetadataTrustCheck(true).build(); - - return new SamlServiceProvider().setEntityId(MOCK_SP_ENTITY_ID).setIdentityZoneId(zoneId) - .setConfig(singleAddDef); - } - - static SamlServiceProvider mockSamlServiceProviderMetadatauriForZone(String metadataURI) { - SamlServiceProviderDefinition singleAddDef = SamlServiceProviderDefinition.Builder.get() - .setMetaDataLocation(metadataURI) - .setNameID("sample-nameID").setSingleSignOnServiceIndex(1) - .setMetadataTrustCheck(false).setSkipSSLValidation(true).build(); - - return new SamlServiceProvider().setEntityId(MOCK_SP_ENTITY_ID).setIdentityZoneId("uaa") - .setConfig(singleAddDef); - } - - static SamlServiceProvider mockSamlServiceProvider(String entityId) { - return mockSamlServiceProvider(entityId, DEFAULT_NAME_ID_FORMATS); - } - - static SamlServiceProvider mockSamlServiceProvider(String entityId, String nameIdFormatsXML) { - SamlServiceProviderDefinition singleAddDef = SamlServiceProviderDefinition.Builder.get() - .setMetaDataLocation(String.format(SamlTestUtils.UNSIGNED_SAML_SP_METADATA_ID_AND_ENTITY_ID, - new RandomValueStringGenerator().generate(), entityId, nameIdFormatsXML)) - .setNameID("sample-nameID").setSingleSignOnServiceIndex(1) - .setMetadataTrustCheck(true).build(); - return new SamlServiceProvider().setEntityId(entityId).setIdentityZoneId("uaa") - .setConfig(singleAddDef); - } - - static SamlServiceProvider mockSamlServiceProviderForZoneWithoutSPSSOInMetadata(String zoneId) { - SamlServiceProviderDefinition singleAddDef = SamlServiceProviderDefinition.Builder.get() - .setMetaDataLocation(String.format(SamlTestUtils.UNSIGNED_SAML_SP_METADATA_ID_AND_ENTITY_ID, - new RandomValueStringGenerator().generate(), MOCK_SP_ENTITY_ID, DEFAULT_NAME_ID_FORMATS)) - .setMetadataTrustCheck(true).build(); - return new SamlServiceProvider().setEntityId(MOCK_SP_ENTITY_ID).setIdentityZoneId(zoneId) - .setConfig(singleAddDef); - } - public static List getCertificates(String metadata, String type) throws Exception { Document doc = getMetadataDoc(metadata); NodeList nodeList = evaluateXPathExpression(doc, "//*[local-name()='KeyDescriptor' and @*[local-name() = 'use']='" + type + "']//*[local-name()='X509Certificate']/text()"); @@ -1037,123 +121,4 @@ public static Document getMetadataDoc(String metadata) throws SAXException, IOEx InputSource is = new InputSource(new StringReader(metadata)); return documentBuilderFactory.newDocumentBuilder().parse(is); } - - private static final String SAML_RESPONSE_XML = """ - - http://uaa-acceptance.cf-app.com/saml-idp - - - - - - - - - - MyMS6YmKuVkw7mwKjEM0yNDBeg/exvjiGcnqh2tb5Ao= - - - - avMFpID6wL5teuIjAikAUMGpLIDD8jlg39w9ZHHyoUzXhTV3/PxI4jzzMBcUjp+3PrlaKAy0na1P7x1zl3OOLHBfxlSCntXtafTXuzlqao4UEWmL28t/S6fT18F1DPcVh0aXXpoiYzqgN8VthTIVd3mcrUjgkjtcLYqotFrQAY47ojBCX9u9hOBm0sYzn6R6UdG1in0qCWTzM08FHhXlicwniugNlxRWaFY9WAoosUcmChIr7ecOsHdbeRcZN7cjrAlW7sFxHK6guGR3QZHt3jTWPKn6Wc+rmqom199iXOnY9ItejGArEKQxIeAWBpUgRj65oQdjYhbPBBH8yl6Exg== - - - - - MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYDVQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwXc2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0BwaXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAaBgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQDDBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlrQHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduOnRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+vZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLuxbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6zV9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk - - - - - - - - http://uaa-acceptance.cf-app.com/saml-idp - - - - - - - - - - e7tjmX8XYbLZEepND4FUVjhT7CTU1HFEIg2jvFZnROk= - - - - snhPsfhCFKCInTy1e1UfDMMW2lXDCdjpUXCQ60lDtsFkwq2FbNP1EdVmKZcN+6OqhW4e69DX9ts78/6C9kgGs3VmT2gadyZz/1PuK202NvaiOodJ/v5mIA8U07Ebq6bZxu7AcDcpPsH3x0cYbF7DGsLsCOFWgCJP9FStrdk3ERkuvNUF9CfY8Z7Phle3HbvCi18bXXtnZ5nURNRi5omHrgp8DUN5idx/cIEM2vaEWwENnFU7zLLVSJVTf4lWT5AkZInO6RYoAlbL/9hblJ8Vbs3cYDxvRomGaH4KRxVVYo9MX8zbzyyVnqVIL3rm9s6+Z30Cs5b+aJF0AfpKx4B+lA== - - - - - MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYDVQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwXc2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0BwaXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAaBgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQDDBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlrQHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduOnRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+vZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLuxbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6zV9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk - - - - - - _797b2928346d2737587b9f55b431d21c68ad5a791e - - - - - - cloudfoundry-saml-login - - - - - urn:oasis:names:tc:SAML:2.0:ac:classes:Password - - - - - marissa@test.org - - - member - marissa - - - Marissa - - - Bloggs - - - 1234567890 - - - marissa@test.org - - - - - """; - - public static String getSamlResponseXml() { - return SAML_RESPONSE_XML; - } } diff --git a/settings.gradle b/settings.gradle index 72d32c1abcd..328271ef3b9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -18,3 +18,8 @@ project(":cloudfoundry-identity-statsd-lib").projectDir = "$rootDir/statsd-lib" project(":cloudfoundry-identity-samples:cloudfoundry-identity-api").projectDir = "$rootDir/samples/api" as File project(":cloudfoundry-identity-samples:cloudfoundry-identity-app").projectDir = "$rootDir/samples/app" as File project(":cloudfoundry-identity-samples").projectDir = "$rootDir/samples" as File + +// Shadow library is needed for FIPS compliance, as opensaml-security-api relies on non-FIPS compliant libraries +//include(":cloudfoundry-identity-shadow-opensaml-security-api") +//project(":cloudfoundry-identity-shadow-opensaml-security-api").projectDir = "$rootDir/shadow/opensaml-security-api" as File + diff --git a/shadow/opensaml-security-api/build.gradle b/shadow/opensaml-security-api/build.gradle new file mode 100644 index 00000000000..1b9b0188bc9 --- /dev/null +++ b/shadow/opensaml-security-api/build.gradle @@ -0,0 +1,23 @@ +apply plugin: 'com.github.johnrengelman.shadow' + +dependencies { + implementation "org.opensaml:opensaml-security-api:${versions.opensaml}" + compileOnly "org.opensaml:opensaml-core:${versions.opensaml}" +} + +configurations { + configureEach { + exclude(group: "org.bouncycastle", module: "bcprov-jdk18on") + exclude(group: "org.bouncycastle", module: "bcpkix-jdk18on") + exclude(group: "org.bouncycastle", module: "bcutil-jdk18on") + } +} + +tasks.named("shadowJar").configure { + archiveBaseName = "cloudfoundry-identity-shadow-opensaml-security-api" + + manifest { + attributes 'Automatic-Module-Name': 'org.opensaml.security' + } + exclude 'META-INF/services/org.opensaml.security.crypto.ec.NamedCurve' +} \ No newline at end of file diff --git a/shadow/opensaml-security-api/src/main/java/org/opensaml/security/config/org/cloudfoundry/identity/uaa/OpenSamlShadowSecurityConfigurationPropertiesSource.java b/shadow/opensaml-security-api/src/main/java/org/opensaml/security/config/org/cloudfoundry/identity/uaa/OpenSamlShadowSecurityConfigurationPropertiesSource.java new file mode 100644 index 00000000000..68f7bf3640e --- /dev/null +++ b/shadow/opensaml-security-api/src/main/java/org/opensaml/security/config/org/cloudfoundry/identity/uaa/OpenSamlShadowSecurityConfigurationPropertiesSource.java @@ -0,0 +1,15 @@ +package org.opensaml.security.config.org.cloudfoundry.identity.uaa; + +import org.opensaml.core.config.ConfigurationPropertiesSource; + +import java.util.Properties; + +public class OpenSamlShadowSecurityConfigurationPropertiesSource implements ConfigurationPropertiesSource { + + @Override + public Properties getProperties() { + Properties properties = new Properties(); + properties.setProperty("opensaml.config.ecdh.defaultKDF", "PBKDF2"); + return properties; + } +} \ No newline at end of file diff --git a/shadow/opensaml-security-api/src/main/resources/META-INF/services/org.opensaml.core.config.ConfigurationPropertiesSource b/shadow/opensaml-security-api/src/main/resources/META-INF/services/org.opensaml.core.config.ConfigurationPropertiesSource new file mode 100644 index 00000000000..2aac80ecb23 --- /dev/null +++ b/shadow/opensaml-security-api/src/main/resources/META-INF/services/org.opensaml.core.config.ConfigurationPropertiesSource @@ -0,0 +1 @@ +org.opensaml.security.config.org.cloudfoundry.identity.uaa.OpenSamlShadowSecurityConfigurationPropertiesSource \ No newline at end of file diff --git a/uaa/build.gradle b/uaa/build.gradle index 40d4f4a8543..e8ea8a0ed79 100644 --- a/uaa/build.gradle +++ b/uaa/build.gradle @@ -95,6 +95,8 @@ dependencies { testImplementation(libraries.commonsIo) testImplementation(libraries.owaspEsapi) testImplementation(libraries.apacheHttpClient) + testImplementation(libraries.openSamlApi) + testImplementation(libraries.xmlUnit) } ext { diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/BootstrapTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/BootstrapTests.java index 4be1aeaede3..3b4ce9c5ee6 100755 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/BootstrapTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/BootstrapTests.java @@ -25,7 +25,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; - import org.springframework.beans.BeansException; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.ResourceEntityResolver; @@ -35,7 +34,6 @@ import org.springframework.mock.web.MockRequestDispatcher; import org.springframework.mock.web.MockServletConfig; import org.springframework.mock.web.MockServletContext; -//import org.springframework.security.saml.log.SAMLDefaultLogger; import org.springframework.util.StringUtils; import org.springframework.web.context.support.AbstractRefreshableWebApplicationContext; import org.springframework.web.servlet.ViewResolver; @@ -104,8 +102,83 @@ class BootstrapTests { LOGIN_IDP_METADATA_URL, LOGIN_SAML_METADATA_TRUST_CHECK); + private final static MockServletContext mockServletContext = new MockServletContext() { + @Override + public RequestDispatcher getNamedDispatcher(String path) { + return new MockRequestDispatcher("/"); + } + + @Override + public String getVirtualServerName() { + return "localhost"; + } + + @Override + public void addListener(Type t) { + //no op + } + }; + private static final AbstractRefreshableWebApplicationContext abstractRefreshableWebApplicationContext = new AbstractRefreshableWebApplicationContext() { + + @Override + protected void loadBeanDefinitions(@NonNull DefaultListableBeanFactory beanFactory) throws BeansException { + XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); + + // Configure the bean definition reader with this context's + // resource loading environment. + beanDefinitionReader.setEnvironment(this.getEnvironment()); + beanDefinitionReader.setResourceLoader(this); + beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); + + beanDefinitionReader.loadBeanDefinitions("file:./src/main/webapp/WEB-INF/spring-servlet.xml"); + } + }; + private ConfigurableApplicationContext context; + static Stream samlSignatureParameterProvider() { + final String yamlPath = "test/config/"; + return Stream.of( + arguments(yamlPath + "saml-algorithm-sha256.yml", SamlConfigurationBean.SignatureAlgorithm.SHA256), + arguments(yamlPath + "saml-algorithm-sha512.yml", SamlConfigurationBean.SignatureAlgorithm.SHA512) + ); + } + + private static SamlIdentityProviderDefinition findProvider( + final List defs, + final String alias) { + for (SamlIdentityProviderDefinition def : defs) { + if (alias.equals(def.getIdpEntityAlias())) { + return def; + } + } + return null; + } + + private static ConfigurableApplicationContext getServletContext( + final String profiles, + final String uaaYamlPath) { + System.setProperty("LOGIN_CONFIG_URL", "file:" + System.getProperty("user.dir") + "/../scripts/cargo/uaa.yml"); + System.setProperty("UAA_CONFIG_URL", "classpath:" + uaaYamlPath); + + abstractRefreshableWebApplicationContext.setServletContext(mockServletContext); + MockServletConfig servletConfig = new MockServletConfig(mockServletContext); + abstractRefreshableWebApplicationContext.setServletConfig(servletConfig); + + YamlServletProfileInitializer initializer = new YamlServletProfileInitializer(); + initializer.initialize(abstractRefreshableWebApplicationContext); + System.clearProperty("LOGIN_CONFIG_URL"); + System.clearProperty("UAA_CONFIG_URL"); + + if (profiles != null) { + abstractRefreshableWebApplicationContext.getEnvironment().setActiveProfiles(StringUtils.commaDelimitedListToStringArray(profiles)); + } + + abstractRefreshableWebApplicationContext.refresh(); + + return abstractRefreshableWebApplicationContext; + } + @Test void xlegacyTestDeprecatedProperties() { context = getServletContext(null, "test/bootstrap/deprecated_properties_still_work.yml"); @@ -179,14 +252,14 @@ void legacySamlMetadataAsUrl() { ); } - @Disabled("SAML test fails") @ParameterizedTest @MethodSource("samlSignatureParameterProvider") - void samlSignatureAlgorithm(String yamlFile, SamlConfigurationBean.SignatureAlgorithm algorithm) { + @Disabled("SAML test fails") + void samlSignatureAlgorithmsWereBootstrapped(String yamlFile, SamlConfigurationBean.SignatureAlgorithm algorithm) { // When we override the SHA1 default for login.saml.signatureAlgorithm in the yaml, make sure it works. context = getServletContext("default", yamlFile); - SamlConfigurationBean samlConfig = context.getBean("defaultSamlConfig", SamlConfigurationBean.class); + SamlConfigurationBean samlConfig = context.getBean(SamlConfigurationBean.class); assertEquals( algorithm, samlConfig.getSignatureAlgorithm(), @@ -194,14 +267,6 @@ void samlSignatureAlgorithm(String yamlFile, SamlConfigurationBean.SignatureAlgo ); } - static Stream samlSignatureParameterProvider() { - final String yamlPath = "test/config/"; - return Stream.of( - arguments(yamlPath + "saml-algorithm-sha256.yml", SamlConfigurationBean.SignatureAlgorithm.SHA256), - arguments(yamlPath + "saml-algorithm-sha512.yml", SamlConfigurationBean.SignatureAlgorithm.SHA512) - ); - } - @Test @Disabled("SAML test doesn't compile") void legacySamlUrlWithoutPort() { @@ -226,73 +291,5 @@ void legacySamlUrlWithoutPort() { ); } - private static SamlIdentityProviderDefinition findProvider( - final List defs, - final String alias) { - for (SamlIdentityProviderDefinition def : defs) { - if (alias.equals(def.getIdpEntityAlias())) { - return def; - } - } - return null; - } - - private static ConfigurableApplicationContext getServletContext( - final String profiles, - final String uaaYamlPath) { - System.setProperty("LOGIN_CONFIG_URL", "file:" + System.getProperty("user.dir") + "/../scripts/cargo/uaa.yml"); - System.setProperty("UAA_CONFIG_URL", "classpath:" + uaaYamlPath); - - abstractRefreshableWebApplicationContext.setServletContext(mockServletContext); - MockServletConfig servletConfig = new MockServletConfig(mockServletContext); - abstractRefreshableWebApplicationContext.setServletConfig(servletConfig); - - YamlServletProfileInitializer initializer = new YamlServletProfileInitializer(); - initializer.initialize(abstractRefreshableWebApplicationContext); - System.clearProperty("LOGIN_CONFIG_URL"); - System.clearProperty("UAA_CONFIG_URL"); - - if (profiles != null) { - abstractRefreshableWebApplicationContext.getEnvironment().setActiveProfiles(StringUtils.commaDelimitedListToStringArray(profiles)); - } - - abstractRefreshableWebApplicationContext.refresh(); - - return abstractRefreshableWebApplicationContext; - } - - private final static MockServletContext mockServletContext = new MockServletContext() { - @Override - public RequestDispatcher getNamedDispatcher(String path) { - return new MockRequestDispatcher("/"); - } - - @Override - public String getVirtualServerName() { - return "localhost"; - } - - @Override - public void addListener(Type t) { - //no op - } - }; - - private static final AbstractRefreshableWebApplicationContext abstractRefreshableWebApplicationContext = new AbstractRefreshableWebApplicationContext() { - - @Override - protected void loadBeanDefinitions(@NonNull DefaultListableBeanFactory beanFactory) throws BeansException { - XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); - - // Configure the bean definition reader with this context's - // resource loading environment. - beanDefinitionReader.setEnvironment(this.getEnvironment()); - beanDefinitionReader.setResourceLoader(this); - beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); - - beanDefinitionReader.loadBeanDefinitions("file:./src/main/webapp/WEB-INF/spring-servlet.xml"); - } - }; - } diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/config/HealthzShouldNotBeProtectedMockMvcTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/config/HealthzShouldNotBeProtectedMockMvcTests.java index 40b2c1bac4f..91c6364090c 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/config/HealthzShouldNotBeProtectedMockMvcTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/config/HealthzShouldNotBeProtectedMockMvcTests.java @@ -67,25 +67,6 @@ void setUp() { chainPostProcessor.setRequireHttps(true); } - @DefaultTestContext - @Nested - class WithHttpPortSetToNonDefaultValue { - @BeforeEach - void setUp() { - chainPostProcessor.setHttpsPort(9998); - } - - @Test - void redirectedRequestsGoToTheConfiguredPort() throws Exception { - MockHttpServletRequestBuilder getRequest = get("/login") - .accept(MediaType.TEXT_HTML); - - mockMvc.perform(getRequest) - .andExpect(status().is3xxRedirection()) - .andExpect(header().string("Location", "https://localhost:9998/login")); - } - } - @ParameterizedTest @ArgumentsSource(HealthzGetRequestParams.class) void healthzIsNotRejected(MockHttpServletRequestBuilder getRequest) throws Exception { @@ -113,6 +94,25 @@ void samlMetadataRedirects() throws Exception { .andExpect(status().is3xxRedirection()) .andExpect(header().string("Location", "https://localhost/saml/metadata")); } + + @DefaultTestContext + @Nested + class WithHttpPortSetToNonDefaultValue { + @BeforeEach + void setUp() { + chainPostProcessor.setHttpsPort(9998); + } + + @Test + void redirectedRequestsGoToTheConfiguredPort() throws Exception { + MockHttpServletRequestBuilder getRequest = get("/login") + .accept(MediaType.TEXT_HTML); + + mockMvc.perform(getRequest) + .andExpect(status().is3xxRedirection()) + .andExpect(header().string("Location", "https://localhost:9998/login")); + } + } } @DefaultTestContext @@ -150,7 +150,6 @@ void samlMetadataReturnsOk() throws Exception { .andExpect(status().isOk()); } -// @Disabled("trailing slash likely routes to processing with RegistrationID and likely is empty") @Test void samlMetadataWithTrailingSlashReturnsOk() throws Exception { MockHttpServletRequestBuilder getRequest = get("/saml/metadata/") diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/saml/SamlAuthenticationMockMvcTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/saml/SamlAuthenticationMockMvcTests.java index ab9b36bae1f..1f2b622ef40 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/saml/SamlAuthenticationMockMvcTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/saml/SamlAuthenticationMockMvcTests.java @@ -7,17 +7,14 @@ import org.apache.logging.log4j.core.appender.AbstractAppender; import org.apache.logging.log4j.core.config.Configurator; import org.cloudfoundry.identity.uaa.DefaultTestContext; -import org.cloudfoundry.identity.uaa.audit.LoggingAuditService; import org.cloudfoundry.identity.uaa.authentication.SamlResponseLoggerBinding; import org.cloudfoundry.identity.uaa.client.UaaClientDetails; import org.cloudfoundry.identity.uaa.constants.OriginKeys; -import org.cloudfoundry.identity.uaa.mock.util.InterceptingLogger; import org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils; import org.cloudfoundry.identity.uaa.oauth.common.util.RandomValueStringGenerator; import org.cloudfoundry.identity.uaa.provider.IdentityProvider; import org.cloudfoundry.identity.uaa.provider.JdbcIdentityProviderProvisioning; import org.cloudfoundry.identity.uaa.provider.SamlIdentityProviderDefinition; -import org.cloudfoundry.identity.uaa.provider.saml.idp.SamlTestUtils; import org.cloudfoundry.identity.uaa.scim.ScimUser; import org.cloudfoundry.identity.uaa.scim.jdbc.JdbcScimUserProvisioning; import org.cloudfoundry.identity.uaa.util.UaaUrlUtils; @@ -25,32 +22,46 @@ import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; -import org.junit.jupiter.api.*; +import org.hamcrest.MatcherAssert; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; import org.owasp.esapi.ESAPI; import org.owasp.esapi.reference.DefaultSecurityConfiguration; -import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultActions; import org.springframework.web.context.WebApplicationContext; +import org.xmlunit.assertj.XmlAssert; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.Base64; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.function.Consumer; -import static javax.xml.crypto.dsig.Transform.BASE64; import static org.apache.logging.log4j.Level.DEBUG; import static org.apache.logging.log4j.Level.WARN; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; import static org.cloudfoundry.identity.uaa.authentication.SamlResponseLoggerBinding.X_VCAP_REQUEST_ID_HEADER; +import static org.cloudfoundry.identity.uaa.provider.saml.Saml2TestUtils.responseWithAssertions; +import static org.cloudfoundry.identity.uaa.provider.saml.Saml2TestUtils.serialize; +import static org.cloudfoundry.identity.uaa.provider.saml.Saml2TestUtils.serializedResponse; +import static org.cloudfoundry.identity.uaa.provider.saml.Saml2TestUtils.xmlNamespaces; +import static org.cloudfoundry.identity.uaa.provider.saml.Saml2Utils.samlDecode; +import static org.cloudfoundry.identity.uaa.provider.saml.Saml2Utils.samlDecodeAndInflate; +import static org.cloudfoundry.identity.uaa.provider.saml.Saml2Utils.samlEncode; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; import static org.springframework.http.HttpHeaders.CONTENT_TYPE; import static org.springframework.http.HttpHeaders.HOST; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -58,7 +69,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.util.Assert.doesNotContain; @DefaultTestContext class SamlAuthenticationMockMvcTests { @@ -78,10 +88,19 @@ class SamlAuthenticationMockMvcTests { private JdbcIdentityProviderProvisioning jdbcIdentityProviderProvisioning; - @Autowired - private LoggingAuditService loggingAuditService; - private InterceptingLogger testLogger; - private Logger originalAuditServiceLogger; + // @Autowired + // private LoggingAuditService loggingAuditService; + // private InterceptingLogger testLogger; + // private Logger originalAuditServiceLogger; + + private static void createUser( + JdbcScimUserProvisioning jdbcScimUserProvisioning, + IdentityZone identityZone + ) { + ScimUser user = new ScimUser(null, "marissa", "first", "last"); + user.setPrimaryEmail("test@test.org"); + jdbcScimUserProvisioning.createUser(user, "secret", identityZone.getId()); + } @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") @BeforeEach @@ -99,11 +118,16 @@ void createSamlRelationship( createUser(jdbcScimUserProvisioning, idpZone); } + // @AfterEach + // void putBackOriginalLogger() { + // loggingAuditService.setLogger(originalAuditServiceLogger); + // } + @BeforeEach void installTestLogger() { - testLogger = new InterceptingLogger(); - originalAuditServiceLogger = loggingAuditService.getLogger(); - loggingAuditService.setLogger(testLogger); + // testLogger = new InterceptingLogger(); + // originalAuditServiceLogger = loggingAuditService.getLogger(); + // loggingAuditService.setLogger(testLogger); Properties esapiProps = new Properties(); esapiProps.put("ESAPI.Logger", "org.owasp.esapi.logging.slf4j.Slf4JLogFactory"); esapiProps.put("ESAPI.Encoder", "org.owasp.esapi.reference.DefaultEncoder"); @@ -116,11 +140,6 @@ void installTestLogger() { ESAPI.override(new DefaultSecurityConfiguration(esapiProps)); } - @AfterEach - void putBackOriginalLogger() { - loggingAuditService.setLogger(originalAuditServiceLogger); - } - @Test void sendAuthnRequestToIdpRedirectBindingMode() throws Exception { MvcResult mvcResult = mockMvc.perform( @@ -134,16 +153,28 @@ void sendAuthnRequestToIdpRedirectBindingMode() throws Exception { String samlRequestUrl = mvcResult.getResponse().getRedirectedUrl(); Map parameterMap = UaaUrlUtils.getParameterMap(samlRequestUrl); - assertThat("SAMLRequest is missing", parameterMap.get("SAMLRequest"), notNullValue()); + MatcherAssert.assertThat("SAMLRequest is missing", parameterMap.get("SAMLRequest"), notNullValue()); assertThat("SigAlg is missing", parameterMap.get("SigAlg"), notNullValue()); assertThat("Signature is missing", parameterMap.get("Signature"), notNullValue()); assertThat("RelayState is missing", parameterMap.get("RelayState"), notNullValue()); assertThat(parameterMap.get("RelayState")[0], equalTo("testsaml-redirect-binding")); + + // Decode & Inflate the SAMLRequest and check the AssertionConsumerServiceURL + String samlRequestXml = samlDecodeAndInflate(parameterMap.get("SAMLRequest")[0]); + assertThat(samlRequestXml) + .contains(" additionalConfigCallback) throws Exception { + idp = new IdentityProvider() + .setType(OriginKeys.SAML) + .setOriginKey(idpZone.getSubdomain()) + .setActive(true) + .setName("SAML IDP for Mock Tests") + .setIdentityZoneId(spZone.getId()); + SamlIdentityProviderDefinition idpDefinition = new SamlIdentityProviderDefinition() + .setMetaDataLocation(getSamlMetadata(idpZone.getSubdomain(), "/saml/idp/metadata")) + .setIdpEntityAlias(idp.getOriginKey()) + .setLinkText(idp.getName()) + .setZoneId(spZone.getId()); + + if (additionalConfigCallback != null) { + additionalConfigCallback.accept(idpDefinition); + } + + idp.setConfig(idpDefinition); + idp = jdbcIdentityProviderProvisioning.create(idp, spZone.getId()); + } + + private IdentityZone createZone(String zoneIdPrefix, UaaClientDetails adminClient) throws Exception { + return MockMvcUtils.createOtherIdentityZoneAndReturnResult( + zoneIdPrefix + generator.generate(), + mockMvc, + webApplicationContext, + adminClient, IdentityZoneHolder.getCurrentZoneId() + ).getIdentityZone(); + } + + private static class MatchesLogEvent extends BaseMatcher { + + private final Level expectedLevel; + private final String expectedMessage; + + public MatchesLogEvent( + final Level expectedLevel, + final String expectedMessage + ) { + this.expectedLevel = expectedLevel; + this.expectedMessage = expectedMessage; + } + + @Override + public boolean matches(Object actual) { + if (!(actual instanceof LogEvent logEvent)) { + return false; + } + + return expectedLevel.equals(logEvent.getLevel()) + && expectedMessage.equals(logEvent.getMessage().getFormattedMessage()); + } + + @Override + public void describeTo(Description description) { + description.appendText(String.format("LogEvent with level of {%s} and message of {%s}", this.expectedLevel, this.expectedMessage)); + } + } + @Nested @DefaultTestContext class WithCustomLogAppender { @@ -279,88 +394,8 @@ private void assertThatMessageWasLogged( final Level expectedLevel, final String expectedMessage ) { - assertThat(logEvents, hasItem(new MatchesLogEvent(expectedLevel, expectedMessage))); - } - } - - private static class MatchesLogEvent extends BaseMatcher { - - private final Level expectedLevel; - private final String expectedMessage; - - public MatchesLogEvent( - final Level expectedLevel, - final String expectedMessage - ) { - this.expectedLevel = expectedLevel; - this.expectedMessage = expectedMessage; - } - - @Override - public boolean matches(Object actual) { - if (!(actual instanceof LogEvent)) { - return false; - } - LogEvent logEvent = (LogEvent) actual; - - return expectedLevel.equals(logEvent.getLevel()) - && expectedMessage.equals(logEvent.getMessage().getFormattedMessage()); - } - - @Override - public void describeTo(Description description) { - description.appendText(String.format("LogEvent with level of {%s} and message of {%s}", this.expectedLevel, this.expectedMessage)); - } - } - - private String getSamlMetadata(String subdomain, String url) throws Exception { - return mockMvc.perform( - get(url) - .header("Host", subdomain + ".localhost") - ) - .andReturn().getResponse().getContentAsString(); - } - - private static void createUser( - JdbcScimUserProvisioning jdbcScimUserProvisioning, - IdentityZone identityZone - ) { - ScimUser user = new ScimUser(null, "marissa", "first", "last"); - user.setPrimaryEmail("test@test.org"); - jdbcScimUserProvisioning.createUser(user, "secret", identityZone.getId()); - } - - void createIdp() throws Exception { - createIdp(null); - } - - private void createIdp(Consumer additionalConfigCallback) throws Exception { - idp = new IdentityProvider<>() - .setType(OriginKeys.SAML) - .setOriginKey(idpZone.getSubdomain()) - .setActive(true) - .setName("SAML IDP for Mock Tests") - .setIdentityZoneId(spZone.getId()); - SamlIdentityProviderDefinition idpDefinition = new SamlIdentityProviderDefinition() - .setMetaDataLocation(getSamlMetadata(idpZone.getSubdomain(), "/saml/idp/metadata")) - .setIdpEntityAlias(idp.getOriginKey()) - .setLinkText(idp.getName()) - .setZoneId(spZone.getId()); - - if (additionalConfigCallback != null) { - additionalConfigCallback.accept(idpDefinition); + assertThat(logEvents).extracting(LogEvent::getLevel, LogEvent::getMessage) + .contains(tuple(expectedLevel, expectedMessage)); } - - idp.setConfig(idpDefinition); - idp = jdbcIdentityProviderProvisioning.create(idp, spZone.getId()); - } - - private IdentityZone createZone(String zoneIdPrefix, UaaClientDetails adminClient) throws Exception { - return MockMvcUtils.createOtherIdentityZoneAndReturnResult( - zoneIdPrefix + generator.generate(), - mockMvc, - webApplicationContext, - adminClient, IdentityZoneHolder.getCurrentZoneId() - ).getIdentityZone(); } } diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/Saml2BearerGrantMockMvcTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/Saml2BearerGrantMockMvcTests.java index 405c86b70c8..79759a4a377 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/Saml2BearerGrantMockMvcTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/Saml2BearerGrantMockMvcTests.java @@ -8,7 +8,6 @@ import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -//import org.opensaml.saml2.core.NameID; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -24,8 +23,7 @@ public class Saml2BearerGrantMockMvcTests extends AbstractTokenMockMvcTests { @Test @Disabled("SAML test doesn't compile") void getTokenUsingSaml2BearerGrant() throws Exception { - SamlTestUtils samlTestUtils = new SamlTestUtils(); -// samlTestUtils.initializeSimple(); + SamlTestUtils.initialize(); final String subdomain = "68uexx"; //all our SAML defaults use :8080/uaa/ so we have to use that here too @@ -36,7 +34,7 @@ void getTokenUsingSaml2BearerGrant() throws Exception { MockMvcUtils.IdentityZoneCreationResult testZone = MockMvcUtils.createOtherIdentityZoneAndReturnResult( - subdomain, mockMvc, this.webApplicationContext, null, + subdomain, mockMvc, this.webApplicationContext, null, IdentityZoneHolder.getCurrentZoneId()); //Mock an IDP metadata @@ -139,7 +137,7 @@ void getTokenUsingSaml2BearerGrant() throws Exception { //create an IDP in the test zone SamlIdentityProviderDefinition idpDef = createLocalSamlIdpDefinition( origin, testZone.getIdentityZone().getId(), idpMetadata); - IdentityProvider provider = new IdentityProvider(); + IdentityProvider provider = new IdentityProvider<>(); provider.setConfig(idpDef); provider.setActive(true); provider.setIdentityZoneId(testZone.getIdentityZone().getId()); diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/util/MockMvcUtils.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/util/MockMvcUtils.java index 782d85bba3e..2cf5b7c75a5 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/util/MockMvcUtils.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/util/MockMvcUtils.java @@ -28,6 +28,9 @@ import org.cloudfoundry.identity.uaa.invitations.InvitationsResponse; import org.cloudfoundry.identity.uaa.login.Prompt; import org.cloudfoundry.identity.uaa.oauth.client.ClientDetailsModification; +import org.cloudfoundry.identity.uaa.oauth.common.util.OAuth2Utils; +import org.cloudfoundry.identity.uaa.oauth.common.util.RandomValueStringGenerator; +import org.cloudfoundry.identity.uaa.oauth.provider.ClientDetails; import org.cloudfoundry.identity.uaa.oauth.token.TokenConstants; import org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.TokenFormat; import org.cloudfoundry.identity.uaa.provider.AbstractIdentityProviderDefinition; @@ -80,9 +83,6 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextImpl; -import org.cloudfoundry.identity.uaa.oauth.common.util.OAuth2Utils; -import org.cloudfoundry.identity.uaa.oauth.common.util.RandomValueStringGenerator; -import org.cloudfoundry.identity.uaa.oauth.provider.ClientDetails; import org.springframework.security.web.PortResolverImpl; import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.security.web.csrf.CsrfToken; @@ -134,42 +134,41 @@ public final class MockMvcUtils { - private MockMvcUtils() { - } - public static final String IDP_META_DATA = - "\n" + - "\n" + - " \n" + - " \n" + - " begl1WVCsXSn7iHixtWPP8d/X+k=BmbKqA3A0oSLcn5jImz/l5WbpVXj+8JIpT/ENWjOjSd/gcAsZm1QvYg+RxYPBk+iV2bBxD+/yAE/w0wibsHrl0u9eDhoMRUJBUSmeyuN1lYzBuoVa08PdAGtb5cGm4DMQT5Rzakb1P0hhEPPEDDHgTTxop89LUu6xx97t2Q03Khy8mXEmBmNt2NlFxJPNt0FwHqLKOHRKBOE/+BpswlBocjOQKFsI9tG3TyjFC68mM2jo0fpUQCgj5ZfhzolvS7z7c6V201d9Tqig0/mMFFJLTN8WuZPavw22AJlMjsDY9my+4R9HKhK5U53DhcTeECs9fb4gd7p5BJy4vVp7tqqOg==\n" + - "MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYDVQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwXc2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0BwaXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAaBgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQDDBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlrQHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduOnRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+vZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLuxbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6zV9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYDVQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwXc2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0BwaXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAaBgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQDDBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlrQHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduOnRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+vZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLuxbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6zV9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYDVQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwXc2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0BwaXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAaBgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQDDBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlrQHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduOnRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+vZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLuxbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6zV9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " urn:oasis:names:tc:SAML:2.0:nameid-format:transient\n" + - " \n" + - " \n" + - " \n" + - " Filip\n" + - " Hanik\n" + - " fhanik@pivotal.io\n" + - " \n" + - ""; + "\n" + + "\n" + + " \n" + + " \n" + + " begl1WVCsXSn7iHixtWPP8d/X+k=BmbKqA3A0oSLcn5jImz/l5WbpVXj+8JIpT/ENWjOjSd/gcAsZm1QvYg+RxYPBk+iV2bBxD+/yAE/w0wibsHrl0u9eDhoMRUJBUSmeyuN1lYzBuoVa08PdAGtb5cGm4DMQT5Rzakb1P0hhEPPEDDHgTTxop89LUu6xx97t2Q03Khy8mXEmBmNt2NlFxJPNt0FwHqLKOHRKBOE/+BpswlBocjOQKFsI9tG3TyjFC68mM2jo0fpUQCgj5ZfhzolvS7z7c6V201d9Tqig0/mMFFJLTN8WuZPavw22AJlMjsDY9my+4R9HKhK5U53DhcTeECs9fb4gd7p5BJy4vVp7tqqOg==\n" + + "MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYDVQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwXc2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0BwaXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAaBgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQDDBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlrQHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduOnRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+vZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLuxbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6zV9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYDVQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwXc2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0BwaXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAaBgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQDDBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlrQHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduOnRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+vZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLuxbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6zV9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYDVQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwXc2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0BwaXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAaBgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQDDBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlrQHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduOnRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+vZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLuxbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6zV9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " urn:oasis:names:tc:SAML:2.0:nameid-format:transient\n" + + " \n" + + " \n" + + " \n" + + " Filip\n" + + " Hanik\n" + + " fhanik@pivotal.io\n" + + " \n" + + ""; + private MockMvcUtils() { + } public static T getEventOfType(ArgumentCaptor captor, Class type) { for (AbstractUaaEvent event : captor.getAllValues()) { @@ -201,20 +200,20 @@ public static void resetLimitedModeStatusFile(ApplicationContext context, File f public static String getSPMetadata(MockMvc mockMvc, String subdomain) throws Exception { return mockMvc.perform( - get("/saml/metadata") - .accept(MediaType.APPLICATION_XML) - .header(HOST, hasText(subdomain) ? subdomain + ".localhost" : "localhost") - ).andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(); + get("/saml/metadata") + .accept(MediaType.APPLICATION_XML) + .header(HOST, hasText(subdomain) ? subdomain + ".localhost" : "localhost") + ).andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); } public static String getIDPMetaData(MockMvc mockMvc, String subdomain) throws Exception { return mockMvc.perform( - get("/saml/idp/metadata") - .accept(MediaType.APPLICATION_XML) - .header(HOST, hasText(subdomain) ? subdomain + ".localhost" : "localhost") - ).andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(); + get("/saml/idp/metadata") + .accept(MediaType.APPLICATION_XML) + .header(HOST, hasText(subdomain) ? subdomain + ".localhost" : "localhost") + ).andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); } public static MockHttpSession getSavedRequestSession() { @@ -226,100 +225,16 @@ public static MockHttpSession getSavedRequestSession() { public static ScimUser getUserByUsername(MockMvc mockMvc, String username, String accessToken) throws Exception { MockHttpServletRequestBuilder get = get("/Users?filter=userName eq \"" + username + "\"") - .header("Authorization", "Bearer " + accessToken) - .header("Accept", APPLICATION_JSON); + .header("Authorization", "Bearer " + accessToken) + .header("Accept", APPLICATION_JSON); MvcResult userResult = mockMvc.perform(get) - .andExpect(status().isOk()).andReturn(); + .andExpect(status().isOk()).andReturn(); SearchResults results = JsonUtils.readValue(userResult.getResponse().getContentAsString(), - new TypeReference>(){}); + new TypeReference>() { + }); return results.getResources().get(0); } - public static class MockSavedRequest extends DefaultSavedRequest { - - public MockSavedRequest() { - super(new MockHttpServletRequest(), new PortResolverImpl()); - } - - @Override - public String getRedirectUrl() { - return "http://test/redirect/oauth/authorize"; - } - - @Override - public String[] getParameterValues(String name) { - if ("client_id".equals(name)) { - return new String[]{"admin"}; - } - return new String[0]; - } - - @Override - public List getCookies() { - return null; - } - - @Override - public String getMethod() { - return null; - } - - @Override - public List getHeaderValues(String name) { - return null; - } - - @Override - public Collection getHeaderNames() { - return null; - } - - @Override - public List getLocales() { - return null; - } - - @Override - public Map getParameterMap() { - return null; - } - - } - - public static class ZoneScimInviteData { - private final IdentityZoneCreationResult zone; - private final String adminToken; - private final ClientDetails scimInviteClient; - private final String defaultZoneAdminToken; - - public ZoneScimInviteData(String adminToken, - IdentityZoneCreationResult zone, - ClientDetails scimInviteClient, - String defaultZoneAdminToken) { - this.adminToken = adminToken; - this.zone = zone; - this.scimInviteClient = scimInviteClient; - this.defaultZoneAdminToken = defaultZoneAdminToken; - } - - public ClientDetails getScimInviteClient() { - return scimInviteClient; - } - - public String getDefaultZoneAdminToken() { - return defaultZoneAdminToken; - } - - public IdentityZoneCreationResult getZone() { - return zone; - } - - public String getAdminToken() { - return adminToken; - } - } - - public static String extractInvitationCode(String inviteLink) { Pattern p = Pattern.compile("accept\\?code=(.*)"); Matcher m = p.matcher(inviteLink); @@ -393,19 +308,19 @@ public static InvitationsResponse sendRequestWithTokenAndReturnResponse(Applicat String requestBody = JsonUtils.writeValueAsString(invitations); MockHttpServletRequestBuilder post = post("/invite_users") - .param(OAuth2Utils.CLIENT_ID, clientId) - .param(OAuth2Utils.REDIRECT_URI, redirectUri) - .header("Authorization", "Bearer " + token) - .contentType(APPLICATION_JSON) - .content(requestBody); + .param(OAuth2Utils.CLIENT_ID, clientId) + .param(OAuth2Utils.REDIRECT_URI, redirectUri) + .header("Authorization", "Bearer " + token) + .contentType(APPLICATION_JSON) + .content(requestBody); if (hasText(subdomain)) { post.header("Host", (subdomain + ".localhost")); } MvcResult result = mockMvc.perform( - post - ) - .andExpect(status().isOk()) - .andReturn(); + post + ) + .andExpect(status().isOk()) + .andReturn(); return JsonUtils.readValue(result.getResponse().getContentAsString(), InvitationsResponse.class); } @@ -431,10 +346,10 @@ public static IdentityProvider createIdentityProvider(MockMvc mockMvc, IdentityZ provider.setType(OriginKeys.UAA); } provider = MockMvcUtils.createIdpUsingWebRequest(mockMvc, - zone.getIdentityZone().getId(), - zone.getZoneAdminToken(), - provider, - status().isCreated()); + zone.getIdentityZone().getId(), + zone.getZoneAdminToken(), + provider, + status().isCreated()); return provider; } @@ -448,14 +363,14 @@ public static ZoneScimInviteData createZoneForInvites(MockMvc mockMvc, Applicati appClient.setClientSecret("secret"); appClient = MockMvcUtils.createClient(mockMvc, zone.getZoneAdminToken(), appClient, zone.getIdentityZone(), - status().isCreated()); + status().isCreated()); appClient.setClientSecret("secret"); String adminToken = MockMvcUtils.getClientCredentialsOAuthAccessToken( - mockMvc, - appClient.getClientId(), - appClient.getClientSecret(), - "", - zone.getIdentityZone().getSubdomain() + mockMvc, + appClient.getClientId(), + appClient.getClientSecret(), + "", + zone.getIdentityZone().getSubdomain() ); @@ -470,10 +385,10 @@ public static ZoneScimInviteData createZoneForInvites(MockMvc mockMvc, Applicati group.setMembers(Collections.singletonList(new ScimGroupMember(user.getId(), USER))); return new ZoneScimInviteData( - adminToken, - zone, - appClient, - superAdmin + adminToken, + zone, + appClient, + superAdmin ); } @@ -494,37 +409,13 @@ public static IdentityZone createZoneUsingWebRequest(MockMvc mockMvc, String acc IdentityZone identityZone = MultitenancyFixture.identityZone(zoneId, zoneId); MvcResult result = mockMvc.perform(post("/identity-zones") - .header("Authorization", "Bearer " + accessToken) - .contentType(APPLICATION_JSON) - .content(JsonUtils.writeValueAsString(identityZone))) - .andExpect(status().isCreated()).andReturn(); + .header("Authorization", "Bearer " + accessToken) + .contentType(APPLICATION_JSON) + .content(JsonUtils.writeValueAsString(identityZone))) + .andExpect(status().isCreated()).andReturn(); return JsonUtils.readValue(result.getResponse().getContentAsString(), IdentityZone.class); } - public static class IdentityZoneCreationResult { - private final IdentityZone identityZone; - private final UaaPrincipal zoneAdmin; - private final String zoneAdminToken; - - public IdentityZoneCreationResult(IdentityZone identityZone, UaaPrincipal zoneAdmin, String zoneAdminToken) { - this.identityZone = identityZone; - this.zoneAdmin = zoneAdmin; - this.zoneAdminToken = zoneAdminToken; - } - - public IdentityZone getIdentityZone() { - return identityZone; - } - - public UaaPrincipal getZoneAdminUser() { - return zoneAdmin; - } - - public String getZoneAdminToken() { - return zoneAdminToken; - } - } - public static IdentityZoneCreationResult createOtherIdentityZoneAndReturnResult( MockMvc mockMvc, ApplicationContext webApplicationContext, @@ -532,11 +423,11 @@ public static IdentityZoneCreationResult createOtherIdentityZoneAndReturnResult( IdentityZone identityZone, String zoneId) throws Exception { return createOtherIdentityZoneAndReturnResult(mockMvc, - webApplicationContext, - bootstrapClient, - identityZone, - true, - zoneId); + webApplicationContext, + bootstrapClient, + identityZone, + true, + zoneId); } public static IdentityZoneCreationResult createOtherIdentityZoneAndReturnResult(MockMvc mockMvc, @@ -546,15 +437,15 @@ public static IdentityZoneCreationResult createOtherIdentityZoneAndReturnResult( boolean useWebRequests, String zoneId) throws Exception { String identityToken = getClientCredentialsOAuthAccessToken(mockMvc, "identity", "identitysecret", - "zones.write,scim.zones", null); + "zones.write,scim.zones", null); if (useWebRequests) { mockMvc.perform(post("/identity-zones") - .header("Authorization", "Bearer " + identityToken) - .contentType(APPLICATION_JSON) - .accept(APPLICATION_JSON) - .content(JsonUtils.writeValueAsString(identityZone))) - .andExpect(status().isCreated()); + .header("Authorization", "Bearer " + identityToken) + .contentType(APPLICATION_JSON) + .accept(APPLICATION_JSON) + .content(JsonUtils.writeValueAsString(identityZone))) + .andExpect(status().isCreated()); } else { webApplicationContext.getBean(IdentityZoneProvisioning.class).create(identityZone); IdentityProvider defaultIdp = new IdentityProvider(); @@ -577,32 +468,32 @@ public static IdentityZoneCreationResult createOtherIdentityZoneAndReturnResult( group.setMembers(Collections.singletonList(new ScimGroupMember(marissa.getId()))); if (useWebRequests) { mockMvc.perform(post("/Groups/zones") - .header("Authorization", "Bearer " + identityToken) - .contentType(APPLICATION_JSON) - .accept(APPLICATION_JSON) - .content(JsonUtils.writeValueAsString(group))) - .andExpect(status().isCreated()); + .header("Authorization", "Bearer " + identityToken) + .contentType(APPLICATION_JSON) + .accept(APPLICATION_JSON) + .content(JsonUtils.writeValueAsString(group))) + .andExpect(status().isCreated()); } else { webApplicationContext.getBean(ScimGroupEndpoints.class).addZoneManagers(group, Mockito.mock(HttpServletResponse.class)); } // use that user to create an admin client in the new zone String zoneAdminAuthcodeToken = getUserOAuthAccessTokenAuthCode(mockMvc, "identity", "identitysecret", - marissa.getId(), "marissa", "koala", zoneAdminScope, zoneId); + marissa.getId(), "marissa", "koala", zoneAdminScope, zoneId); if (bootstrapClient != null) { if (useWebRequests) { mockMvc.perform(post("/oauth/clients") - .header("Authorization", "Bearer " + zoneAdminAuthcodeToken) - .header("X-Identity-Zone-Id", identityZone.getId()) - .contentType(APPLICATION_JSON) - .accept(APPLICATION_JSON) - .content(JsonUtils.writeValueAsString(bootstrapClient))) - .andExpect(status().isCreated()); + .header("Authorization", "Bearer " + zoneAdminAuthcodeToken) + .header("X-Identity-Zone-Id", identityZone.getId()) + .contentType(APPLICATION_JSON) + .accept(APPLICATION_JSON) + .content(JsonUtils.writeValueAsString(bootstrapClient))) + .andExpect(status().isCreated()); } else { webApplicationContext.getBean(MultitenantJdbcClientDetailsService.class).addClientDetails( - bootstrapClient, - identityZone.getId() + bootstrapClient, + identityZone.getId() ); } } @@ -623,7 +514,7 @@ public static IdentityZoneCreationResult createOtherIdentityZoneAndReturnResult( public static IdentityZoneCreationResult createOtherIdentityZoneAndReturnResult(String subdomain, MockMvc mockMvc, ApplicationContext webApplicationContext, - ClientDetails bootstrapClient, + ClientDetails bootstrapClient, String zoneId) throws Exception { return createOtherIdentityZoneAndReturnResult(subdomain, mockMvc, webApplicationContext, bootstrapClient, true, zoneId); @@ -632,14 +523,14 @@ public static IdentityZoneCreationResult createOtherIdentityZoneAndReturnResult( public static IdentityZone createOtherIdentityZone(String subdomain, MockMvc mockMvc, ApplicationContext webApplicationContext, - ClientDetails bootstrapClient, String zoneId) throws Exception { + ClientDetails bootstrapClient, String zoneId) throws Exception { return createOtherIdentityZone(subdomain, mockMvc, webApplicationContext, bootstrapClient, true, zoneId); } public static IdentityZone createOtherIdentityZone(String subdomain, MockMvc mockMvc, ApplicationContext webApplicationContext, - ClientDetails bootstrapClient, + ClientDetails bootstrapClient, boolean useWebRequests, String zoneId) throws Exception { return createOtherIdentityZoneAndReturnResult(subdomain, mockMvc, webApplicationContext, bootstrapClient, useWebRequests, zoneId).getIdentityZone(); @@ -658,7 +549,7 @@ public static IdentityZone createOtherIdentityZone(String subdomain, String zoneId) throws Exception { UaaClientDetails client = new UaaClientDetails("admin", null, null, "client_credentials", - "clients.admin,scim.read,scim.write,idps.write,uaa.admin", "http://redirect.url"); + "clients.admin,scim.read,scim.write,idps.write,uaa.admin", "http://redirect.url"); client.setClientSecret("admin-secret"); return createOtherIdentityZone(subdomain, mockMvc, webApplicationContext, client, useWebRequests, zoneId); @@ -670,13 +561,13 @@ public static IdentityZone updateIdentityZone(IdentityZone zone, ApplicationCont public static void deleteIdentityZone(String zoneId, MockMvc mockMvc) throws Exception { String identityToken = getClientCredentialsOAuthAccessToken(mockMvc, "identity", "identitysecret", - "zones.write,scim.zones", null); + "zones.write,scim.zones", null); mockMvc.perform(delete("/identity-zones/" + zoneId) - .header("Authorization", "Bearer " + identityToken) - .contentType(APPLICATION_JSON) - .accept(APPLICATION_JSON)) - .andExpect(status().isOk()); + .header("Authorization", "Bearer " + identityToken) + .contentType(APPLICATION_JSON) + .accept(APPLICATION_JSON)) + .andExpect(status().isOk()); } public static IdentityProvider createIdpUsingWebRequest(MockMvc mockMvc, String zoneId, String token, @@ -687,24 +578,24 @@ public static IdentityProvider createIdpUsingWebRequest(MockMvc mockMvc, String public static IdentityProvider createIdpUsingWebRequest(MockMvc mockMvc, String zoneId, String token, IdentityProvider identityProvider, ResultMatcher resultMatcher, boolean update) throws Exception { MockHttpServletRequestBuilder requestBuilder = - update ? - put("/identity-providers/" + identityProvider.getId()) - .header("Authorization", "Bearer " + token) - .contentType(APPLICATION_JSON) - .content(JsonUtils.writeValueAsString(identityProvider)) - : - post("/identity-providers/") - .header("Authorization", "Bearer " + token) - .contentType(APPLICATION_JSON) - .content(JsonUtils.writeValueAsString(identityProvider)); + update ? + put("/identity-providers/" + identityProvider.getId()) + .header("Authorization", "Bearer " + token) + .contentType(APPLICATION_JSON) + .content(JsonUtils.writeValueAsString(identityProvider)) + : + post("/identity-providers/") + .header("Authorization", "Bearer " + token) + .contentType(APPLICATION_JSON) + .content(JsonUtils.writeValueAsString(identityProvider)); if (zoneId != null) { requestBuilder.header(IdentityZoneSwitchingFilter.HEADER, zoneId); } MvcResult result = mockMvc.perform(requestBuilder) - .andExpect(resultMatcher) - .andReturn(); + .andExpect(resultMatcher) + .andReturn(); if (hasText(result.getResponse().getContentAsString())) { try { return JsonUtils.readValue(result.getResponse().getContentAsString(), IdentityProvider.class); @@ -728,14 +619,14 @@ public static ScimUser createUserInZone(MockMvc mockMvc, String accessToken, Sci String requestDomain = subdomain.equals("") ? "localhost" : subdomain + ".localhost"; MockHttpServletRequestBuilder post = post("/Users"); post.header("Authorization", "Bearer " + accessToken) - .with(new SetServerNameRequestPostProcessor(requestDomain)) - .contentType(APPLICATION_JSON) - .content(JsonUtils.writeValueAsBytes(user)); + .with(new SetServerNameRequestPostProcessor(requestDomain)) + .contentType(APPLICATION_JSON) + .content(JsonUtils.writeValueAsBytes(user)); if (hasText(zoneId)) { post.header(IdentityZoneSwitchingFilter.HEADER, zoneId); } MvcResult userResult = mockMvc.perform(post) - .andExpect(status().isCreated()).andReturn(); + .andExpect(status().isCreated()).andReturn(); return JsonUtils.readValue(userResult.getResponse().getContentAsString(), ScimUser.class); } @@ -743,13 +634,13 @@ public static ScimUser readUserInZone(MockMvc mockMvc, String accessToken, Strin String requestDomain = subdomain.equals("") ? "localhost" : subdomain + ".localhost"; MockHttpServletRequestBuilder get = get("/Users/" + userId); get.header("Authorization", "Bearer " + accessToken) - .with(new SetServerNameRequestPostProcessor(requestDomain)) - .accept(APPLICATION_JSON); + .with(new SetServerNameRequestPostProcessor(requestDomain)) + .accept(APPLICATION_JSON); if (hasText(zoneId)) { get.header(IdentityZoneSwitchingFilter.HEADER, zoneId); } MvcResult userResult = mockMvc.perform(get) - .andExpect(status().isOk()).andReturn(); + .andExpect(status().isOk()).andReturn(); return JsonUtils.readValue(userResult.getResponse().getContentAsString(), ScimUser.class); } @@ -790,13 +681,13 @@ public static ScimGroup getGroup(MockMvc mockMvc, String accessToken, String dis builder.header("Host", subdomain + ".localhost"); } SearchResults results = JsonUtils.readValue( - mockMvc.perform(builder - .header("Authorization", "Bearer " + accessToken) - .contentType(APPLICATION_JSON) - .param("filter", filter)) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); + mockMvc.perform(builder + .header("Authorization", "Bearer " + accessToken) + .contentType(APPLICATION_JSON) + .param("filter", filter)) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); if (results == null || results.getResources() == null || results.getResources().isEmpty()) { return null; } else { @@ -828,33 +719,32 @@ public static ScimGroup createGroup(MockMvc mockMvc, String accessToken, ScimGro public static ScimGroup createGroup(MockMvc mockMvc, String accessToken, String subdomain, ScimGroup group) throws Exception { MockHttpServletRequestBuilder post = post("/Groups") - .header("Authorization", "Bearer " + accessToken) - .contentType(APPLICATION_JSON) - .content(JsonUtils.writeValueAsString(group)); + .header("Authorization", "Bearer " + accessToken) + .contentType(APPLICATION_JSON) + .content(JsonUtils.writeValueAsString(group)); if (hasText(subdomain)) { post.header("Host", subdomain + ".localhost"); } return JsonUtils.readValue( - mockMvc.perform(post) - .andExpect(status().isCreated()) - .andReturn().getResponse().getContentAsString(), - ScimGroup.class); + mockMvc.perform(post) + .andExpect(status().isCreated()) + .andReturn().getResponse().getContentAsString(), + ScimGroup.class); } - public static ScimGroup createGroup(MockMvc mockMvc, String accessToken, ScimGroup group, String zoneId) throws Exception { MockHttpServletRequestBuilder post = post("/Groups") - .header("Authorization", "Bearer " + accessToken) - .contentType(APPLICATION_JSON) - .content(JsonUtils.writeValueAsString(group)); + .header("Authorization", "Bearer " + accessToken) + .contentType(APPLICATION_JSON) + .content(JsonUtils.writeValueAsString(group)); if (hasText(zoneId)) { post.header(IdentityZoneSwitchingFilter.HEADER, zoneId); } return JsonUtils.readValue( - mockMvc.perform(post) - .andExpect(status().isCreated()) - .andReturn().getResponse().getContentAsString(), - ScimGroup.class); + mockMvc.perform(post) + .andExpect(status().isCreated()) + .andReturn().getResponse().getContentAsString(), + ScimGroup.class); } public static ScimGroup updateGroup(MockMvc mockMvc, String accessToken, ScimGroup group) throws Exception { @@ -867,13 +757,13 @@ public static ScimGroup updateGroup(MockMvc mockMvc, String accessToken, ScimGro put.header("Host", zone.getSubdomain() + ".localhost"); } return JsonUtils.readValue( - mockMvc.perform(put.header("If-Match", group.getVersion()) - .header("Authorization", "Bearer " + accessToken) - .contentType(APPLICATION_JSON) - .content(JsonUtils.writeValueAsString(group))) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - ScimGroup.class); + mockMvc.perform(put.header("If-Match", group.getVersion()) + .header("Authorization", "Bearer " + accessToken) + .contentType(APPLICATION_JSON) + .content(JsonUtils.writeValueAsString(group))) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + ScimGroup.class); } public static UaaClientDetails createClient(MockMvc mockMvc, String accessToken, UaaClientDetails clientDetails) throws Exception { @@ -886,30 +776,30 @@ public static UaaClientDetails createClient(MockMvc mockMvc, IdentityZone identi public static void deleteClient(MockMvc mockMvc, String accessToken, String clientId, String zoneSubdomain) throws Exception { MockHttpServletRequestBuilder createClientDelete = delete("/oauth/clients/" + clientId) - .header("Authorization", "Bearer " + accessToken) - .accept(APPLICATION_JSON); + .header("Authorization", "Bearer " + accessToken) + .accept(APPLICATION_JSON); if (!zoneSubdomain.equals(IdentityZone.getUaa())) { createClientDelete = createClientDelete.header(IdentityZoneSwitchingFilter.SUBDOMAIN_HEADER, zoneSubdomain); } mockMvc.perform(createClientDelete) - .andExpect(status().is(not(500))); + .andExpect(status().is(not(500))); } public static UaaClientDetails createClient(MockMvc mockMvc, String accessToken, UaaClientDetails clientDetails, - IdentityZone zone, ResultMatcher status) - throws Exception { + IdentityZone zone, ResultMatcher status) + throws Exception { MockHttpServletRequestBuilder createClientPost = post("/oauth/clients") - .header("Authorization", "Bearer " + accessToken) - .accept(APPLICATION_JSON) - .contentType(APPLICATION_JSON) - .content(JsonUtils.writeValueAsString(clientDetails)); + .header("Authorization", "Bearer " + accessToken) + .accept(APPLICATION_JSON) + .contentType(APPLICATION_JSON) + .content(JsonUtils.writeValueAsString(clientDetails)); if (!zone.isUaa()) { createClientPost = createClientPost.header(IdentityZoneSwitchingFilter.HEADER, zone.getId()); } return JsonUtils.readValue( - mockMvc.perform(createClientPost) - .andExpect(status) - .andReturn().getResponse().getContentAsString(), UaaClientDetails.class); + mockMvc.perform(createClientPost) + .andExpect(status) + .andReturn().getResponse().getContentAsString(), UaaClientDetails.class); } public static UaaClientDetails createClient(ApplicationContext context, UaaClientDetails clientDetails, IdentityZone zone) { @@ -925,14 +815,14 @@ public static UaaClientDetails createClient(ApplicationContext context, UaaClien public static ClientDetails createClient(MockMvc mockMvc, String adminAccessToken, String id, String secret, Collection resourceIds, List scopes, List grantTypes, String authorities) throws Exception { return createClient(mockMvc, adminAccessToken, - id, - secret, - resourceIds, - scopes, - grantTypes, - authorities, - Collections.singleton("http://redirect.url"), - IdentityZone.getUaa()); + id, + secret, + resourceIds, + scopes, + grantTypes, + authorities, + Collections.singleton("http://redirect.url"), + IdentityZone.getUaa()); } public static ClientDetails createClient(MockMvc mockMvc, String adminAccessToken, String id, String secret, Collection resourceIds, Collection scopes, Collection grantTypes, String authorities, Set redirectUris, IdentityZone zone) throws Exception { @@ -959,38 +849,38 @@ public static UaaClientDetails updateClient(ApplicationContext context, UaaClien } public static UaaClientDetails updateClient(MockMvc mockMvc, String accessToken, UaaClientDetails clientDetails, IdentityZone zone) - throws Exception { + throws Exception { MockHttpServletRequestBuilder updateClientPut = - put("/oauth/clients/" + clientDetails.getClientId()) - .header("Authorization", "Bearer " + accessToken) - .accept(APPLICATION_JSON) - .contentType(APPLICATION_JSON) - .content(JsonUtils.writeValueAsString(clientDetails)); + put("/oauth/clients/" + clientDetails.getClientId()) + .header("Authorization", "Bearer " + accessToken) + .accept(APPLICATION_JSON) + .contentType(APPLICATION_JSON) + .content(JsonUtils.writeValueAsString(clientDetails)); if (!zone.isUaa()) { updateClientPut = updateClientPut.header(IdentityZoneSwitchingFilter.HEADER, zone.getId()); } return JsonUtils.readValue( - mockMvc.perform(updateClientPut) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), UaaClientDetails.class); + mockMvc.perform(updateClientPut) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), UaaClientDetails.class); } public static UaaClientDetails getClient(MockMvc mockMvc, String accessToken, String clientId, IdentityZone zone) - throws Exception { + throws Exception { MockHttpServletRequestBuilder readClientGet = - get("/oauth/clients/" + clientId) - .header("Authorization", "Bearer " + accessToken) - .accept(APPLICATION_JSON) - .contentType(APPLICATION_JSON); + get("/oauth/clients/" + clientId) + .header("Authorization", "Bearer " + accessToken) + .accept(APPLICATION_JSON) + .contentType(APPLICATION_JSON); if (!zone.isUaa()) { readClientGet = readClientGet.header(IdentityZoneSwitchingFilter.HEADER, zone.getId()); } return JsonUtils.readValue( - mockMvc.perform(readClientGet) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), UaaClientDetails.class); + mockMvc.perform(readClientGet) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), UaaClientDetails.class); } public static String getZoneAdminToken(MockMvc mockMvc, String adminToken, String zoneId) throws Exception { @@ -1008,13 +898,13 @@ public static String getZoneAdminToken(MockMvc mockMvc, String adminToken, Strin group.setMembers(Collections.singletonList(new ScimGroupMember(user.getId()))); MockMvcUtils.createGroup(mockMvc, adminToken, group); return getUserOAuthAccessTokenAuthCode(mockMvc, - "identity", - "identitysecret", - user.getId(), - user.getUserName(), - "secr3T", - group.getDisplayName(), - zoneId + "identity", + "identitysecret", + user.getId(), + user.getUserName(), + "secr3T", + group.getDisplayName(), + zoneId ); } @@ -1036,13 +926,13 @@ public static String getUserOAuthAccessToken(MockMvc mockMvc, String scope, IdentityZone zone) throws Exception { return getUserOAuthAccessToken(mockMvc, - clientId, - clientSecret, - username, - password, - scope, - zone, - false); + clientId, + clientSecret, + username, + password, + scope, + zone, + false); } public static String getUserOAuthAccessToken(MockMvc mockMvc, @@ -1054,15 +944,15 @@ public static String getUserOAuthAccessToken(MockMvc mockMvc, IdentityZone zone, boolean opaque) throws Exception { String basicDigestHeaderValue = "Basic " - + new String(Base64.encodeBase64((clientId + ":" + clientSecret).getBytes())); + + new String(Base64.encodeBase64((clientId + ":" + clientSecret).getBytes())); MockHttpServletRequestBuilder oauthTokenPost = - post("/oauth/token") - .header("Authorization", basicDigestHeaderValue) - .param("grant_type", "password") - .param("client_id", clientId) - .param("username", username) - .param("password", password) - .param("scope", scope); + post("/oauth/token") + .header("Authorization", basicDigestHeaderValue) + .param("grant_type", "password") + .param("client_id", clientId) + .param("username", username) + .param("password", password) + .param("scope", scope); if (zone != null) { oauthTokenPost.header("Host", zone.getSubdomain() + ".localhost"); } @@ -1072,7 +962,7 @@ public static String getUserOAuthAccessToken(MockMvc mockMvc, MvcResult result = mockMvc.perform(oauthTokenPost).andDo(print()).andExpect(status().isOk()).andReturn(); OAuthToken oauthToken = JsonUtils.readValue(result.getResponse().getContentAsString(), - OAuthToken.class); + OAuthToken.class); return oauthToken.accessToken; } @@ -1099,8 +989,8 @@ public static String getUserOAuthAccessTokenAuthCode(MockMvc mockMvc, String cli public static String getUserOAuthAccessTokenAuthCode(MockMvc mockMvc, String clientId, String clientSecret, String userId, String username, String password, String scope, String zoneId, TokenFormat tokenFormat) throws Exception { String basicDigestHeaderValue = "Basic " - + new String(org.apache.commons.codec.binary.Base64.encodeBase64((clientId + ":" + clientSecret) - .getBytes())); + + new String(org.apache.commons.codec.binary.Base64.encodeBase64((clientId + ":" + clientSecret) + .getBytes())); UaaPrincipal p = new UaaPrincipal(userId, username, "test@test.org", OriginKeys.UAA, "", zoneId); UaaAuthentication auth = new UaaAuthentication(p, UaaAuthority.USER_AUTHORITIES, null); Assert.assertTrue(auth.isAuthenticated()); @@ -1108,21 +998,21 @@ public static String getUserOAuthAccessTokenAuthCode(MockMvc mockMvc, String cli SecurityContextHolder.getContext().setAuthentication(auth); MockHttpSession session = new MockHttpSession(); session.setAttribute( - HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, - new MockSecurityContext(auth) + HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, + new MockSecurityContext(auth) ); String state = new AlphanumericRandomValueStringGenerator().generate(); MockHttpServletRequestBuilder authRequest = get("/oauth/authorize") - .header("Authorization", basicDigestHeaderValue) - .header("Accept", MediaType.APPLICATION_JSON_VALUE) - .session(session) - .param(OAuth2Utils.GRANT_TYPE, GRANT_TYPE_AUTHORIZATION_CODE) - .param(OAuth2Utils.RESPONSE_TYPE, "code") - .param(TokenConstants.REQUEST_TOKEN_FORMAT, tokenFormat.getStringValue()) - .param(OAuth2Utils.STATE, state) - .param(OAuth2Utils.CLIENT_ID, clientId) - .param(OAuth2Utils.REDIRECT_URI, "http://localhost/test"); + .header("Authorization", basicDigestHeaderValue) + .header("Accept", MediaType.APPLICATION_JSON_VALUE) + .session(session) + .param(OAuth2Utils.GRANT_TYPE, GRANT_TYPE_AUTHORIZATION_CODE) + .param(OAuth2Utils.RESPONSE_TYPE, "code") + .param(TokenConstants.REQUEST_TOKEN_FORMAT, tokenFormat.getStringValue()) + .param(OAuth2Utils.STATE, state) + .param(OAuth2Utils.CLIENT_ID, clientId) + .param(OAuth2Utils.REDIRECT_URI, "http://localhost/test"); if (StringUtils.hasText(scope)) { authRequest.param(OAuth2Utils.SCOPE, scope); } @@ -1133,18 +1023,18 @@ public static String getUserOAuthAccessTokenAuthCode(MockMvc mockMvc, String cli String code = builder.build().getQueryParams().get("code").get(0); authRequest = post("/oauth/token") - .header("Authorization", basicDigestHeaderValue) - .header("Accept", MediaType.APPLICATION_JSON_VALUE) - .param(OAuth2Utils.GRANT_TYPE, GRANT_TYPE_AUTHORIZATION_CODE) - .param("code", code) - .param(OAuth2Utils.CLIENT_ID, clientId) - .param(OAuth2Utils.REDIRECT_URI, "http://localhost/test"); + .header("Authorization", basicDigestHeaderValue) + .header("Accept", MediaType.APPLICATION_JSON_VALUE) + .param(OAuth2Utils.GRANT_TYPE, GRANT_TYPE_AUTHORIZATION_CODE) + .param("code", code) + .param(OAuth2Utils.CLIENT_ID, clientId) + .param(OAuth2Utils.REDIRECT_URI, "http://localhost/test"); if (StringUtils.hasText(scope)) { authRequest.param(OAuth2Utils.SCOPE, scope); } result = mockMvc.perform(authRequest).andExpect(status().is2xxSuccessful()).andReturn(); OAuthToken oauthToken = JsonUtils.readValue(result.getResponse().getContentAsString(), - OAuthToken.class); + OAuthToken.class); return oauthToken.accessToken; } @@ -1153,8 +1043,8 @@ public static String getScimInviteUserToken(MockMvc mockMvc, String clientId, St String adminToken = getClientCredentialsOAuthAccessToken(mockMvc, adminClientId, adminClientSecret, - "", - zone == null ? null : zone.getSubdomain() + "", + zone == null ? null : zone.getSubdomain() ); // create a user (with the required permissions) to perform the actual /invite_users action String username = new AlphanumericRandomValueStringGenerator().generate().toLowerCase() + "@example.com"; @@ -1171,9 +1061,9 @@ public static String getScimInviteUserToken(MockMvc mockMvc, String clientId, St createGroup(mockMvc, adminToken, zone.getSubdomain(), inviteGroup); } ScimGroup group = getGroup(mockMvc, - adminToken, - scope, - zone == null ? null : zone.getSubdomain() + adminToken, + scope, + zone == null ? null : zone.getSubdomain() ); group.getMembers().add(member); updateGroup(mockMvc, adminToken, group, zone); @@ -1181,16 +1071,15 @@ public static String getScimInviteUserToken(MockMvc mockMvc, String clientId, St // get a bearer token for the user return getUserOAuthAccessToken(mockMvc, - clientId, - clientSecret, - user.getUserName(), - "password", - "scim.invite", - zone + clientId, + clientSecret, + user.getUserName(), + "password", + "scim.invite", + zone ); } - public static String getClientCredentialsOAuthAccessToken(MockMvc mockMvc, String clientId, String clientSecret, @@ -1207,9 +1096,9 @@ public static String getClientCredentialsOAuthAccessToken(MockMvc mockMvc, boolean opaque) throws Exception { MockHttpServletRequestBuilder oauthTokenPost = post("/oauth/token") .with(httpBasic(clientId, clientSecret)) - .param("grant_type", "client_credentials") - .param("client_id", clientId) - .param("recovable", "true"); + .param("grant_type", "client_credentials") + .param("client_id", clientId) + .param("revocable", "true"); if (!isEmpty(scope)) { oauthTokenPost.param("scope", scope); } @@ -1220,9 +1109,9 @@ public static String getClientCredentialsOAuthAccessToken(MockMvc mockMvc, oauthTokenPost.param(TokenConstants.REQUEST_TOKEN_FORMAT, OPAQUE.getStringValue()); } MvcResult result = mockMvc.perform(oauthTokenPost) - .andDo(print()) - .andExpect(status().isOk()) - .andReturn(); + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); OAuthToken oauthToken = JsonUtils.readValue(result.getResponse().getContentAsString(), OAuthToken.class); return oauthToken.accessToken; } @@ -1265,6 +1154,133 @@ public static void removeEventListener(ListableBeanFactory applicationContext, A } } + public static RequestPostProcessor httpBearer(String authorization) { + return new HttpBearerAuthRequestPostProcessor(authorization); + } + + public static IdentityZone updateZone(MockMvc mockMvc, IdentityZone updatedZone) throws Exception { + String token = + getClientCredentialsOAuthAccessToken(mockMvc, "admin", "adminsecret", "uaa.admin", null); + + String responseAsString = + mockMvc.perform(put("/identity-zones/" + updatedZone.getId()) + .header("Authorization", "Bearer " + token) + .contentType(APPLICATION_JSON) + .accept(APPLICATION_JSON) + .content(JsonUtils.writeValueAsString(updatedZone))) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + return JsonUtils.readValue(responseAsString, IdentityZone.class); + } + + public static class MockSavedRequest extends DefaultSavedRequest { + + public MockSavedRequest() { + super(new MockHttpServletRequest(), new PortResolverImpl()); + } + + @Override + public String getRedirectUrl() { + return "http://test/redirect/oauth/authorize"; + } + + @Override + public String[] getParameterValues(String name) { + if ("client_id".equals(name)) { + return new String[]{"admin"}; + } + return new String[0]; + } + + @Override + public List getCookies() { + return null; + } + + @Override + public String getMethod() { + return null; + } + + @Override + public List getHeaderValues(String name) { + return null; + } + + @Override + public Collection getHeaderNames() { + return null; + } + + @Override + public List getLocales() { + return null; + } + + @Override + public Map getParameterMap() { + return null; + } + + } + + public static class ZoneScimInviteData { + private final IdentityZoneCreationResult zone; + private final String adminToken; + private final ClientDetails scimInviteClient; + private final String defaultZoneAdminToken; + + public ZoneScimInviteData(String adminToken, + IdentityZoneCreationResult zone, + ClientDetails scimInviteClient, + String defaultZoneAdminToken) { + this.adminToken = adminToken; + this.zone = zone; + this.scimInviteClient = scimInviteClient; + this.defaultZoneAdminToken = defaultZoneAdminToken; + } + + public ClientDetails getScimInviteClient() { + return scimInviteClient; + } + + public String getDefaultZoneAdminToken() { + return defaultZoneAdminToken; + } + + public IdentityZoneCreationResult getZone() { + return zone; + } + + public String getAdminToken() { + return adminToken; + } + } + + public static class IdentityZoneCreationResult { + private final IdentityZone identityZone; + private final UaaPrincipal zoneAdmin; + private final String zoneAdminToken; + + public IdentityZoneCreationResult(IdentityZone identityZone, UaaPrincipal zoneAdmin, String zoneAdminToken) { + this.identityZone = identityZone; + this.zoneAdmin = zoneAdmin; + this.zoneAdminToken = zoneAdminToken; + } + + public IdentityZone getIdentityZone() { + return identityZone; + } + + public UaaPrincipal getZoneAdminUser() { + return zoneAdmin; + } + + public String getZoneAdminToken() { + return zoneAdminToken; + } + } + public static class MockSecurityContext implements SecurityContext { private static final long serialVersionUID = -1386535243513362694L; @@ -1290,6 +1306,10 @@ public static class CookieCsrfPostProcessor implements RequestPostProcessor { private boolean useInvalidToken = false; + public static CookieCsrfPostProcessor cookieCsrf() { + return new CookieCsrfPostProcessor(); + } + public CookieCsrfPostProcessor useInvalidToken() { useInvalidToken = true; return this; @@ -1329,18 +1349,10 @@ protected void addCsrfCookie(MockHttpServletRequest request, Cookie cookie, Cook request.setCookies(newcookies); } } - - public static CookieCsrfPostProcessor cookieCsrf() { - return new CookieCsrfPostProcessor(); - } - } - - public static RequestPostProcessor httpBearer(String authorization) { - return new HttpBearerAuthRequestPostProcessor(authorization); } private static class HttpBearerAuthRequestPostProcessor implements RequestPostProcessor { - private String headerValue; + private final String headerValue; private HttpBearerAuthRequestPostProcessor(String authorization) { this.headerValue = "Bearer " + authorization; @@ -1361,19 +1373,4 @@ public String generate() { return "test" + counter.incrementAndGet(); } } - - public static IdentityZone updateZone(MockMvc mockMvc, IdentityZone updatedZone) throws Exception { - String token = - getClientCredentialsOAuthAccessToken(mockMvc, "admin", "adminsecret", "uaa.admin", null); - - String responseAsString = - mockMvc.perform(put("/identity-zones/" + updatedZone.getId()) - .header("Authorization", "Bearer " + token) - .contentType(APPLICATION_JSON) - .accept(APPLICATION_JSON) - .content(JsonUtils.writeValueAsString(updatedZone))) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(); - return JsonUtils.readValue(responseAsString, IdentityZone.class); - } }