1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package edu.internet2.middleware.shibboleth.idp.profile.saml2;
18
19 import java.util.ArrayList;
20
21 import org.opensaml.common.SAMLObjectBuilder;
22 import org.opensaml.common.binding.BasicEndpointSelector;
23 import org.opensaml.common.binding.decoding.SAMLMessageDecoder;
24 import org.opensaml.common.xml.SAMLConstants;
25 import org.opensaml.saml2.core.AttributeQuery;
26 import org.opensaml.saml2.core.AttributeStatement;
27 import org.opensaml.saml2.core.Response;
28 import org.opensaml.saml2.core.Statement;
29 import org.opensaml.saml2.core.StatusCode;
30 import org.opensaml.saml2.core.Subject;
31 import org.opensaml.saml2.metadata.AssertionConsumerService;
32 import org.opensaml.saml2.metadata.AttributeAuthorityDescriptor;
33 import org.opensaml.saml2.metadata.Endpoint;
34 import org.opensaml.saml2.metadata.EntityDescriptor;
35 import org.opensaml.saml2.metadata.SPSSODescriptor;
36 import org.opensaml.saml2.metadata.provider.MetadataProvider;
37 import org.opensaml.ws.message.decoder.MessageDecodingException;
38 import org.opensaml.ws.transport.http.HTTPInTransport;
39 import org.opensaml.ws.transport.http.HTTPOutTransport;
40 import org.opensaml.xml.security.SecurityException;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43 import org.slf4j.helpers.MessageFormatter;
44
45 import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
46 import edu.internet2.middleware.shibboleth.common.profile.provider.BaseSAMLProfileRequestContext;
47 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml2.AttributeQueryConfiguration;
48 import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
49 import edu.internet2.middleware.shibboleth.idp.session.Session;
50
51
52 public class AttributeQueryProfileHandler extends AbstractSAML2ProfileHandler {
53
54
55 private static Logger log = LoggerFactory.getLogger(AttributeQueryProfileHandler.class);
56
57
58 private SAMLObjectBuilder<AssertionConsumerService> acsEndpointBuilder;
59
60
61 public AttributeQueryProfileHandler() {
62 super();
63
64 acsEndpointBuilder = (SAMLObjectBuilder<AssertionConsumerService>) getBuilderFactory().getBuilder(
65 AssertionConsumerService.DEFAULT_ELEMENT_NAME);
66 }
67
68
69 public String getProfileId() {
70 return AttributeQueryConfiguration.PROFILE_ID;
71 }
72
73
74 public void processRequest(HTTPInTransport inTransport, HTTPOutTransport outTransport) throws ProfileException {
75 Response samlResponse;
76
77 AttributeQueryContext requestContext = new AttributeQueryContext();
78
79 try {
80 decodeRequest(requestContext, inTransport, outTransport);
81
82 if (requestContext.getProfileConfiguration() == null) {
83 String msg = MessageFormatter.format(
84 "SAML 2 Attribute Query profile is not configured for relying party '{}'", requestContext
85 .getInboundMessage());
86 requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, StatusCode.REQUEST_DENIED_URI,
87 msg));
88 log.warn(msg);
89 samlResponse = buildErrorResponse(requestContext);
90 } else {
91 checkSamlVersion(requestContext);
92
93
94 AttributeQuery query = requestContext.getInboundSAMLMessage();
95 if (query.getAttributes() != null && !query.getAttributes().isEmpty()) {
96 log.warn("Specific attributes requested in query '{}'. This functionality is not yet supported",
97 requestContext.getInboundMessageIssuer());
98 requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI,
99 StatusCode.REQUEST_UNSUPPORTED_URI,
100 "Request of specific attributes during an attribute query is not supported"));
101 throw new ProfileException();
102 }
103
104
105 resolvePrincipal(requestContext);
106
107 Session idpSession = getSessionManager().getSession(requestContext.getPrincipalName());
108 if (idpSession != null) {
109 AuthenticationMethodInformation authnInfo = idpSession.getAuthenticationMethods().get(
110 requestContext.getInboundMessageIssuer());
111 if (authnInfo != null) {
112 requestContext.setPrincipalAuthenticationMethod(authnInfo.getAuthenticationMethod());
113 }
114 }
115
116 resolveAttributes(requestContext);
117 requestContext.setReleasedAttributes(requestContext.getAttributes().keySet());
118
119
120 ArrayList<Statement> statements = new ArrayList<Statement>();
121 AttributeStatement attributeStatement = buildAttributeStatement(requestContext);
122 if (attributeStatement != null) {
123 statements.add(attributeStatement);
124 }
125
126
127 samlResponse = buildResponse(requestContext, "urn:oasis:names:tc:SAML:2.0:cm:sender-vouches",
128 statements);
129 }
130 } catch (ProfileException e) {
131 samlResponse = buildErrorResponse(requestContext);
132 }
133
134 requestContext.setOutboundSAMLMessage(samlResponse);
135 requestContext.setOutboundSAMLMessageId(samlResponse.getID());
136 requestContext.setOutboundSAMLMessageIssueInstant(samlResponse.getIssueInstant());
137
138 encodeResponse(requestContext);
139 writeAuditLogEntry(requestContext);
140 }
141
142
143
144
145
146
147
148
149
150
151 protected void decodeRequest(AttributeQueryContext requestContext, HTTPInTransport inTransport,
152 HTTPOutTransport outTransport) throws ProfileException {
153 if (log.isDebugEnabled()) {
154 log.debug("Decoding message with decoder binding '{}'", getInboundMessageDecoder(requestContext)
155 .getBindingURI());
156 }
157
158 requestContext.setCommunicationProfileId(getProfileId());
159
160 MetadataProvider metadataProvider = getMetadataProvider();
161 requestContext.setMetadataProvider(metadataProvider);
162
163 requestContext.setInboundMessageTransport(inTransport);
164 requestContext.setInboundSAMLProtocol(SAMLConstants.SAML20P_NS);
165 requestContext.setSecurityPolicyResolver(getSecurityPolicyResolver());
166 requestContext.setPeerEntityRole(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
167
168 requestContext.setOutboundMessageTransport(outTransport);
169 requestContext.setOutboundSAMLProtocol(SAMLConstants.SAML20P_NS);
170
171 try {
172 SAMLMessageDecoder decoder = getInboundMessageDecoder(requestContext);
173 requestContext.setMessageDecoder(decoder);
174 decoder.decode(requestContext);
175 log.debug("Decoded request from relying party '{}'", requestContext.getInboundMessage());
176
177 if (!(requestContext.getInboundSAMLMessage() instanceof AttributeQuery)) {
178 log.warn("Incoming message was not a AttributeQuery, it was a {}", requestContext
179 .getInboundSAMLMessage().getClass().getName());
180 requestContext.setFailureStatus(buildStatus(StatusCode.REQUESTER_URI, null,
181 "Invalid SAML AttributeQuery message."));
182 throw new ProfileException("Invalid SAML AttributeQuery message.");
183 }
184 } catch (MessageDecodingException e) {
185 String msg = "Error decoding attribute query message";
186 log.warn(msg, e);
187 requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null, msg));
188 throw new ProfileException(msg);
189 } catch (SecurityException e) {
190 String msg = "Message did not meet security requirements";
191 log.warn(msg, e);
192 requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, StatusCode.REQUEST_DENIED_URI, msg));
193 throw new ProfileException(msg, e);
194 } finally {
195
196 populateRequestContext(requestContext);
197 }
198 }
199
200
201 protected void populateRelyingPartyInformation(BaseSAMLProfileRequestContext requestContext)
202 throws ProfileException {
203 super.populateRelyingPartyInformation(requestContext);
204
205 EntityDescriptor relyingPartyMetadata = requestContext.getPeerEntityMetadata();
206 if (relyingPartyMetadata != null) {
207 requestContext.setPeerEntityRole(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
208 requestContext.setPeerEntityRoleMetadata(relyingPartyMetadata.getSPSSODescriptor(SAMLConstants.SAML20P_NS));
209 }
210 }
211
212
213 protected void populateAssertingPartyInformation(BaseSAMLProfileRequestContext requestContext)
214 throws ProfileException {
215 super.populateAssertingPartyInformation(requestContext);
216
217 EntityDescriptor localEntityDescriptor = requestContext.getLocalEntityMetadata();
218 if (localEntityDescriptor != null) {
219 requestContext.setLocalEntityRole(AttributeAuthorityDescriptor.DEFAULT_ELEMENT_NAME);
220 requestContext.setLocalEntityRoleMetadata(localEntityDescriptor
221 .getAttributeAuthorityDescriptor(SAMLConstants.SAML20P_NS));
222 }
223 }
224
225
226
227
228
229
230
231
232
233
234
235
236 protected void populateSAMLMessageInformation(BaseSAMLProfileRequestContext requestContext) throws ProfileException {
237 AttributeQuery query = (AttributeQuery) requestContext.getInboundSAMLMessage();
238 if (query != null) {
239 Subject subject = query.getSubject();
240 if (subject == null) {
241 String msg = "Attribute query did not contain a proper subject";
242 log.warn(msg);
243 ((AttributeQueryContext) requestContext).setFailureStatus(buildStatus(StatusCode.REQUESTER_URI, null,
244 msg));
245 throw new ProfileException(msg);
246 }
247 requestContext.setSubjectNameIdentifier(subject.getNameID());
248 }
249 }
250
251
252
253
254
255
256
257
258 protected Endpoint selectEndpoint(BaseSAMLProfileRequestContext requestContext) {
259 Endpoint endpoint;
260
261 if (getInboundBinding().equals(SAMLConstants.SAML2_SOAP11_BINDING_URI)) {
262 endpoint = acsEndpointBuilder.buildObject();
263 endpoint.setBinding(SAMLConstants.SAML2_SOAP11_BINDING_URI);
264 } else {
265 BasicEndpointSelector endpointSelector = new BasicEndpointSelector();
266 endpointSelector.setEndpointType(AssertionConsumerService.DEFAULT_ELEMENT_NAME);
267 endpointSelector.setMetadataProvider(getMetadataProvider());
268 endpointSelector.setEntityMetadata(requestContext.getPeerEntityMetadata());
269 endpointSelector.setEntityRoleMetadata(requestContext.getPeerEntityRoleMetadata());
270 endpointSelector.setSamlRequest(requestContext.getInboundSAMLMessage());
271 endpointSelector.getSupportedIssuerBindings().addAll(getSupportedOutboundBindings());
272 endpoint = endpointSelector.selectEndpoint();
273 }
274
275 return endpoint;
276 }
277
278
279 protected class AttributeQueryContext extends
280 BaseSAML2ProfileRequestContext<AttributeQuery, Response, AttributeQueryConfiguration> {
281
282 }
283 }