2 * Copyright 2001-2007 Internet2
\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
8 * http://www.apache.org/licenses/LICENSE-2.0
\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
18 * SimpleAttributeResolver.cpp
\r
20 * AttributeResolver based on a simple mapping of SAML information.
\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
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
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
63 class SHIBSP_DLLLOCAL SimpleContext : public ResolutionContext
\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
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_metadata(NULL), m_entity(issuer),
\r
78 m_nameid(nameid), m_tokens(tokens) {
\r
83 m_metadata->unlock();
\r
84 for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<shibsp::Attribute>());
\r
85 for_each(m_assertions.begin(), m_assertions.end(), xmltooling::cleanup<opensaml::Assertion>());
\r
88 const Application& getApplication() const {
\r
91 const char* getClientAddress() const {
\r
92 return m_session ? m_session->getClientAddress() : m_client_addr;
\r
94 const EntityDescriptor* getEntityDescriptor() const {
\r
97 if (m_session && m_session->getEntityID()) {
\r
98 m_metadata = m_app.getMetadataProvider();
\r
100 m_metadata->lock();
\r
101 return m_entity = m_metadata->getEntityDescriptor(m_session->getEntityID());
\r
106 const NameID* getNameID() const {
\r
109 const vector<const opensaml::Assertion*>* getTokens() const {
\r
112 const Session* getSession() const {
\r
115 vector<shibsp::Attribute*>& getResolvedAttributes() {
\r
116 return m_attributes;
\r
118 vector<opensaml::Assertion*>& getResolvedAssertions() {
\r
119 return m_assertions;
\r
123 const Application& m_app;
\r
124 const Session* m_session;
\r
125 const char* m_client_addr;
\r
126 mutable MetadataProvider* m_metadata;
\r
127 mutable const EntityDescriptor* m_entity;
\r
128 const NameID* m_nameid;
\r
129 const vector<const opensaml::Assertion*>* m_tokens;
\r
130 vector<shibsp::Attribute*> m_attributes;
\r
131 vector<opensaml::Assertion*> m_assertions;
\r
134 #if defined (_MSC_VER)
\r
135 #pragma warning( push )
\r
136 #pragma warning( disable : 4250 )
\r
139 class SimpleResolverImpl
\r
142 SimpleResolverImpl(const DOMElement* e);
\r
143 ~SimpleResolverImpl() {
\r
144 for_each(m_decoderMap.begin(), m_decoderMap.end(), cleanup_pair<string,AttributeDecoder>());
\r
146 m_document->release();
\r
149 void setDocument(DOMDocument* doc) {
\r
154 ResolutionContext& ctx, const NameIdentifier& nameid, const set<string>* attributes=NULL
\r
157 ResolutionContext& ctx, const NameID& nameid, const set<string>* attributes=NULL
\r
160 ResolutionContext& ctx, const saml1::Assertion* token, const set<string>* attributes=NULL
\r
163 ResolutionContext& ctx, const saml2::Assertion* token, const set<string>* attributes=NULL
\r
169 void populateQuery(saml1p::AttributeQuery& query, const string& id) const;
\r
170 void populateQuery(saml2p::AttributeQuery& query, const string& id) const;
\r
172 DOMDocument* m_document;
\r
173 map<string,AttributeDecoder*> m_decoderMap;
\r
174 #ifdef HAVE_GOOD_STL
\r
175 typedef map< pair<xstring,xstring>,pair<const AttributeDecoder*,string> > attrmap_t;
\r
177 typedef map< pair<string,string>,pair<const AttributeDecoder*,string> > attrmap_t;
\r
179 attrmap_t m_attrMap;
\r
182 class SimpleResolver : public AttributeResolver, public ReloadableXMLFile
\r
185 SimpleResolver(const DOMElement* e) : ReloadableXMLFile(e), m_impl(NULL) {
\r
188 ~SimpleResolver() {
\r
192 ResolutionContext* createResolutionContext(
\r
193 const Application& application,
\r
194 const char* client_addr,
\r
195 const EntityDescriptor* issuer,
\r
196 const NameID* nameid,
\r
197 const vector<const opensaml::Assertion*>* tokens=NULL
\r
199 return new SimpleContext(application,client_addr,issuer,nameid,tokens);
\r
202 ResolutionContext* createResolutionContext(const Application& application, const Session& session) const {
\r
203 return new SimpleContext(application,session);
\r
206 void resolveAttributes(ResolutionContext& ctx, const set<string>* attributes=NULL) const;
\r
209 pair<bool,DOMElement*> load();
\r
210 SimpleResolverImpl* m_impl;
\r
213 #if defined (_MSC_VER)
\r
214 #pragma warning( pop )
\r
217 AttributeResolver* SHIBSP_DLLLOCAL SimpleAttributeResolverFactory(const DOMElement* const & e)
\r
219 return new SimpleResolver(e);
\r
222 static const XMLCh SIMPLE_NS[] = {
\r
223 chLatin_u, chLatin_r, chLatin_n, chColon, chLatin_m, chLatin_a, chLatin_c, chLatin_e, chColon,
\r
224 chLatin_s, chLatin_h, chLatin_i, chLatin_b, chLatin_b, chLatin_o, chLatin_l, chLatin_e, chLatin_t, chLatin_h, chColon,
\r
225 chDigit_2, chPeriod, chDigit_0, chColon,
\r
226 chLatin_r, chLatin_e, chLatin_s, chLatin_o, chLatin_l, chLatin_v, chLatin_e, chLatin_r, chColon,
\r
227 chLatin_s, chLatin_i, chLatin_m, chLatin_p, chLatin_l, chLatin_e, chNull
\r
229 static const XMLCh _AttributeDecoder[] = UNICODE_LITERAL_16(A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r);
\r
230 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
231 static const XMLCh allowQuery[] = UNICODE_LITERAL_10(a,l,l,o,w,Q,u,e,r,y);
\r
232 static const XMLCh decoderId[] = UNICODE_LITERAL_9(d,e,c,o,d,e,r,I,d);
\r
233 static const XMLCh _id[] = UNICODE_LITERAL_2(i,d);
\r
234 static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e);
\r
237 SimpleResolverImpl::SimpleResolverImpl(const DOMElement* e) : m_document(NULL), m_allowQuery(true)
\r
240 xmltooling::NDC ndc("SimpleResolverImpl");
\r
242 Category& log=Category::getInstance(SHIBSP_LOGCAT".AttributeResolver");
\r
244 if (!XMLHelper::isNodeNamed(e, SIMPLE_NS, _AttributeResolver))
\r
245 throw ConfigurationException("Simple resolver requires resolver:AttributeResolver at root of configuration.");
\r
247 const XMLCh* flag = e->getAttributeNS(NULL,allowQuery);
\r
248 if (flag && (*flag==chLatin_f || *flag==chDigit_0)) {
\r
249 log.info("SAML attribute queries disabled");
\r
250 m_allowQuery = false;
\r
253 DOMElement* child = XMLHelper::getFirstChildElement(e, SIMPLE_NS, _AttributeDecoder);
\r
255 auto_ptr_char id(child->getAttributeNS(NULL, _id));
\r
256 auto_ptr_char type(child->getAttributeNS(NULL, _type));
\r
258 log.info("building AttributeDecoder (%s) of type %s", id.get(), type.get());
\r
259 m_decoderMap[id.get()] = SPConfig::getConfig().AttributeDecoderManager.newPlugin(type.get(), child);
\r
261 catch (exception& ex) {
\r
262 log.error("error building AttributeDecoder (%s): %s", id.get(), ex.what());
\r
264 child = XMLHelper::getNextSiblingElement(child, SIMPLE_NS, _AttributeDecoder);
\r
267 child = XMLHelper::getFirstChildElement(e, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME);
\r
269 // Check for missing Name.
\r
270 const XMLCh* name = child->getAttributeNS(NULL, saml2::Attribute::NAME_ATTRIB_NAME);
\r
271 if (!name || !*name) {
\r
272 log.warn("skipping saml:Attribute declared with no Name");
\r
273 child = XMLHelper::getNextSiblingElement(child, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME);
\r
277 const AttributeDecoder* decoder=NULL;
\r
278 auto_ptr_char id(child->getAttributeNS(NULL, saml2::Attribute::FRIENDLYNAME_ATTRIB_NAME));
\r
279 auto_ptr_char d(child->getAttributeNS(SIMPLE_NS, decoderId));
\r
280 if (!id.get() || !*id.get() || !d.get() || !*d.get() || !(decoder=m_decoderMap[d.get()])) {
\r
281 log.warn("skipping saml:Attribute declared with no FriendlyName or resolvable AttributeDecoder");
\r
282 child = XMLHelper::getNextSiblingElement(child, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME);
\r
286 // Empty NameFormat implies the usual Shib URI naming defaults.
\r
287 const XMLCh* format = child->getAttributeNS(NULL, saml2::Attribute::NAMEFORMAT_ATTRIB_NAME);
\r
288 if (!format || XMLString::equals(format, shibspconstants::SHIB1_ATTRIBUTE_NAMESPACE_URI) ||
\r
289 XMLString::equals(format, saml2::Attribute::URI_REFERENCE))
\r
290 format = &chNull; // ignore default Format/Namespace values
\r
292 // Fetch/create the map entry and see if it's a duplicate rule.
\r
293 #ifdef HAVE_GOOD_STL
\r
294 pair<const AttributeDecoder*,string>& decl = m_attrMap[make_pair(name,format)];
\r
296 auto_ptr_char n(name);
\r
297 auto_ptr_char f(format);
\r
298 pair<const AttributeDecoder*,string>& decl = m_attrMap[make_pair(n.get(),f.get())];
\r
301 log.warn("skipping duplicate saml:Attribute declaration (same Name and NameFormat)");
\r
302 child = XMLHelper::getNextSiblingElement(child, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME);
\r
306 if (log.isInfoEnabled()) {
\r
307 #ifdef HAVE_GOOD_STL
\r
308 auto_ptr_char n(name);
\r
309 auto_ptr_char f(format);
\r
311 log.info("creating declaration for Attribute %s%s%s", n.get(), *f.get() ? ", Format/Namespace:" : "", f.get());
\r
314 decl.first = decoder;
\r
315 decl.second = id.get();
\r
317 child = XMLHelper::getNextSiblingElement(child, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME);
\r
321 void SimpleResolverImpl::resolve(
\r
322 ResolutionContext& ctx, const saml1::Assertion* token, const set<string>* attributes
\r
325 vector<shibsp::Attribute*>& resolved = ctx.getResolvedAttributes();
\r
327 auto_ptr_char assertingParty(ctx.getEntityDescriptor() ? ctx.getEntityDescriptor()->getEntityID() : NULL);
\r
328 const char* relyingParty = ctx.getApplication().getString("providerId").second;
\r
330 #ifdef HAVE_GOOD_STL
\r
331 map< pair<xstring,xstring>,pair<const AttributeDecoder*,string> >::const_iterator rule;
\r
333 map< pair<string,string>,pair<const AttributeDecoder*,string> >::const_iterator rule;
\r
337 const XMLCh* format;
\r
339 // Check the NameID based on the format.
\r
340 if (ctx.getNameID()) {
\r
341 format = ctx.getNameID()->getFormat();
\r
342 if (!format || !*format)
\r
343 format = NameID::UNSPECIFIED;
\r
344 #ifdef HAVE_GOOD_STL
\r
345 if ((rule=m_attrMap.find(make_pair(format,xstring()))) != m_attrMap.end()) {
\r
347 auto_ptr_char temp(format);
\r
348 if ((rule=m_attrMap.find(make_pair(temp.get(),string()))) != m_attrMap.end()) {
\r
350 if (!attributes || attributes->count(rule->second.second)) {
\r
351 resolved.push_back(
\r
352 rule->second.first->decode(
\r
353 rule->second.second.c_str(), ctx.getNameID(), assertingParty.get(), relyingParty
\r
360 const vector<saml1::AttributeStatement*>& statements = token->getAttributeStatements();
\r
361 for (vector<saml1::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s) {
\r
362 const vector<saml1::Attribute*>& attrs = const_cast<const saml1::AttributeStatement*>(*s)->getAttributes();
\r
363 for (vector<saml1::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a) {
\r
364 name = (*a)->getAttributeName();
\r
365 format = (*a)->getAttributeNamespace();
\r
366 if (!name || !*name)
\r
368 if (!format || XMLString::equals(format, shibspconstants::SHIB1_ATTRIBUTE_NAMESPACE_URI))
\r
370 #ifdef HAVE_GOOD_STL
\r
371 if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {
\r
373 auto_ptr_char temp1(name);
\r
374 auto_ptr_char temp2(format);
\r
375 if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {
\r
377 if (!attributes || attributes->count(rule->second.second)) {
\r
378 resolved.push_back(
\r
379 rule->second.first->decode(rule->second.second.c_str(), *a, assertingParty.get(), relyingParty)
\r
387 void SimpleResolverImpl::resolve(
\r
388 ResolutionContext& ctx, const saml2::Assertion* token, const set<string>* attributes
\r
391 vector<shibsp::Attribute*>& resolved = ctx.getResolvedAttributes();
\r
393 auto_ptr_char assertingParty(ctx.getEntityDescriptor() ? ctx.getEntityDescriptor()->getEntityID() : NULL);
\r
394 const char* relyingParty = ctx.getApplication().getString("providerId").second;
\r
396 #ifdef HAVE_GOOD_STL
\r
397 map< pair<xstring,xstring>,pair<const AttributeDecoder*,string> >::const_iterator rule;
\r
399 map< pair<string,string>,pair<const AttributeDecoder*,string> >::const_iterator rule;
\r
403 const XMLCh* format;
\r
405 // Check the NameID based on the format.
\r
406 if (ctx.getNameID()) {
\r
407 format = ctx.getNameID()->getFormat();
\r
408 if (!format || !*format)
\r
409 format = NameID::UNSPECIFIED;
\r
410 #ifdef HAVE_GOOD_STL
\r
411 if ((rule=m_attrMap.find(make_pair(format,xstring()))) != m_attrMap.end()) {
\r
413 auto_ptr_char temp(format);
\r
414 if ((rule=m_attrMap.find(make_pair(temp.get(),string()))) != m_attrMap.end()) {
\r
416 if (!attributes || attributes->count(rule->second.second)) {
\r
417 resolved.push_back(
\r
418 rule->second.first->decode(
\r
419 rule->second.second.c_str(), ctx.getNameID(), assertingParty.get(), relyingParty
\r
426 const vector<saml2::AttributeStatement*>& statements = token->getAttributeStatements();
\r
427 for (vector<saml2::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s) {
\r
428 const vector<saml2::Attribute*>& attrs = const_cast<const saml2::AttributeStatement*>(*s)->getAttributes();
\r
429 for (vector<saml2::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a) {
\r
430 name = (*a)->getName();
\r
431 format = (*a)->getNameFormat();
\r
432 if (!name || !*name)
\r
434 if (!format || !*format)
\r
435 format = saml2::Attribute::UNSPECIFIED;
\r
436 else if (XMLString::equals(format, saml2::Attribute::URI_REFERENCE))
\r
438 #ifdef HAVE_GOOD_STL
\r
439 if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {
\r
441 auto_ptr_char temp1(name);
\r
442 auto_ptr_char temp2(format);
\r
443 if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {
\r
445 if (!attributes || attributes->count(rule->second.second)) {
\r
446 resolved.push_back(
\r
447 rule->second.first->decode(rule->second.second.c_str(), *a, assertingParty.get(), relyingParty)
\r
453 const vector<saml2::EncryptedAttribute*>& encattrs = const_cast<const saml2::AttributeStatement*>(*s)->getEncryptedAttributes();
\r
454 if (!encattrs.empty()) {
\r
455 const XMLCh* recipient = ctx.getApplication().getXMLString("providerId").second;
\r
456 CredentialResolver* cr = ctx.getApplication().getCredentialResolver();
\r
458 Category::getInstance(SHIBSP_LOGCAT".AttributeResolver").warn(
\r
459 "found encrypted attributes, but no CredentialResolver was available"
\r
464 // We look up credentials based on the peer who did the encrypting.
\r
465 CredentialCriteria cc;
\r
466 cc.setPeerName(assertingParty.get());
\r
468 Locker credlocker(cr);
\r
469 for (vector<saml2::EncryptedAttribute*>::const_iterator ea = encattrs.begin(); ea!=encattrs.end(); ++ea) {
\r
470 auto_ptr<XMLObject> decrypted((*ea)->decrypt(*cr, recipient, &cc));
\r
471 const saml2::Attribute* decattr = dynamic_cast<const saml2::Attribute*>(decrypted.get());
\r
472 name = decattr->getName();
\r
473 format = decattr->getNameFormat();
\r
474 if (!name || !*name)
\r
476 if (!format || !*format)
\r
477 format = saml2::Attribute::UNSPECIFIED;
\r
478 else if (XMLString::equals(format, saml2::Attribute::URI_REFERENCE))
\r
480 #ifdef HAVE_GOOD_STL
\r
481 if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {
\r
483 auto_ptr_char temp1(name);
\r
484 auto_ptr_char temp2(format);
\r
485 if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {
\r
487 if (!attributes || attributes->count(rule->second.second)) {
\r
488 resolved.push_back(
\r
489 rule->second.first->decode(rule->second.second.c_str(), decattr, assertingParty.get(), relyingParty)
\r
498 void SimpleResolverImpl::query(ResolutionContext& ctx, const NameIdentifier& nameid, const set<string>* attributes) const
\r
501 xmltooling::NDC ndc("query");
\r
503 Category& log=Category::getInstance(SHIBSP_LOGCAT".AttributeResolver");
\r
505 const EntityDescriptor* entity = ctx.getEntityDescriptor();
\r
507 log.debug("no issuer information available, skipping query");
\r
512 const AttributeAuthorityDescriptor* AA = entity->getAttributeAuthorityDescriptor(samlconstants::SAML11_PROTOCOL_ENUM);
\r
514 AA = entity->getAttributeAuthorityDescriptor(samlconstants::SAML10_PROTOCOL_ENUM);
\r
518 log.info("no SAML 1.x AttributeAuthority role found in metadata");
\r
522 SecurityPolicy policy;
\r
523 MetadataCredentialCriteria mcc(*AA);
\r
524 shibsp::SOAPClient soaper(ctx.getApplication(),policy);
\r
525 const PropertySet* policySettings = ctx.getApplication().getServiceProvider().getPolicySettings(ctx.getApplication().getString("policyId").second);
\r
526 pair<bool,bool> signedAssertions = policySettings->getBool("signedAssertions");
\r
528 auto_ptr_XMLCh binding(samlconstants::SAML1_BINDING_SOAP);
\r
529 saml1p::Response* response=NULL;
\r
530 const vector<AttributeService*>& endpoints=AA->getAttributeServices();
\r
531 for (vector<AttributeService*>::const_iterator ep=endpoints.begin(); !response && ep!=endpoints.end(); ++ep) {
\r
533 if (!XMLString::equals((*ep)->getBinding(),binding.get()))
\r
535 auto_ptr_char loc((*ep)->getLocation());
\r
536 auto_ptr_XMLCh issuer(ctx.getApplication().getString("providerId").second);
\r
537 saml1::Subject* subject = saml1::SubjectBuilder::buildSubject();
\r
538 subject->setNameIdentifier(nameid.cloneNameIdentifier());
\r
539 saml1p::AttributeQuery* query = saml1p::AttributeQueryBuilder::buildAttributeQuery();
\r
540 query->setSubject(subject);
\r
541 Request* request = RequestBuilder::buildRequest();
\r
542 request->setAttributeQuery(query);
\r
543 query->setResource(issuer.get());
\r
544 request->setMinorVersion(version);
\r
546 for (set<string>::const_iterator a = attributes->begin(); a!=attributes->end(); ++a)
\r
547 populateQuery(*query, *a);
\r
550 SAML1SOAPClient client(soaper);
\r
551 client.sendSAML(request, mcc, loc.get());
\r
552 response = client.receiveSAML();
\r
554 catch (exception& ex) {
\r
555 log.error("exception making SAML query: %s", ex.what());
\r
561 log.error("unable to successfully query for attributes");
\r
565 const vector<saml1::Assertion*>& assertions = const_cast<const saml1p::Response*>(response)->getAssertions();
\r
566 if (assertions.size()>1)
\r
567 log.warn("simple resolver only supports one assertion in the query response");
\r
569 auto_ptr<saml1p::Response> wrapper(response);
\r
570 saml1::Assertion* newtoken = assertions.front();
\r
572 if (!newtoken->getSignature() && signedAssertions.first && signedAssertions.second) {
\r
573 log.error("assertion unsigned, rejecting it based on signedAssertions policy");
\r
578 policy.evaluate(*newtoken);
\r
579 if (!policy.isSecure())
\r
580 throw SecurityPolicyException("Security of SAML 1.x query result not established.");
\r
581 saml1::AssertionValidator tokval(ctx.getApplication().getAudiences(), time(NULL));
\r
582 tokval.validateAssertion(*newtoken);
\r
584 catch (exception& ex) {
\r
585 log.error("assertion failed policy/validation: %s", ex.what());
\r
587 newtoken->detach();
\r
589 ctx.getResolvedAssertions().push_back(newtoken);
\r
590 resolve(ctx, newtoken, attributes);
\r
593 void SimpleResolverImpl::populateQuery(saml1p::AttributeQuery& query, const string& id) const
\r
595 for (attrmap_t::const_iterator i = m_attrMap.begin(); i!=m_attrMap.end(); ++i) {
\r
596 if (i->second.second == id) {
\r
597 AttributeDesignator* a = AttributeDesignatorBuilder::buildAttributeDesignator();
\r
598 #ifdef HAVE_GOOD_STL
\r
599 a->setAttributeName(i->first.second.c_str());
\r
600 a->setAttributeNamespace(i->first.first.empty() ? shibspconstants::SHIB1_ATTRIBUTE_NAMESPACE_URI : i->first.first.c_str());
\r
602 auto_ptr_XMLCh n(i->first.second);
\r
603 a->setAttributeName(n.get());
\r
604 if (i->first.first.empty())
\r
605 a->setAttributeNamespace(shibspconstants::SHIB1_ATTRIBUTE_NAMESPACE_URI);
\r
607 auto_ptr_XMLCh ns(i->first.first);
\r
608 a->setAttributeNamespace(ns.get());
\r
611 query.getAttributeDesignators().push_back(a);
\r
616 void SimpleResolverImpl::query(ResolutionContext& ctx, const NameID& nameid, const set<string>* attributes) const
\r
619 xmltooling::NDC ndc("query");
\r
621 Category& log=Category::getInstance(SHIBSP_LOGCAT".AttributeResolver");
\r
623 const EntityDescriptor* entity = ctx.getEntityDescriptor();
\r
625 log.debug("no issuer information available, skipping query");
\r
628 const AttributeAuthorityDescriptor* AA = entity->getAttributeAuthorityDescriptor(samlconstants::SAML20P_NS);
\r
630 log.info("no SAML 2 AttributeAuthority role found in metadata");
\r
634 SecurityPolicy policy;
\r
635 MetadataCredentialCriteria mcc(*AA);
\r
636 shibsp::SOAPClient soaper(ctx.getApplication(),policy);
\r
637 const PropertySet* policySettings = ctx.getApplication().getServiceProvider().getPolicySettings(ctx.getApplication().getString("policyId").second);
\r
638 pair<bool,bool> signedAssertions = policySettings->getBool("signedAssertions");
\r
640 auto_ptr_XMLCh binding(samlconstants::SAML20_BINDING_SOAP);
\r
641 saml2p::StatusResponseType* srt=NULL;
\r
642 const vector<AttributeService*>& endpoints=AA->getAttributeServices();
\r
643 for (vector<AttributeService*>::const_iterator ep=endpoints.begin(); !srt && ep!=endpoints.end(); ++ep) {
\r
645 if (!XMLString::equals((*ep)->getBinding(),binding.get()))
\r
647 auto_ptr_char loc((*ep)->getLocation());
\r
648 auto_ptr_XMLCh issuer(ctx.getApplication().getString("providerId").second);
\r
649 saml2::Subject* subject = saml2::SubjectBuilder::buildSubject();
\r
650 subject->setNameID(nameid.cloneNameID());
\r
651 saml2p::AttributeQuery* query = saml2p::AttributeQueryBuilder::buildAttributeQuery();
\r
652 query->setSubject(subject);
\r
653 Issuer* iss = IssuerBuilder::buildIssuer();
\r
654 query->setIssuer(iss);
\r
655 iss->setName(issuer.get());
\r
657 for (set<string>::const_iterator a = attributes->begin(); a!=attributes->end(); ++a)
\r
658 populateQuery(*query, *a);
\r
661 SAML2SOAPClient client(soaper);
\r
662 client.sendSAML(query, mcc, loc.get());
\r
663 srt = client.receiveSAML();
\r
665 catch (exception& ex) {
\r
666 log.error("exception making SAML query: %s", ex.what());
\r
672 log.error("unable to successfully query for attributes");
\r
675 saml2p::Response* response = dynamic_cast<saml2p::Response*>(srt);
\r
678 log.error("message was not a samlp:Response");
\r
682 const vector<saml2::Assertion*>& assertions = const_cast<const saml2p::Response*>(response)->getAssertions();
\r
683 if (assertions.size()>1)
\r
684 log.warn("simple resolver only supports one assertion in the query response");
\r
686 auto_ptr<saml2p::Response> wrapper(response);
\r
687 saml2::Assertion* newtoken = assertions.front();
\r
689 if (!newtoken->getSignature() && signedAssertions.first && signedAssertions.second) {
\r
690 log.error("assertion unsigned, rejecting it based on signedAssertions policy");
\r
695 policy.evaluate(*newtoken);
\r
696 if (!policy.isSecure())
\r
697 throw SecurityPolicyException("Security of SAML 2.0 query result not established.");
\r
698 saml2::AssertionValidator tokval(ctx.getApplication().getAudiences(), time(NULL));
\r
699 tokval.validateAssertion(*newtoken);
\r
701 catch (exception& ex) {
\r
702 log.error("assertion failed policy/validation: %s", ex.what());
\r
704 newtoken->detach();
\r
706 ctx.getResolvedAssertions().push_back(newtoken);
\r
707 resolve(ctx, newtoken, attributes);
\r
710 void SimpleResolverImpl::populateQuery(saml2p::AttributeQuery& query, const string& id) const
\r
712 for (attrmap_t::const_iterator i = m_attrMap.begin(); i!=m_attrMap.end(); ++i) {
\r
713 if (i->second.second == id) {
\r
714 saml2::Attribute* a = saml2::AttributeBuilder::buildAttribute();
\r
715 #ifdef HAVE_GOOD_STL
\r
716 a->setName(i->first.second.c_str());
\r
717 a->setNameFormat(i->first.first.empty() ? saml2::Attribute::URI_REFERENCE : i->first.first.c_str());
\r
719 auto_ptr_XMLCh n(i->first.second);
\r
720 a->setName(n.get());
\r
721 if (i->first.first.empty())
\r
722 a->setNameFormat(saml2::Attribute::URI_REFERENCE);
\r
724 auto_ptr_XMLCh ns(i->first.first);
\r
725 a->setNameFormat(ns.get());
\r
728 query.getAttributes().push_back(a);
\r
733 void SimpleResolver::resolveAttributes(ResolutionContext& ctx, const set<string>* attributes) const
\r
736 xmltooling::NDC ndc("resolveAttributes");
\r
738 Category& log=Category::getInstance(SHIBSP_LOGCAT".AttributeResolver");
\r
740 log.debug("examining tokens to resolve");
\r
742 bool query = m_impl->m_allowQuery;
\r
743 const saml1::Assertion* token1;
\r
744 const saml2::Assertion* token2;
\r
745 if (ctx.getTokens()) {
\r
746 for (vector<const opensaml::Assertion*>::const_iterator t = ctx.getTokens()->begin(); t!=ctx.getTokens()->end(); ++t) {
\r
747 token2 = dynamic_cast<const saml2::Assertion*>(*t);
\r
748 if (token2 && !token2->getAttributeStatements().empty()) {
\r
749 log.debug("resolving SAML 2 token with an AttributeStatement");
\r
750 m_impl->resolve(ctx, token2, attributes);
\r
754 token1 = dynamic_cast<const saml1::Assertion*>(*t);
\r
755 if (token1 && !token1->getAttributeStatements().empty()) {
\r
756 log.debug("resolving SAML 1 token with an AttributeStatement");
\r
757 m_impl->resolve(ctx, token1, attributes);
\r
765 if (token1 && !token1->getAuthenticationStatements().empty()) {
\r
766 const AuthenticationStatement* statement = token1->getAuthenticationStatements().front();
\r
767 if (statement && statement->getSubject() && statement->getSubject()->getNameIdentifier()) {
\r
768 log.debug("attempting SAML 1.x attribute query");
\r
769 return m_impl->query(ctx, *(statement->getSubject()->getNameIdentifier()), attributes);
\r
772 else if (token2 && ctx.getNameID()) {
\r
773 log.debug("attempting SAML 2.0 attribute query");
\r
774 return m_impl->query(ctx, *ctx.getNameID(), attributes);
\r
776 log.warn("can't attempt attribute query, no identifier in assertion subject");
\r
780 pair<bool,DOMElement*> SimpleResolver::load()
\r
782 // Load from source using base class.
\r
783 pair<bool,DOMElement*> raw = ReloadableXMLFile::load();
\r
785 // If we own it, wrap it.
\r
786 XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : NULL);
\r
788 SimpleResolverImpl* impl = new SimpleResolverImpl(raw.second);
\r
790 // If we held the document, transfer it to the impl. If we didn't, it's a no-op.
\r
791 impl->setDocument(docjanitor.release());
\r
796 return make_pair(false,(DOMElement*)NULL);
\r