rework saml 1 sso handler. it's cleaner and fixes a lot of small bugs
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / idp / profile / saml1 / AbstractSAML1ProfileHandler.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.saml1;
18
19 import javax.servlet.ServletResponse;
20
21 import org.apache.log4j.Logger;
22 import org.opensaml.Configuration;
23 import org.opensaml.common.SAMLObjectBuilder;
24 import org.opensaml.common.SAMLVersion;
25 import org.opensaml.common.binding.BindingException;
26 import org.opensaml.common.binding.encoding.MessageEncoder;
27 import org.opensaml.common.impl.SAMLObjectContentReference;
28 import org.opensaml.common.impl.SecureRandomIdentifierGenerator;
29 import org.opensaml.saml1.core.Assertion;
30 import org.opensaml.saml1.core.Status;
31 import org.opensaml.saml1.core.StatusCode;
32 import org.opensaml.saml1.core.StatusMessage;
33 import org.opensaml.saml1.core.Response;
34 import org.opensaml.saml2.metadata.AssertionConsumerService;
35 import org.opensaml.saml2.metadata.Endpoint;
36 import org.opensaml.saml2.metadata.RoleDescriptor;
37 import org.opensaml.saml2.metadata.provider.MetadataProvider;
38 import org.opensaml.xml.XMLObjectBuilder;
39 import org.opensaml.xml.XMLObjectBuilderFactory;
40 import org.opensaml.xml.security.credential.Credential;
41 import org.opensaml.xml.signature.Signature;
42 import org.opensaml.xml.signature.Signer;
43
44 import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
45 import edu.internet2.middleware.shibboleth.common.profile.ProfileResponse;
46 import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
47 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml1.AbstractSAML1ProfileConfiguration;
48 import edu.internet2.middleware.shibboleth.idp.profile.AbstractSAMLProfileHandler;
49
50
51 /**
52  * Common implementation details for profile handlers.
53  */
54 public abstract class AbstractSAML1ProfileHandler extends AbstractSAMLProfileHandler {
55     
56     /** SAML Version for this profile handler. */
57     public static final SAMLVersion SAML_VERSION = SAMLVersion.VERSION_11;
58     
59     /** Class logger. */
60     private static Logger log = Logger.getLogger(AbstractSAML1ProfileHandler.class);
61     
62     /** For generating random ids. */
63     private SecureRandomIdentifierGenerator idGenerator;
64     
65     /** Builder for Status objects. */
66     protected SAMLObjectBuilder<Status> statusBuilder;
67     
68     /** Builder for StatusCode objects. */
69     protected SAMLObjectBuilder<StatusCode> statusCodeBuilder;
70     
71     /** Builder for StatusMessage objects. */
72     protected SAMLObjectBuilder<StatusMessage> statusMessageBuilder;
73     
74     /** For building signature. */
75     private XMLObjectBuilder<Signature> signatureBuilder;
76     
77     /**
78      * Default constructor.
79      */
80     public AbstractSAML1ProfileHandler() {
81         idGenerator = new SecureRandomIdentifierGenerator();
82         
83         statusBuilder        = (SAMLObjectBuilder<Status>) getBuilderFactory().getBuilder(Status.DEFAULT_ELEMENT_NAME);
84         statusCodeBuilder    = (SAMLObjectBuilder<StatusCode>) getBuilderFactory().getBuilder(StatusCode.DEFAULT_ELEMENT_NAME);
85         statusMessageBuilder = (SAMLObjectBuilder<StatusMessage>) getBuilderFactory().getBuilder(StatusMessage.DEFAULT_ELEMENT_NAME);
86         signatureBuilder     = (XMLObjectBuilder<Signature>) getBuilderFactory().getBuilder(Signature.DEFAULT_ELEMENT_NAME);
87     }
88     
89     /**
90      * Returns the id generator.
91      *
92      * @return Returns the idGenerator.
93      */
94     public SecureRandomIdentifierGenerator getIdGenerator() {
95         return idGenerator;
96     }
97     
98     /**
99      * Build a SAML 1 Status element.
100      *
101      * @param statusCode The status code - see oasis-sstc-saml-core-1.1, section 3.4.3.1.
102      * @param statusMessage The status message, or <code>null</code> if none is to be set.
103      *
104      * @return The Status object, or <code>null</code> on error.
105      */
106     protected Status buildStatus(String statusCode, String statusMessage) {
107         
108         if (statusCode == null || statusCode.equals("")) {
109             return null;
110         }
111         
112         Status status = statusBuilder.buildObject();
113         StatusCode sc = statusCodeBuilder.buildObject();
114         sc.setValue(statusCode);
115         status.setStatusCode(sc);
116         
117         if (statusMessage != null || !(statusMessage.equals(""))) {
118             
119             StatusMessage sm = statusMessageBuilder.buildObject();
120             sm.setMessage(statusMessage);
121             status.setStatusMessage(sm);
122         }
123         
124         return status;
125     }
126     
127     /**
128      * Signs the given assertion if either the current profile configuration or the relying party configuration contains
129      * signing credentials.
130      *
131      * @param assertion assertion to sign
132      * @param rpConfig relying party configuration
133      * @param profileConfig current profile configuration
134      */
135     protected void signAssertion(Assertion assertion, RelyingPartyConfiguration rpConfig,
136             AbstractSAML1ProfileConfiguration profileConfig) {
137         if (!profileConfig.getSignAssertions()) {
138             return;
139         }
140         
141         Credential signatureCredential = profileConfig.getSigningCredential();
142         if (signatureCredential == null) {
143             signatureCredential = rpConfig.getDefaultSigningCredential();
144         }
145         
146         if (signatureCredential == null) {
147             return;
148         }
149         
150         SAMLObjectContentReference contentRef = new SAMLObjectContentReference(assertion);
151         Signature signature = signatureBuilder.buildObject(Signature.DEFAULT_ELEMENT_NAME);
152         signature.getContentReferences().add(contentRef);
153         assertion.setSignature(signature);
154         
155         Signer.signObject(signature);
156     }
157     
158     /**
159      * Encode a SAML Response.
160      * 
161      * @param binding The SAML protocol binding to use.
162      * @param profileResponse The Raw output stream to send the message to.
163      * @param samlResponse The SAML Response to send.
164      * @param relyingParty The relying party to send the message to.
165      * @param roleDescriptor The role of the message sender.
166      * @param endpoint The endpoint to which the message should be send.
167      * 
168      * @throws ProfileException On error.
169      */
170     protected void encodeResponse(String binding,final ProfileResponse<ServletResponse> profileResponse,
171             final Response samlResponse, final RelyingPartyConfiguration relyingParty,
172             final RoleDescriptor roleDescriptor, final Endpoint endpoint) throws ProfileException {
173         
174         MessageEncoder<ServletResponse> encoder = getMessageEncoderFactory().getMessageEncoder(binding);
175         if (encoder == null) {
176             log.error("No MessageEncoder registered for " + binding);
177             throw new ProfileException("No MessageEncoder registered for " + binding);
178         }
179         
180         encoder.setResponse(profileResponse.getRawResponse());
181         encoder.setIssuer(relyingParty.getProviderId());
182         encoder.setMetadataProvider(getRelyingPartyConfigurationManager().getMetadataProvider());
183         encoder.setRelyingPartyRole(roleDescriptor);
184         encoder.setSigningCredential(relyingParty.getDefaultSigningCredential());
185         encoder.setSamlMessage(samlResponse);
186         encoder.setRelyingPartyEndpoint(endpoint);
187         
188         try {
189             encoder.encode();
190         } catch (BindingException ex) {
191             log.error("Unable to encode response the relying party: " + relyingParty.getRelyingPartyId(), ex);
192             throw new ProfileException("Unable to encode response the relying party: "
193                     + relyingParty.getRelyingPartyId(), ex);
194         }
195         
196     }
197 }