Auto-wrap chained Application plugins, relax order child elements, add some logging.
[shibboleth/sp.git] / shibsp / attribute / resolver / impl / ChainingAttributeResolver.cpp
1 /*
2  *  Copyright 2001-2010 Internet2
3  *
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /**
18  * ChainingAttributeResolver.cpp
19  *
20  * Chains together multiple AttributeResolver plugins.
21  */
22
23 #include "internal.h"
24 #include "Application.h"
25 #include "ServiceProvider.h"
26 #include "attribute/Attribute.h"
27 #include "attribute/resolver/AttributeResolver.h"
28 #include "attribute/resolver/ResolutionContext.h"
29
30 #include <saml/Assertion.h>
31 #include <xercesc/util/XMLUniDefs.hpp>
32 #include <xmltooling/util/XMLHelper.h>
33
34 using namespace shibsp;
35 using namespace opensaml::saml2;
36 using namespace opensaml::saml2md;
37 using namespace xmltooling;
38 using namespace std;
39
40 namespace shibsp {
41
42     struct SHIBSP_DLLLOCAL ChainingContext : public ResolutionContext
43     {
44         ChainingContext(
45             const Application& application,
46             const EntityDescriptor* issuer,
47             const XMLCh* protocol,
48             const NameID* nameid,
49             const XMLCh* authncontext_class,
50             const XMLCh* authncontext_decl,
51             const vector<const opensaml::Assertion*>* tokens,
52             const vector<shibsp::Attribute*>* attributes
53             ) : m_app(application), m_issuer(issuer), m_protocol(protocol), m_nameid(nameid), m_authclass(authncontext_class), m_authdecl(authncontext_decl), m_session(nullptr) {
54             if (tokens)
55                 m_tokens.assign(tokens->begin(), tokens->end());
56             if (attributes)
57                 m_attributes.assign(attributes->begin(), attributes->end());
58         }
59
60         ChainingContext(const Application& application, const Session& session) : m_app(application), m_session(&session) {
61         }
62
63         ~ChainingContext() {
64             for_each(m_ownedAttributes.begin(), m_ownedAttributes.end(), xmltooling::cleanup<shibsp::Attribute>());
65             for_each(m_ownedAssertions.begin(), m_ownedAssertions.end(), xmltooling::cleanup<opensaml::Assertion>());
66         }
67
68         vector<shibsp::Attribute*>& getResolvedAttributes() {
69             return m_ownedAttributes;
70         }
71         vector<opensaml::Assertion*>& getResolvedAssertions() {
72             return m_ownedAssertions;
73         }
74
75         vector<shibsp::Attribute*> m_ownedAttributes;
76         vector<opensaml::Assertion*> m_ownedAssertions;
77
78         const Application& m_app;
79         const EntityDescriptor* m_issuer;
80         const XMLCh* m_protocol;
81         const NameID* m_nameid;
82         const XMLCh* m_authclass;
83         const XMLCh* m_authdecl;
84         vector<const opensaml::Assertion*> m_tokens;
85         vector<shibsp::Attribute*> m_attributes;
86
87         const Session* m_session;
88     };
89
90     class SHIBSP_DLLLOCAL ChainingAttributeResolver : public AttributeResolver
91     {
92     public:
93         ChainingAttributeResolver(const DOMElement* e);
94         virtual ~ChainingAttributeResolver() {
95             for_each(m_resolvers.begin(), m_resolvers.end(), xmltooling::cleanup<AttributeResolver>());
96         }
97
98         Lockable* lock() {
99             return this;
100         }
101         void unlock() {
102         }
103
104         ResolutionContext* createResolutionContext(
105             const Application& application,
106             const EntityDescriptor* issuer,
107             const XMLCh* protocol,
108             const NameID* nameid=nullptr,
109             const XMLCh* authncontext_class=nullptr,
110             const XMLCh* authncontext_decl=nullptr,
111             const vector<const opensaml::Assertion*>* tokens=nullptr,
112             const vector<shibsp::Attribute*>* attributes=nullptr
113             ) const {
114             return new ChainingContext(application, issuer, protocol, nameid, authncontext_class, authncontext_decl, tokens, attributes);
115         }
116
117         ResolutionContext* createResolutionContext(const Application& application, const Session& session) const {
118             return new ChainingContext(application, session);
119         }
120
121         void resolveAttributes(ResolutionContext& ctx) const;
122
123         void getAttributeIds(vector<string>& attributes) const {
124             for (vector<AttributeResolver*>::const_iterator i=m_resolvers.begin(); i!=m_resolvers.end(); ++i) {
125                 Locker locker(*i);
126                 (*i)->getAttributeIds(attributes);
127             }
128         }
129
130     private:
131         vector<AttributeResolver*> m_resolvers;
132     };
133
134     static const XMLCh _AttributeResolver[] =   UNICODE_LITERAL_17(A,t,t,r,i,b,u,t,e,R,e,s,o,l,v,e,r);
135     static const XMLCh _type[] =                UNICODE_LITERAL_4(t,y,p,e);
136
137     SHIBSP_DLLLOCAL PluginManager<AttributeResolver,string,const DOMElement*>::Factory QueryResolverFactory;
138     SHIBSP_DLLLOCAL PluginManager<AttributeResolver,string,const DOMElement*>::Factory SimpleAggregationResolverFactory;
139
140     AttributeResolver* SHIBSP_DLLLOCAL ChainingResolverFactory(const DOMElement* const & e)
141     {
142         return new ChainingAttributeResolver(e);
143     }
144 };
145
146 void SHIBSP_API shibsp::registerAttributeResolvers()
147 {
148     SPConfig::getConfig().AttributeResolverManager.registerFactory(QUERY_ATTRIBUTE_RESOLVER, QueryResolverFactory);
149     SPConfig::getConfig().AttributeResolverManager.registerFactory(SIMPLEAGGREGATION_ATTRIBUTE_RESOLVER, SimpleAggregationResolverFactory);
150     SPConfig::getConfig().AttributeResolverManager.registerFactory(CHAINING_ATTRIBUTE_RESOLVER, ChainingResolverFactory);
151 }
152
153 ResolutionContext::ResolutionContext()
154 {
155 }
156
157 ResolutionContext::~ResolutionContext()
158 {
159 }
160
161 AttributeResolver::AttributeResolver()
162 {
163 }
164
165 AttributeResolver::~AttributeResolver()
166 {
167 }
168
169 ChainingAttributeResolver::ChainingAttributeResolver(const DOMElement* e)
170 {
171     SPConfig& conf = SPConfig::getConfig();
172
173     // Load up the chain of handlers.
174     e = XMLHelper::getFirstChildElement(e, _AttributeResolver);
175     while (e) {
176         string t(XMLHelper::getAttrString(e, nullptr, _type));
177         if (!t.empty()) {
178             try {
179                 Category::getInstance(SHIBSP_LOGCAT".AttributeResolver.Chaining").info(
180                     "building AttributeResolver of type (%s)...", t.c_str()
181                     );
182                 m_resolvers.push_back(conf.AttributeResolverManager.newPlugin(t.c_str(), e));
183             }
184             catch (exception& ex) {
185                 Category::getInstance(SHIBSP_LOGCAT".AttributeResolver.Chaining").error(
186                     "caught exception processing embedded AttributeResolver element: %s", ex.what()
187                     );
188             }
189         }
190         e = XMLHelper::getNextSiblingElement(e, _AttributeResolver);
191     }
192 }
193
194 void ChainingAttributeResolver::resolveAttributes(ResolutionContext& ctx) const
195 {
196     ChainingContext& chain = dynamic_cast<ChainingContext&>(ctx);
197     for (vector<AttributeResolver*>::const_iterator i=m_resolvers.begin(); i!=m_resolvers.end(); ++i) {
198         Locker locker(*i);
199         auto_ptr<ResolutionContext> context(
200             chain.m_session ?
201                 (*i)->createResolutionContext(chain.m_app, *chain.m_session) :
202                 (*i)->createResolutionContext(
203                     chain.m_app, chain.m_issuer, chain.m_protocol, chain.m_nameid, chain.m_authclass, chain.m_authdecl, &chain.m_tokens, &chain.m_attributes
204                     )
205             );
206
207         (*i)->resolveAttributes(*context.get());
208
209         chain.m_attributes.insert(chain.m_attributes.end(), context->getResolvedAttributes().begin(), context->getResolvedAttributes().end());
210         chain.m_ownedAttributes.insert(chain.m_ownedAttributes.end(), context->getResolvedAttributes().begin(), context->getResolvedAttributes().end());
211         context->getResolvedAttributes().clear();
212
213         chain.m_tokens.insert(chain.m_tokens.end(), context->getResolvedAssertions().begin(), context->getResolvedAssertions().end());
214         chain.m_ownedAssertions.insert(chain.m_ownedAssertions.end(), context->getResolvedAssertions().begin(), context->getResolvedAssertions().end());
215         context->getResolvedAssertions().clear();
216     }
217 }