Improve property inheritance, first batch of SessionInitiators, rename providerId.
[shibboleth/sp.git] / shibsp / handler / impl / Shib1SessionInitiator.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  * Shib1SessionInitiator.cpp
19  * 
20  * Shibboleth 1.x AuthnRequest/WAYF support.
21  */
22
23 #include "internal.h"
24 #include "Application.h"
25 #include "exceptions.h"
26 #include "SPRequest.h"
27 #include "handler/AbstractHandler.h"
28 #include "util/SPConstants.h"
29
30 #include <saml/saml2/metadata/Metadata.h>
31 #include <saml/saml2/metadata/EndpointManager.h>
32 #include <xercesc/util/XMLUniDefs.hpp>
33 #include <xmltooling/XMLToolingConfig.h>
34 #include <xmltooling/util/XMLHelper.h>
35 #include <xmltooling/util/URLEncoder.h>
36
37 using namespace shibsp;
38 using namespace opensaml::saml2md;
39 using namespace opensaml;
40 using namespace xmltooling;
41 using namespace log4cpp;
42 using namespace std;
43
44 namespace shibsp {
45
46 #if defined (_MSC_VER)
47     #pragma warning( push )
48     #pragma warning( disable : 4250 )
49 #endif
50
51     class SHIBSP_DLLLOCAL Shib1SessionInitiator : public AbstractHandler
52     {
53     public:
54         Shib1SessionInitiator(const DOMElement* e, const char* appId)
55             : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".SessionInitiator")) {}
56         virtual ~Shib1SessionInitiator() {}
57         
58         pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
59
60     private:
61         pair<bool,long> doAuthnRequest(SPRequest& request, const Handler* shire, const char* dest, string& target) const;
62     };
63
64 #if defined (_MSC_VER)
65     #pragma warning( pop )
66 #endif
67
68     Handler* SHIBSP_DLLLOCAL Shib1SessionInitiatorFactory(const pair<const DOMElement*,const char*>& p)
69     {
70         return new Shib1SessionInitiator(p.first, p.second);
71     }
72
73 };
74
75 pair<bool,long> Shib1SessionInitiator::run(SPRequest& request, bool isHandler) const
76 {
77     string target;
78     const char* entityID=NULL;
79     const Handler* ACS=NULL;
80     const Application& app=request.getApplication();
81     
82     if (isHandler) {
83         entityID=request.getParameter("acsIndex");
84         if (entityID)
85             ACS=app.getAssertionConsumerServiceByIndex(atoi(entityID));
86
87         entityID = request.getParameter("target");
88         if (entityID)
89             target = entityID;
90         recoverRelayState(request, target);
91
92         // Try and establish which IdP to use.
93         entityID=request.getParameter("entityID");
94         if (!entityID || !*entityID)
95             entityID=request.getParameter("providerId");
96         if (!entityID || !*entityID)
97             entityID=getString("entityID").second;
98     }
99     else {
100         // We're running as a "virtual handler" from within the filter.
101         // The target resource is the current one and everything else is defaulted.
102         entityID=getString("entityID").second;
103         target=request.getRequestURL();
104     }
105         
106     if (entityID && *entityID) {
107         m_log.debug("attempting to initiate session using SAML 1.x with provider (%s)", entityID);
108
109         // Use metadata to invoke the SSO service directly.
110         MetadataProvider* m=app.getMetadataProvider();
111         Locker locker(m);
112         const EntityDescriptor* entity=m->getEntityDescriptor(entityID);
113         if (!entity) {
114             m_log.error("unable to locate metadata for provider (%s)", entityID);
115             return make_pair(false,0);
116         }
117         const IDPSSODescriptor* role=entity->getIDPSSODescriptor(shibspconstants::SHIB1_PROTOCOL_ENUM);
118         if (!role) {
119             m_log.error("unable to locate Shibboleth-aware identity provider role for provider (%s)", entityID);
120             return make_pair(false,0);
121         }
122         const EndpointType* ep=EndpointManager<SingleSignOnService>(role->getSingleSignOnServices()).getByBinding(
123             shibspconstants::SHIB1_AUTHNREQUEST_PROFILE_URI
124             );
125         if (!ep) {
126             m_log.error("unable to locate compatible SSO service for provider (%s)", entityID);
127             return make_pair(false,0);
128         }
129         auto_ptr_char dest(ep->getLocation());
130         return doAuthnRequest(request, ACS ? ACS : app.getDefaultAssertionConsumerService(), dest.get(), target);
131     }
132     
133     // Fall back to optional legacy discovery service.
134     pair<bool,const char*> wayfURL=getString("wayfURL");
135     if (!wayfURL.first)
136         return make_pair(false,0);
137     return doAuthnRequest(request, ACS ? ACS : app.getDefaultAssertionConsumerService(), wayfURL.second, target);
138 }
139
140 pair<bool,long> Shib1SessionInitiator::doAuthnRequest(SPRequest& request, const Handler* shire, const char* dest, string& target) const
141 {
142     // Compute the ACS URL. We add the ACS location to the base handlerURL.
143     string ACSloc=request.getHandlerURL(target.c_str());
144     pair<bool,const char*> loc=shire ? shire->getString("Location") : pair<bool,const char*>(false,NULL);
145     if (loc.first) ACSloc+=loc.second;
146
147     preserveRelayState(request, target);
148
149     char timebuf[16];
150     sprintf(timebuf,"%u",time(NULL));
151     const URLEncoder* urlenc = XMLToolingConfig::getConfig().getURLEncoder();
152     string req=string(dest) + "?shire=" + urlenc->encode(ACSloc.c_str()) + "&time=" + timebuf + "&target=" + target +
153         "&providerId=" + urlenc->encode(request.getApplication().getString("entityID").second);
154
155     return make_pair(true, request.sendRedirect(req.c_str()));
156 }