SSPCPP-382 - cb58f699-b61c-0410-a6fe-9272a202ed29
[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
36 #include <ctime>
37
38 using namespace shibsp;
39 using namespace xmltooling;
40 using namespace std;
41
42 namespace shibsp {
43
44 #if defined (_MSC_VER)
45     #pragma warning( push )
46     #pragma warning( disable : 4250 )
47 #endif
48
49     class SHIBSP_DLLLOCAL Blocker : public DOMNodeFilter
50     {
51     public:
52 #ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
53         short
54 #else
55         FilterAction
56 #endif
57         acceptNode(const DOMNode* node) const {
58             return FILTER_REJECT;
59         }
60     };
61
62     static SHIBSP_DLLLOCAL Blocker g_Blocker;
63
64     class SHIBSP_API SessionHandler : public AbstractHandler
65     {
66     public:
67         SessionHandler(const DOMElement* e, const char* appId);
68         virtual ~SessionHandler() {}
69
70         pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
71
72     private:
73         bool m_values;
74         set<string> m_acl;
75     };
76
77 #if defined (_MSC_VER)
78     #pragma warning( pop )
79 #endif
80
81     Handler* SHIBSP_DLLLOCAL SessionHandlerFactory(const pair<const DOMElement*,const char*>& p)
82     {
83         return new SessionHandler(p.first, p.second);
84     }
85
86 };
87
88 SessionHandler::SessionHandler(const DOMElement* e, const char* appId)
89     : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".SessionHandler"), &g_Blocker), m_values(false)
90 {
91     pair<bool,const char*> acl = getString("acl");
92     if (acl.first) {
93         string aclbuf=acl.second;
94         int j = 0;
95         for (unsigned int i=0;  i < aclbuf.length();  i++) {
96             if (aclbuf.at(i)==' ') {
97                 m_acl.insert(aclbuf.substr(j, i-j));
98                 j = i+1;
99             }
100         }
101         m_acl.insert(aclbuf.substr(j, aclbuf.length()-j));
102     }
103
104     pair<bool,bool> flag = getBool("showAttributeValues");
105     if (flag.first)
106         m_values = flag.second;
107 }
108
109 pair<bool,long> SessionHandler::run(SPRequest& request, bool isHandler) const
110 {
111     if (!m_acl.empty() && m_acl.count(request.getRemoteAddr()) == 0) {
112         m_log.error("session handler request blocked from invalid address (%s)", request.getRemoteAddr().c_str());
113         istringstream msg("Session Handler Blocked");
114         return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN));
115     }
116
117     stringstream s;
118     s << "<html><head><title>Session Summary</title></head><body><pre>" << endl;
119
120     Session* session = nullptr;
121     try {
122         session = request.getSession();
123         if (!session) {
124             s << "A valid session was not found.</pre></body></html>" << endl;
125             request.setContentType("text/html");
126             request.setResponseHeader("Expires","Wed, 01 Jan 1997 12:00:00 GMT");
127             request.setResponseHeader("Cache-Control","private,no-store,no-cache,max-age=0");
128             return make_pair(true, request.sendResponse(s));
129         }
130     }
131     catch (exception& ex) {
132         s << "Exception while retrieving active session:" << endl
133             << '\t' << ex.what() << "</pre></body></html>" << endl;
134         request.setContentType("text/html");
135         request.setResponseHeader("Expires","Wed, 01 Jan 1997 12:00:00 GMT");
136         request.setResponseHeader("Cache-Control","private,no-store,no-cache,max-age=0");
137         return make_pair(true, request.sendResponse(s));
138     }
139
140     s << "<u>Miscellaneous</u>" << endl;
141
142     s << "<strong>Client Address:</strong> " << (session->getClientAddress() ? session->getClientAddress() : "(none)") << endl;
143     s << "<strong>Identity Provider:</strong> " << (session->getEntityID() ? session->getEntityID() : "(none)") << endl;
144     s << "<strong>SSO Protocol:</strong> " << (session->getProtocol() ? session->getProtocol() : "(none)") << endl;
145     s << "<strong>Authentication Time:</strong> " << (session->getAuthnInstant() ? session->getAuthnInstant() : "(none)") << endl;
146     s << "<strong>Authentication Context Class:</strong> " << (session->getAuthnContextClassRef() ? session->getAuthnContextClassRef() : "(none)") << endl;
147     s << "<strong>Authentication Context Decl:</strong> " << (session->getAuthnContextDeclRef() ? session->getAuthnContextDeclRef() : "(none)") << endl;
148     s << "<strong>Session Expiration (barring inactivity):</strong> ";
149     if (session->getExpiration())
150         s << ((session->getExpiration() - time(nullptr)) / 60) << " minute(s)" << endl;
151     else
152         s << "Infinite" << endl;
153
154     s << endl << "<u>Attributes</u>" << endl;
155
156     string key;
157     vector<string>::size_type count=0;
158     const multimap<string,const Attribute*>& attributes = session->getIndexedAttributes();
159     for (multimap<string,const Attribute*>::const_iterator a = attributes.begin(); a != attributes.end(); ++a) {
160         if (a->first != key) {
161             if (a != attributes.begin()) {
162                 if (m_values)
163                     s << endl;
164                 else {
165                     s << count << " value(s)" << endl;
166                     count = 0;
167                 }
168             }
169             s << "<strong>" << a->first << "</strong>: ";
170         }
171
172         if (m_values) {
173             const vector<string>& vals = a->second->getSerializedValues();
174             for (vector<string>::const_iterator v = vals.begin(); v!=vals.end(); ++v) {
175                 if (v != vals.begin() || a->first == key)
176                     s << ';';
177                 string::size_type pos = v->find_first_of(';',string::size_type(0));
178                 if (pos!=string::npos) {
179                     string value(*v);
180                     for (; pos != string::npos; pos = value.find_first_of(';',pos)) {
181                         value.insert(pos, "\\");
182                         pos += 2;
183                     }
184                     s << value;
185                 }
186                 else {
187                     s << *v;
188                 }
189             }
190         }
191         else {
192             count += a->second->getSerializedValues().size();
193         }
194     }
195
196     if (!m_values && !attributes.empty())
197         s << count << " value(s)" << endl;
198
199     s << "</pre></body></html>";
200     request.setContentType("text/html; charset=UTF-8");
201     request.setResponseHeader("Expires","Wed, 01 Jan 1997 12:00:00 GMT");
202     request.setResponseHeader("Cache-Control","private,no-store,no-cache,max-age=0");
203     return make_pair(true, request.sendResponse(s));
204 }