|
1 | 1 | package org.cloudfoundry.identity.uaa.provider.saml; |
2 | 2 |
|
3 | 3 | import org.cloudfoundry.identity.uaa.zone.IdentityZone; |
| 4 | +import org.cloudfoundry.identity.uaa.zone.SamlConfig; |
4 | 5 | import org.cloudfoundry.identity.uaa.zone.ZoneAware; |
| 6 | +import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; |
5 | 7 | import org.opensaml.saml.common.xml.SAMLConstants; |
6 | 8 | import org.opensaml.saml.saml2.metadata.EntityDescriptor; |
7 | 9 | import org.opensaml.saml.saml2.metadata.SPSSODescriptor; |
|
11 | 13 | import org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResolver; |
12 | 14 | import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; |
13 | 15 | import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; |
14 | | -import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver; |
15 | | -import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; |
16 | 16 | import org.springframework.util.Assert; |
17 | 17 | import org.springframework.web.bind.annotation.GetMapping; |
18 | 18 | import org.springframework.web.bind.annotation.PathVariable; |
19 | 19 | import org.springframework.web.bind.annotation.RestController; |
20 | 20 |
|
21 | | -import javax.servlet.http.HttpServletRequest; |
22 | 21 | import javax.servlet.http.HttpServletResponse; |
23 | 22 | import java.net.URLEncoder; |
24 | 23 | import java.nio.charset.StandardCharsets; |
|
27 | 26 | @RestController |
28 | 27 | public class SamlMetadataEndpoint implements ZoneAware { |
29 | 28 | public static final String DEFAULT_REGISTRATION_ID = "example"; |
30 | | - private static final String DEFAULT_FILE_NAME = "saml-sp.xml"; |
31 | 29 | private static final String APPLICATION_XML_CHARSET_UTF_8 = "application/xml; charset=UTF-8"; |
32 | | - private static final String CONTENT_DISPOSITION_FORMAT = "attachment; filename=\"%s\"; filename*=UTF-8''%s"; |
33 | 30 |
|
34 | | - // @todo - this should be a Zone aware resolver |
35 | | - private final RelyingPartyRegistrationResolver relyingPartyRegistrationResolver; |
36 | 31 | private final Saml2MetadataResolver saml2MetadataResolver; |
| 32 | + private final IdentityZoneManager identityZoneManager; |
37 | 33 |
|
38 | | - private String fileName; |
39 | | - private String encodedFileName; |
40 | | - |
41 | | - private final Boolean wantAssertionSigned; |
42 | 34 | private final RelyingPartyRegistrationRepository relyingPartyRegistrationRepository; |
43 | 35 |
|
44 | 36 | public SamlMetadataEndpoint(RelyingPartyRegistrationRepository relyingPartyRegistrationRepository, |
45 | | - SamlConfigProps samlConfigProps) { |
| 37 | + IdentityZoneManager identityZoneManager) { |
46 | 38 | Assert.notNull(relyingPartyRegistrationRepository, "relyingPartyRegistrationRepository cannot be null"); |
47 | 39 | this.relyingPartyRegistrationRepository = relyingPartyRegistrationRepository; |
48 | | - this.relyingPartyRegistrationResolver = new DefaultRelyingPartyRegistrationResolver(relyingPartyRegistrationRepository); |
| 40 | + this.identityZoneManager = identityZoneManager; |
49 | 41 | OpenSamlMetadataResolver resolver = new OpenSamlMetadataResolver(); |
50 | 42 | this.saml2MetadataResolver = resolver; |
51 | 43 | resolver.setEntityDescriptorCustomizer(new EntityDescriptorCustomizer()); |
52 | | - this.wantAssertionSigned = samlConfigProps.getWantAssertionSigned(); |
53 | | - setFileName(DEFAULT_FILE_NAME); |
54 | 44 | } |
55 | 45 |
|
56 | 46 | private class EntityDescriptorCustomizer implements Consumer<OpenSamlMetadataResolver.EntityDescriptorParameters> { |
57 | 47 | @Override |
58 | 48 | public void accept(OpenSamlMetadataResolver.EntityDescriptorParameters entityDescriptorParameters) { |
| 49 | + SamlConfig samlConfig = identityZoneManager.getCurrentIdentityZone().getConfig().getSamlConfig(); |
| 50 | + |
59 | 51 | EntityDescriptor descriptor = entityDescriptorParameters.getEntityDescriptor(); |
60 | 52 | SPSSODescriptor spssodescriptor = descriptor.getSPSSODescriptor(SAMLConstants.SAML20P_NS); |
61 | | - spssodescriptor.setWantAssertionsSigned(wantAssertionSigned); |
| 53 | + spssodescriptor.setWantAssertionsSigned(samlConfig.isWantAssertionSigned()); |
62 | 54 | spssodescriptor.setAuthnRequestsSigned(entityDescriptorParameters.getRelyingPartyRegistration().getAssertingPartyDetails().getWantAuthnRequestsSigned()); |
63 | 55 | } |
64 | 56 | } |
65 | 57 |
|
66 | 58 | @GetMapping(value = "/saml/metadata", produces = APPLICATION_XML_CHARSET_UTF_8) |
67 | | - public ResponseEntity<String> legacyMetadataEndpoint(HttpServletRequest request) { |
68 | | - return metadataEndpoint(DEFAULT_REGISTRATION_ID, request); |
| 59 | + public ResponseEntity<String> legacyMetadataEndpoint() { |
| 60 | + return metadataEndpoint(DEFAULT_REGISTRATION_ID); |
69 | 61 | } |
70 | 62 |
|
71 | 63 | @GetMapping(value = "/saml/metadata/{registrationId}", produces = APPLICATION_XML_CHARSET_UTF_8) |
72 | | - public ResponseEntity<String> metadataEndpoint(@PathVariable String registrationId, HttpServletRequest request) { |
| 64 | + public ResponseEntity<String> metadataEndpoint(@PathVariable String registrationId) { |
73 | 65 | RelyingPartyRegistration relyingPartyRegistration = relyingPartyRegistrationRepository.findByRegistrationId(registrationId); |
74 | 66 | if (relyingPartyRegistration == null) { |
75 | 67 | return ResponseEntity.status(HttpServletResponse.SC_UNAUTHORIZED).build(); |
76 | 68 | } |
77 | 69 | String metadata = saml2MetadataResolver.resolve(relyingPartyRegistration); |
78 | 70 |
|
79 | | - // @todo - fileName may need to be dynamic based on registrationID |
80 | | - String[] fileNames = retrieveZoneAwareFileNames(); |
| 71 | + String contentDisposition = ContentDispositionFilename.getContentDisposition(retrieveZone()); |
81 | 72 | return ResponseEntity.ok() |
82 | | - .header(HttpHeaders.CONTENT_DISPOSITION, String.format( |
83 | | - CONTENT_DISPOSITION_FORMAT, fileNames[0], fileNames[1])) |
| 73 | + .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition) |
84 | 74 | .body(metadata); |
85 | 75 | } |
| 76 | +} |
86 | 77 |
|
87 | | - public void setFileName(String fileName) { |
88 | | - encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8); |
89 | | - this.fileName = fileName; |
90 | | - } |
| 78 | +record ContentDispositionFilename(String fileName) { |
| 79 | + private static final String CONTENT_DISPOSITION_FORMAT = "attachment; filename=\"%s\"; filename*=UTF-8''%s"; |
| 80 | + private static final String DEFAULT_FILE_NAME = "saml-sp.xml"; |
91 | 81 |
|
92 | | - private String[] retrieveZoneAwareFileNames() { |
93 | | - IdentityZone zone = retrieveZone(); |
94 | | - String[] fileNames = new String[2]; |
| 82 | + static ContentDispositionFilename retrieveZoneAwareContentDispositionFilename(IdentityZone zone) { |
95 | 83 | if (zone.isUaa()) { |
96 | | - fileNames[0] = fileName; |
97 | | - fileNames[1] = encodedFileName; |
98 | | - } |
99 | | - else { |
100 | | - fileNames[0] = "saml-" + zone.getSubdomain() + "-sp.xml"; |
101 | | - fileNames[1] = URLEncoder.encode(fileNames[0], |
102 | | - StandardCharsets.UTF_8); |
| 84 | + return new ContentDispositionFilename(DEFAULT_FILE_NAME); |
103 | 85 | } |
104 | | - return fileNames; |
| 86 | + String filename = "saml-%s-sp.xml".formatted(zone.getSubdomain()); |
| 87 | + return new ContentDispositionFilename(filename); |
| 88 | + } |
| 89 | + |
| 90 | + static String getContentDisposition(IdentityZone zone) { |
| 91 | + return retrieveZoneAwareContentDispositionFilename(zone).getContentDisposition(); |
| 92 | + } |
| 93 | + |
| 94 | + String getContentDisposition() { |
| 95 | + String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8); |
| 96 | + return CONTENT_DISPOSITION_FORMAT.formatted(fileName, encodedFileName); |
105 | 97 | } |
106 | 98 | } |
0 commit comments