Imported Upstream version 2.4+dfsg
[shibboleth/sp.git] / shibsp / binding / impl / XMLProtocolProvider.cpp
1 /*\r
2  *  Copyright 2010 Internet2\r
3  *\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
7  *\r
8  *     http://www.apache.org/licenses/LICENSE-2.0\r
9  *\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
15  */\r
16 \r
17 /**\r
18  * XMLProtocolProvider.cpp\r
19  *\r
20  * XML-based protocol provider.\r
21  */\r
22 \r
23 #include "internal.h"\r
24 #include "exceptions.h"\r
25 #include "binding/ProtocolProvider.h"\r
26 #include "util/DOMPropertySet.h"\r
27 #include "util/SPConstants.h"\r
28 \r
29 #include <map>\r
30 #include <xmltooling/io/HTTPResponse.h>\r
31 #include <xmltooling/util/NDC.h>\r
32 #include <xmltooling/util/ReloadableXMLFile.h>\r
33 #include <xmltooling/util/Threads.h>\r
34 #include <xmltooling/util/XMLHelper.h>\r
35 #include <xercesc/util/XMLUniDefs.hpp>\r
36 \r
37 using shibspconstants::SHIB2SPPROTOCOLS_NS;\r
38 using namespace shibsp;\r
39 using namespace xmltooling;\r
40 using namespace std;\r
41 \r
42 namespace shibsp {\r
43 \r
44     static const XMLCh _id[] =          UNICODE_LITERAL_2(i,d);\r
45     static const XMLCh Binding[] =      UNICODE_LITERAL_7(B,i,n,d,i,n,g);\r
46     static const XMLCh Initiator[] =    UNICODE_LITERAL_9(I,n,i,t,i,a,t,o,r);\r
47     static const XMLCh Protocol[] =     UNICODE_LITERAL_8(P,r,o,t,o,c,o,l);\r
48     static const XMLCh Protocols[] =    UNICODE_LITERAL_9(P,r,o,t,o,c,o,l,s);\r
49     static const XMLCh Service[] =      UNICODE_LITERAL_7(S,e,r,v,i,c,e);\r
50 \r
51 #if defined (_MSC_VER)\r
52     #pragma warning( push )\r
53     #pragma warning( disable : 4250 )\r
54 #endif\r
55 \r
56     class SHIBSP_DLLLOCAL XMLProtocolProviderImpl : public DOMNodeFilter, DOMPropertySet\r
57     {\r
58     public:\r
59         XMLProtocolProviderImpl(const DOMElement* e, Category& log);\r
60         ~XMLProtocolProviderImpl() {\r
61             for (protmap_t::iterator i = m_map.begin(); i != m_map.end(); ++i) {\r
62                 delete i->second.first;\r
63                 for_each(i->second.second.begin(), i->second.second.end(), xmltooling::cleanup<PropertySet>());\r
64             }\r
65             if (m_document)\r
66                 m_document->release();\r
67         }\r
68 \r
69         void setDocument(DOMDocument* doc) {\r
70             m_document = doc;\r
71         }\r
72 \r
73 #ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE\r
74         short\r
75 #else\r
76         FilterAction\r
77 #endif\r
78         acceptNode(const DOMNode* node) const {\r
79             return FILTER_REJECT;\r
80         }\r
81 \r
82     private:\r
83         DOMDocument* m_document;\r
84         // Map of protocol/service pair to an Initiator propset plus an array of Binding propsets.\r
85         typedef map< pair<string,string>, pair< PropertySet*,vector<const PropertySet*> > > protmap_t;\r
86         protmap_t m_map;\r
87 \r
88         friend class SHIBSP_DLLLOCAL XMLProtocolProvider;\r
89     };\r
90 \r
91     class XMLProtocolProvider : public ProtocolProvider, public ReloadableXMLFile\r
92     {\r
93     public:\r
94         XMLProtocolProvider(const DOMElement* e)\r
95                 : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT".ProtocolProvider.XML")), m_impl(nullptr) {\r
96             background_load(); // guarantees an exception or the policy is loaded\r
97         }\r
98 \r
99         ~XMLProtocolProvider() {\r
100             shutdown();\r
101             delete m_impl;\r
102         }\r
103 \r
104         const PropertySet* getInitiator(const char* protocol, const char* service) const {\r
105             XMLProtocolProviderImpl::protmap_t::const_iterator i = m_impl->m_map.find(pair<string,string>(protocol,service));\r
106             return (i != m_impl->m_map.end()) ? i->second.first : nullptr;\r
107         }\r
108 \r
109         const vector<const PropertySet*>& getBindings(const char* protocol, const char* service) const {\r
110             XMLProtocolProviderImpl::protmap_t::const_iterator i = m_impl->m_map.find(pair<string,string>(protocol,service));\r
111             return (i != m_impl->m_map.end()) ? i->second.second : m_noBindings;\r
112         }\r
113 \r
114     protected:\r
115         pair<bool,DOMElement*> load(bool backup);\r
116         pair<bool,DOMElement*> background_load();\r
117 \r
118     private:\r
119         static vector<const PropertySet*> m_noBindings;\r
120         XMLProtocolProviderImpl* m_impl;\r
121     };\r
122 \r
123 #if defined (_MSC_VER)\r
124     #pragma warning( pop )\r
125 #endif\r
126 \r
127     ProtocolProvider* SHIBSP_DLLLOCAL XMLProtocolProviderFactory(const DOMElement* const & e)\r
128     {\r
129         return new XMLProtocolProvider(e);\r
130     }\r
131 }\r
132 \r
133 void SHIBSP_API shibsp::registerProtocolProviders()\r
134 {\r
135     SPConfig::getConfig().ProtocolProviderManager.registerFactory(XML_PROTOCOL_PROVIDER, XMLProtocolProviderFactory);\r
136 }\r
137 \r
138 ProtocolProvider::ProtocolProvider()\r
139 {\r
140 }\r
141 \r
142 ProtocolProvider::~ProtocolProvider()\r
143 {\r
144 }\r
145 \r
146 vector<const PropertySet*> XMLProtocolProvider::m_noBindings;\r
147 \r
148 XMLProtocolProviderImpl::XMLProtocolProviderImpl(const DOMElement* e, Category& log) : m_document(nullptr)\r
149 {\r
150 #ifdef _DEBUG\r
151     xmltooling::NDC ndc("XMLProtocolProviderImpl");\r
152 #endif\r
153     //typedef map< pair<string,string>, pair< PropertySet*,vector<const PropertySet*> > > protmap_t;\r
154 \r
155     if (!XMLHelper::isNodeNamed(e, SHIB2SPPROTOCOLS_NS, Protocols))\r
156         throw ConfigurationException("XML ProtocolProvider requires prot:Protocols at root of configuration.");\r
157 \r
158     e = XMLHelper::getFirstChildElement(e, SHIB2SPPROTOCOLS_NS, Protocol);\r
159     while (e) {\r
160         string id = XMLHelper::getAttrString(e, nullptr, _id);\r
161         if (!id.empty()) {\r
162             const DOMElement* svc = XMLHelper::getFirstChildElement(e, SHIB2SPPROTOCOLS_NS, Service);\r
163             while (svc) {\r
164                 string svcid = XMLHelper::getAttrString(svc, nullptr, _id);\r
165                 if (!svcid.empty() && m_map.count(make_pair(id,svcid)) == 0) {\r
166                     pair< PropertySet*,vector<const PropertySet*> >& entry = m_map[make_pair(id,svcid)];\r
167                     // Wrap the Initiator in a propset, if any.\r
168                     const DOMElement* child = XMLHelper::getFirstChildElement(svc, SHIB2SPPROTOCOLS_NS, Initiator);\r
169                     if (child) {\r
170                         DOMPropertySet* initprop = new DOMPropertySet();\r
171                         entry.first = initprop;\r
172                         initprop->load(child, nullptr, this);\r
173                     }\r
174                     else {\r
175                         entry.first = nullptr;\r
176                     }\r
177 \r
178                     // Walk the Bindings.\r
179                     child = XMLHelper::getFirstChildElement(svc, SHIB2SPPROTOCOLS_NS, Binding);\r
180                     while (child) {\r
181                         DOMPropertySet* bindprop = new DOMPropertySet();\r
182                         entry.second.push_back(bindprop);\r
183                         bindprop->load(child, nullptr, this);\r
184                         child = XMLHelper::getNextSiblingElement(child, SHIB2SPPROTOCOLS_NS, Binding);\r
185                     }\r
186                 }\r
187                 svc = XMLHelper::getNextSiblingElement(svc, SHIB2SPPROTOCOLS_NS, Service);\r
188             }\r
189         }\r
190         e = XMLHelper::getNextSiblingElement(e, SHIB2SPPROTOCOLS_NS, Protocol);\r
191     }\r
192 }\r
193 \r
194 \r
195 pair<bool,DOMElement*> XMLProtocolProvider::load(bool backup)\r
196 {\r
197     // Load from source using base class.\r
198     pair<bool,DOMElement*> raw = ReloadableXMLFile::load(backup);\r
199 \r
200     // If we own it, wrap it.\r
201     XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : nullptr);\r
202 \r
203     XMLProtocolProviderImpl* impl = new XMLProtocolProviderImpl(raw.second, m_log);\r
204 \r
205     // If we held the document, transfer it to the impl. If we didn't, it's a no-op.\r
206     impl->setDocument(docjanitor.release());\r
207 \r
208     // Perform the swap inside a lock.\r
209     if (m_lock)\r
210         m_lock->wrlock();\r
211     SharedLock locker(m_lock, false);\r
212     delete m_impl;\r
213     m_impl = impl;\r
214 \r
215 \r
216     return make_pair(false,(DOMElement*)nullptr);\r
217 }\r
218 \r
219 pair<bool,DOMElement*> XMLProtocolProvider::background_load()\r
220 {\r
221     try {\r
222         return load(false);\r
223     }\r
224     catch (long& ex) {\r
225         if (ex == HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED)\r
226             m_log.info("remote resource (%s) unchanged", m_source.c_str());\r
227         if (!m_loaded && !m_backing.empty())\r
228             return load(true);\r
229         throw;\r
230     }\r
231     catch (exception&) {\r
232         if (!m_loaded && !m_backing.empty())\r
233             return load(true);\r
234         throw;\r
235     }\r
236 }\r