2 * Copyright 2001-2005 Internet2
\r
4 * Licensed under the Apache License, Version 2.0 (the "License");
\r
5 * you may not use this file except in compliance with the License.
\r
6 * You may obtain a copy of the License at
\r
8 * http://www.apache.org/licenses/LICENSE-2.0
\r
10 * Unless required by applicable law or agreed to in writing, software
\r
11 * distributed under the License is distributed on an "AS IS" BASIS,
\r
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
13 * See the License for the specific language governing permissions and
\r
14 * limitations under the License.
\r
17 /** StorageServiceSessionCache.cpp
\r
19 * StorageService-based SessionCache implementation
\r
22 #include "internal.h"
\r
23 #include "Application.h"
\r
24 #include "exceptions.h"
\r
25 #include "ServiceProvider.h"
\r
26 #include "SessionCache.h"
\r
27 #include "TransactionLog.h"
\r
28 #include "attribute/Attribute.h"
\r
29 #include "remoting/ListenerService.h"
\r
30 #include "util/SPConstants.h"
\r
32 #include <log4cpp/Category.hh>
\r
33 #include <saml/SAMLConfig.h>
\r
34 #include <xmltooling/util/NDC.h>
\r
35 #include <xmltooling/util/StorageService.h>
\r
36 #include <xmltooling/util/XMLHelper.h>
\r
37 #include <xercesc/util/XMLUniDefs.hpp>
\r
39 using namespace shibsp;
\r
40 using namespace opensaml::saml2md;
\r
41 using namespace opensaml;
\r
42 using namespace xmltooling;
\r
43 using namespace log4cpp;
\r
44 using namespace std;
\r
49 class StoredSession : public virtual Session
\r
52 StoredSession(SSCache* cache, DDF& obj) : m_cache(cache), m_obj(obj) {}
\r
63 const char* getClientAddress() const {
\r
64 return m_obj["client_address"].string();
\r
66 const char* getEntityID() const {
\r
67 return m_obj["entity_id"].string();
\r
69 const char* getAuthnInstant() const {
\r
70 return m_obj["authn_instant"].string();
\r
72 const opensaml::saml2::NameID& getNameID() const {
\r
75 const char* getSessionIndex() const {
\r
76 return m_obj["session_index"].string();
\r
78 const char* getAuthnContextClassRef() const {
\r
79 return m_obj["authncontext_class"].string();
\r
81 const char* getAuthnContextDeclRef() const {
\r
82 return m_obj["authncontext_decl"].string();
\r
84 const vector<const Attribute*>& getAttributes() const {
\r
85 return m_attributes;
\r
87 const vector<const char*>& getAssertionIDs() const {
\r
88 if (m_ids.empty()) {
\r
89 DDF id = m_obj["assertion_ids"].first();
\r
90 while (id.isstring()) {
\r
91 m_ids.push_back(id.string());
\r
98 void addAttributes(const vector<Attribute*>& attributes);
\r
99 const RootObject* getAssertion(const char* id) const;
\r
100 void addAssertion(RootObject* assertion);
\r
104 saml2::NameID* m_nameid;
\r
105 vector<const Attribute*> m_attributes;
\r
106 mutable vector<const char*> m_ids;
\r
107 mutable map<string,RootObject*> m_tokens;
\r
108 time_t m_sessionCreated,m_lastAccess;
\r
112 class SSCache : public SessionCache
\r
115 SSCache(const DOMElement* e);
\r
118 DDF receive(const DDF& in);
\r
121 const Application& application,
\r
122 const char* client_addr,
\r
123 const saml2md::EntityDescriptor* issuer,
\r
124 const saml2::NameID& nameid,
\r
125 const char* authn_instant=NULL,
\r
126 const char* session_index=NULL,
\r
127 const char* authncontext_class=NULL,
\r
128 const char* authncontext_decl=NULL,
\r
129 const RootObject* ssoToken=NULL,
\r
130 const vector<Attribute*>* attributes=NULL
\r
132 Session* find(const char* key, const Application& application, const char* client_addr);
\r
133 void remove(const char* key, const Application& application, const char* client_addr);
\r
136 StorageService* m_storage;
\r
139 SessionCache* SHIBSP_DLLLOCAL StorageServiceCacheFactory(const DOMElement* const & e)
\r
141 return new SSCache(e);
\r
144 static const XMLCh storageService[] = UNICODE_LITERAL_14(s,t,o,r,a,g,e,S,e,r,v,i,c,e);
\r
147 StoredSession::~StoredSession()
\r
151 for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());
\r
152 for_each(m_tokens.begin(), m_tokens.end(), xmltooling::cleanup_pair<string,RootObject>());
\r
155 void StoredSession::addAttributes(const vector<Attribute*>& attributes)
\r
158 xmltooling::NDC ndc("addAttributes");
\r
161 m_cache->m_log.debug("adding attributes to session (%s)", m_obj.name());
\r
164 DDF attrs = m_obj["attributes"];
\r
165 if (!attrs.islist())
\r
166 attrs = m_obj.addmember("attributes").list();
\r
167 for (vector<Attribute*>::const_iterator a=attributes.begin(); a!=attributes.end(); ++a) {
\r
168 attr = (*a)->marshall();
\r
172 ostringstream record;
\r
175 if (!m_cache->m_storage->updateText(m_obj["application_id"].string(), m_obj.name(), record.str().c_str())) {
\r
176 // Roll back modification to record.
\r
177 vector<Attribute*>::size_type count = attributes.size();
\r
179 attrs.last().destroy();
\r
180 m_cache->m_log.error("updateText failed on StorageService for session (%s)", m_obj.name());
\r
181 throw IOException("Unable to update stored session.");
\r
184 // Transfer ownership to us.
\r
185 m_attributes.insert(m_attributes.end(), attributes.begin(), attributes.end());
\r
187 TransactionLog* xlog = SPConfig::getConfig().getServiceProvider()->getTransactionLog();
\r
188 Locker locker(xlog);
\r
189 xlog->log.infoStream() <<
\r
190 "Added the following attributes to session (ID: " <<
\r
192 ") for (applicationId: " <<
\r
193 m_obj["application_id"].string() <<
\r
195 for (vector<Attribute*>::const_iterator a=attributes.begin(); a!=attributes.end(); ++a)
\r
196 xlog->log.infoStream() << "\t" << (*a)->getId() << " (" << (*a)->valueCount() << " values)";
\r
197 xlog->log.info("}");
\r
200 const RootObject* StoredSession::getAssertion(const char* id) const
\r
202 map<string,RootObject*>::const_iterator i = m_tokens.find(id);
\r
203 if (i!=m_tokens.end())
\r
206 // Parse and bind the document into an XMLObject.
\r
207 const char* tokenstr = m_obj["assertions"][id].string();
\r
209 throw FatalProfileException("Assertion not found in cache.");
\r
210 istringstream instr(tokenstr);
\r
211 DOMDocument* doc = XMLToolingConfig::getConfig().getParser().parse(instr);
\r
212 XercesJanitor<DOMDocument> janitor(doc);
\r
213 auto_ptr<XMLObject> xmlObject(XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true));
\r
216 RootObject* token = dynamic_cast<RootObject*>(xmlObject.get());
\r
217 if (!token || !token->isAssertion())
\r
218 throw FatalProfileException("Request for cached assertion returned an unknown object type.");
\r
220 // Transfer ownership to us.
\r
221 xmlObject.release();
\r
222 m_tokens[id]=token;
\r
226 void StoredSession::addAssertion(RootObject* assertion)
\r
229 xmltooling::NDC ndc("addAssertion");
\r
232 if (!assertion || !assertion->isAssertion())
\r
233 throw FatalProfileException("Unknown object type passed to session for storage.");
\r
235 auto_ptr_char id(assertion->getID());
\r
237 m_cache->m_log.debug("adding assertion (%s) to session (%s)", id.get(), m_obj.name());
\r
242 DDF tokens = m_obj["assertions"];
\r
243 if (!tokens.isstruct())
\r
244 tokens = m_obj.addmember("assertions").structure();
\r
245 tokens = tokens.addmember(id.get()).string(os.str().c_str());
\r
247 ostringstream record;
\r
250 if (!m_cache->m_storage->updateText(m_obj["application_id"].string(), m_obj.name(), record.str().c_str())) {
\r
251 // Roll back modification to record.
\r
253 m_cache->m_log.error("updateText failed on StorageService for session (%s)", m_obj.name());
\r
254 throw IOException("Unable to update stored session.");
\r
259 TransactionLog* xlog = SPConfig::getConfig().getServiceProvider()->getTransactionLog();
\r
260 Locker locker(xlog);
\r
262 "Added assertion (ID: %s) to session for (applicationId: %s) with (ID: %s)",
\r
263 id.get(), m_obj["application_id"].string(), m_obj.name()
\r
267 SSCache::SSCache(const DOMElement* e)
\r
268 : SessionCache(e), m_log(Category::getInstance(SHIBSP_LOGCAT".SessionCache")), m_storage(NULL)
\r
270 // TODO: assign storage service
\r
273 string SSCache::insert(
\r
274 const Application& application,
\r
275 const char* client_addr,
\r
276 const saml2md::EntityDescriptor* issuer,
\r
277 const saml2::NameID& nameid,
\r
278 const char* authn_instant,
\r
279 const char* session_index,
\r
280 const char* authncontext_class,
\r
281 const char* authncontext_decl,
\r
282 const RootObject* ssoToken,
\r
283 const vector<Attribute*>* attributes
\r
287 xmltooling::NDC ndc("insert");
\r
290 m_log.debug("creating new session");
\r
292 auto_ptr_char key(SAMLConfig::getConfig().generateIdentifier());
\r
294 time_t created = time(NULL);
\r
295 #ifndef HAVE_GMTIME_R
\r
296 struct tm* ptime=gmtime(&created);
\r
299 struct tm* ptime=gmtime_r(&created,&res);
\r
302 strftime(timebuf,32,"%Y-%m-%dT%H:%M:%SZ",ptime);
\r
304 // Store session properties in DDF.
\r
305 DDF obj = DDF(key.get()).structure();
\r
306 obj.addmember("created").string(timebuf);
\r
307 obj.addmember("client_address").string(client_addr);
\r
308 obj.addmember("application_id").string(application.getId());
\r
310 auto_ptr_char entity_id(issuer->getEntityID());
\r
311 obj.addmember("entity_id").string(entity_id.get());
\r
314 obj.addmember("authn_instant").string(authn_instant);
\r
316 obj.addmember("session_index").string(session_index);
\r
317 if (authncontext_class)
\r
318 obj.addmember("authncontext_class").string(authncontext_class);
\r
319 if (authncontext_decl)
\r
320 obj.addmember("authncontext_decl").string(authncontext_decl);
\r
322 ostringstream namestr;
\r
324 obj.addmember("nameid").string(namestr.str().c_str());
\r
327 ostringstream tokenstr;
\r
328 tokenstr << *ssoToken;
\r
329 auto_ptr_char tokenid(ssoToken->getID());
\r
330 obj.addmember("assertions").structure().addmember(tokenid.get()).string(tokenstr.str().c_str());
\r
335 DDF attrlist = obj.addmember("attributes").list();
\r
336 for (vector<Attribute*>::const_iterator a=attributes->begin(); a!=attributes->end(); ++a) {
\r
337 attr = (*a)->marshall();
\r
338 attrlist.add(attr);
\r
342 ostringstream record;
\r
345 m_log.debug("storing new session...");
\r
346 m_storage->createText(application.getId(), key.get(), record.str().c_str(), created + m_cacheTimeout);
\r
347 const char* pid = obj["entity_id"].string();
\r
348 m_log.debug("new session created: SessionID (%s) IdP (%s) Address (%s)", key.get(), pid ? pid : "none", client_addr);
\r
350 // Transaction Logging
\r
351 auto_ptr_char name(nameid.getName());
\r
352 TransactionLog* xlog = SPConfig::getConfig().getServiceProvider()->getTransactionLog();
\r
353 Locker locker(xlog);
\r
354 xlog->log.infoStream() <<
\r
355 "New session (ID: " <<
\r
357 ") with (applicationId: " <<
\r
358 application.getId() <<
\r
359 ") for principal from (IdP: " <<
\r
360 (pid ? pid : "none") <<
\r
361 ") at (ClientAddress: " <<
\r
363 ") with (NameIdentifier: " <<
\r
370 Session* SSCache::find(const char* key, const Application& application, const char* client_addr)
\r
375 void SSCache::remove(const char* key, const Application& application, const char* client_addr)
\r
378 xmltooling::NDC ndc("remove");
\r
381 m_log.debug("removing session (%s)", key);
\r
383 m_storage->deleteText(application.getId(), key);
\r
385 TransactionLog* xlog = SPConfig::getConfig().getServiceProvider()->getTransactionLog();
\r
386 Locker locker(xlog);
\r
387 xlog->log.info("Destroyed session (applicationId: %s) (ID: %s)", application.getId(), key);
\r