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* lookup(const XMLCh* id, bool requireValidMetadata=true) const;
59 const EntityDescriptor* lookup(const char* id, bool requireValidMetadata=true) const;
60 const EntitiesDescriptor* lookupGroup(const XMLCh* name, bool requireValidMetadata=true) const;
61 const EntitiesDescriptor* lookupGroup(const char* name, bool requireValidMetadata=true) const;
64 XMLObject* load() const;
66 void index(EntityDescriptor* site, time_t validUntil=LLONG_MAX);
67 void index(EntitiesDescriptor* group, time_t validUntil=LLONG_MAX);
69 // index of loaded metadata
70 typedef multimap<string,const EntityDescriptor*> sitemap_t;
71 typedef multimap<string,const EntitiesDescriptor*> groupmap_t;
76 const DOMElement* m_root; // survives only until init() method is done
84 MetadataProvider* SAML_DLLLOCAL FilesystemMetadataProviderFactory(const DOMElement* const & e)
86 return new FilesystemMetadataProvider(e);
92 static const XMLCh uri[] = UNICODE_LITERAL_3(u,r,i);
93 static const XMLCh url[] = UNICODE_LITERAL_3(u,r,l);
94 static const XMLCh path[] = UNICODE_LITERAL_4(p,a,t,h);
95 static const XMLCh pathname[] = UNICODE_LITERAL_8(p,a,t,h,n,a,m,e);
96 static const XMLCh file[] = UNICODE_LITERAL_4(f,i,l,e);
97 static const XMLCh filename[] = UNICODE_LITERAL_8(f,i,l,e,n,a,m,e);
98 static const XMLCh validate[] = UNICODE_LITERAL_8(v,a,l,i,d,a,t,e);
100 FilesystemMetadataProvider::FilesystemMetadataProvider(const DOMElement* e)
101 : m_root(e), m_filestamp(0), m_validate(false), m_lock(NULL), m_object(NULL)
104 NDC ndc("FilesystemMetadataProvider");
106 Category& log=Category::getInstance(SAML_LOGCAT".Metadata");
108 // Establish source of data...
109 const XMLCh* source=e->getAttributeNS(NULL,uri);
110 if (!source || !*source) {
111 source=e->getAttributeNS(NULL,url);
112 if (!source || !*source) {
113 source=e->getAttributeNS(NULL,path);
114 if (!source || !*source) {
115 source=e->getAttributeNS(NULL,pathname);
116 if (!source || !*source) {
117 source=e->getAttributeNS(NULL,file);
118 if (!source || !*source) {
119 source=e->getAttributeNS(NULL,filename);
126 if (source && *source) {
127 const XMLCh* valflag=e->getAttributeNS(NULL,validate);
128 m_validate=(XMLString::equals(valflag,XMLConstants::XML_TRUE) || XMLString::equals(valflag,XMLConstants::XML_ONE));
130 auto_ptr_char temp(source);
132 log.debug("using external metadata file (%s)", temp.get());
135 struct _stat stat_buf;
136 if (_stat(m_source.c_str(), &stat_buf) == 0)
138 struct stat stat_buf;
139 if (stat(m_source.c_str(), &stat_buf) == 0)
141 m_filestamp=stat_buf.st_mtime;
142 m_lock=RWLock::create();
145 log.debug("no file path/name supplied, will look for metadata inline");
148 FilesystemMetadataProvider::~FilesystemMetadataProvider()
154 void FilesystemMetadataProvider::init()
160 XMLObject* FilesystemMetadataProvider::load() const
165 Category& log=Category::getInstance(SAML_LOGCAT".Metadata");
168 XMLObject* xmlObject=NULL;
170 if (!m_source.empty()) {
171 // Data comes from a file we have to parse.
172 log.debug("loading metadata from file...");
173 auto_ptr_XMLCh widenit(m_source.c_str());
174 LocalFileInputSource src(widenit.get());
175 Wrapper4InputSource dsrc(&src,false);
176 DOMDocument* doc=NULL;
178 doc=XMLToolingConfig::getConfig().getValidatingParser().parse(dsrc);
180 doc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
181 XercesJanitor<DOMDocument> docjanitor(doc);
182 log.infoStream() << "loaded and parsed XML file (" << m_source << ")" << CategoryStream::ENDLINE;
184 // Unmarshall objects, binding the document.
185 xmlObject = XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true);
186 docjanitor.release();
189 // Data comes from the DOM we were handed.
190 log.debug("loading inline metadata...");
191 DOMElement* child = XMLHelper::getFirstChildElement(m_root);
193 throw XMLToolingException("No metadata was found inline.");
194 xmlObject = XMLObjectBuilder::buildOneFromElement(child);
197 auto_ptr<XMLObject> xmlObjectPtr(xmlObject);
200 log.info("applying metadata filter (%s)", m_filter->getId());
201 m_filter->doFilter(*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::lookupGroup(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::lookupGroup(const XMLCh* name, bool strict) const
326 auto_ptr_char temp(name);
327 return lookupGroup(temp.get(),strict);
330 const EntityDescriptor* FilesystemMetadataProvider::lookup(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::lookup(const XMLCh* name, bool strict) const
347 auto_ptr_char temp(name);
348 return lookup(temp.get(),strict);