mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-31 06:38:42 +00:00 
			
		
		
		
	Merge branch '6.0.x'
Closes gh-12800
This commit is contained in:
		
						commit
						41fadaecd3
					
				| @ -1,5 +1,36 @@ | |||||||
| [[servlet-saml2login-metadata]] | [[servlet-saml2login-metadata]] | ||||||
| = Producing `<saml2:SPSSODescriptor>` Metadata | = Saml 2.0 Metadata | ||||||
|  | 
 | ||||||
|  | Spring Security can <<parsing-asserting-party-metadata,parse asserting party metadata>> to produce an `AssertingPartyDetails` instance as well as <<publishing-relying-party-metadata,publish relying party metadata>> from a `RelyingPartyRegistration` instance. | ||||||
|  | 
 | ||||||
|  | [[parsing-asserting-party-metadata]] | ||||||
|  | == Parsing `<saml2:IDPSSODescriptor>` metadata | ||||||
|  | 
 | ||||||
|  | You can parse an asserting party's metadata xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistrationrepository[using `RelyingPartyRegistrations`]. | ||||||
|  | 
 | ||||||
|  | When using the OpenSAML vendor support, the resulting `AssertingPartyDetails` will be of type `OpenSamlAssertingPartyDetails`. | ||||||
|  | This means you'll be able to do get the underlying OpenSAML XMLObject by doing the following: | ||||||
|  | 
 | ||||||
|  | ==== | ||||||
|  | .Java | ||||||
|  | [source,java,role="primary"] | ||||||
|  | ---- | ||||||
|  | OpenSamlAssertingPartyDetails details = (OpenSamlAssertingPartyDetails) | ||||||
|  |         registration.getAssertingPartyDetails(); | ||||||
|  | EntityDescriptor openSamlEntityDescriptor = details.getEntityDescriptor(); | ||||||
|  | ---- | ||||||
|  | 
 | ||||||
|  | .Kotlin | ||||||
|  | [source,kotlin,role="secondary"] | ||||||
|  | ---- | ||||||
|  | val details: OpenSamlAssertingPartyDetails = | ||||||
|  |         registration.getAssertingPartyDetails() as OpenSamlAssertingPartyDetails; | ||||||
|  | val openSamlEntityDescriptor: EntityDescriptor = details.getEntityDescriptor(); | ||||||
|  | ---- | ||||||
|  | ==== | ||||||
|  | 
 | ||||||
|  | [[publishing-relying-party-metadata]] | ||||||
|  | == Producing `<saml2:SPSSODescriptor>` Metadata | ||||||
| 
 | 
 | ||||||
