浏览代码

Allow RelyingPartyRegistration Placeholder Resolution in XML

Closes gh-14645
Josh Cummings 10 月之前
父节点
当前提交
27294b2e11

+ 17 - 11
config/src/main/java/org/springframework/security/config/saml2/RelyingPartyRegistrationsBeanDefinitionParser.java

@@ -213,8 +213,10 @@ public final class RelyingPartyRegistrationsBeanDefinitionParser implements Bean
 	private static RelyingPartyRegistration.Builder getBuilderFromMetadataLocationIfPossible(
 			Element relyingPartyRegistrationElt, Map<String, Map<String, Object>> assertingParties,
 			ParserContext parserContext) {
-		String registrationId = relyingPartyRegistrationElt.getAttribute(ATT_REGISTRATION_ID);
-		String metadataLocation = relyingPartyRegistrationElt.getAttribute(ATT_METADATA_LOCATION);
+		String registrationId = resolveAttribute(parserContext,
+				relyingPartyRegistrationElt.getAttribute(ATT_REGISTRATION_ID));
+		String metadataLocation = resolveAttribute(parserContext,
+				relyingPartyRegistrationElt.getAttribute(ATT_METADATA_LOCATION));
 		RelyingPartyRegistration.Builder builder;
 		if (StringUtils.hasText(metadataLocation)) {
 			builder = RelyingPartyRegistrations.fromMetadataLocation(metadataLocation).registrationId(registrationId);
@@ -224,20 +226,20 @@ public final class RelyingPartyRegistrationsBeanDefinitionParser implements Bean
 				.assertingPartyMetadata((apBuilder) -> buildAssertingParty(relyingPartyRegistrationElt,
 						assertingParties, apBuilder, parserContext));
 		}
-		addRemainingProperties(relyingPartyRegistrationElt, builder);
+		addRemainingProperties(parserContext, relyingPartyRegistrationElt, builder);
 		return builder;
 	}
 
-	private static void addRemainingProperties(Element relyingPartyRegistrationElt,
+	private static void addRemainingProperties(ParserContext pc, Element relyingPartyRegistrationElt,
 			RelyingPartyRegistration.Builder builder) {
-		String entityId = relyingPartyRegistrationElt.getAttribute(ATT_ENTITY_ID);
-		String singleLogoutServiceLocation = relyingPartyRegistrationElt
-			.getAttribute(ATT_SINGLE_LOGOUT_SERVICE_LOCATION);
-		String singleLogoutServiceResponseLocation = relyingPartyRegistrationElt
-			.getAttribute(ATT_SINGLE_LOGOUT_SERVICE_RESPONSE_LOCATION);
+		String entityId = resolveAttribute(pc, relyingPartyRegistrationElt.getAttribute(ATT_ENTITY_ID));
+		String singleLogoutServiceLocation = resolveAttribute(pc,
+				relyingPartyRegistrationElt.getAttribute(ATT_SINGLE_LOGOUT_SERVICE_LOCATION));
+		String singleLogoutServiceResponseLocation = resolveAttribute(pc,
+				relyingPartyRegistrationElt.getAttribute(ATT_SINGLE_LOGOUT_SERVICE_RESPONSE_LOCATION));
 		Saml2MessageBinding singleLogoutServiceBinding = getSingleLogoutServiceBinding(relyingPartyRegistrationElt);
-		String assertionConsumerServiceLocation = relyingPartyRegistrationElt
-			.getAttribute(ATT_ASSERTION_CONSUMER_SERVICE_LOCATION);
+		String assertionConsumerServiceLocation = resolveAttribute(pc,
+				relyingPartyRegistrationElt.getAttribute(ATT_ASSERTION_CONSUMER_SERVICE_LOCATION));
 		Saml2MessageBinding assertionConsumerServiceBinding = getAssertionConsumerServiceBinding(
 				relyingPartyRegistrationElt);
 		if (StringUtils.hasText(entityId)) {
@@ -400,4 +402,8 @@ public final class RelyingPartyRegistrationsBeanDefinitionParser implements Bean
 		}
 	}
 
+	private static String resolveAttribute(ParserContext pc, String value) {
+		return pc.getReaderContext().getEnvironment().resolvePlaceholders(value);
+	}
+
 }

+ 27 - 0
config/src/test/java/org/springframework/security/config/saml2/RelyingPartyRegistrationsBeanDefinitionParserTests.java

@@ -35,6 +35,7 @@ import org.springframework.security.saml2.provider.service.registration.InMemory
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
 import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
+import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;
 import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -288,6 +289,32 @@ public class RelyingPartyRegistrationsBeanDefinitionParserTests {
 		verify(relayStateResolver).convert(request);
 	}
 
+	@Test
+	public void parseWhenPlaceholdersThenResolves() throws Exception {
+		RelyingPartyRegistration sample = TestRelyingPartyRegistrations.relyingPartyRegistration().build();
+		System.setProperty("registration-id", sample.getRegistrationId());
+		System.setProperty("entity-id", sample.getEntityId());
+		System.setProperty("acs-location", sample.getAssertionConsumerServiceLocation());
+		System.setProperty("slo-location", sample.getSingleLogoutServiceLocation());
+		System.setProperty("slo-response-location", sample.getSingleLogoutServiceResponseLocation());
+		try (MockWebServer web = new MockWebServer()) {
+			web.start();
+			String serverUrl = web.url("/metadata").toString();
+			web.enqueue(xmlResponse(METADATA_RESPONSE));
+			System.setProperty("metadata-location", serverUrl);
+			this.spring.configLocations(xml("PlaceholderRegistration")).autowire();
+		}
+		RelyingPartyRegistration registration = this.relyingPartyRegistrationRepository
+			.findByRegistrationId(sample.getRegistrationId());
+		assertThat(registration.getRegistrationId()).isEqualTo(sample.getRegistrationId());
+		assertThat(registration.getEntityId()).isEqualTo(sample.getEntityId());
+		assertThat(registration.getAssertionConsumerServiceLocation())
+			.isEqualTo(sample.getAssertionConsumerServiceLocation());
+		assertThat(registration.getSingleLogoutServiceLocation()).isEqualTo(sample.getSingleLogoutServiceLocation());
+		assertThat(registration.getSingleLogoutServiceResponseLocation())
+			.isEqualTo(sample.getSingleLogoutServiceResponseLocation());
+	}
+
 	private static MockResponse xmlResponse(String xml) {
 		return new MockResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE).setBody(xml);
 	}

+ 35 - 0
config/src/test/resources/org/springframework/security/config/saml2/RelyingPartyRegistrationsBeanDefinitionParserTests-PlaceholderRegistration.xml

@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xmlns="http://www.springframework.org/schema/security"
+         xsi:schemaLocation="
+			http://www.springframework.org/schema/security
+			https://www.springframework.org/schema/security/spring-security.xsd
+			http://www.springframework.org/schema/beans
+			https://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<relying-party-registrations>
+		<relying-party-registration registration-id="${registration-id}"
+	                                entity-id="${entity-id}"
+	                                metadata-location="${metadata-location}"
+	                                assertion-consumer-service-location="${acs-location}"
+	                                single-logout-service-location="${slo-location}"
+	                                single-logout-service-response-location="${slo-response-location}"/>
+	</relying-party-registrations>
+</b:beans>