Add option to use redirection for handler errors, capture relay state when propagatin...
[shibboleth/cpp-sp.git] / shibsp / handler / impl / AbstractHandler.cpp
1 /*
2  *  Copyright 2001-2007 Internet2
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 /**
18  * AbstractHandler.cpp
19  * 
20  * Base class for handlers based on a DOMPropertySet. 
21  */
22
23 #include "internal.h"
24 #include "Application.h"
25 #include "exceptions.h"
26 #include "SPRequest.h"
27 #include "handler/AbstractHandler.h"
28
29 #include <saml/saml1/core/Protocols.h>
30 #include <saml/saml2/core/Protocols.h>
31 #include <saml/util/SAMLConstants.h>
32 #include <xmltooling/XMLToolingConfig.h>
33 #include <xmltooling/util/URLEncoder.h>
34
35 using namespace shibsp;
36 using namespace samlconstants;
37 using namespace opensaml;
38 using namespace xmltooling;
39 using namespace xercesc;
40 using namespace std;
41
42 namespace shibsp {
43     SHIBSP_DLLLOCAL PluginManager<Handler,const DOMElement*>::Factory SAML1ConsumerFactory;
44 };
45
46 void SHIBSP_API shibsp::registerHandlers()
47 {
48     SPConfig& conf=SPConfig::getConfig();
49     conf.AssertionConsumerServiceManager.registerFactory(SAML1_PROFILE_BROWSER_ARTIFACT, SAML1ConsumerFactory);
50     conf.AssertionConsumerServiceManager.registerFactory(SAML1_PROFILE_BROWSER_POST, SAML1ConsumerFactory);
51 }
52
53 AbstractHandler::AbstractHandler(
54     const DOMElement* e, log4cpp::Category& log, DOMNodeFilter* filter, const map<string,string>* remapper
55     ) : m_log(log) {
56     load(e,log,filter,remapper);
57 }
58
59 void AbstractHandler::checkError(const XMLObject* response) const
60 {
61     const saml2p::StatusResponseType* r2 = dynamic_cast<const saml2p::StatusResponseType*>(response);
62     if (r2) {
63         const saml2p::Status* status = r2->getStatus();
64         if (status) {
65             const saml2p::StatusCode* sc = status->getStatusCode();
66             const XMLCh* code = sc ? sc->getValue() : NULL;
67             if (code && !XMLString::equals(code,saml2p::StatusCode::SUCCESS)) {
68                 FatalProfileException ex("SAML Response message contained an error.");
69                 auto_ptr_char c1(code);
70                 ex.addProperty("StatusCode", c1.get());
71                 if (sc->getStatusCode()) {
72                     code = sc->getStatusCode()->getValue();
73                     auto_ptr_char c2(code);
74                     ex.addProperty("StatusCode2", c2.get());
75                 }
76                 if (status->getStatusMessage()) {
77                     auto_ptr_char msg(status->getStatusMessage()->getMessage());
78                     ex.addProperty("StatusMessage", msg.get());
79                 }
80             }
81         }
82     }
83
84     const saml1p::Response* r1 = dynamic_cast<const saml1p::Response*>(response);
85     if (r1) {
86         const saml1p::Status* status = r1->getStatus();
87         if (status) {
88             const saml1p::StatusCode* sc = status->getStatusCode();
89             const QName* code = sc ? sc->getValue() : NULL;
90             if (code && *code != saml1p::StatusCode::SUCCESS) {
91                 FatalProfileException ex("SAML Response message contained an error.");
92                 ex.addProperty("StatusCode", code->toString().c_str());
93                 if (sc->getStatusCode()) {
94                     code = sc->getStatusCode()->getValue();
95                     if (code)
96                         ex.addProperty("StatusCode2", code->toString().c_str());
97                 }
98                 if (status->getStatusMessage()) {
99                     auto_ptr_char msg(status->getStatusMessage()->getMessage());
100                     ex.addProperty("StatusMessage", msg.get());
101                 }
102             }
103         }
104     }
105 }
106
107 void AbstractHandler::recoverRelayState(HTTPRequest& httpRequest, string& relayState) const
108 {
109     SPConfig& conf = SPConfig::getConfig();
110     if (conf.isEnabled(SPConfig::OutOfProcess)) {
111         // Out of process, we look for StorageService-backed state.
112         // TODO: something like ss:SSID:key?
113     }
114     
115     if (conf.isEnabled(SPConfig::InProcess)) {
116         // In process, we should be able to cast down to a full SPRequest.
117         SPRequest& request = dynamic_cast<SPRequest&>(httpRequest);
118         if (relayState.empty() || relayState == "cookie") {
119             // Pull the value from the "relay state" cookie.
120             pair<string,const char*> relay_cookie = request.getApplication().getCookieNameProps("_shibstate_");
121             const char* state = request.getCookie(relay_cookie.first.c_str());
122             if (state && *state) {
123                 // URL-decode the value.
124                 char* rscopy=strdup(state);
125                 XMLToolingConfig::getConfig().getURLEncoder()->decode(rscopy);
126                 relayState = rscopy;
127                 free(rscopy);
128                 
129                 // Clear the cookie.
130                 request.setCookie(relay_cookie.first.c_str(),relay_cookie.second);
131             }
132             else
133                 relayState = "default"; // fall through...
134         }
135         
136         // Check for "default" value.
137         if (relayState == "default") {
138             pair<bool,const char*> homeURL=request.getApplication().getString("homeURL");
139             relayState=homeURL.first ? homeURL.second : "/";
140         }
141     }
142 }