4d6631a6ae1a9f4f467b88b68c6bb8dce9e57123
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / idp / profile / saml2 / AbstractProfileHandler.java
1 /*
2  * Copyright [2007] [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 java.util.List;
20
21 import javax.servlet.ServletRequest;
22 import javax.servlet.ServletResponse;
23
24 import org.apache.log4j.Logger;
25 import org.joda.time.DateTime;
26 import org.opensaml.Configuration;
27 import org.opensaml.common.IdentifierGenerator;
28 import org.opensaml.common.SAMLObject;
29 import org.opensaml.common.SAMLVersion;
30 import org.opensaml.common.binding.BindingException;
31 import org.opensaml.common.binding.MessageDecoder;
32 import org.opensaml.common.binding.MessageEncoder;
33 import org.opensaml.common.impl.SecureRandomIdentifierGenerator;
34 import org.opensaml.saml2.core.Assertion;
35 import org.opensaml.saml2.core.Audience;
36 import org.opensaml.saml2.core.AudienceRestriction;
37 import org.opensaml.saml2.core.Conditions;
38 import org.opensaml.saml2.core.Issuer;
39 import org.opensaml.saml2.core.Response;
40 import org.opensaml.saml2.core.Status;
41 import org.opensaml.saml2.core.StatusCode;
42 import org.opensaml.saml2.core.StatusMessage;
43 import org.opensaml.saml2.core.Subject;
44 import org.opensaml.xml.XMLObjectBuilder;
45 import org.opensaml.xml.XMLObjectBuilderFactory;
46
47 import edu.internet2.middleware.shibboleth.common.profile.ProfileHandler;
48 import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyManager;
49
50 /**
51  * Common implementation details for profile handlers.
52  */
53 public abstract class AbstractProfileHandler implements ProfileHandler {
54
55     /** SAML Version for this profile handler. */
56     public static final SAMLVersion SAML_VERSION = SAMLVersion.VERSION_20;
57
58     /** Class logger. */
59     private static Logger log = Logger.getLogger(AbstractProfileHandler.class);
60
61     /** For building XML. */
62     private XMLObjectBuilderFactory builderFactory;
63
64     /** For generating random ids. */
65     private IdentifierGenerator idGenerator;
66
67     /** Relying party configuration. */
68     private RelyingPartyManager relyingPartyManager;
69
70     /** Builder for Response elements. */
71     protected XMLObjectBuilder responseBuilder;
72
73     /** Builder for Status elements. */
74     private XMLObjectBuilder statusBuilder;
75
76     /** Builder for StatusCode elements. */
77     private XMLObjectBuilder statusCodeBuilder;
78
79     /** Builder for StatusMessage elements. */
80     private XMLObjectBuilder statusMessageBuilder;
81
82     /** Builder for Issuer elements. */
83     protected XMLObjectBuilder issuerBuilder;
84
85     /**
86      * Default constructor.
87      */
88     public AbstractProfileHandler() {
89         builderFactory = Configuration.getBuilderFactory();
90         idGenerator = new SecureRandomIdentifierGenerator();
91
92         responseBuilder = builderFactory.getBuilder(Response.DEFAULT_ELEMENT_NAME);
93         statusBuilder = builderFactory.getBuilder(Status.DEFAULT_ELEMENT_NAME);
94         statusCodeBuilder = builderFactory.getBuilder(StatusCode.DEFAULT_ELEMENT_NAME);
95         statusMessageBuilder = builderFactory.getBuilder(StatusMessage.DEFAULT_ELEMENT_NAME);
96         issuerBuilder = builderFactory.getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
97     }
98
99     /**
100      * Returns the XML builder factory.
101      * 
102      * @return Returns the builderFactory.
103      */
104     public XMLObjectBuilderFactory getBuilderFactory() {
105         return builderFactory;
106     }
107
108     /**
109      * Returns the id generator.
110      * 
111      * @return Returns the idGenerator.
112      */
113     public IdentifierGenerator getIdGenerator() {
114         return idGenerator;
115     }
116
117     /**
118      * Returns the relying party manager.
119      * 
120      * @return Returns the relyingPartyManager.
121      */
122     public RelyingPartyManager getRelyingPartyManager() {
123         return relyingPartyManager;
124     }
125
126     /**
127      * Sets the relying party manager.
128      * 
129      * @param m The relyingPartyManager to set.
130      */
131     public void setRelyingPartyManager(RelyingPartyManager m) {
132         relyingPartyManager = m;
133     }
134
135     /**
136      * This decodes the attribute query message from the supplied request.
137      * 
138      * @param decoder <code>MessageDecoder</code>
139      * @param request <code>ServletRequest</code>
140      * @return <code>SAMLObject</code>
141      * @throws BindingException if the request cannot be decoded
142      */
143     public static SAMLObject decodeMessage(MessageDecoder<ServletRequest> decoder, ServletRequest request)
144             throws BindingException {
145
146         decoder.setRequest(request);
147         decoder.decode();
148         if (log.isDebugEnabled()) {
149             log.debug("decoded servlet request");
150         }
151
152         return decoder.getSAMLMessage();
153     }
154
155     /**
156      * This encodes the supplied response.
157      * 
158      * @param encoder <code>MessageEncoder</code>
159      * @param response <code>SAMLObject</code>
160      * @throws BindingException if the response cannot be encoded
161      */
162     public static void encodeResponse(MessageEncoder<ServletResponse> encoder, SAMLObject response)
163             throws BindingException {
164
165         encoder.setSAMLMessage(response);
166         encoder.encode();
167     }
168
169     /**
170      * Build a status message, with an optional second-level failure message.
171      * 
172      * @param topLevelCode The top-level status code. Should be from saml-core-2.0-os, sec. 3.2.2.2
173      * @param secondLevelCode An optional second-level failure code. Should be from saml-core-2.0-is, sec 3.2.2.2. If
174      *            null, no second-level Status element will be set.
175      * @param secondLevelFailureMessage An optional second-level failure message.
176      * 
177      * @return a Status object.
178      */
179     protected Status buildStatus(String topLevelCode, String secondLevelCode, String secondLevelFailureMessage) {
180
181         Status status = (Status) statusBuilder.buildObject(Status.DEFAULT_ELEMENT_NAME);
182         StatusCode statusCode = (StatusCode) statusCodeBuilder.buildObject(StatusCode.DEFAULT_ELEMENT_NAME);
183
184         statusCode.setValue(topLevelCode);
185         if (secondLevelCode != null) {
186             StatusCode secondLevelStatusCode = (StatusCode) statusCodeBuilder
187                     .buildObject(StatusCode.DEFAULT_ELEMENT_NAME);
188             secondLevelStatusCode.setValue(secondLevelCode);
189             statusCode.setStatusCode(secondLevelStatusCode);
190         }
191
192         if (secondLevelFailureMessage != null) {
193             StatusMessage msg = (StatusMessage) statusMessageBuilder.buildObject(StatusMessage.DEFAULT_ELEMENT_NAME);
194             msg.setMessage(secondLevelFailureMessage);
195             status.setMessage(msg);
196         }
197
198         return status;
199     }
200
201     /**
202      * Build a SAML 2 Response element with basic fields populated.
203      * 
204      * Failure handlers can send the returned response element to the RP. Success handlers should add the assertions
205      * before sending it.
206      * 
207      * @param inResponseTo The ID of the request this is in response to.
208      * @param issuer The URI of the RP issuing the response.
209      * @param status The response's status code.
210      * 
211      * @return The populated Response object.
212      */
213     protected Response buildResponse(String inResponseTo, String issuer, final Status status) {
214
215         Response response = (Response) responseBuilder.buildObject(Response.DEFAULT_ELEMENT_NAME);
216
217         Issuer i = (Issuer) issuerBuilder.buildObject(Issuer.DEFAULT_ELEMENT_NAME);
218         i.setValue(issuer);
219
220         response.setVersion(SAML_VERSION);
221         response.setId(getIdGenerator().generateIdentifier());
222         response.setInResponseto(inResponseTo);
223         response.setIssueInstance(new DateTime());
224         response.setIssuer(i);
225         response.setStatus(status);
226
227         return response;
228     }
229
230     protected Assertion buildAssertion(final Subject subject, final Conditions conditions, String issuer,
231             final String[] audiences) {
232
233         Assertion assertion = (Assertion) assertionBuilder.buildObject(Assertion.DEFAULT_ELEMENT_NAME);
234         assertion.setID(getIdGenerator().generateIdentifier());
235         assertion.setVersion(SAML_VERSION);
236         assertion.setIssueInstant(new DateTime());
237         assertion.setConditions(conditions);
238         assertion.setSubject(subject);
239
240         Issuer i = (Issuer) issuerBuilder.buildObject(Issuer.DEFAULT_ELEMENT_NAME);
241         i.setValue(issuer);
242         assertion.setIssuer(i);
243
244         // if audiences were specified, set an AudienceRestriction condition
245         if (audiences != null && audiences.length > 0) {
246
247             Conditions conditions = assertion.getConditions();
248             List<AudienceRestriction> audienceRestrictionConditions = conditions.getAudienceRestrictions();
249
250             for (String audienceURI : audiences) {
251
252                 Audience audience = (Audience) audienceBuilder.buildObject(Audience.DEFAULT_ELEMENT_NAME);
253                 audience.setAudienceURI(audienceURI);
254
255                 AudienceRestriction audienceRestriction = (AudienceRestriction) audienceRestrictionBuilder
256                         .buildObject(AudienceRestriction.DEFAULT_ELEMENT_NAME);
257                 List<Audience> audienceList = audienceRestriction.getAudiences();
258                 audienceList.add(audience);
259
260                 audienceRestrictionConditions.add(audienceRestriction);
261             }
262         }
263
264         return assertion;
265     }
266 }