2 * Copyright 2001-2006 Internet2
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * FilesystemMetadataProvider.cpp
20 * Supplies metadata from a local file, detecting and reloading changes.
24 #include "saml2/metadata/MetadataProvider.h"
27 #include <sys/types.h>
29 #include <log4cpp/Category.hh>
30 #include <xercesc/framework/LocalFileInputSource.hpp>
31 #include <xercesc/framework/Wrapper4InputSource.hpp>
32 #include <xmltooling/util/NDC.h>
33 #include <xmltooling/util/Threads.h>
35 using namespace opensaml::saml2md;
36 using namespace xmltooling;
37 using namespace log4cpp;
44 class SAML_DLLLOCAL FilesystemMetadataProvider : public MetadataProvider
47 FilesystemMetadataProvider(const DOMElement* e);
48 ~FilesystemMetadataProvider();
58 const EntityDescriptor* getEntityDescriptor(const XMLCh* id, bool requireValidMetadata=true) const;
59 const EntityDescriptor* getEntityDescriptor(const char* id, bool requireValidMetadata=true) const;
60 const EntitiesDescriptor* getEntitiesDescriptor(const XMLCh* name, bool requireValidMetadata=true) const;
61 const EntitiesDescriptor* getEntitiesDescriptor(const char* name, bool requireValidMetadata=true) const;
62 const XMLObject* getMetadata() const {
67 XMLObject* load() const;
69 void index(EntityDescriptor* site, time_t validUntil=LLONG_MAX);
70 void index(EntitiesDescriptor* group, time_t validUntil=LLONG_MAX);
72 // index of loaded metadata
73 typedef multimap<string,const EntityDescriptor*> sitemap_t;
74 typedef multimap<string,const EntitiesDescriptor*> groupmap_t;
79 const DOMElement* m_root; // survives only until init() method is done
87 MetadataProvider* SAML_DLLLOCAL FilesystemMetadataProviderFactory(const DOMElement* const & e)
89 return new FilesystemMetadataProvider(e);
95 static const XMLCh uri[] = UNICODE_LITERAL_3(u,r,i);
96 static const XMLCh url[] = UNICODE_LITERAL_3(u,r,l);
97 static const XMLCh path[] = UNICODE_LITERAL_4(p,a,t,h);
98 static const XMLCh pathname[] = UNICODE_LITERAL_8(p,a,t,h,n,a,m,e);
99 static const XMLCh file[] = UNICODE_LITERAL_4(f,i,l,e);
100 static const XMLCh filename[] = UNICODE_LITERAL_8(f,i,l,e,n,a,m,e);
101 static const XMLCh validate[] = UNICODE_LITERAL_8(v,a,l,i,d,a,t,e);
103 FilesystemMetadataProvider::FilesystemMetadataProvider(const DOMElement* e)
104 : MetadataProvider(e), m_root(e), m_filestamp(0), m_validate(false), m_lock(NULL), m_object(NULL)
107 NDC ndc("FilesystemMetadataProvider");
109 Category& log=Category::getInstance(SAML_LOGCAT".Metadata");
111 // Establish source of data...
112 const XMLCh* source=e->getAttributeNS(NULL,uri);
113 if (!source || !*source) {
114 source=e->getAttributeNS(NULL,url);
115 if (!source || !*source) {
116 source=e->getAttributeNS(NULL,path);
117 if (!source || !*source) {
118 source=e->getAttributeNS(NULL,pathname);
119 if (!source || !*source) {
120 source=e->getAttributeNS(NULL,file);
121 if (!source || !*source) {
122 source=e->getAttributeNS(NULL,filename);
129 if (source && *source) {
130 const XMLCh* valflag=e->getAttributeNS(NULL,validate);
131 m_validate=(XMLString::equals(valflag,XMLConstants::XML_TRUE) || XMLString::equals(valflag,XMLConstants::XML_ONE));
133 auto_ptr_char temp(source);
135 log.debug("using external metadata file (%s)", temp.get());
138 struct _stat stat_buf;
139 if (_stat(m_source.c_str(), &stat_buf) == 0)
141 struct stat stat_buf;
142 if (stat(m_source.c_str(), &stat_buf) == 0)
144 m_filestamp=stat_buf.st_mtime;
145 m_lock=RWLock::create();
148 log.debug("no file path/name supplied, will look for metadata inline");
151 FilesystemMetadataProvider::~FilesystemMetadataProvider()
157 void FilesystemMetadataProvider::init()
163 XMLObject* FilesystemMetadataProvider::load() const
168 Category& log=Category::getInstance(SAML_LOGCAT".Metadata");
171 XMLObject* xmlObject=NULL;
173 if (!m_source.empty()) {
174 // Data comes from a file we have to parse.
175 log.debug("loading metadata from file...");
176 auto_ptr_XMLCh widenit(m_source.c_str());
177 LocalFileInputSource src(widenit.get());
178 Wrapper4InputSource dsrc(&src,false);
179 DOMDocument* doc=NULL;
181 doc=XMLToolingConfig::getConfig().getValidatingParser().parse(dsrc);
183 doc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
184 XercesJanitor<DOMDocument> docjanitor(doc);
185 log.infoStream() << "loaded and parsed XML file (" << m_source << ")" << CategoryStream::ENDLINE;
187 // Unmarshall objects, binding the document.
188 xmlObject = XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true);
189 docjanitor.release();
192 // Data comes from the DOM we were handed.
193 log.debug("loading inline metadata...");
194 DOMElement* child = XMLHelper::getFirstChildElement(m_root);
196 throw XMLToolingException("No metadata was found inline.");
197 xmlObject = XMLObjectBuilder::buildOneFromElement(child);
200 auto_ptr<XMLObject> xmlObjectPtr(xmlObject);
202 doFilters(*xmlObject);
204 xmlObjectPtr->releaseThisAndChildrenDOM();
205 xmlObjectPtr->setDocument(NULL);
206 return xmlObjectPtr.release();
208 catch (XMLException& e) {
209 auto_ptr_char msg(e.getMessage());
210 log.errorStream() << "Xerces parser error while loading metadata from ("
211 << (m_source.empty() ? "inline" : m_source) << "): " << msg.get() << CategoryStream::ENDLINE;
212 throw XMLParserException(msg.get());
214 catch (XMLToolingException& e) {
215 log.errorStream() << "error while loading metadata from ("
216 << (m_source.empty() ? "inline" : m_source) << "): " << e.what() << CategoryStream::ENDLINE;
221 Lockable* FilesystemMetadataProvider::lock()
228 // Check if we need to refresh.
230 struct _stat stat_buf;
231 if (_stat(m_source.c_str(), &stat_buf) == 0)
233 struct stat stat_buf;
234 if (stat(m_source.c_str(), &stat_buf) == 0)
237 if (m_filestamp>0 && m_filestamp<stat_buf.st_mtime) {
238 // Elevate lock and recheck.
241 if (m_filestamp>0 && m_filestamp<stat_buf.st_mtime) {
242 SharedLock lockwrap(m_lock,false); // pops write lock
244 // Update the timestamp regardless. No point in repeatedly trying.
245 m_filestamp=stat_buf.st_mtime;
246 XMLObject* newstuff = load();
251 catch(XMLToolingException& e) {
252 Category::getInstance(SAML_LOGCAT".Metadata").error("failed to reload metadata from file, sticking with what we have: %s", e.what());
264 void FilesystemMetadataProvider::index()
270 EntitiesDescriptor* group=dynamic_cast<EntitiesDescriptor*>(m_object);
275 EntityDescriptor* site=dynamic_cast<EntityDescriptor*>(m_object);
279 void FilesystemMetadataProvider::index(EntityDescriptor* site, time_t validUntil)
281 if (validUntil < site->getValidUntilEpoch())
282 site->setValidUntil(validUntil);
284 auto_ptr_char id(site->getEntityID());
286 m_sites.insert(make_pair(id.get(),site));
290 void FilesystemMetadataProvider::index(EntitiesDescriptor* group, time_t validUntil)
292 if (validUntil < group->getValidUntilEpoch())
293 group->setValidUntil(validUntil);
295 auto_ptr_char name(group->getName());
297 m_groups.insert(make_pair(name.get(),group));
300 const vector<EntitiesDescriptor*>& groups=const_cast<const EntitiesDescriptor*>(group)->getEntitiesDescriptors();
301 for (vector<EntitiesDescriptor*>::const_iterator i=groups.begin(); i!=groups.end(); i++)
302 index(*i,group->getValidUntilEpoch());
304 const vector<EntityDescriptor*>& sites=const_cast<const EntitiesDescriptor*>(group)->getEntityDescriptors();
305 for (vector<EntityDescriptor*>::const_iterator j=sites.begin(); j!=sites.end(); j++)
306 index(*j,group->getValidUntilEpoch());
309 const EntitiesDescriptor* FilesystemMetadataProvider::getEntitiesDescriptor(const char* name, bool strict) const
311 pair<groupmap_t::const_iterator,groupmap_t::const_iterator> range=m_groups.equal_range(name);
313 time_t now=time(NULL);
314 for (groupmap_t::const_iterator i=range.first; i!=range.second; i++)
315 if (now < i->second->getValidUntilEpoch())
318 if (!strict && range.first!=range.second)
319 return range.first->second;
324 const EntitiesDescriptor* FilesystemMetadataProvider::getEntitiesDescriptor(const XMLCh* name, bool strict) const
326 auto_ptr_char temp(name);
327 return getEntitiesDescriptor(temp.get(),strict);
330 const EntityDescriptor* FilesystemMetadataProvider::getEntityDescriptor(const char* name, bool strict) const
332 pair<sitemap_t::const_iterator,sitemap_t::const_iterator> range=m_sites.equal_range(name);
334 time_t now=time(NULL);
335 for (sitemap_t::const_iterator i=range.first; i!=range.second; i++)
336 if (now < i->second->getValidUntilEpoch())
339 if (!strict && range.first!=range.second)
340 return range.first->second;
345 const EntityDescriptor* FilesystemMetadataProvider::getEntityDescriptor(const XMLCh* name, bool strict) const
347 auto_ptr_char temp(name);
348 return getEntityDescriptor(temp.get(),strict);