Boost changes
[shibboleth/cpp-sp.git] / shibsp / handler / impl / SessionHandler.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  * SessionHandler.cpp
23  *
24  * Handler for dumping information about an active session.
25  */
26
27 #include "internal.h"
28 #include "Application.h"
29 #include "exceptions.h"
30 #include "ServiceProvider.h"
31 #include "SessionCache.h"
32 #include "SPRequest.h"
33 #include "attribute/Attribute.h"
34 #include "handler/AbstractHandler.h"
35 #include "util/IPRange.h"
36
37 #include <ctime>
38 #include <boost/bind.hpp>
39 #include <boost/algorithm/string.hpp>
40
41 using namespace shibsp;
42 using namespace xmltooling;
43 using namespace boost;
44 using namespace std;
45
46 namespace shibsp {
47
48 #if defined (_MSC_VER)
49     #pragma warning( push )
50     #pragma warning( disable : 4250 )
51 #endif
52
53     class SHIBSP_DLLLOCAL Blocker : public DOMNodeFilter
54     {
55     public:
56 #ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
57         short
58 #else
59         FilterAction
60 #endif
61         acceptNode(const DOMNode* node) const {
62             return FILTER_REJECT;
63         }
64     };
65
66     static SHIBSP_DLLLOCAL Blocker g_Blocker;
67
68     class SHIBSP_API SessionHandler : public AbstractHandler
69     {
70     public:
71         SessionHandler(const DOMElement* e, const char* appId);
72         virtual ~SessionHandler() {}
73
74         pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
75
76     private:
77         void parseACL(const string& acl) {
78             try {
79                 m_acl.push_back(IPRange::parseCIDRBlock(acl.c_str()));
80             }
81             catch (std::exception& ex) {
82                 m_log.error("invalid CIDR block (%s): %s", acl.c_str(), ex.what());
83             }
84         }
85
86         bool m_values;
87         vector<IPRange> m_acl;
88     };
89
90 #if defined (_MSC_VER)
91     #pragma warning( pop )
92 #endif
93
94     Handler* SHIBSP_DLLLOCAL SessionHandlerFactory(const pair<const DOMElement*,const char*>& p)
95     {
96         return new SessionHandler(p.first, p.second);
97     }
98
99 };
100
101 SessionHandler::SessionHandler(const DOMElement* e, const char* appId)
102     : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".SessionHandler"), &g_Blocker), m_values(false)
103 {
104     pair<bool,const char*> acl = getString("acl");
105     if (acl.first) {
106         string aclbuf=acl.second;
107         vector<string> aclarray;
108         split(aclarray, aclbuf, is_space(), algorithm::token_compress_on);
109         for_each(aclarray.begin(), aclarray.end(), boost::bind(&SessionHandler::parseACL, this, _1));
110         if (m_acl.empty()) {
111             m_log.warn("invalid CIDR range(s) in Session handler acl property, allowing 127.0.0.1 as a fall back");
112             m_acl.push_back(IPRange::parseCIDRBlock("127.0.0.1"));
113         }
114     }
115
116     pair<bool,bool> flag = getBool("showAttributeValues");
117     if (flag.first)
118         m_values = flag.second;
119 }
120
121 pair<bool,long> SessionHandler::run(SPRequest& request, bool isHandler) const
122 {
123     if (!m_acl.empty()) {
124         static bool (IPRange::* contains)(const char*) const = &IPRange::contains;
125         if (find_if(m_acl.begin(), m_acl.end(), boost::bind(contains, _1, request.getRemoteAddr().c_str())) == m_acl.end()) {
126             m_log.error("session handler request blocked from invalid address (%s)", request.getRemoteAddr().c_str());
127             istringstream msg("Session Handler Blocked");
128             return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN));
129         }
130     }
131
132     stringstream s;
133     s << "<html><head><title>Session Summary</title></head><body><pre>" << endl;
134
135     Session* session = nullptr;
136     try {
137         session = request.getSession(); // caches the locked session in the request so it's unlocked automatically
138         if (!session) {
139             s << "A valid session was not found.</pre></body></html>" << endl;
140             request.setContentType("text/html");
141             request.setResponseHeader("Expires","Wed, 01 Jan 1997 12:00:00 GMT");
142             request.setResponseHeader("Cache-Control","private,no-store,no-cache,max-age=0");
143             return make_pair(true, request.sendResponse(s));
144         }
145     }
146     catch (std::exception& ex) {
147         s << "Exception while retrieving active session:" << endl
148             << '\t' << ex.what() << "</pre></body></html>" << endl;
149         request.setContentType("text/html");
150         request.setResponseHeader("Expires","Wed, 01 Jan 1997 12:00:00 GMT");
151         request.setResponseHeader("Cache-Control","private,no-store,no-cache,max-age=0");
152         return make_pair(true, request.sendResponse(s));
153     }
154
155     s << "<u>Miscellaneous</u>" << endl;
156
157     s << "<strong>Client Address:</strong> " << (session->getClientAddress() ? session->getClientAddress() : "(none)") << endl;
158     s << "<strong>Identity Provider:</strong> " << (session->getEntityID() ? session->getEntityID() : "(none)") << endl;
159     s << "<strong>SSO Protocol:</strong> " << (session->getProtocol() ? session->getProtocol() : "(none)") << endl;
160     s << "<strong>Authentication Time:</strong> " << (session->getAuthnInstant() ? session->getAuthnInstant() : "(none)") << endl;
161     s << "<strong>Authentication Context Class:</strong> " << (session->getAuthnContextClassRef() ? session->getAuthnContextClassRef() : "(none)") << endl;
162     s << "<strong>Authentication Context Decl:</strong> " << (session->getAuthnContextDeclRef() ? session->getAuthnContextDeclRef() : "(none)") << endl;
163     s << "<strong>Session Expiration (barring inactivity):</strong> ";
164     if (session->getExpiration())
165         s << ((session->getExpiration() - time(nullptr)) / 60) << " minute(s)" << endl;
166     else
167         s << "Infinite" << endl;
168
169     s << endl << "<u>Attributes</u>" << endl;
170
171     string key;
172     vector<string>::size_type count=0;
173     const multimap<string,const Attribute*>& attributes = session->getIndexedAttributes();
174     for (multimap<string,const Attribute*>::const_iterator a = attributes.begin(); a != attributes.end(); ++a) {
175         if (a->first != key) {
176             if (a != attributes.begin()) {
177                 if (m_values)
178                     s << endl;
179                 else {
180                     s << count << " value(s)" << endl;
181                     count = 0;
182                 }
183             }
184             s << "<strong>" << a->first << "</strong>: ";
185         }
186
187         if (m_values) {
188             const vector<string>& vals = a->second->getSerializedValues();
189             for (vector<string>::const_iterator v = vals.begin(); v!=vals.end(); ++v) {
190                 if (v != vals.begin() || a->first == key)
191                     s << ';';
192                 string::size_type pos = v->find_first_of(';',string::size_type(0));
193                 if (pos!=string::npos) {
194                     string value(*v);
195                     for (; pos != string::npos; pos = value.find_first_of(';',pos)) {
196                         value.insert(pos, "\\");
197                         pos += 2;
198                     }
199                     s << value;
200                 }
201                 else {
202                     s << *v;
203                 }
204             }
205         }
206         else {
207             count += a->second->getSerializedValues().size();
208         }
209     }
210
211     if (!m_values && !attributes.empty())
212         s << count << " value(s)" << endl;
213
214     s << "</pre></body></html>";
215     request.setContentType("text/html; charset=UTF-8");
216     request.setResponseHeader("Expires","Wed, 01 Jan 1997 12:00:00 GMT");
217     request.setResponseHeader("Cache-Control","private,no-store,no-cache,max-age=0");
218     return make_pair(true, request.sendResponse(s));
219 }