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 : 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);
203 log.info("applying metadata filter (%s)", m_filter->getId());
204 m_filter->doFilter(*xmlObject);
207 xmlObjectPtr->releaseThisAndChildrenDOM();
208 xmlObjectPtr->setDocument(NULL);
209 return xmlObjectPtr.release();
211 catch (XMLException& e) {
212 auto_ptr_char msg(e.getMessage());
213 log.errorStream() << "Xerces parser error while loading metadata from ("
214 << (m_source.empty() ? "inline" : m_source) << "): " << msg.get() << CategoryStream::ENDLINE;
215 throw XMLParserException(msg.get());
217 catch (XMLToolingException& e) {
218 log.errorStream() << "error while loading metadata from ("
219 << (m_source.empty() ? "inline" : m_source) << "): " << e.what() << CategoryStream::ENDLINE;
224 Lockable* FilesystemMetadataProvider::lock()
231 // Check if we need to refresh.
233 struct _stat stat_buf;
234 if (_stat(m_source.c_str(), &stat_buf) == 0)
236 struct stat stat_buf;
237 if (stat(m_source.c_str(), &stat_buf) == 0)
240 if (m_filestamp>0 && m_filestamp<stat_buf.st_mtime) {
241 // Elevate lock and recheck.
244 if (m_filestamp>0 && m_filestamp<stat_buf.st_mtime) {
245 SharedLock lockwrap(m_lock,false); // pops write lock
247 // Update the timestamp regardless. No point in repeatedly trying.
248 m_filestamp=stat_buf.st_mtime;
249 XMLObject* newstuff = load();
254 catch(XMLToolingException& e) {
255 Category::getInstance(SAML_LOGCAT".Metadata").error("failed to reload metadata from file, sticking with what we have: %s", e.what());
267 void FilesystemMetadataProvider::index()
273 EntitiesDescriptor* group=dynamic_cast<EntitiesDescriptor*>(m_object);
278 EntityDescriptor* site=dynamic_cast<EntityDescriptor*>(m_object);
282 void FilesystemMetadataProvider::index(EntityDescriptor* site, time_t validUntil)
284 if (validUntil < site->getValidUntilEpoch())
285 site->setValidUntil(validUntil);
287 auto_ptr_char id(site->getEntityID());
289 m_sites.insert(make_pair(id.get(),site));
293 void FilesystemMetadataProvider::index(EntitiesDescriptor* group, time_t validUntil)
295 if (validUntil < group->getValidUntilEpoch())
296 group->setValidUntil(validUntil);
298 auto_ptr_char name(group->getName());
300 m_groups.insert(make_pair(name.get(),group));
303 const vector<EntitiesDescriptor*>& groups=const_cast<const EntitiesDescriptor*>(group)->getEntitiesDescriptors();
304 for (vector<EntitiesDescriptor*>::const_iterator i=groups.begin(); i!=groups.end(); i++)
305 index(*i,group->getValidUntilEpoch());
307 const vector<EntityDescriptor*>& sites=const_cast<const EntitiesDescriptor*>(group)->getEntityDescriptors();
308 for (vector<EntityDescriptor*>::const_iterator j=sites.begin(); j!=sites.end(); j++)
309 index(*j,group->getValidUntilEpoch());
312 const EntitiesDescriptor* FilesystemMetadataProvider::getEntitiesDescriptor(const char* name, bool strict) const
314 pair<groupmap_t::const_iterator,groupmap_t::const_iterator> range=m_groups.equal_range(name);
316 time_t now=time(NULL);
317 for (groupmap_t::const_iterator i=range.first; i!=range.second; i++)
318 if (now < i->second->getValidUntilEpoch())
321 if (!strict && range.first!=range.second)
322 return range.first->second;
327 const EntitiesDescriptor* FilesystemMetadataProvider::getEntitiesDescriptor(const XMLCh* name, bool strict) const
329 auto_ptr_char temp(name);
330 return getEntitiesDescriptor(temp.get(),strict);
333 const EntityDescriptor* FilesystemMetadataProvider::getEntityDescriptor(const char* name, bool strict) const
335 pair<sitemap_t::const_iterator,sitemap_t::const_iterator> range=m_sites.equal_range(name);
337 time_t now=time(NULL);
338 for (sitemap_t::const_iterator i=range.first; i!=range.second; i++)
339 if (now < i->second->getValidUntilEpoch())
342 if (!strict && range.first!=range.second)
343 return range.first->second;
348 const EntityDescriptor* FilesystemMetadataProvider::getEntityDescriptor(const XMLCh* name, bool strict) const
350 auto_ptr_char temp(name);
351 return getEntityDescriptor(temp.get(),strict);