| You can publish a metadata endpoint by adding the `Saml2MetadataFilter` to the filter chain, as you'll see below: | You can publish a metadata endpoint by adding the `Saml2MetadataFilter` to the filter chain, as you'll see below: | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -0,0 +1,35 @@ | |||||||
|  | /* | ||||||
|  |  * 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.springframework.security.saml2.provider.service.registration; | ||||||
|  | 
 | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.Collection; | ||||||
|  | 
 | ||||||
|  | class OpenSamlMetadataRelyingPartyRegistrationConverter { | ||||||
|  | 
 | ||||||
|  | 	private final OpenSamlMetadataAssertingPartyDetailsConverter converter = new OpenSamlMetadataAssertingPartyDetailsConverter(); | ||||||
|  | 
 | ||||||
|  | 	Collection<RelyingPartyRegistration.Builder> convert(InputStream source) { | ||||||
|  | 		Collection<RelyingPartyRegistration.Builder> builders = new ArrayList<>(); | ||||||
|  | 		for (RelyingPartyRegistration.AssertingPartyDetails.Builder builder : this.converter.convert(source)) { | ||||||
|  | 			builders.add(new RelyingPartyRegistration.Builder(builder)); | ||||||
|  | 		} | ||||||
|  | 		return builders; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -1,5 +1,5 @@ | |||||||
| /* | /* | ||||||
|  * Copyright 2002-2022 the original author or authors. |  * Copyright 2002-2023 the original author or authors. | ||||||
|  * |  * | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  * you may not use this file except in compliance with the License. |  * you may not use this file except in compliance with the License. | ||||||
| @ -89,8 +89,7 @@ public class OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter | |||||||
| 	@Override | 	@Override | ||||||
| 	public RelyingPartyRegistration.Builder read(Class<? extends RelyingPartyRegistration.Builder> clazz, | 	public RelyingPartyRegistration.Builder read(Class<? extends RelyingPartyRegistration.Builder> clazz, | ||||||
| 			HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { | 			HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { | ||||||
| 		return RelyingPartyRegistration | 		return new RelyingPartyRegistration.Builder(this.converter.convert(inputMessage.getBody()).iterator().next()); | ||||||
| 				.withAssertingPartyDetails(this.converter.convert(inputMessage.getBody()).iterator().next().build()); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Override | 	@Override | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| /* | /* | ||||||
|  * Copyright 2002-2022 the original author or authors. |  * Copyright 2002-2023 the original author or authors. | ||||||
|  * |  * | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  * you may not use this file except in compliance with the License. |  * you may not use this file except in compliance with the License. | ||||||
| @ -26,6 +26,7 @@ import java.util.function.Consumer; | |||||||
| 
 | 
 | ||||||
| import org.opensaml.xmlsec.signature.support.SignatureConstants; | import org.opensaml.xmlsec.signature.support.SignatureConstants; | ||||||
| 
 | 
 | ||||||
|  | import org.springframework.core.convert.converter.Converter; | ||||||
| import org.springframework.security.saml2.core.Saml2X509Credential; | import org.springframework.security.saml2.core.Saml2X509Credential; | ||||||
| import org.springframework.util.Assert; | import org.springframework.util.Assert; | ||||||
| import org.springframework.util.CollectionUtils; | import org.springframework.util.CollectionUtils; | ||||||
| @ -737,7 +738,7 @@ public final class RelyingPartyRegistration { | |||||||
| 
 | 
 | ||||||
| 	public static final class Builder { | 	public static final class Builder { | ||||||
| 
 | 
 | ||||||
| 		private String registrationId; | 		private Converter<AssertingPartyDetails, String> registrationId = AssertingPartyDetails::getEntityId; | ||||||
| 
 | 
 | ||||||
| 		private String entityId = "{baseUrl}/saml2/service-provider-metadata/{registrationId}"; | 		private String entityId = "{baseUrl}/saml2/service-provider-metadata/{registrationId}"; | ||||||
| 
 | 
 | ||||||
| @ -757,10 +758,15 @@ public final class RelyingPartyRegistration { | |||||||
| 
 | 
 | ||||||
| 		private String nameIdFormat = null; | 		private String nameIdFormat = null; | ||||||
| 
 | 
 | ||||||
| 		private AssertingPartyDetails.Builder assertingPartyDetailsBuilder = new AssertingPartyDetails.Builder(); | 		private AssertingPartyDetails.Builder assertingPartyDetailsBuilder; | ||||||
| 
 | 
 | ||||||
| 		private Builder(String registrationId) { | 		private Builder(String registrationId) { | ||||||
| 			this.registrationId = registrationId; | 			this.registrationId = (party) -> registrationId; | ||||||
|  | 			this.assertingPartyDetailsBuilder = new AssertingPartyDetails.Builder(); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		Builder(AssertingPartyDetails.Builder builder) { | ||||||
|  | 			this.assertingPartyDetailsBuilder = builder; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		/** | 		/** | ||||||
| @ -769,7 +775,7 @@ public final class RelyingPartyRegistration { | |||||||
| 		 * @return this object | 		 * @return this object | ||||||
| 		 */ | 		 */ | ||||||
| 		public Builder registrationId(String id) { | 		public Builder registrationId(String id) { | ||||||
| 			this.registrationId = id; | 			this.registrationId = (party) -> id; | ||||||
| 			return this; | 			return this; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| @ -967,11 +973,12 @@ public final class RelyingPartyRegistration { | |||||||
| 				this.singleLogoutServiceBindings.add(Saml2MessageBinding.POST); | 				this.singleLogoutServiceBindings.add(Saml2MessageBinding.POST); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			return new RelyingPartyRegistration(this.registrationId, this.entityId, | 			AssertingPartyDetails party = this.assertingPartyDetailsBuilder.build(); | ||||||
| 					this.assertionConsumerServiceLocation, this.assertionConsumerServiceBinding, | 			String registrationId = this.registrationId.convert(party); | ||||||
| 					this.singleLogoutServiceLocation, this.singleLogoutServiceResponseLocation, | 			return new RelyingPartyRegistration(registrationId, this.entityId, this.assertionConsumerServiceLocation, | ||||||
| 					this.singleLogoutServiceBindings, this.assertingPartyDetailsBuilder.build(), this.nameIdFormat, | 					this.assertionConsumerServiceBinding, this.singleLogoutServiceLocation, | ||||||
| 					this.decryptionX509Credentials, this.signingX509Credentials); | 					this.singleLogoutServiceResponseLocation, this.singleLogoutServiceBindings, party, | ||||||
|  | 					this.nameIdFormat, this.decryptionX509Credentials, this.signingX509Credentials); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| /* | /* | ||||||
|  * Copyright 2002-2022 the original author or authors. |  * Copyright 2002-2023 the original author or authors. | ||||||
|  * |  * | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  * you may not use this file except in compliance with the License. |  * you may not use this file except in compliance with the License. | ||||||
| @ -18,13 +18,11 @@ package org.springframework.security.saml2.provider.service.registration; | |||||||
| 
 | 
 | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.io.InputStream; | import java.io.InputStream; | ||||||
| import java.util.ArrayList; |  | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| 
 | 
 | ||||||
| import org.springframework.core.io.DefaultResourceLoader; | import org.springframework.core.io.DefaultResourceLoader; | ||||||
| import org.springframework.core.io.ResourceLoader; | import org.springframework.core.io.ResourceLoader; | ||||||
| import org.springframework.security.saml2.Saml2Exception; | import org.springframework.security.saml2.Saml2Exception; | ||||||
| import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration.AssertingPartyDetails; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * A utility class for constructing instances of {@link RelyingPartyRegistration} |  * A utility class for constructing instances of {@link RelyingPartyRegistration} | ||||||
| @ -36,7 +34,7 @@ import org.springframework.security.saml2.provider.service.registration.RelyingP | |||||||
|  */ |  */ | ||||||
| public final class RelyingPartyRegistrations { | public final class RelyingPartyRegistrations { | ||||||
| 
 | 
 | ||||||
| 	private static final OpenSamlMetadataAssertingPartyDetailsConverter assertingPartyMetadataConverter = new OpenSamlMetadataAssertingPartyDetailsConverter(); | 	private static final OpenSamlMetadataRelyingPartyRegistrationConverter relyingPartyRegistrationConverter = new OpenSamlMetadataRelyingPartyRegistrationConverter(); | ||||||
| 
 | 
 | ||||||
| 	private static final ResourceLoader resourceLoader = new DefaultResourceLoader(); | 	private static final ResourceLoader resourceLoader = new DefaultResourceLoader(); | ||||||
| 
 | 
 | ||||||
| @ -215,11 +213,7 @@ public final class RelyingPartyRegistrations { | |||||||
| 	 * @since 5.7 | 	 * @since 5.7 | ||||||
| 	 */ | 	 */ | ||||||
| 	public static Collection<RelyingPartyRegistration.Builder> collectionFromMetadata(InputStream source) { | 	public static Collection<RelyingPartyRegistration.Builder> collectionFromMetadata(InputStream source) { | ||||||
| 		Collection<RelyingPartyRegistration.Builder> builders = new ArrayList<>(); | 		return relyingPartyRegistrationConverter.convert(source); | ||||||
| 		for (AssertingPartyDetails.Builder builder : assertingPartyMetadataConverter.convert(source)) { |  | ||||||
| 			builders.add(RelyingPartyRegistration.withAssertingPartyDetails(builder.build())); |  | ||||||
| 		} |  | ||||||
| 		return builders; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -0,0 +1,57 @@ | |||||||
|  | /* | ||||||
|  |  * 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.springframework.security.saml2.provider.service.registration; | ||||||
|  | 
 | ||||||
|  | import java.io.BufferedReader; | ||||||
|  | import java.io.ByteArrayInputStream; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.io.InputStreamReader; | ||||||
|  | import java.nio.charset.StandardCharsets; | ||||||
|  | import java.util.stream.Collectors; | ||||||
|  | 
 | ||||||
|  | import org.junit.jupiter.api.BeforeEach; | ||||||
|  | import org.junit.jupiter.api.Test; | ||||||
|  | 
 | ||||||
|  | import org.springframework.core.io.ClassPathResource; | ||||||
|  | 
 | ||||||
|  | import static org.assertj.core.api.Assertions.assertThat; | ||||||
|  | 
 | ||||||
|  | public class OpenSamlMetadataRelyingPartyRegistrationConverterTests { | ||||||
|  | 
 | ||||||
|  | 	private OpenSamlMetadataRelyingPartyRegistrationConverter converter = new OpenSamlMetadataRelyingPartyRegistrationConverter(); | ||||||
|  | 
 | ||||||
|  | 	private String metadata; | ||||||
|  | 
 | ||||||
|  | 	@BeforeEach | ||||||
|  | 	public void setup() throws Exception { | ||||||
|  | 		ClassPathResource resource = new ClassPathResource("test-metadata.xml"); | ||||||
|  | 		try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream()))) { | ||||||
|  | 			this.metadata = reader.lines().collect(Collectors.joining()); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// gh-12667 | ||||||
|  | 	@Test | ||||||
|  | 	public void convertWhenDefaultsThenAssertingPartyInstanceOfOpenSaml() throws Exception { | ||||||
|  | 		try (InputStream source = new ByteArrayInputStream(this.metadata.getBytes(StandardCharsets.UTF_8))) { | ||||||
|  | 			this.converter.convert(source) | ||||||
|  | 					.forEach((registration) -> assertThat(registration.build().getAssertingPartyDetails()) | ||||||
|  | 							.isInstanceOf(OpenSamlAssertingPartyDetails.class)); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user