|
1 | 1 | /* |
2 | | - * Copyright 2002-2023 the original author or authors. |
| 2 | + * Copyright 2002-2024 the original author or authors. |
3 | 3 | * |
4 | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | 5 | * you may not use this file except in compliance with the License. |
|
16 | 16 |
|
17 | 17 | package org.springframework.security.saml2.provider.service.metadata; |
18 | 18 |
|
19 | | -import java.io.UnsupportedEncodingException; |
20 | | -import java.net.URLEncoder; |
21 | | -import java.nio.charset.StandardCharsets; |
22 | | -import java.util.Collections; |
23 | | -import java.util.LinkedHashMap; |
24 | | -import java.util.Map; |
25 | | -import java.util.UUID; |
26 | | - |
27 | | -import jakarta.servlet.http.HttpServletRequest; |
28 | | - |
29 | | -import org.springframework.security.saml2.Saml2Exception; |
30 | 19 | import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; |
31 | 20 | import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; |
32 | | -import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationPlaceholderResolvers; |
33 | | -import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationPlaceholderResolvers.UriResolver; |
34 | | -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; |
35 | | -import org.springframework.security.web.util.matcher.OrRequestMatcher; |
36 | 21 | import org.springframework.security.web.util.matcher.RequestMatcher; |
37 | | -import org.springframework.util.Assert; |
38 | 22 |
|
39 | 23 | /** |
40 | 24 | * An implementation of {@link Saml2MetadataResponseResolver} that identifies which |
41 | 25 | * {@link RelyingPartyRegistration}s to use with a {@link RequestMatcher} |
42 | 26 | * |
43 | 27 | * @author Josh Cummings |
44 | 28 | * @since 6.1 |
| 29 | + * @deprecated Please use |
| 30 | + * {@link org.springframework.security.saml2.provider.service.web.metadata.RequestMatcherMetadataResponseResolver} |
45 | 31 | */ |
46 | | -public final class RequestMatcherMetadataResponseResolver implements Saml2MetadataResponseResolver { |
47 | | - |
48 | | - private static final String DEFAULT_METADATA_FILENAME = "saml-{registrationId}-metadata.xml"; |
49 | | - |
50 | | - private RequestMatcher matcher = new OrRequestMatcher( |
51 | | - new AntPathRequestMatcher("/saml2/service-provider-metadata/{registrationId}"), |
52 | | - new AntPathRequestMatcher("/saml2/metadata/{registrationId}"), |
53 | | - new AntPathRequestMatcher("/saml2/metadata")); |
54 | | - |
55 | | - private String filename = DEFAULT_METADATA_FILENAME; |
56 | | - |
57 | | - private final RelyingPartyRegistrationRepository registrations; |
58 | | - |
59 | | - private final Saml2MetadataResolver metadata; |
| 32 | +@Deprecated |
| 33 | +public final class RequestMatcherMetadataResponseResolver extends |
| 34 | + org.springframework.security.saml2.provider.service.web.metadata.RequestMatcherMetadataResponseResolver { |
60 | 35 |
|
61 | 36 | /** |
62 | | - * Construct a {@link RequestMatcherMetadataResponseResolver} |
| 37 | + * Construct a |
| 38 | + * {@link org.springframework.security.saml2.provider.service.web.metadata.RequestMatcherMetadataResponseResolver} |
63 | 39 | * @param registrations the source for relying party metadata |
64 | 40 | * @param metadata the strategy for converting {@link RelyingPartyRegistration}s into |
65 | 41 | * metadata |
66 | 42 | */ |
67 | 43 | public RequestMatcherMetadataResponseResolver(RelyingPartyRegistrationRepository registrations, |
68 | 44 | Saml2MetadataResolver metadata) { |
69 | | - Assert.notNull(registrations, "relyingPartyRegistrationRepository cannot be null"); |
70 | | - Assert.notNull(metadata, "saml2MetadataResolver cannot be null"); |
71 | | - this.registrations = registrations; |
72 | | - this.metadata = metadata; |
73 | | - } |
74 | | - |
75 | | - /** |
76 | | - * Construct and serialize a relying party's SAML 2.0 metadata based on the given |
77 | | - * {@link HttpServletRequest}. Uses the configured {@link RequestMatcher} to identify |
78 | | - * the metadata request, including looking for any indicated {@code registrationId}. |
79 | | - * |
80 | | - * <p> |
81 | | - * If a {@code registrationId} is found in the request, it will attempt to use that, |
82 | | - * erroring if no {@link RelyingPartyRegistration} is found. |
83 | | - * |
84 | | - * <p> |
85 | | - * If no {@code registrationId} is found in the request, it will attempt to show all |
86 | | - * {@link RelyingPartyRegistration}s in an {@code <md:EntitiesDescriptor>}. To |
87 | | - * exercise this functionality, the provided |
88 | | - * {@link RelyingPartyRegistrationRepository} needs to implement {@link Iterable}. |
89 | | - * @param request the HTTP request |
90 | | - * @return a {@link Saml2MetadataResponse} instance |
91 | | - * @throws Saml2Exception if the {@link RequestMatcher} specifies a non-existent |
92 | | - * {@code registrationId} |
93 | | - */ |
94 | | - @Override |
95 | | - public Saml2MetadataResponse resolve(HttpServletRequest request) { |
96 | | - RequestMatcher.MatchResult result = this.matcher.matcher(request); |
97 | | - if (!result.isMatch()) { |
98 | | - return null; |
99 | | - } |
100 | | - String registrationId = result.getVariables().get("registrationId"); |
101 | | - Saml2MetadataResponse response = responseByRegistrationId(request, registrationId); |
102 | | - if (response != null) { |
103 | | - return response; |
104 | | - } |
105 | | - if (this.registrations instanceof Iterable<?>) { |
106 | | - Iterable<RelyingPartyRegistration> registrations = (Iterable<RelyingPartyRegistration>) this.registrations; |
107 | | - return responseByIterable(request, registrations); |
108 | | - } |
109 | | - return null; |
110 | | - } |
111 | | - |
112 | | - private Saml2MetadataResponse responseByRegistrationId(HttpServletRequest request, String registrationId) { |
113 | | - if (registrationId == null) { |
114 | | - return null; |
115 | | - } |
116 | | - RelyingPartyRegistration registration = this.registrations.findByRegistrationId(registrationId); |
117 | | - if (registration == null) { |
118 | | - throw new Saml2Exception("registration not found"); |
119 | | - } |
120 | | - return responseByIterable(request, Collections.singleton(registration)); |
121 | | - } |
122 | | - |
123 | | - private Saml2MetadataResponse responseByIterable(HttpServletRequest request, |
124 | | - Iterable<RelyingPartyRegistration> registrations) { |
125 | | - Map<String, RelyingPartyRegistration> results = new LinkedHashMap<>(); |
126 | | - for (RelyingPartyRegistration registration : registrations) { |
127 | | - UriResolver uriResolver = RelyingPartyRegistrationPlaceholderResolvers.uriResolver(request, registration); |
128 | | - String entityId = uriResolver.resolve(registration.getEntityId()); |
129 | | - results.computeIfAbsent(entityId, (e) -> { |
130 | | - String ssoLocation = uriResolver.resolve(registration.getAssertionConsumerServiceLocation()); |
131 | | - String sloLocation = uriResolver.resolve(registration.getSingleLogoutServiceLocation()); |
132 | | - String sloResponseLocation = uriResolver.resolve(registration.getSingleLogoutServiceResponseLocation()); |
133 | | - return registration.mutate() |
134 | | - .entityId(entityId) |
135 | | - .assertionConsumerServiceLocation(ssoLocation) |
136 | | - .singleLogoutServiceLocation(sloLocation) |
137 | | - .singleLogoutServiceResponseLocation(sloResponseLocation) |
138 | | - .build(); |
139 | | - }); |
140 | | - } |
141 | | - String metadata = this.metadata.resolve(results.values()); |
142 | | - String value = (results.size() == 1) ? results.values().iterator().next().getRegistrationId() |
143 | | - : UUID.randomUUID().toString(); |
144 | | - String fileName = this.filename.replace("{registrationId}", value); |
145 | | - try { |
146 | | - String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()); |
147 | | - return new Saml2MetadataResponse(metadata, encodedFileName); |
148 | | - } |
149 | | - catch (UnsupportedEncodingException ex) { |
150 | | - throw new Saml2Exception(ex); |
151 | | - } |
152 | | - } |
153 | | - |
154 | | - /** |
155 | | - * Use this {@link RequestMatcher} to identity which requests to generate metadata |
156 | | - * for. By default, matches {@code /saml2/metadata}, |
157 | | - * {@code /saml2/metadata/{registrationId}}, {@code /saml2/service-provider-metadata}, |
158 | | - * and {@code /saml2/service-provider-metadata/{registrationId}} |
159 | | - * @param requestMatcher the {@link RequestMatcher} to use |
160 | | - */ |
161 | | - public void setRequestMatcher(RequestMatcher requestMatcher) { |
162 | | - Assert.notNull(requestMatcher, "requestMatcher cannot be empty"); |
163 | | - this.matcher = requestMatcher; |
164 | | - } |
165 | | - |
166 | | - /** |
167 | | - * Sets the metadata filename template. If it contains the {@code {registrationId}} |
168 | | - * placeholder, it will be resolved as a random UUID if there are multiple |
169 | | - * {@link RelyingPartyRegistration}s. Otherwise, it will be replaced by the |
170 | | - * {@link RelyingPartyRegistration}'s id. |
171 | | - * |
172 | | - * <p> |
173 | | - * The default value is {@code saml-{registrationId}-metadata.xml} |
174 | | - * @param metadataFilename metadata filename, must contain a {registrationId} |
175 | | - */ |
176 | | - public void setMetadataFilename(String metadataFilename) { |
177 | | - Assert.hasText(metadataFilename, "metadataFilename cannot be empty"); |
178 | | - this.filename = metadataFilename; |
| 45 | + super(registrations, metadata); |
179 | 46 | } |
180 | 47 |
|
181 | 48 | } |
0 commit comments