|
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