1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.opensaml.saml2.binding;
18
19 import java.util.ArrayList;
20 import java.util.Iterator;
21 import java.util.List;
22
23 import org.opensaml.common.binding.BasicEndpointSelector;
24 import org.opensaml.saml2.core.AuthnRequest;
25 import org.opensaml.saml2.metadata.Endpoint;
26 import org.opensaml.saml2.metadata.IndexedEndpoint;
27 import org.opensaml.xml.util.DatatypeHelper;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31
32
33
34
35
36
37 public class AuthnResponseEndpointSelector extends BasicEndpointSelector {
38
39
40 private final Logger log = LoggerFactory.getLogger(AuthnResponseEndpointSelector.class);
41
42
43 @SuppressWarnings("unchecked")
44 public Endpoint selectEndpoint() {
45 if (getEntityRoleMetadata() == null) {
46 log.debug("Unable to select endpoint, no entity role metadata available.");
47 return null;
48 }
49
50 List<? extends Endpoint> endpoints = getEntityRoleMetadata().getEndpoints(getEndpointType());
51 if (endpoints == null || endpoints.size() == 0) {
52 return null;
53 }
54
55 Endpoint endpoint = null;
56 AuthnRequest request = (AuthnRequest) getSamlRequest();
57 if (request != null) {
58 endpoints = filterEndpointsByProtocolBinding(endpoints);
59 if (endpoints == null || endpoints.isEmpty()) {
60 return null;
61 }
62
63 if (request.getAssertionConsumerServiceIndex() != null) {
64 log.debug("Selecting endpoint by ACS index '{}' for request '{}' from entity '{}'",
65 new Object[] { request.getAssertionConsumerServiceIndex(), request.getID(),
66 getEntityMetadata().getEntityID() });
67 endpoint = selectEndpointByACSIndex(request, (List<IndexedEndpoint>) endpoints);
68 } else if (request.getAssertionConsumerServiceURL() != null) {
69 log
70 .debug(
71 "Selecting endpoint by ACS URL '{}' and protocol binding '{}' for request '{}' from entity '{}'",
72 new Object[] { request.getAssertionConsumerServiceURL(), request.getProtocolBinding(),
73 request.getID(), getEntityMetadata().getEntityID() });
74 endpoint = selectEndpointByACSURL(request, (List<IndexedEndpoint>) endpoints);
75 }
76 }
77
78 if (endpoint == null && request.getAssertionConsumerServiceIndex() == null
79 && request.getAssertionConsumerServiceURL() == null) {
80 log.debug("No ACS index or URL given, selecting endpoint without additional constraints.");
81 if (endpoints.get(0) instanceof IndexedEndpoint) {
82 endpoint = selectIndexedEndpoint((List<IndexedEndpoint>) endpoints);
83 } else {
84 endpoint = selectNonIndexedEndpoint((List<Endpoint>) endpoints);
85 }
86 }
87
88 return endpoint;
89 }
90
91
92
93
94
95
96
97
98
99 protected List<? extends Endpoint> filterEndpointsByProtocolBinding(List<? extends Endpoint> endpoints) {
100 log.debug("Filtering peer endpoints. Supported peer endpoint bindings: {}", getSupportedIssuerBindings());
101 AuthnRequest request = (AuthnRequest) getSamlRequest();
102
103 boolean filterByRequestBinding = false;
104 String acsBinding = DatatypeHelper.safeTrimOrNullString(request.getProtocolBinding());
105 if (acsBinding != null && request.getAssertionConsumerServiceIndex() != null) {
106 filterByRequestBinding = true;
107 }
108
109 List<Endpoint> filteredEndpoints = new ArrayList<Endpoint>(endpoints);
110 Iterator<Endpoint> endpointItr = filteredEndpoints.iterator();
111 Endpoint endpoint;
112 while (endpointItr.hasNext()) {
113 endpoint = endpointItr.next();
114 if (!getSupportedIssuerBindings().contains(endpoint.getBinding())) {
115 log.debug("Removing endpoint {} because its binding {} is not supported", endpoint.getLocation(),
116 endpoint.getBinding());
117 endpointItr.remove();
118 continue;
119 }
120
121 if (filterByRequestBinding && !endpoint.getBinding().equals(acsBinding)) {
122 log.debug("Removing endpoint {} because its binding {} does not match request's requested binding",
123 endpoint.getLocation(), endpoint.getBinding());
124 endpointItr.remove();
125 }
126 }
127
128 return filteredEndpoints;
129 }
130
131
132
133
134
135
136
137
138
139 protected Endpoint selectEndpointByACSIndex(AuthnRequest request, List<IndexedEndpoint> endpoints) {
140 Integer acsIndex = request.getAssertionConsumerServiceIndex();
141 for (IndexedEndpoint endpoint : endpoints) {
142 if (endpoint == null || !getSupportedIssuerBindings().contains(endpoint.getBinding())) {
143 log
144 .debug(
145 "Endpoint '{}' with binding '{}' discarded because it requires an unsupported outbound binding.",
146 endpoint.getLocation(), endpoint.getBinding());
147 continue;
148 }
149
150 if (DatatypeHelper.safeEquals(acsIndex, endpoint.getIndex())) {
151 return endpoint;
152 } else {
153 log.debug("Endpoint '{}' with index '{}' discard because it does have the required index '{}'",
154 new Object[] { endpoint.getLocation(), endpoint.getIndex(), acsIndex });
155 }
156 }
157
158 log.warn("Relying party '{}' requested the response to be returned to endpoint with ACS index '{}' "
159 + "however no endpoint, with that index and using a supported binding, can be found "
160 + " in the relying party's metadata ", getEntityMetadata().getEntityID(), acsIndex);
161 return null;
162 }
163
164
165
166
167
168
169
170
171
172 protected Endpoint selectEndpointByACSURL(AuthnRequest request, List<IndexedEndpoint> endpoints) {
173 String acsBinding = DatatypeHelper.safeTrimOrNullString(request.getProtocolBinding());
174
175 for (IndexedEndpoint endpoint : endpoints) {
176 if (!getSupportedIssuerBindings().contains(endpoint.getBinding())) {
177 log.debug(
178 "Endpoint '{}' with binding '{}' discarded because that is not a supported outbound binding.",
179 endpoint.getLocation(), endpoint.getBinding());
180 continue;
181 }
182
183 if (acsBinding != null) {
184 if (!DatatypeHelper.safeEquals(acsBinding, endpoint.getBinding())) {
185 log.debug("Endpoint '{}' with binding '{}' discarded because it does not meet protocol binding selection criteria",
186 endpoint.getLocation(), endpoint.getBinding());
187 continue;
188 }
189 }
190
191 if (DatatypeHelper.safeEquals(endpoint.getLocation(), request.getAssertionConsumerServiceURL())
192 || DatatypeHelper.safeEquals(endpoint.getResponseLocation(), request
193 .getAssertionConsumerServiceURL())) {
194 return endpoint;
195 } else {
196 log.debug(
197 "Endpoint '{}' discarded because neither its Location nor ResponseLocation match ACS URL '{}'",
198 endpoint.getLocation(), request.getAssertionConsumerServiceURL());
199 }
200 }
201
202 log.warn("Relying party '{}' requested the response to be returned to endpoint with ACS URL '{}' "
203 + " and binding '{}' however no endpoint, with that URL and using a supported binding, "
204 + " can be found in the relying party's metadata ", new Object[] { getEntityMetadata().getEntityID(),
205 request.getAssertionConsumerServiceURL(), (acsBinding == null) ? "any" : acsBinding });
206 return null;
207 }
208 }