f9b4c8a6683f68284299cb3795b97333a47b0f03
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / idp / profile / saml2 / AbstractAttributeQuery.java
1 /*
2  * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package edu.internet2.middleware.shibboleth.idp.profile.saml2;
18
19 import org.apache.log4j.Logger;
20 import org.opensaml.common.SAMLObjectBuilder;
21 import org.opensaml.saml2.core.Advice;
22 import org.opensaml.saml2.core.Assertion;
23 import org.opensaml.saml2.core.Audience;
24 import org.opensaml.saml2.core.AudienceRestriction;
25 import org.opensaml.saml2.core.Conditions;
26 import org.opensaml.saml2.core.Issuer;
27 import org.opensaml.saml2.core.ProxyRestriction;
28 import org.opensaml.saml2.core.Response;
29 import org.opensaml.saml2.core.Status;
30 import org.opensaml.saml2.core.StatusCode;
31 import org.opensaml.saml2.core.Subject;
32 import org.opensaml.saml2.encryption.Encrypter;
33 import org.opensaml.saml2.metadata.provider.MetadataProvider;
34 import org.opensaml.xml.XMLObjectBuilder;
35 import org.opensaml.xml.encryption.EncryptionException;
36 import org.opensaml.xml.signature.Signature;
37 import org.opensaml.xml.signature.Signer;
38
39 import edu.internet2.middleware.shibboleth.common.attribute.SAML2AttributeAuthority;
40 import edu.internet2.middleware.shibboleth.common.attribute.provider.ShibbolethAttributeRequestContext;
41 import edu.internet2.middleware.shibboleth.common.attribute.provider.ShibbolethSAML2AttributeAuthority;
42 import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolver;
43 import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
44 import edu.internet2.middleware.shibboleth.common.relyingparty.saml2.AttributeQueryConfiguration;
45
46 /**
47  * SAML 2.0 Attribute Query profile handler.
48  */
49 public abstract class AbstractAttributeQuery extends AbstractSAML2ProfileHandler {
50
51     /** Class logger. */
52     private static Logger log = Logger.getLogger(AbstractAttributeQuery.class);
53
54     /** For building response. */
55     private SAMLObjectBuilder<Response> responseBuilder;
56
57     /** For building status. */
58     private SAMLObjectBuilder<Status> statusBuilder;
59
60     /** For building statuscode. */
61     private SAMLObjectBuilder<StatusCode> statusCodeBuilder;
62
63     /** For building assertion. */
64     private SAMLObjectBuilder<Assertion> assertionBuilder;
65
66     /** For building issuer. */
67     private SAMLObjectBuilder<Issuer> issuerBuilder;
68
69     /** For building subject. */
70     private SAMLObjectBuilder<Subject> subjectBuilder;
71
72     /** For building conditions. */
73     private SAMLObjectBuilder<Conditions> conditionsBuilder;
74
75     /** For building audience restriction. */
76     private SAMLObjectBuilder<AudienceRestriction> audienceRestrictionBuilder;
77
78     /** For building audience. */
79     private SAMLObjectBuilder<Audience> audienceBuilder;
80
81     /** For building advice. */
82     private SAMLObjectBuilder<Advice> adviceBuilder;
83
84     /** For building signature. */
85     private XMLObjectBuilder<Signature> signatureBuilder;
86
87     /** Attribute authority. */
88     private SAML2AttributeAuthority attributeAuthority;
89
90     /**
91      * This creates a new attribute query.
92      * 
93      * @param ar <code>AttributeResolver</code>
94      */
95     public AbstractAttributeQuery(AttributeResolver<ShibbolethAttributeRequestContext> ar) {
96         // instantiate attribute authority
97         attributeAuthority = new ShibbolethSAML2AttributeAuthority(ar);
98
99         // instantiate XML builders
100         responseBuilder = (SAMLObjectBuilder<Response>) getBuilderFactory().getBuilder(Response.DEFAULT_ELEMENT_NAME);
101         statusBuilder = (SAMLObjectBuilder<Status>) getBuilderFactory().getBuilder(Status.DEFAULT_ELEMENT_NAME);
102         statusCodeBuilder = (SAMLObjectBuilder<StatusCode>) getBuilderFactory().getBuilder(
103                 StatusCode.DEFAULT_ELEMENT_NAME);
104         issuerBuilder = (SAMLObjectBuilder<Issuer>) getBuilderFactory().getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
105         assertionBuilder = (SAMLObjectBuilder<Assertion>) getBuilderFactory()
106                 .getBuilder(Assertion.DEFAULT_ELEMENT_NAME);
107         subjectBuilder = (SAMLObjectBuilder<Subject>) getBuilderFactory().getBuilder(Subject.DEFAULT_ELEMENT_NAME);
108         conditionsBuilder = (SAMLObjectBuilder<Conditions>) getBuilderFactory().getBuilder(
109                 Conditions.DEFAULT_ELEMENT_NAME);
110         audienceRestrictionBuilder = (SAMLObjectBuilder<AudienceRestriction>) getBuilderFactory().getBuilder(
111                 AudienceRestriction.DEFAULT_ELEMENT_NAME);
112         audienceBuilder = (SAMLObjectBuilder<Audience>) getBuilderFactory().getBuilder(Audience.DEFAULT_ELEMENT_NAME);
113         adviceBuilder = (SAMLObjectBuilder<Advice>) getBuilderFactory().getBuilder(Advice.DEFAULT_ELEMENT_NAME);
114         signatureBuilder = (XMLObjectBuilder<Signature>) getBuilderFactory().getBuilder(Signature.DEFAULT_ELEMENT_NAME);
115     }
116
117     /**
118      * This returns the <code>RelyingPartyConfiguration</code> for the supplied provider id.
119      * 
120      * @param providerId <code>String</code>
121      * @return <code>RelyingPartyConfiguration</code>
122      */
123     protected RelyingPartyConfiguration getRelyingPartyConfiguration(String providerId) {
124         return getRelyingPartyConfigurationManager().getRelyingPartyConfiguration(providerId);
125     }
126
127     /**
128      * This returns the <code>AttributeQueryConfiguration</code> for the supplied provider id.
129      * 
130      * @param providerId <code>String</code>
131      * @return <code>AttributeQueryConfiguration</code>
132      */
133     protected AttributeQueryConfiguration getAttributeQueryConfiguration(String providerId) {
134         return (AttributeQueryConfiguration) getRelyingPartyConfiguration(providerId).getProfileConfigurations().get(
135                 AttributeQueryConfiguration.PROFILE_ID);
136     }
137
138     /**
139      * This returns the <code>MetadataProvider</code> for this attribute query.
140      * 
141      * @return <code>MetadataProvider</code>
142      */
143     protected MetadataProvider getMetadataProvider() {
144         return getRelyingPartyConfigurationManager().getMetadataProvider();
145     }
146
147     /**
148      * This returns the <code>AttributeAuthority</code> for this attribute query.
149      * 
150      * @return <code>SAML2AttributeAuthority</code>
151      */
152     protected SAML2AttributeAuthority getAttributeAuthority() {
153         return attributeAuthority;
154     }
155
156     /**
157      * This builds the response for this SAML request.
158      * 
159      * @param responseContext <code>ProfileResponseContext</code>
160      * @return <code>Response</code>
161      * @throws EncryptionException if an error occurs attempting to encrypt data
162      */
163     protected Response buildResponse(ProfileResponseContext responseContext) throws EncryptionException {
164         AttributeQueryConfiguration config = getAttributeQueryConfiguration(responseContext.getProviderId());
165
166         /*
167          * required: samlp:Status, ID, Version, IssueInstant
168          */
169         Response response = responseBuilder.buildObject();
170         response.setVersion(SAML_VERSION);
171         response.setID(getIdGenerator().generateIdentifier());
172         response.setInResponseTo(responseContext.getIssuer());
173         response.setIssueInstant(responseContext.getIssueInstant());
174         response.setDestination(responseContext.getDestination());
175
176         response.setIssuer(buildIssuer(responseContext));
177
178         /*
179          * Will be hard coded in the future: if (consent != null) { response.setConsent(consent); }
180          * 
181          */
182
183         /*
184          * No extensions currently exist, will be hardcorded in the future: if (extensions != null) {
185          * response.setExtensions(extensions); }
186          * 
187          */
188
189         if (config.getSignAssertions()) {
190             Signature s = buildSignature();
191             s.setSigningKey(config.getSigningCredential().getPrivateKey());
192             if (config.getEncryptAssertion()) {
193                 // TODO load encryption parameters
194                 Encrypter encrypter = null;
195                 Assertion a = buildAssertion(responseContext);
196                 a.setSignature(s);
197                 Signer.signObject(s);
198                 response.getEncryptedAssertions().add(encrypter.encrypt(a));
199             } else {
200                 Assertion a = buildAssertion(responseContext);
201                 a.setSignature(s);
202                 Signer.signObject(s);
203                 response.getAssertions().add(a);
204             }
205         } else {
206             if (config.getEncryptAssertion()) {
207                 // TODO load encryption parameters
208                 Encrypter encrypter = null;
209                 response.getEncryptedAssertions().add(encrypter.encrypt(buildAssertion(responseContext)));
210             } else {
211                 response.getAssertions().add(buildAssertion(responseContext));
212             }
213         }
214         response.setStatus(buildStatus(StatusCode.SUCCESS_URI));
215         return response;
216     }
217
218     /**
219      * This builds the status response for this SAML request.
220      * 
221      * @param statusCodeUri <code>String</code> to set
222      * @return <code>Status</code>
223      */
224     private Status buildStatus(String statusCodeUri) {
225         Status status = statusBuilder.buildObject();
226         StatusCode statusCode = statusCodeBuilder.buildObject();
227         statusCode.setValue(statusCodeUri);
228         status.setStatusCode(statusCode);
229         return status;
230     }
231
232     /**
233      * This builds the assertion for this SAML request.
234      * 
235      * @param responseContext <code>ProfileResponseContext</code>
236      * @return <code>Assertion</code>
237      * @throws EncryptionException if an error occurs attempting to encrypt data
238      */
239     private Assertion buildAssertion(ProfileResponseContext responseContext) throws EncryptionException {
240         AttributeQueryConfiguration config = getAttributeQueryConfiguration(responseContext.getProviderId());
241
242         /*
243          * required: saml:Issuer, ID, Version, IssueInstant
244          */
245         Assertion assertion = assertionBuilder.buildObject();
246         assertion.setID(getIdGenerator().generateIdentifier());
247         assertion.setIssueInstant(responseContext.getIssueInstant());
248         assertion.setVersion(SAML_VERSION);
249         assertion.setIssuer(buildIssuer(responseContext));
250
251         // build subject
252         assertion.setSubject(buildSubject(responseContext, config.getEncryptNameID()));
253         // build conditions
254         assertion.setConditions(buildConditions(responseContext));
255         // build advice
256         assertion.setAdvice(buildAdvice());
257         // add attribute statement
258         assertion.getAttributeStatements().add(responseContext.getAttributeStatement());
259         return assertion;
260     }
261
262     /**
263      * This builds the issuer response for this SAML request.
264      * 
265      * @param responseContext <code>ProfileResponseContext</code>
266      * @return <code>Issuer</code>
267      */
268     private Issuer buildIssuer(ProfileResponseContext responseContext) {
269         RelyingPartyConfiguration relyingPartyConfiguration = getRelyingPartyConfiguration(responseContext
270                 .getProviderId());
271         Issuer issuer = issuerBuilder.buildObject();
272         issuer.setValue(relyingPartyConfiguration.getProviderId());
273         return issuer;
274     }
275
276     /**
277      * This builds the subject for this SAML request.
278      * 
279      * @param responseContext <code>ProfileResponseContext</code>
280      * @param encryptNameId <code>boolean</code>
281      * @return <code>Subject</code>
282      * @throws EncryptionException if encryption of the name id fails
283      */
284     private Subject buildSubject(ProfileResponseContext responseContext, boolean encryptNameId)
285             throws EncryptionException {
286         Subject subject = subjectBuilder.buildObject();
287         if (encryptNameId) {
288             // TODO load encryption parameters
289             Encrypter encrypter = null;
290             subject.setEncryptedID(encrypter.encrypt(responseContext.getMessage().getSubject().getNameID()));
291         } else {
292             subject.setNameID(responseContext.getMessage().getSubject().getNameID());
293             // TODO when is subject.setBaseID(newBaseID) called, if ever?
294         }
295         return subject;
296     }
297
298     /**
299      * This builds the conditions for this SAML request.
300      * 
301      * @param responseContext <code>ProfileResponseContext</code>
302      * @return <code>Conditions</code>
303      */
304     private Conditions buildConditions(ProfileResponseContext responseContext) {
305         AttributeQueryConfiguration config = getAttributeQueryConfiguration(responseContext.getProviderId());
306
307         Conditions conditions = conditionsBuilder.buildObject();
308         conditions.setNotBefore(responseContext.getIssueInstant());
309         conditions.setNotOnOrAfter(responseContext.getIssueInstant().plus(config.getAssertionLifetime()));
310
311         // add audience restrictions
312         AudienceRestriction audienceRestriction = audienceRestrictionBuilder.buildObject();
313         for (String s : config.getAssertionAudiences()) {
314             Audience audience = audienceBuilder.buildObject();
315             audience.setAudienceURI(s);
316             audienceRestriction.getAudiences().add(audience);
317         }
318         conditions.getAudienceRestrictions().add(audienceRestriction);
319
320         // add proxy restrictions
321         ProxyRestriction proxyRestriction = conditions.getProxyRestriction();
322         for (String s : config.getProxyAudiences()) {
323             Audience audience = audienceBuilder.buildObject();
324             audience.setAudienceURI(s);
325             proxyRestriction.getAudiences().add(audience);
326         }
327         proxyRestriction.setProxyCount(new Integer(config.getProxyCount()));
328
329         /*
330          * OneTimeUse and additional conditions not supported yet
331          */
332
333         return conditions;
334     }
335
336     /**
337      * This builds the advice for this SAML request.
338      * 
339      * @return <code>Advice</code>
340      */
341     private Advice buildAdvice() {
342         /*
343          * Advice not supported at this time
344          */
345         Advice advice = adviceBuilder.buildObject();
346         return advice;
347     }
348
349     /**
350      * This builds a signature for this SAML request.
351      * 
352      * @return <code>Signature</code>
353      */
354     private Signature buildSignature() {
355         Signature signature = signatureBuilder.buildObject(Signature.DEFAULT_ELEMENT_NAME);
356         return signature;
357     }
358 }