268677fbc312e465a86bb8c55f77951a4f29e1d6
[shibboleth/sp.git] / shibsp / attribute / resolver / impl / SimpleAttributeResolver.cpp
1 /*\r
2  *  Copyright 2001-2007 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  * SimpleAttributeResolver.cpp\r
19  * \r
20  * AttributeResolver based on a simple mapping of SAML information.\r
21  */\r
22 \r
23 #include "internal.h"\r
24 #include "Application.h"\r
25 #include "ServiceProvider.h"\r
26 #include "SessionCache.h"\r
27 #include "attribute/AttributeDecoder.h"\r
28 #include "attribute/resolver/AttributeResolver.h"\r
29 #include "attribute/resolver/ResolutionContext.h"\r
30 #include "binding/SOAPClient.h"\r
31 #include "util/SPConstants.h"\r
32 \r
33 \r
34 #include <log4cpp/Category.hh>\r
35 #include <saml/binding/SecurityPolicy.h>\r
36 #include <saml/saml1/binding/SAML1SOAPClient.h>\r
37 #include <saml/saml1/core/Assertions.h>\r
38 #include <saml/saml1/core/Protocols.h>\r
39 #include <saml/saml1/profile/AssertionValidator.h>\r
40 #include <saml/saml2/binding/SAML2SOAPClient.h>\r
41 #include <saml/saml2/core/Protocols.h>\r
42 #include <saml/saml2/metadata/Metadata.h>\r
43 #include <saml/saml2/metadata/MetadataProvider.h>\r
44 #include <saml/saml2/profile/AssertionValidator.h>\r
45 #include <xmltooling/util/NDC.h>\r
46 #include <xmltooling/util/ReloadableXMLFile.h>\r
47 #include <xmltooling/util/XMLHelper.h>\r
48 #include <xercesc/util/XMLUniDefs.hpp>\r
49 \r
50 using namespace shibsp;\r
51 using namespace opensaml::saml1;\r
52 using namespace opensaml::saml1p;\r
53 using namespace opensaml::saml2;\r
54 using namespace opensaml::saml2p;\r
55 using namespace opensaml::saml2md;\r
56 using namespace opensaml;\r
57 using namespace xmltooling;\r
58 using namespace log4cpp;\r
59 using namespace std;\r
60 \r
61 namespace shibsp {\r
62 \r
63     class SHIBSP_DLLLOCAL SimpleContext : public ResolutionContext\r
64     {\r
65     public:\r
66         SimpleContext(const Application& application, const Session& session)\r
67             : m_app(application), m_session(&session), m_client_addr(NULL), m_metadata(NULL), m_entity(NULL),\r
68                 m_nameid(session.getNameID()), m_tokens(NULL) {\r
69         }\r
70         \r
71         SimpleContext(\r
72             const Application& application,\r
73             const char* client_addr,\r
74             const EntityDescriptor* issuer,\r
75             const NameID& nameid,\r
76             const vector<const opensaml::Assertion*>* tokens=NULL\r
77             ) : m_app(application), m_session(NULL), m_client_addr(client_addr), m_entity(issuer), m_nameid(nameid), m_tokens(tokens) {\r
78         }\r
79         \r
80         ~SimpleContext() {\r
81             if (m_metadata)\r
82                 m_metadata->unlock();\r
83             for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<shibsp::Attribute>());\r
84             for_each(m_assertions.begin(), m_assertions.end(), xmltooling::cleanup<opensaml::Assertion>());\r
85         }\r
86     \r
87         const Application& getApplication() const {\r
88             return m_app;\r
89         }\r
90         const char* getClientAddress() const {\r
91             return m_session ? m_session->getClientAddress() : m_client_addr;\r
92         }\r
93         const EntityDescriptor* getEntityDescriptor() const {\r
94             if (m_entity)\r
95                 return m_entity;\r
96             if (m_session && m_session->getEntityID()) {\r
97                 m_metadata = m_app.getMetadataProvider();\r
98                 if (m_metadata) {\r
99                     m_metadata->lock();\r
100                     return m_entity = m_metadata->getEntityDescriptor(m_session->getEntityID());\r
101                 }\r
102             }\r
103             return NULL;\r
104         }\r
105         const NameID& getNameID() const {\r
106             return m_nameid;\r
107         }\r
108         const vector<const opensaml::Assertion*>* getTokens() const {\r
109             return m_tokens;\r
110         }\r
111         const Session* getSession() const {\r
112             return m_session;\r
113         }        \r
114         vector<shibsp::Attribute*>& getResolvedAttributes() {\r
115             return m_attributes;\r
116         }\r
117         vector<opensaml::Assertion*>& getResolvedAssertions() {\r
118             return m_assertions;\r
119         }\r
120 \r
121     private:\r
122         const Application& m_app;\r
123         const Session* m_session;\r
124         const char* m_client_addr;\r
125         mutable MetadataProvider* m_metadata;\r
126         mutable const EntityDescriptor* m_entity;\r
127         const NameID& m_nameid;\r
128         const vector<const opensaml::Assertion*>* m_tokens;\r
129         vector<shibsp::Attribute*> m_attributes;\r
130         vector<opensaml::Assertion*> m_assertions;\r
131     };\r
132     \r
133 #if defined (_MSC_VER)\r
134     #pragma warning( push )\r
135     #pragma warning( disable : 4250 )\r
136 #endif\r
137 \r
138     class SimpleResolverImpl\r
139     {\r
140     public:\r
141         SimpleResolverImpl(const DOMElement* e);\r
142         ~SimpleResolverImpl() {\r
143             for_each(m_decoderMap.begin(), m_decoderMap.end(), cleanup_pair<string,AttributeDecoder>());\r
144             if (m_document)\r
145                 m_document->release();\r
146         }\r
147 \r
148         void setDocument(DOMDocument* doc) {\r
149             m_document = doc;\r
150         }\r
151 \r
152         void query(\r
153             ResolutionContext& ctx, const NameIdentifier& nameid, const vector<const char*>* attributes=NULL\r
154             ) const;\r
155         void query(\r
156             ResolutionContext& ctx, const NameID& nameid, const vector<const char*>* attributes=NULL\r
157             ) const;\r
158         void resolve(\r
159             ResolutionContext& ctx, const saml1::Assertion* token, const vector<const char*>* attributes=NULL\r
160             ) const;\r
161         void resolve(\r
162             ResolutionContext& ctx, const saml2::Assertion* token, const vector<const char*>* attributes=NULL\r
163             ) const;\r
164 \r
165         bool m_allowQuery;\r
166     private:\r
167         DOMDocument* m_document;\r
168         map<string,AttributeDecoder*> m_decoderMap;\r
169 #ifdef HAVE_GOOD_STL\r
170         map< pair<xstring,xstring>,pair<const AttributeDecoder*,string> > m_attrMap;\r
171 #else\r
172         map< pair<string,string>,pair<const AttributeDecoder*,string> > m_attrMap;\r
173 #endif\r
174     };\r
175     \r
176     class SimpleResolver : public AttributeResolver, public ReloadableXMLFile\r
177     {\r
178     public:\r
179         SimpleResolver(const DOMElement* e) : ReloadableXMLFile(e), m_impl(NULL) {\r
180             load();\r
181         }\r
182         ~SimpleResolver() {\r
183             delete m_impl;\r
184         }\r
185         \r
186         ResolutionContext* createResolutionContext(\r
187             const Application& application,\r
188             const char* client_addr,\r
189             const EntityDescriptor* issuer,\r
190             const NameID& nameid,\r
191             const vector<const opensaml::Assertion*>* tokens=NULL\r
192             ) const {\r
193             return new SimpleContext(application,client_addr,issuer,nameid,tokens);\r
194         }\r
195 \r
196         ResolutionContext* createResolutionContext(const Application& application, const Session& session) const {\r
197             return new SimpleContext(application,session);\r
198         }\r
199         \r
200         void resolveAttributes(ResolutionContext& ctx, const vector<const char*>* attributes=NULL) const;\r
201 \r
202     protected:\r
203         pair<bool,DOMElement*> load();\r
204         SimpleResolverImpl* m_impl;\r
205     };\r
206 \r
207 #if defined (_MSC_VER)\r
208     #pragma warning( pop )\r
209 #endif\r
210 \r
211     AttributeResolver* SHIBSP_DLLLOCAL SimpleAttributeResolverFactory(const DOMElement* const & e)\r
212     {\r
213         return new SimpleResolver(e);\r
214     }\r
215     \r
216     static const XMLCh SIMPLE_NS[] = {\r
217         chLatin_u, chLatin_r, chLatin_n, chColon, chLatin_m, chLatin_a, chLatin_c, chLatin_e, chColon,\r
218         chLatin_s, chLatin_h, chLatin_i, chLatin_b, chLatin_b, chLatin_o, chLatin_l, chLatin_e, chLatin_t, chLatin_h, chColon,\r
219         chDigit_2, chPeriod, chDigit_0, chColon,\r
220         chLatin_r, chLatin_e, chLatin_s, chLatin_o, chLatin_l, chLatin_v, chLatin_e, chLatin_r, chColon,\r
221         chLatin_s, chLatin_i, chLatin_m, chLatin_p, chLatin_l, chLatin_e, chNull\r
222     };\r
223     static const XMLCh _AttributeDecoder[] =    UNICODE_LITERAL_16(A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r);\r
224     static const XMLCh _AttributeResolver[] =   UNICODE_LITERAL_17(A,t,t,r,i,b,u,t,e,R,e,s,o,l,v,e,r);\r
225     static const XMLCh allowQuery[] =           UNICODE_LITERAL_10(a,l,l,o,w,Q,u,e,r,y);\r
226     static const XMLCh decoderId[] =            UNICODE_LITERAL_9(d,e,c,o,d,e,r,I,d);\r
227     static const XMLCh _id[] =                  UNICODE_LITERAL_2(i,d);\r
228     static const XMLCh _type[] =                UNICODE_LITERAL_4(t,y,p,e);\r
229 };\r
230 \r
231 SimpleResolverImpl::SimpleResolverImpl(const DOMElement* e) : m_document(NULL), m_allowQuery(true)\r
232 {\r
233 #ifdef _DEBUG\r
234     xmltooling::NDC ndc("SimpleResolverImpl");\r
235 #endif\r
236     Category& log=Category::getInstance(SHIBSP_LOGCAT".AttributeResolver");\r
237     \r
238     if (!XMLHelper::isNodeNamed(e, SIMPLE_NS, _AttributeResolver))\r
239         throw ConfigurationException("Simple resolver requires resolver:AttributeResolver at root of configuration.");\r
240     \r
241     const XMLCh* flag = e->getAttributeNS(NULL,allowQuery);\r
242     if (flag && (*flag==chLatin_f || *flag==chDigit_0)) {\r
243         log.info("SAML attribute queries disabled");\r
244         m_allowQuery = false;\r
245     }\r
246 \r
247     DOMElement* child = XMLHelper::getFirstChildElement(e, SIMPLE_NS, _AttributeDecoder);\r
248     while (child) {\r
249         auto_ptr_char id(child->getAttributeNS(NULL, _id));\r
250         auto_ptr_char type(child->getAttributeNS(NULL, _type));\r
251         try {\r
252             log.info("building AttributeDecoder (%s) of type %s", id.get(), type.get());\r
253             m_decoderMap[id.get()] = SPConfig::getConfig().AttributeDecoderManager.newPlugin(type.get(), child);\r
254         }\r
255         catch (exception& ex) {\r
256             log.error("error building AttributeDecoder (%s): %s", id.get(), ex.what());\r
257         }\r
258         child = XMLHelper::getNextSiblingElement(child, SIMPLE_NS, _AttributeDecoder);\r
259     }\r
260     \r
261     child = XMLHelper::getFirstChildElement(e, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME);\r
262     while (child) {\r
263         // Check for missing Name.\r
264         const XMLCh* name = child->getAttributeNS(NULL, saml2::Attribute::NAME_ATTRIB_NAME);\r
265         if (!name || !*name) {\r
266             log.warn("skipping saml:Attribute declared with no Name");\r
267             child = XMLHelper::getNextSiblingElement(child, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME);\r
268             continue;\r
269         }\r
270 \r
271         const AttributeDecoder* decoder=NULL;\r
272         auto_ptr_char id(child->getAttributeNS(NULL, saml2::Attribute::FRIENDLYNAME_ATTRIB_NAME));\r
273         auto_ptr_char d(child->getAttributeNS(SIMPLE_NS, decoderId));\r
274         if (!id.get() || !*id.get() || !d.get() || !*d.get() || !(decoder=m_decoderMap[d.get()])) {\r
275             log.warn("skipping saml:Attribute declared with no FriendlyName or resolvable AttributeDecoder");\r
276             child = XMLHelper::getNextSiblingElement(child, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME);\r
277             continue;\r
278         }\r
279         \r
280         // Empty NameFormat implies the usual Shib URI naming defaults.\r
281         const XMLCh* format = child->getAttributeNS(NULL, saml2::Attribute::NAMEFORMAT_ATTRIB_NAME);\r
282         if (!format || XMLString::equals(format, shibspconstants::SHIB1_ATTRIBUTE_NAMESPACE_URI) ||\r
283                 XMLString::equals(format, saml2::Attribute::URI_REFERENCE))\r
284             format = &chNull;  // ignore default Format/Namespace values\r
285 \r
286         // Fetch/create the map entry and see if it's a duplicate rule.\r
287 #ifdef HAVE_GOOD_STL\r
288         pair<const AttributeDecoder*,string>& decl = m_attrMap[make_pair(name,format)];\r
289 #else\r
290         auto_ptr_char n(name);\r
291         auto_ptr_char f(format);\r
292         pair<const AttributeDecoder*,string>& decl = m_attrMap[make_pair(n.get(),f.get())];\r
293 #endif\r
294         if (decl.first) {\r
295             log.warn("skipping duplicate saml:Attribute declaration (same Name and NameFormat)");\r
296             child = XMLHelper::getNextSiblingElement(child, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME);\r
297             continue;\r
298         }\r
299 \r
300         if (log.isDebugEnabled()) {\r
301 #ifdef HAVE_GOOD_STL\r
302             auto_ptr_char n(name);\r
303             auto_ptr_char f(format);\r
304 #endif\r
305             log.debug("creating declaration for Attribute %s%s%s", n.get(), *f.get() ? ", Format/Namespace:" : "", f.get());\r
306         }\r
307         \r
308         decl.first = decoder;\r
309         decl.second = id.get();\r
310         \r
311         child = XMLHelper::getNextSiblingElement(child, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME);\r
312     }\r
313 }\r
314 \r
315 void SimpleResolverImpl::resolve(\r
316     ResolutionContext& ctx, const saml1::Assertion* token, const vector<const char*>* attributes\r
317     ) const\r
318 {\r
319     set<string> aset;\r
320     if (attributes)\r
321         for(vector<const char*>::const_iterator i=attributes->begin(); i!=attributes->end(); ++i)\r
322             aset.insert(*i);\r
323 \r
324     vector<shibsp::Attribute*>& resolved = ctx.getResolvedAttributes();\r
325 \r
326     auto_ptr_char assertingParty(ctx.getEntityDescriptor() ? ctx.getEntityDescriptor()->getEntityID() : NULL);\r
327     const char* relyingParty = ctx.getApplication().getString("providerId").second;\r
328 \r
329 #ifdef HAVE_GOOD_STL\r
330     map< pair<xstring,xstring>,pair<const AttributeDecoder*,string> >::const_iterator rule;\r
331 #else\r
332     map< pair<string,string>,pair<const AttributeDecoder*,string> >::const_iterator rule;\r
333 #endif\r
334 \r
335     // Check the NameID based on the format.\r
336     const XMLCh* name;\r
337     const XMLCh* format = ctx.getNameID().getFormat();\r
338     if (!format) {\r
339         format = NameID::UNSPECIFIED;\r
340 #ifdef HAVE_GOOD_STL\r
341         if ((rule=m_attrMap.find(make_pair(format,xstring()))) != m_attrMap.end()) {\r
342 #else\r
343         auto_ptr_char temp(format);\r
344         if ((rule=m_attrMap.find(make_pair(temp.get(),string()))) != m_attrMap.end()) {\r
345 #endif\r
346             if (aset.empty() || aset.count(rule->second.second)) {\r
347                 resolved.push_back(\r
348                     rule->second.first->decode(\r
349                         rule->second.second.c_str(), &ctx.getNameID(), assertingParty.get(), relyingParty\r
350                         )\r
351                     );\r
352             }\r
353         }\r
354     }\r
355 \r
356     const vector<saml1::AttributeStatement*>& statements = token->getAttributeStatements();\r
357     for (vector<saml1::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s) {\r
358         const vector<saml1::Attribute*>& attrs = const_cast<const saml1::AttributeStatement*>(*s)->getAttributes();\r
359         for (vector<saml1::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a) {\r
360             name = (*a)->getAttributeName();\r
361             format = (*a)->getAttributeNamespace();\r
362             if (!name || !*name)\r
363                 continue;\r
364             if (!format)\r
365                 format = &chNull;\r
366 #ifdef HAVE_GOOD_STL\r
367             if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {\r
368 #else\r
369             auto_ptr_char temp1(name);\r
370             auto_ptr_char temp2(format);\r
371             if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {\r
372 #endif\r
373                 if (aset.empty() || aset.count(rule->second.second)) {\r
374                     resolved.push_back(\r
375                         rule->second.first->decode(rule->second.second.c_str(), *a, assertingParty.get(), relyingParty)\r
376                         );\r
377                 }\r
378             }\r
379         }\r
380     }\r
381 }\r
382 \r
383 void SimpleResolverImpl::resolve(\r
384     ResolutionContext& ctx, const saml2::Assertion* token, const vector<const char*>* attributes\r
385     ) const\r
386 {\r
387     set<string> aset;\r
388     if (attributes)\r
389         for(vector<const char*>::const_iterator i=attributes->begin(); i!=attributes->end(); ++i)\r
390             aset.insert(*i);\r
391 \r
392     vector<shibsp::Attribute*>& resolved = ctx.getResolvedAttributes();\r
393 \r
394     auto_ptr_char assertingParty(ctx.getEntityDescriptor() ? ctx.getEntityDescriptor()->getEntityID() : NULL);\r
395     const char* relyingParty = ctx.getApplication().getString("providerId").second;\r
396 \r
397 #ifdef HAVE_GOOD_STL\r
398     map< pair<xstring,xstring>,pair<const AttributeDecoder*,string> >::const_iterator rule;\r
399 #else\r
400     map< pair<string,string>,pair<const AttributeDecoder*,string> >::const_iterator rule;\r
401 #endif\r
402 \r
403     // Check the NameID based on the format.\r
404     const XMLCh* name;\r
405     const XMLCh* format = ctx.getNameID().getFormat();\r
406     if (!format) {\r
407         format = NameID::UNSPECIFIED;\r
408 #ifdef HAVE_GOOD_STL\r
409         if ((rule=m_attrMap.find(make_pair(format,xstring()))) != m_attrMap.end()) {\r
410 #else\r
411         auto_ptr_char temp(format);\r
412         if ((rule=m_attrMap.find(make_pair(temp.get(),string()))) != m_attrMap.end()) {\r
413 #endif\r
414             if (aset.empty() || aset.count(rule->second.second)) {\r
415                 resolved.push_back(\r
416                     rule->second.first->decode(\r
417                         rule->second.second.c_str(), &ctx.getNameID(), assertingParty.get(), relyingParty\r
418                         )\r
419                     );\r
420             }\r
421         }\r
422     }\r
423 \r
424     const vector<saml2::AttributeStatement*>& statements = token->getAttributeStatements();\r
425     for (vector<saml2::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s) {\r
426         const vector<saml2::Attribute*>& attrs = const_cast<const saml2::AttributeStatement*>(*s)->getAttributes();\r
427         for (vector<saml2::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a) {\r
428             name = (*a)->getName();\r
429             format = (*a)->getNameFormat();\r
430             if (!name || !*name)\r
431                 continue;\r
432             if (!format)\r
433                 format = &chNull;\r
434 #ifdef HAVE_GOOD_STL\r
435             if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {\r
436 #else\r
437             auto_ptr_char temp1(name);\r
438             auto_ptr_char temp2(format);\r
439             if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {\r
440 #endif\r
441                 if (aset.empty() || aset.count(rule->second.second)) {\r
442                     resolved.push_back(\r
443                         rule->second.first->decode(rule->second.second.c_str(), *a, assertingParty.get(), relyingParty)\r
444                         );\r
445                 }\r
446             }\r
447         }\r
448     }\r
449 }\r
450 \r
451 void SimpleResolverImpl::query(ResolutionContext& ctx, const NameIdentifier& nameid, const vector<const char*>* attributes) const\r
452 {\r
453 #ifdef _DEBUG\r
454     xmltooling::NDC ndc("query");\r
455 #endif\r
456     Category& log=Category::getInstance(SHIBSP_LOGCAT".AttributeResolver");\r
457 \r
458     const EntityDescriptor* entity = ctx.getEntityDescriptor();\r
459     if (!entity) {\r
460         log.debug("no issuer information available, skipping query");\r
461         return;\r
462     }\r
463 \r
464     int version = 1;\r
465     const AttributeAuthorityDescriptor* AA = entity->getAttributeAuthorityDescriptor(samlconstants::SAML11_PROTOCOL_ENUM);\r
466     if (!AA) {\r
467         AA = entity->getAttributeAuthorityDescriptor(samlconstants::SAML10_PROTOCOL_ENUM);\r
468         version = 0;\r
469     }\r
470     if (!AA) {\r
471         log.info("no SAML 1.x AttributeAuthority role found in metadata");\r
472         return;\r
473     }\r
474 \r
475     SecurityPolicy policy;\r
476     shibsp::SOAPClient soaper(ctx.getApplication(),policy);\r
477     const PropertySet* policySettings = ctx.getApplication().getServiceProvider().getPolicySettings(ctx.getApplication().getString("policyId").second);\r
478     pair<bool,bool> signedAssertions = policySettings->getBool("signedAssertions");\r
479 \r
480     auto_ptr_XMLCh binding(samlconstants::SAML1_BINDING_SOAP);\r
481     saml1p::Response* response=NULL;\r
482     const vector<AttributeService*>& endpoints=AA->getAttributeServices();\r
483     for (vector<AttributeService*>::const_iterator ep=endpoints.begin(); !response && ep!=endpoints.end(); ++ep) {\r
484         try {\r
485             if (!XMLString::equals((*ep)->getBinding(),binding.get()))\r
486                 continue;\r
487             auto_ptr_char loc((*ep)->getLocation());\r
488             auto_ptr_XMLCh issuer(ctx.getApplication().getString("providerId").second);\r
489             saml1::Subject* subject = saml1::SubjectBuilder::buildSubject();\r
490             subject->setNameIdentifier(nameid.cloneNameIdentifier());\r
491             saml1p::AttributeQuery* query = saml1p::AttributeQueryBuilder::buildAttributeQuery();\r
492             query->setSubject(subject);\r
493             Request* request = RequestBuilder::buildRequest();\r
494             request->setAttributeQuery(query);\r
495             query->setResource(issuer.get());\r
496             request->setMinorVersion(version);\r
497             SAML1SOAPClient client(soaper);\r
498             client.sendSAML(request, *AA, loc.get());\r
499             response = client.receiveSAML();\r
500         }\r
501         catch (exception& ex) {\r
502             log.error("exception making SAML query: %s", ex.what());\r
503             soaper.reset();\r
504         }\r
505     }\r
506 \r
507     if (!response) {\r
508         log.error("unable to successfully query for attributes");\r
509         return;\r
510     }\r
511 \r
512     const vector<saml1::Assertion*>& assertions = const_cast<const saml1p::Response*>(response)->getAssertions();\r
513     if (assertions.size()>1)\r
514         log.warn("simple resolver only supports one assertion in the query response");\r
515 \r
516     auto_ptr<saml1p::Response> wrapper(response);\r
517     saml1::Assertion* newtoken = assertions.front();\r
518 \r
519     if (!newtoken->getSignature() && signedAssertions.first && signedAssertions.second) {\r
520         log.error("assertion unsigned, rejecting it based on signedAssertions policy");\r
521         return;\r
522     }\r
523 \r
524     try {\r
525         policy.evaluate(*newtoken);\r
526         if (!policy.isSecure())\r
527             throw SecurityPolicyException("Security of SAML 1.x query result not established.");\r
528         saml1::AssertionValidator tokval(ctx.getApplication().getAudiences(), time(NULL));\r
529         tokval.validateAssertion(*newtoken);\r
530     }\r
531     catch (exception& ex) {\r
532         log.error("assertion failed policy/validation: %s", ex.what());\r
533     }\r
534     newtoken->detach();\r
535     wrapper.release();\r
536     ctx.getResolvedAssertions().push_back(newtoken);\r
537     resolve(ctx, newtoken, attributes);\r
538 }\r
539 \r
540 void SimpleResolverImpl::query(ResolutionContext& ctx, const NameID& nameid, const vector<const char*>* attributes) const\r
541 {\r
542 #ifdef _DEBUG\r
543     xmltooling::NDC ndc("query");\r
544 #endif\r
545     Category& log=Category::getInstance(SHIBSP_LOGCAT".AttributeResolver");\r
546 \r
547     const EntityDescriptor* entity = ctx.getEntityDescriptor();\r
548     if (!entity) {\r
549         log.debug("no issuer information available, skipping query");\r
550         return;\r
551     }\r
552     const AttributeAuthorityDescriptor* AA = entity->getAttributeAuthorityDescriptor(samlconstants::SAML20P_NS);\r
553     if (!AA) {\r
554         log.info("no SAML 2 AttributeAuthority role found in metadata");\r
555         return;\r
556     }\r
557 \r
558     SecurityPolicy policy;\r
559     shibsp::SOAPClient soaper(ctx.getApplication(),policy);\r
560     const PropertySet* policySettings = ctx.getApplication().getServiceProvider().getPolicySettings(ctx.getApplication().getString("policyId").second);\r
561     pair<bool,bool> signedAssertions = policySettings->getBool("signedAssertions");\r
562 \r
563     auto_ptr_XMLCh binding(samlconstants::SAML20_BINDING_SOAP);\r
564     saml2p::StatusResponseType* srt=NULL;\r
565     const vector<AttributeService*>& endpoints=AA->getAttributeServices();\r
566     for (vector<AttributeService*>::const_iterator ep=endpoints.begin(); !srt && ep!=endpoints.end(); ++ep) {\r
567         try {\r
568             if (!XMLString::equals((*ep)->getBinding(),binding.get()))\r
569                 continue;\r
570             auto_ptr_char loc((*ep)->getLocation());\r
571             auto_ptr_XMLCh issuer(ctx.getApplication().getString("providerId").second);\r
572             saml2::Subject* subject = saml2::SubjectBuilder::buildSubject();\r
573             subject->setNameID(nameid.cloneNameID());\r
574             saml2p::AttributeQuery* query = saml2p::AttributeQueryBuilder::buildAttributeQuery();\r
575             query->setSubject(subject);\r
576             Issuer* iss = IssuerBuilder::buildIssuer();\r
577             query->setIssuer(iss);\r
578             iss->setName(issuer.get());\r
579             SAML2SOAPClient client(soaper);\r
580             client.sendSAML(query, *AA, loc.get());\r
581             srt = client.receiveSAML();\r
582         }\r
583         catch (exception& ex) {\r
584             log.error("exception making SAML query: %s", ex.what());\r
585             soaper.reset();\r
586         }\r
587     }\r
588 \r
589     if (!srt) {\r
590         log.error("unable to successfully query for attributes");\r
591         return;\r
592     }\r
593     saml2p::Response* response = dynamic_cast<saml2p::Response*>(srt);\r
594     if (!response) {\r
595         delete srt;\r
596         log.error("message was not a samlp:Response");\r
597         return;\r
598     }\r
599 \r
600     const vector<saml2::Assertion*>& assertions = const_cast<const saml2p::Response*>(response)->getAssertions();\r
601     if (assertions.size()>1)\r
602         log.warn("simple resolver only supports one assertion in the query response");\r
603 \r
604     auto_ptr<saml2p::Response> wrapper(response);\r
605     saml2::Assertion* newtoken = assertions.front();\r
606 \r
607     if (!newtoken->getSignature() && signedAssertions.first && signedAssertions.second) {\r
608         log.error("assertion unsigned, rejecting it based on signedAssertions policy");\r
609         return;\r
610     }\r
611 \r
612     try {\r
613         policy.evaluate(*newtoken);\r
614         if (!policy.isSecure())\r
615             throw SecurityPolicyException("Security of SAML 2.0 query result not established.");\r
616         saml2::AssertionValidator tokval(ctx.getApplication().getAudiences(), time(NULL));\r
617         tokval.validateAssertion(*newtoken);\r
618     }\r
619     catch (exception& ex) {\r
620         log.error("assertion failed policy/validation: %s", ex.what());\r
621     }\r
622     newtoken->detach();\r
623     wrapper.release();\r
624     ctx.getResolvedAssertions().push_back(newtoken);\r
625     resolve(ctx, newtoken, attributes);\r
626 }\r
627 \r
628 void SimpleResolver::resolveAttributes(ResolutionContext& ctx, const vector<const char*>* attributes) const\r
629 {\r
630 #ifdef _DEBUG\r
631     xmltooling::NDC ndc("resolveAttributes");\r
632 #endif\r
633     Category& log=Category::getInstance(SHIBSP_LOGCAT".AttributeResolver");\r
634     \r
635     log.debug("examining tokens to resolve");\r
636 \r
637     bool query = m_impl->m_allowQuery;\r
638     const saml1::Assertion* token1;\r
639     const saml2::Assertion* token2;\r
640     if (ctx.getTokens()) {\r
641         for (vector<const opensaml::Assertion*>::const_iterator t = ctx.getTokens()->begin(); t!=ctx.getTokens()->end(); ++t) {\r
642             token2 = dynamic_cast<const saml2::Assertion*>(*t);\r
643             if (token2 && !token2->getAttributeStatements().empty()) {\r
644                 log.debug("resolving SAML 2 token with an AttributeStatement");\r
645                 m_impl->resolve(ctx, token2, attributes);\r
646                 query = false;\r
647             }\r
648             else {\r
649                 token1 = dynamic_cast<const saml1::Assertion*>(*t);\r
650                 if (token1 && !token1->getAttributeStatements().empty()) {\r
651                     log.debug("resolving SAML 1 token with an AttributeStatement");\r
652                     m_impl->resolve(ctx, token1, attributes);\r
653                     query = false;\r
654                 }\r
655             }\r
656         }\r
657     }\r
658 \r
659     if (query) {\r
660         if (token1 && !token1->getAuthenticationStatements().empty()) {\r
661             log.debug("attempting SAML 1.x attribute query");\r
662             return m_impl->query(ctx, *(token1->getAuthenticationStatements().front()->getSubject()->getNameIdentifier()), attributes);\r
663         }\r
664         log.debug("attempting SAML 2.0 attribute query");\r
665         m_impl->query(ctx, ctx.getNameID(), attributes);\r
666     }\r
667 }\r
668 \r
669 pair<bool,DOMElement*> SimpleResolver::load()\r
670 {\r
671     // Load from source using base class.\r
672     pair<bool,DOMElement*> raw = ReloadableXMLFile::load();\r
673     \r
674     // If we own it, wrap it.\r
675     XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : NULL);\r
676 \r
677     SimpleResolverImpl* impl = new SimpleResolverImpl(raw.second);\r
678     \r
679     // If we held the document, transfer it to the impl. If we didn't, it's a no-op.\r
680     impl->setDocument(docjanitor.release());\r
681 \r
682     delete m_impl;\r
683     m_impl = impl;\r
684 \r
685     return make_pair(false,(DOMElement*)NULL);\r
686 }\r