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