Implement SP authn by credential names via metadata, dump 1.1 legacy mode.
[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.io.IOException;
20 import java.security.cert.X509Certificate;
21 import java.util.ArrayList;
22 import java.util.Iterator;
23
24 import javax.security.auth.x500.X500Principal;
25 import javax.servlet.ServletException;
26 import javax.servlet.http.HttpServletRequest;
27 import javax.servlet.http.HttpServletResponse;
28
29 import org.apache.log4j.Logger;
30 import org.opensaml.SAMLAssertion;
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.w3c.dom.Element;
37
38 import edu.internet2.middleware.shibboleth.artifact.ArtifactMapping;
39 import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
40 import edu.internet2.middleware.shibboleth.idp.IdPProtocolHandler;
41 import edu.internet2.middleware.shibboleth.idp.IdPProtocolSupport;
42 import edu.internet2.middleware.shibboleth.metadata.EntityDescriptor;
43 import edu.internet2.middleware.shibboleth.metadata.RoleDescriptor;
44
45 /**
46  * @author Walter Hoehn
47  */
48 public class SAMLv1_1ArtifactQueryHandler extends BaseServiceHandler implements IdPProtocolHandler {
49
50         private static Logger log = Logger.getLogger(SAMLv1_1ArtifactQueryHandler.class.getName());
51
52         public SAMLv1_1ArtifactQueryHandler(Element config) throws ShibbolethConfigurationException {
53
54                 super(config);
55         }
56
57         /*
58          * @see edu.internet2.middleware.shibboleth.idp.ProtocolHandler#getHandlerName()
59          */
60         public String getHandlerName() {
61
62                 return "SAML v1.1 Artifact Query";
63         }
64
65         /*
66          * @see edu.internet2.middleware.shibboleth.idp.ProtocolHandler#processRequest(javax.servlet.http.HttpServletRequest,
67          *      javax.servlet.http.HttpServletResponse, edu.internet2.middleware.shibboleth.idp.ProtocolSupport)
68          */
69         public SAMLResponse processRequest(HttpServletRequest request, HttpServletResponse response,
70                         SAMLRequest samlRequest, IdPProtocolSupport support) throws SAMLException, IOException, ServletException {
71
72                 log.info("Recieved a request to dereference assertion artifacts.");
73
74                 // Pull credential from request
75                 X509Certificate credential = getCredentialFromProvider(request);
76                 if (credential == null || credential.getSubjectX500Principal().getName(X500Principal.RFC2253).equals("")) {
77                         // The spec says that mutual authentication is required for the
78                         // artifact profile
79                         log.info("Request is from an unauthenticated serviceprovider.");
80                         throw new SAMLException(SAMLException.REQUESTER,
81                                         "SAML Artifacts cannot be dereferenced for unauthenticated requesters.");
82                 }
83                 log.info("Request contains credential: (" + credential.getSubjectX500Principal().getName(X500Principal.RFC2253)
84                                 + ").");
85                 ArrayList assertions = new ArrayList();
86                 Iterator artifacts = samlRequest.getArtifacts();
87
88                 if (!artifacts.hasNext()) {
89                         log.error("Protocol Handler received a SAML Request, but is unable to handle it.  No "
90                                         + "artifacts were included in the request.");
91                         throw new SAMLException(SAMLException.REQUESTER, "General error processing request.");
92                 }
93
94                 int queriedArtifacts = 0;
95                 // for transaction log
96                 StringBuffer dereferencedArtifacts = new StringBuffer();
97
98                 while (artifacts.hasNext()) {
99                         queriedArtifacts++;
100                         Artifact artifact = (Artifact) artifacts.next();
101                         log.info("Dereferencing artifact: (" + artifact.encode() + ").");
102                         ArtifactMapping mapping = support.getArtifactMapper().recoverAssertion(artifact);
103
104                         if (mapping == null) {
105                                 log.info("Could not map artifact to a SAML Assertion.");
106
107                         } else if (mapping.isExpired()) {
108                                 log.error("Artifact is expired.  Skipping...");
109
110                         } else {
111                                 SAMLAssertion assertion = mapping.getAssertion();
112                                 // See if we have metadata for this provider
113                                 EntityDescriptor provider = support.lookup(mapping.getServiceProviderId());
114                                 if (provider == null) {
115                                         log.info("No metadata found for provider: (" + mapping.getServiceProviderId() + ").");
116                                         throw new SAMLException(SAMLException.REQUESTER, "Invalid service provider.");
117                                 }
118                                 RoleDescriptor role = provider.getSPSSODescriptor(XML.SAML11_PROTOCOL_ENUM);
119                                 if (role == null) {
120                                         log
121                                                         .info("SPSSO role not found in metadata for provider: (" + mapping.getServiceProviderId()
122                                                                         + ").");
123                                         throw new SAMLException(SAMLException.REQUESTER, "Invalid service provider role.");
124                                 }
125
126                                 // Make sure that the suppplied credential is valid for the provider to which the artifact was issued
127                                 X509Certificate[] chain = (X509Certificate[]) request
128                                                 .getAttribute("javax.servlet.request.X509Certificate");
129                                 if (!support.getTrust().validate((chain != null && chain.length > 0) ? chain[0] : null, chain, role)) {
130                                         log.error("Supplied credential ("
131                                                         + credential.getSubjectX500Principal().getName(X500Principal.RFC2253)
132                                                         + ") is NOT valid for provider (" + mapping.getServiceProviderId()
133                                                         + "), to whom this artifact was issued.");
134                                         throw new SAMLException(SAMLException.REQUESTER, "Invalid credential.");
135                                 }
136                                 log.debug("Supplied credential validated for the provider to which this artifact was issued.");
137                                 assertions.add(assertion);
138                                 dereferencedArtifacts.append("(" + artifact.encode() + ")");
139                         }
140                 }
141
142                 // The spec requires that if any artifacts are dereferenced, they must
143                 // all be dereferenced
144                 if (assertions.size() > 0 && assertions.size() != queriedArtifacts) { throw new SAMLException(
145                                 SAMLException.REQUESTER, "Unable to successfully dereference all artifacts."); }
146
147                 // Create and send response
148                 // The spec says that we should send "success" in the case where no artifacts match
149                 SAMLResponse samlResponse = new SAMLResponse(samlRequest.getId(), null, assertions, null);
150                 if (log.isDebugEnabled()) {
151                         log.debug("Dumping generated SAML Response:" + System.getProperty("line.separator")
152                                         + samlResponse.toString());
153                 }
154
155                 support.getTransactionLog().info(
156                                 "Succesfully dereferenced the following artifacts: " + dereferencedArtifacts.toString());
157                 return samlResponse;
158         }
159
160 }