SSPCPP-616 - clean up concatenated string literals
[shibboleth/cpp-sp.git] / shibsp / handler / impl / AttributeCheckerHandler.cpp
1 /**
2  * Licensed to the University Corporation for Advanced Internet
3  * Development, Inc. (UCAID) under one or more contributor license
4  * agreements. See the NOTICE file distributed with this work for
5  * additional information regarding copyright ownership.
6  *
7  * UCAID licenses this file to you under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17  * either express or implied. See the License for the specific
18  * language governing permissions and limitations under the License.
19  */
20
21 /**
22  * AttributeCheckerHandler.cpp
23  *
24  * Handler for checking a session for required attributes.
25  */
26
27 #include "internal.h"
28 #include "AccessControl.h"
29 #include "Application.h"
30 #include "exceptions.h"
31 #include "ServiceProvider.h"
32 #include "SessionCache.h"
33 #include "SPRequest.h"
34 #include "attribute/Attribute.h"
35 #include "handler/AbstractHandler.h"
36 #include "util/TemplateParameters.h"
37
38 #include <fstream>
39 #include <sstream>
40 #include <boost/bind.hpp>
41 #include <boost/scoped_ptr.hpp>
42 #include <boost/algorithm/string.hpp>
43 #include <xercesc/util/XMLUniDefs.hpp>
44 #include <xmltooling/XMLToolingConfig.h>
45 #include <xmltooling/util/PathResolver.h>
46 #include <xmltooling/util/XMLHelper.h>
47
48 using namespace shibsp;
49 using namespace xmltooling;
50 using namespace boost;
51 using namespace std;
52
53 namespace shibsp {
54
55 #if defined (_MSC_VER)
56     #pragma warning( push )
57     #pragma warning( disable : 4250 )
58 #endif
59
60     class SHIBSP_DLLLOCAL Blocker : public DOMNodeFilter
61     {
62     public:
63 #ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
64         short
65 #else
66         FilterAction
67 #endif
68         acceptNode(const DOMNode* node) const {
69             return FILTER_REJECT;
70         }
71     };
72
73     static SHIBSP_DLLLOCAL Blocker g_Blocker;
74
75     class SHIBSP_API AttributeCheckerHandler : public AbstractHandler
76     {
77     public:
78         AttributeCheckerHandler(const DOMElement* e, const char* appId);
79         virtual ~AttributeCheckerHandler() {}
80
81         pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
82
83     private:
84         void flushSession(SPRequest& request) const {
85             try {
86                 request.getApplication().getServiceProvider().getSessionCache()->remove(request.getApplication(), request, &request);
87             }
88             catch (std::exception&) {
89             }
90         }
91
92         string m_template;
93         bool m_flushSession;
94         vector<string> m_attributes;
95         scoped_ptr<AccessControl> m_acl;
96     };
97
98 #if defined (_MSC_VER)
99     #pragma warning( pop )
100 #endif
101
102     Handler* SHIBSP_DLLLOCAL AttributeCheckerFactory(const pair<const DOMElement*,const char*>& p)
103     {
104         return new AttributeCheckerHandler(p.first, p.second);
105     }
106
107     static const XMLCh attributes[] =   UNICODE_LITERAL_10(a,t,t,r,i,b,u,t,e,s);
108     static const XMLCh _flushSession[] = UNICODE_LITERAL_12(f,l,u,s,h,S,e,s,s,i,o,n);
109     static const XMLCh _template[] =    UNICODE_LITERAL_8(t,e,m,p,l,a,t,e);
110 };
111
112 AttributeCheckerHandler::AttributeCheckerHandler(const DOMElement* e, const char* appId)
113     : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT ".AttributeCheckerHandler"), &g_Blocker)
114 {
115     if (!SPConfig::getConfig().isEnabled(SPConfig::InProcess))
116         return;
117     m_template = XMLHelper::getAttrString(e, nullptr, _template);
118     if (m_template.empty())
119         throw ConfigurationException("AttributeChecker missing required template setting.");
120     XMLToolingConfig::getConfig().getPathResolver()->resolve(m_template, PathResolver::XMLTOOLING_CFG_FILE);
121
122     m_flushSession = XMLHelper::getAttrBool(e, false, _flushSession);
123
124     string attrs(XMLHelper::getAttrString(e, nullptr, attributes));
125     if (!attrs.empty()) {
126         trim(attrs);
127         split(m_attributes, attrs, is_space(), algorithm::token_compress_on);
128         if (m_attributes.empty())
129             throw ConfigurationException("AttributeChecker unable to parse attributes setting.");
130     }
131     else {
132         m_acl.reset(SPConfig::getConfig().AccessControlManager.newPlugin(XML_ACCESS_CONTROL, e));
133     }
134 }
135
136 pair<bool,long> AttributeCheckerHandler::run(SPRequest& request, bool isHandler) const
137 {
138     // If the checking passes, we route to the return URL, target URL, or homeURL in that order.
139     const char* returnURL = request.getParameter("return");
140     const char* target = request.getParameter("target");
141     if (!returnURL)
142         returnURL = target;
143     if (returnURL)
144         request.getApplication().limitRedirect(request, returnURL);
145     else
146         returnURL = request.getApplication().getString("homeURL").second;
147     if (!returnURL)
148         returnURL = "/";
149        
150     Session* session = nullptr;
151     try {
152         session = request.getSession(true, false, false);
153         if (!session)
154             request.log(SPRequest::SPWarn, "AttributeChecker found session unavailable immediately after creation");
155     }
156     catch (std::exception& ex) {
157         request.log(SPRequest::SPWarn, string("AttributeChecker caught exception accessing session immediately after creation: ") + ex.what());
158     }
159
160     Locker sessionLocker(session, false);
161
162     bool checked = false;
163     if (session) {
164         if (!m_attributes.empty()) {
165             typedef multimap<string,const Attribute*> indexed_t;
166             static indexed_t::const_iterator (indexed_t::* fn)(const string&) const = &indexed_t::find;
167             const indexed_t& indexed = session->getIndexedAttributes();
168             // Look for an attribute in the list that is not in the session multimap.
169             // If that fails, the check succeeds.
170             checked = (
171                 find_if(m_attributes.begin(), m_attributes.end(),
172                     boost::bind(fn, boost::cref(indexed), _1) == indexed.end()) == m_attributes.end()
173                 );
174         }
175         else {
176             checked = (m_acl && m_acl->authorized(request, session) == AccessControl::shib_acl_true);
177         }
178     }
179
180     if (checked) {
181         string loc(returnURL);
182         request.absolutize(loc);
183         return make_pair(true, request.sendRedirect(loc.c_str()));
184     }
185
186     request.setContentType("text/html; charset=UTF-8");
187     request.setResponseHeader("Expires","Wed, 01 Jan 1997 12:00:00 GMT");
188     request.setResponseHeader("Cache-Control","private,no-store,no-cache,max-age=0");
189
190     ifstream infile(m_template.c_str());
191     if (infile) {
192         TemplateParameters tp(nullptr, request.getApplication().getPropertySet("Errors"), session);
193         tp.m_request = &request;
194         stringstream str;
195         XMLToolingConfig::getConfig().getTemplateEngine()->run(infile, str, tp);
196         if (m_flushSession) {
197             sessionLocker.assign(); // unlock the session
198             flushSession(request);
199         }
200         return make_pair(true, request.sendError(str));
201     }
202
203     if (m_flushSession) {
204         sessionLocker.assign(); // unlock the session
205         flushSession(request);
206     }
207     m_log.error("could not process error template (%s)", m_template.c_str());
208     istringstream msg("Internal Server Error. Please contact the site administrator.");
209     return make_pair(true, request.sendResponse(msg));
210 }