Renamed AttributeQuery to AbstractAttributeQuery.
[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      * @param issuer <code>String</code>
161      * @param destination <code>String</code>
162      * @return <code>Response</code>
163      * @throws EncryptionException if an error occurs attempting to encrypt data
164      */
165     protected Response buildResponse(ProfileResponseContext responseContext, String issuer, String destination)
166             throws EncryptionException {
167         AttributeQueryConfiguration config = getAttributeQueryConfiguration(responseContext.getProviderId());
168
169         /*
170          * required: samlp:Status, ID, Version, IssueInstant
171          */
172         Response response = responseBuilder.buildObject();
173         response.setVersion(SAML_VERSION);
174         response.setID(getIdGenerator().generateIdentifier());
175         response.setInResponseTo(issuer);
176         response.setIssueInstant(responseContext.getIssueInstant());
177         response.setDestination(destination);
178
179         response.setIssuer(buildIssuer(responseContext.getProviderId()));
180
181         /*
182          * Will be hard coded in the future: if (consent != null) { response.setConsent(consent); }
183          * 
184          */
185
186         /*
187          * No extensions currently exist, will be hardcorded in the future: if (extensions != null) {
188          * response.setExtensions(extensions); }
189          * 
190          */
191
192         if (config.getSignAssertions()) {
193             Signature s = buildSignature();
194             s.setSigningKey(config.getSigningCredential().getPrivateKey());
195             if (config.getEncryptAssertion()) {
196                 // TODO load encryption parameters
197                 Encrypter encrypter = null;
198                 Assertion a = buildAssertion(responseContext);
199                 a.setSignature(s);
200                 Signer.signObject(s);
201                 response.getEncryptedAssertions().add(encrypter.encrypt(a));
202             } else {
203                 Assertion a = buildAssertion(responseContext);
204                 a.setSignature(s);
205                 Signer.signObject(s);
206                 response.getAssertions().add(a);
207             }
208         } else {
209             if (config.getEncryptAssertion()) {
210                 // TODO load encryption parameters
211                 Encrypter encrypter = null;
212                 response.getEncryptedAssertions().add(encrypter.encrypt(buildAssertion(responseContext)));
213             } else {
214                 response.getAssertions().add(buildAssertion(responseContext));
215             }
216         }
217         response.setStatus(buildStatus(StatusCode.SUCCESS_URI));
218         return response;
219     }
220
221     /**
222      * This builds the status response for this SAML request.
223      * 
224      * @param statusCodeUri <code>String</code> to set
225      * @return <code>Status</code>
226      */
227     private Status buildStatus(String statusCodeUri) {
228         Status status = statusBuilder.buildObject();
229         StatusCode statusCode = statusCodeBuilder.buildObject();
230         statusCode.setValue(statusCodeUri);
231         status.setStatusCode(statusCode);
232         return status;
233     }
234
235     /**
236      * This builds the assertion for this SAML request.
237      * 
238      * @param responseContext <code>ProfileResponseContext</code>
239      * @return <code>Assertion</code>
240      * @throws EncryptionException if an error occurs attempting to encrypt data
241      */
242     private Assertion buildAssertion(ProfileResponseContext responseContext) throws EncryptionException {
243         AttributeQueryConfiguration config = getAttributeQueryConfiguration(responseContext.getProviderId());
244
245         /*
246          * required: saml:Issuer, ID, Version, IssueInstant
247          */
248         Assertion assertion = assertionBuilder.buildObject();
249         assertion.setID(getIdGenerator().generateIdentifier());
250         assertion.setIssueInstant(responseContext.getIssueInstant());
251         assertion.setVersion(SAML_VERSION);
252         assertion.setIssuer(buildIssuer(responseContext.getProviderId()));
253
254         // build subject
255         assertion.setSubject(buildSubject(responseContext.getMessage().getSubject(), config.getEncryptNameID()));
256         // build conditions
257         assertion.setConditions(buildConditions(responseContext));
258         // build advice
259         assertion.setAdvice(buildAdvice());
260         // add attribute statement
261         assertion.getAttributeStatements().add(responseContext.getAttributeStatement());
262         return assertion;
263     }
264
265     /**
266      * This builds the issuer response for this SAML request.
267      * 
268      * @param providerId <code>String</code>
269      * @return <code>Issuer</code>
270      */
271     private Issuer buildIssuer(String providerId) {
272         RelyingPartyConfiguration relyingPartyConfiguration = getRelyingPartyConfiguration(providerId);
273         Issuer issuer = issuerBuilder.buildObject();
274         issuer.setValue(relyingPartyConfiguration.getProviderId());
275         return issuer;
276     }
277
278     /**
279      * This builds the subject for this SAML request.
280      * 
281      * @param messageSubject <code>Subject</code>
282      * @param encryptNameId <code>boolean</code>
283      * @return <code>Subject</code>
284      * @throws EncryptionException if encryption of the name id fails
285      */
286     private Subject buildSubject(Subject messageSubject, boolean encryptNameId) throws EncryptionException {
287         Subject subject = subjectBuilder.buildObject();
288         if (encryptNameId) {
289             // TODO load encryption parameters
290             Encrypter encrypter = null;
291             subject.setEncryptedID(encrypter.encrypt(messageSubject.getNameID()));
292         } else {
293             subject.setNameID(messageSubject.getNameID());
294             // TODO when is subject.setBaseID(newBaseID) called, if ever?
295         }
296         return subject;
297     }
298
299     /**
300      * This builds the conditions for this SAML request.
301      * 
302      * @param responseContext <code>ProfileResponseContext</code>
303      * @return <code>Conditions</code>
304      */
305     private Conditions buildConditions(ProfileResponseContext responseContext) {
306         AttributeQueryConfiguration config = getAttributeQueryConfiguration(responseContext.getProviderId());
307
308         Conditions conditions = conditionsBuilder.buildObject();
309         conditions.setNotBefore(responseContext.getIssueInstant());
310         conditions.setNotOnOrAfter(responseContext.getIssueInstant().plus(config.getAssertionLifetime()));
311
312         // add audience restrictions
313         AudienceRestriction audienceRestriction = audienceRestrictionBuilder.buildObject();
314         for (String s : config.getAssertionAudiences()) {
315             Audience audience = audienceBuilder.buildObject();
316             audience.setAudienceURI(s);
317             audienceRestriction.getAudiences().add(audience);
318         }
319         conditions.getAudienceRestrictions().add(audienceRestriction);
320
321         // add proxy restrictions
322         ProxyRestriction proxyRestriction = conditions.getProxyRestriction();
323         for (String s : config.getProxyAudiences()) {
324             Audience audience = audienceBuilder.buildObject();
325             audience.setAudienceURI(s);
326             proxyRestriction.getAudiences().add(audience);
327         }
328         proxyRestriction.setProxyCount(new Integer(config.getProxyCount()));
329
330         /*
331          * OneTimeUse and additional conditions not supported yet
332          */
333
334         return conditions;
335     }
336
337     /**
338      * This builds the advice for this SAML request.
339      * 
340      * @return <code>Advice</code>
341      */
342     private Advice buildAdvice() {
343         /*
344          * Advice not supported at this time
345          */
346         Advice advice = adviceBuilder.buildObject();
347         return advice;
348     }
349
350     /**
351      * This builds a signature for this SAML request.
352      * 
353      * @return <code>Signature</code>
354      */
355     private Signature buildSignature() {
356         Signature signature = signatureBuilder.buildObject(Signature.DEFAULT_ELEMENT_NAME);
357         return signature;
358     }
359 }