fa64b0dcf6407e8beed1b4c0ce9bde59cdc1861b
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / idp / provider / SAMLv1_1ArtifactQueryHandler.java
1 /*
2  * Copyright [2005] [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.provider;
18
19 import java.security.cert.X509Certificate;
20 import java.util.ArrayList;
21 import java.util.Iterator;
22
23 import javax.security.auth.x500.X500Principal;
24 import javax.servlet.ServletException;
25 import javax.servlet.http.HttpServletRequest;
26 import javax.servlet.http.HttpServletResponse;
27
28 import org.apache.log4j.Logger;
29 import org.opensaml.SAMLAssertion;
30 import org.opensaml.SAMLBinding;
31 import org.opensaml.SAMLException;
32 import org.opensaml.SAMLRequest;
33 import org.opensaml.SAMLResponse;
34 import org.opensaml.XML;
35 import org.opensaml.artifact.Artifact;
36 import org.opensaml.saml2.metadata.EntityDescriptor;
37 import org.opensaml.saml2.metadata.SPSSODescriptor;
38 import org.opensaml.saml2.metadata.provider.MetadataProviderException;
39 import org.opensaml.security.impl.HttpX509EntityCredential;
40 import org.w3c.dom.Element;
41
42 import edu.internet2.middleware.shibboleth.artifact.ArtifactMapping;
43 import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
44 import edu.internet2.middleware.shibboleth.idp.IdPProtocolHandler;
45 import edu.internet2.middleware.shibboleth.idp.IdPProtocolSupport;
46 import edu.internet2.middleware.shibboleth.idp.RequestHandlingException;
47
48 /**
49  * @author Walter Hoehn
50  */
51 public class SAMLv1_1ArtifactQueryHandler extends SAMLv1_Base_QueryHandler implements IdPProtocolHandler {
52
53         private static Logger log = Logger.getLogger(SAMLv1_1ArtifactQueryHandler.class.getName());
54         private SAMLBinding binding;
55
56         public SAMLv1_1ArtifactQueryHandler(Element config) throws ShibbolethConfigurationException {
57
58                 super(config);
59         }
60
61         /*
62          * @see edu.internet2.middleware.shibboleth.idp.ProtocolHandler#getHandlerName()
63          */
64         public String getHandlerName() {
65
66                 return "SAML v1.1 Artifact Query";
67         }
68
69         /*
70          * @see edu.internet2.middleware.shibboleth.idp.ProtocolHandler#processRequest(javax.servlet.http.HttpServletRequest,
71          *      javax.servlet.http.HttpServletResponse, edu.internet2.middleware.shibboleth.idp.ProtocolSupport)
72          */
73         public void processRequest(HttpServletRequest request, HttpServletResponse response, IdPProtocolSupport support)
74                         throws RequestHandlingException, ServletException {
75
76                 log.info("Received a request to dereference assertion artifacts.");
77
78                 SAMLRequest samlRequest = parseSAMLRequest(request);
79
80                 try {
81
82                         // Pull credential from request
83                         X509Certificate[] chain = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
84                         if (chain == null || chain.length == 0
85                                         || chain[0].getSubjectX500Principal().getName(X500Principal.RFC2253).equals("")) {
86                                 // The spec says that mutual authentication is required for the
87                                 // artifact profile
88                                 if (samlRequest.isSigned()) {
89                                         log.info("Request is signed, will authenticate it later.");
90                                 } else {
91                                         log.info("Request is from an unauthenticated serviceprovider.");
92                                         throw new SAMLException(SAMLException.REQUESTER,
93                                                         "SAML Artifacts cannot be dereferenced for unauthenticated requesters.");
94                                 }
95                         } else {
96                                 log.info("Request contains TLS credential: ("
97                                                 + chain[0].getSubjectX500Principal().getName(X500Principal.RFC2253) + ").");
98                         }
99                         ArrayList<SAMLAssertion> assertions = new ArrayList<SAMLAssertion>();
100                         Iterator artifacts = samlRequest.getArtifacts();
101
102                         if (!artifacts.hasNext()) {
103                                 log.error("Protocol Handler received a SAML Request, but is unable to handle it.  No "
104                                                 + "artifacts were included in the request.");
105                                 throw new SAMLException(SAMLException.REQUESTER, "General error processing request.");
106                         }
107
108                         int queriedArtifacts = 0;
109                         // for transaction log
110                         StringBuffer dereferencedArtifacts = new StringBuffer();
111
112                         while (artifacts.hasNext()) {
113                                 queriedArtifacts++;
114                                 Artifact artifact = (Artifact) artifacts.next();
115                                 log.info("Dereferencing artifact: (" + artifact.encode() + ").");
116                                 ArtifactMapping mapping = support.getArtifactMapper().recoverAssertion(artifact);
117
118                                 if (mapping == null) {
119                                         log.info("Could not map artifact to a SAML Assertion.");
120
121                                 } else if (mapping.isExpired()) {
122                                         log.error("Artifact is expired.  Skipping...");
123
124                                 } else {
125                                         SAMLAssertion assertion = mapping.getAssertion();
126                                         // See if we have metadata for this provider
127                                         EntityDescriptor provider = null;
128                                         try {
129                                                 provider = support.getEntityDescriptor(mapping.getServiceProviderId());
130                                         } catch (MetadataProviderException e) {
131                                                 log.error("Metadata lookup for provider (" + mapping.getServiceProviderId()
132                                                                 + ") encountered an error: " + e);
133                                         }
134                                         if (provider == null) {
135                                                 log.info("No metadata found for provider: (" + mapping.getServiceProviderId() + ").");
136                                                 throw new SAMLException(SAMLException.REQUESTER, "Invalid service provider.");
137                                         }
138                                         SPSSODescriptor role = provider.getSPSSODescriptor(XML.SAML11_PROTOCOL_ENUM);
139                                         if (role == null) {
140                                                 log.info("SPSSO role not found in metadata for provider: (" + mapping.getServiceProviderId()
141                                                                 + ").");
142                                                 throw new SAMLException(SAMLException.REQUESTER, "Invalid service provider role.");
143                                         }
144
145                                         boolean authenticated = false;
146
147                                         // Make sure that the suppplied credential is valid for the provider to which the artifact was
148                                         // issued
149                                         if (chain != null && chain.length > 0) {
150                                                 if (!support.getTrustEngine().validate(new HttpX509EntityCredential(request), role)) {
151                                                         log.error("Supplied TLS credential ("
152                                                                         + chain[0].getSubjectX500Principal().getName(X500Principal.RFC2253)
153                                                                         + ") is NOT valid for provider (" + mapping.getServiceProviderId()
154                                                                         + "), to whom this artifact was issued.");
155                                                         throw new SAMLException(SAMLException.REQUESTER, "Invalid credential.");
156                                                 }
157                                                 authenticated = true;
158                                         }
159                                         if (samlRequest.isSigned()) {
160
161                                                 if (!support.getTrustEngine().validate(samlRequest, role)) {
162                                                         log.error("Signed SAML request message did NOT contain a valid signature from provider ("
163                                                                         + mapping.getServiceProviderId() + "), to whom this artifact was issued.");
164                                                         throw new SAMLException(SAMLException.REQUESTER, "Invalid signature.");
165                                                 }
166                                                 authenticated = true;
167                                         }
168                                         if (!authenticated) {
169                                                 log.info("Request could not be authenticated.");
170                                                 throw new SAMLException(SAMLException.REQUESTER,
171                                                                 "SAML Artifacts cannot be dereferenced for unauthenticated requesters.");
172                                         }
173                                         log.debug("Supplied credentials validated for the provider to which this artifact was issued.");
174                                         assertions.add(assertion);
175                                         dereferencedArtifacts.append("(" + artifact.encode() + ")");
176                                 }
177                         }
178
179                         // The spec requires that if any artifacts are dereferenced, they must
180                         // all be dereferenced
181                         if (assertions.size() > 0 && assertions.size() != queriedArtifacts) { throw new SAMLException(
182                                         SAMLException.REQUESTER, "Unable to successfully dereference all artifacts."); }
183
184                         // Create and send response
185                         // The spec says that we should send "success" in the case where no artifacts match
186                         SAMLResponse samlResponse = new SAMLResponse(samlRequest.getId(), null, assertions, null);
187                         if (log.isDebugEnabled()) {
188                                 log.debug("Dumping generated SAML Response:" + System.getProperty("line.separator")
189                                                 + samlResponse.toString());
190                         }
191
192                         support.getTransactionLog().info(
193                                         "Succesfully dereferenced the following artifacts: " + dereferencedArtifacts.toString());
194
195                         binding.respond(response, samlResponse, null);
196
197                 } catch (SAMLException e) {
198                         respondWithError(response, samlRequest, e);
199                 }
200         }
201
202 }