Multi-line svn commit, see body.
[shibboleth/cpp-opensaml.git] / saml / saml2 / metadata / impl / DynamicMetadataProvider.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  * DynamicMetadataProvider.cpp
19  * 
20  * Simple implementation of a dynamic caching MetadataProvider.
21  */
22
23 #include "internal.h"
24 #include "binding/SAMLArtifact.h"
25 #include "saml2/metadata/Metadata.h"
26 #include "saml2/metadata/DynamicMetadataProvider.h"
27
28 #include <xercesc/framework/Wrapper4InputSource.hpp>
29 #include <xercesc/framework/URLInputSource.hpp>
30 #include <xercesc/util/XMLUniDefs.hpp>
31 #include <xmltooling/logging.h>
32 #include <xmltooling/util/XMLHelper.h>
33
34 using namespace opensaml::saml2md;
35 using namespace xmltooling::logging;
36 using namespace xmltooling;
37 using namespace std;
38
39 static const XMLCh validate[] = UNICODE_LITERAL_8(v,a,l,i,d,a,t,e);
40
41 namespace opensaml {
42     namespace saml2md {
43         MetadataProvider* SAML_DLLLOCAL DynamicMetadataProviderFactory(const DOMElement* const & e)
44         {
45             return new DynamicMetadataProvider(e);
46         }
47     };
48 };
49
50 DynamicMetadataProvider::DynamicMetadataProvider(const DOMElement* e)
51     : AbstractMetadataProvider(e), m_lock(RWLock::create())
52 {
53     const XMLCh* flag=e ? e->getAttributeNS(NULL,validate) : NULL;
54     m_validate=(XMLString::equals(flag,xmlconstants::XML_TRUE) || XMLString::equals(flag,xmlconstants::XML_ONE));
55 }
56
57 DynamicMetadataProvider::~DynamicMetadataProvider()
58 {
59     // Each entity in the map is unique (no multimap semantics), so this is safe.
60     clearDescriptorIndex(true);
61     delete m_lock;
62 }
63
64 pair<const EntityDescriptor*,const RoleDescriptor*> DynamicMetadataProvider::getEntityDescriptor(const Criteria& criteria) const
65 {
66     // Check cache while holding the read lock.
67     pair<const EntityDescriptor*,const RoleDescriptor*> entity = AbstractMetadataProvider::getEntityDescriptor(criteria);
68     if (entity.first)   // even if the role isn't found, we're done
69         return entity;
70
71     string name;
72     if (criteria.entityID_ascii)
73         name = criteria.entityID_ascii;
74     else if (criteria.entityID_unicode) {
75         auto_ptr_char temp(criteria.entityID_unicode);
76         name = temp.get();
77     }
78     else if (criteria.artifact)
79         name = criteria.artifact->getSource();
80     else
81         return entity;
82
83     Category& log = Category::getInstance(SAML_LOGCAT".MetadataProvider.Dynamic");
84     log.info("resolving metadata for (%s)", name.c_str());
85
86     // Try resolving it.
87     auto_ptr<EntityDescriptor> entity2(resolve(name.c_str()));
88
89     // Filter it, which may throw.
90     doFilters(*entity2.get());
91
92     log.info("caching resolved metadata for (%s)", name.c_str());
93
94     // Translate cacheDuration into validUntil.
95     if (entity2->getCacheDuration())
96         entity2->setValidUntil(time(NULL) + entity2->getCacheDurationEpoch());
97
98     // Upgrade our lock so we can cache the new metadata.
99     m_lock->unlock();
100     m_lock->wrlock();
101
102     // Notify observers.
103     emitChangeEvent();
104
105     // Make sure we clear out any existing copies, including stale metadata or if somebody snuck in.
106     index(entity2.release(), SAMLTIME_MAX, true);
107
108     // Downgrade back to a read lock.
109     m_lock->unlock();
110     m_lock->rdlock();
111
112     // Rinse and repeat.
113     return getEntityDescriptor(criteria);
114 }
115
116 EntityDescriptor* DynamicMetadataProvider::resolve(const char* entityID) const
117 {
118     try {
119         DOMDocument* doc=NULL;
120         auto_ptr_XMLCh widenit(entityID);
121         URLInputSource src(widenit.get());
122         Wrapper4InputSource dsrc(&src,false);
123         if (m_validate)
124             doc=XMLToolingConfig::getConfig().getValidatingParser().parse(dsrc);
125         else
126             doc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
127
128         // Wrap the document for now.
129         XercesJanitor<DOMDocument> docjanitor(doc);
130                 
131         // Unmarshall objects, binding the document.
132         auto_ptr<XMLObject> xmlObject(XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true));
133         docjanitor.release();
134
135         // Make sure it's metadata.
136         EntityDescriptor* entity = dynamic_cast<EntityDescriptor*>(xmlObject.get());
137         if (!entity) {
138             throw MetadataException(
139                 "Root of metadata instance not recognized: $1", params(1,xmlObject->getElementQName().toString().c_str())
140                 );
141         }
142         xmlObject.release();
143         return entity;
144     }
145     catch (XMLException& e) {
146         auto_ptr_char msg(e.getMessage());
147         Category::getInstance(SAML_LOGCAT".MetadataProvider.Dynamic").error(
148             "Xerces error while resolving entityID (%s): %s", entityID, msg.get()
149             );
150         throw MetadataException(msg.get());
151     }
152     catch (exception& e) {
153         Category::getInstance(SAML_LOGCAT".MetadataProvider.Dynamic").error(
154             "error while resolving entityID (%s): %s", entityID, e.what()
155             );
156         throw;
157     }
158 }