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