4261b33da9225e6bc1572a7b5f061d6f7351785c
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / idp / profile / saml2 / AbstractSAML2ProfileHandler.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.idp.profile.AbstractSAMLProfileHandler;
48
49 /**
50  * Common implementation details for profile handlers.
51  */
52 public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHandler {
53
54     /** SAML Version for this profile handler. */
55     public static final SAMLVersion SAML_VERSION = SAMLVersion.VERSION_20;
56
57     /** Class logger. */
58     private static Logger log = Logger.getLogger(AbstractSAML2ProfileHandler.class);
59
60     /** For building XML. */
61     private XMLObjectBuilderFactory builderFactory;
62
63     /** For generating random ids. */
64     private IdentifierGenerator idGenerator;
65
66     /** Builder for Response elements. */
67     protected XMLObjectBuilder responseBuilder;
68
69     /** Builder for Status elements. */
70     private XMLObjectBuilder statusBuilder;
71
72     /** Builder for StatusCode elements. */
73     private XMLObjectBuilder statusCodeBuilder;
74
75     /** Builder for StatusMessage elements. */
76     private XMLObjectBuilder statusMessageBuilder;
77
78     /** Builder for Issuer elements. */
79     protected XMLObjectBuilder issuerBuilder;
80
81     /**
82      * Default constructor.
83      */
84     public AbstractSAML2ProfileHandler() {
85         builderFactory = Configuration.getBuilderFactory();
86         idGenerator = new SecureRandomIdentifierGenerator();
87
88         responseBuilder = builderFactory.getBuilder(Response.DEFAULT_ELEMENT_NAME);
89         statusBuilder = builderFactory.getBuilder(Status.DEFAULT_ELEMENT_NAME);
90         statusCodeBuilder = builderFactory.getBuilder(StatusCode.DEFAULT_ELEMENT_NAME);
91         statusMessageBuilder = builderFactory.getBuilder(StatusMessage.DEFAULT_ELEMENT_NAME);
92         issuerBuilder = builderFactory.getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
93     }
94
95     /**
96      * Returns the XML builder factory.
97      * 
98      * @return Returns the builderFactory.
99      */
100     public XMLObjectBuilderFactory getBuilderFactory() {
101         return builderFactory;
102     }
103
104     /**
105      * Returns the id generator.
106      * 
107      * @return Returns the idGenerator.
108      */
109     public IdentifierGenerator getIdGenerator() {
110         return idGenerator;
111     }
112
113     /**
114      * Build a status message, with an optional second-level failure message.
115      * 
116      * @param topLevelCode The top-level status code. Should be from saml-core-2.0-os, sec. 3.2.2.2
117      * @param secondLevelCode An optional second-level failure code. Should be from saml-core-2.0-is, sec 3.2.2.2. If
118      *            null, no second-level Status element will be set.
119      * @param secondLevelFailureMessage An optional second-level failure message.
120      * 
121      * @return a Status object.
122      */
123     protected Status buildStatus(String topLevelCode, String secondLevelCode, String secondLevelFailureMessage) {
124
125         Status status = (Status) statusBuilder.buildObject(Status.DEFAULT_ELEMENT_NAME);
126         StatusCode statusCode = (StatusCode) statusCodeBuilder.buildObject(StatusCode.DEFAULT_ELEMENT_NAME);
127
128         statusCode.setValue(topLevelCode);
129         if (secondLevelCode != null) {
130             StatusCode secondLevelStatusCode = (StatusCode) statusCodeBuilder
131                     .buildObject(StatusCode.DEFAULT_ELEMENT_NAME);
132             secondLevelStatusCode.setValue(secondLevelCode);
133             statusCode.setStatusCode(secondLevelStatusCode);
134         }
135
136         if (secondLevelFailureMessage != null) {
137             StatusMessage msg = (StatusMessage) statusMessageBuilder.buildObject(StatusMessage.DEFAULT_ELEMENT_NAME);
138             msg.setMessage(secondLevelFailureMessage);
139             status.setMessage(msg);
140         }
141
142         return status;
143     }
144
145     /**
146      * Build a SAML 2 Response element with basic fields populated.
147      * 
148      * Failure handlers can send the returned response element to the RP. Success handlers should add the assertions
149      * before sending it.
150      * 
151      * @param inResponseTo The ID of the request this is in response to.
152      * @param issuer The URI of the RP issuing the response.
153      * @param status The response's status code.
154      * 
155      * @return The populated Response object.
156      */
157     protected Response buildResponse(String inResponseTo, String issuer, final Status status) {
158
159         Response response = (Response) responseBuilder.buildObject(Response.DEFAULT_ELEMENT_NAME);
160
161         Issuer i = (Issuer) issuerBuilder.buildObject(Issuer.DEFAULT_ELEMENT_NAME);
162         i.setValue(issuer);
163
164         response.setVersion(SAML_VERSION);
165         response.setId(getIdGenerator().generateIdentifier());
166         response.setInResponseto(inResponseTo);
167         response.setIssueInstance(new DateTime());
168         response.setIssuer(i);
169         response.setStatus(status);
170
171         return response;
172     }
173
174     protected Assertion buildAssertion(final Subject subject, final Conditions conditions, String issuer,
175             final String[] audiences) {
176
177         Assertion assertion = (Assertion) assertionBuilder.buildObject(Assertion.DEFAULT_ELEMENT_NAME);
178         assertion.setID(getIdGenerator().generateIdentifier());
179         assertion.setVersion(SAML_VERSION);
180         assertion.setIssueInstant(new DateTime());
181         assertion.setConditions(conditions);
182         assertion.setSubject(subject);
183
184         Issuer i = (Issuer) issuerBuilder.buildObject(Issuer.DEFAULT_ELEMENT_NAME);
185         i.setValue(issuer);
186         assertion.setIssuer(i);
187
188         // if audiences were specified, set an AudienceRestriction condition
189         if (audiences != null && audiences.length > 0) {
190
191             Conditions conditions = assertion.getConditions();
192             List<AudienceRestriction> audienceRestrictionConditions = conditions.getAudienceRestrictions();
193
194             for (String audienceURI : audiences) {
195
196                 Audience audience = (Audience) audienceBuilder.buildObject(Audience.DEFAULT_ELEMENT_NAME);
197                 audience.setAudienceURI(audienceURI);
198
199                 AudienceRestriction audienceRestriction = (AudienceRestriction) audienceRestrictionBuilder
200                         .buildObject(AudienceRestriction.DEFAULT_ELEMENT_NAME);
201                 List<Audience> audienceList = audienceRestriction.getAudiences();
202                 audienceList.add(audience);
203
204                 audienceRestrictionConditions.add(audienceRestriction);
205             }
206         }
207
208         return assertion;
209     }
210 }