2 * Licensed to the University Corporation for Advanced Internet
3 * Development, Inc. (UCAID) under one or more contributor license
4 * agreements. See the NOTICE file distributed with this work for
5 * additional information regarding copyright ownership.
7 * UCAID licenses this file to you under the Apache License,
8 * Version 2.0 (the "License"); you may not use this file except
9 * in compliance with the License. You may obtain a copy of the
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17 * either express or implied. See the License for the specific
18 * language governing permissions and limitations under the License.
22 * TemplateAttributeResolver.cpp
24 * AttributeResolver plugin for composing input values.
29 #include <boost/bind.hpp>
30 #include <boost/algorithm/string.hpp>
31 #include <shibsp/exceptions.h>
32 #include <shibsp/SessionCache.h>
33 #include <shibsp/attribute/SimpleAttribute.h>
34 #include <shibsp/attribute/resolver/AttributeResolver.h>
35 #include <shibsp/attribute/resolver/ResolutionContext.h>
36 #include <xmltooling/XMLToolingConfig.h>
37 #include <xmltooling/util/Predicates.h>
38 #include <xmltooling/util/XMLHelper.h>
39 #include <xercesc/util/XMLUniDefs.hpp>
41 using namespace shibsp;
42 using namespace xmltooling;
43 using namespace xercesc;
44 using namespace boost;
49 class SHIBSP_DLLLOCAL TemplateContext : public ResolutionContext
52 TemplateContext(const vector<Attribute*>* attributes) : m_inputAttributes(attributes) {
56 for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());
59 const vector<Attribute*>* getInputAttributes() const {
60 return m_inputAttributes;
62 vector<Attribute*>& getResolvedAttributes() {
65 vector<opensaml::Assertion*>& getResolvedAssertions() {
70 const vector<Attribute*>* m_inputAttributes;
71 vector<Attribute*> m_attributes;
72 static vector<opensaml::Assertion*> m_assertions; // empty dummy
76 class SHIBSP_DLLLOCAL TemplateAttributeResolver : public AttributeResolver
79 TemplateAttributeResolver(const DOMElement* e);
80 virtual ~TemplateAttributeResolver() {}
88 ResolutionContext* createResolutionContext(
89 const Application& application,
90 const opensaml::saml2md::EntityDescriptor* issuer,
91 const XMLCh* protocol,
92 const opensaml::saml2::NameID* nameid=nullptr,
93 const XMLCh* authncontext_class=nullptr,
94 const XMLCh* authncontext_decl=nullptr,
95 const vector<const opensaml::Assertion*>* tokens=nullptr,
96 const vector<Attribute*>* attributes=nullptr
98 // Make sure new method gets run.
99 return createResolutionContext(application, nullptr, issuer, protocol, nameid, authncontext_class, authncontext_decl, tokens, attributes);
102 ResolutionContext* createResolutionContext(
103 const Application& application,
104 const GenericRequest* request,
105 const opensaml::saml2md::EntityDescriptor* issuer,
106 const XMLCh* protocol,
107 const opensaml::saml2::NameID* nameid=nullptr,
108 const XMLCh* authncontext_class=nullptr,
109 const XMLCh* authncontext_decl=nullptr,
110 const vector<const opensaml::Assertion*>* tokens=nullptr,
111 const vector<Attribute*>* attributes=nullptr
113 return new TemplateContext(attributes);
116 ResolutionContext* createResolutionContext(const Application& application, const Session& session) const {
117 return new TemplateContext(&session.getAttributes());
120 void resolveAttributes(ResolutionContext& ctx) const;
122 void getAttributeIds(vector<string>& attributes) const {
123 attributes.push_back(m_dest.front());
129 vector<string> m_sources,m_dest;
132 static const XMLCh dest[] = UNICODE_LITERAL_4(d,e,s,t);
133 static const XMLCh _sources[] = UNICODE_LITERAL_7(s,o,u,r,c,e,s);
134 static const XMLCh Template[] = UNICODE_LITERAL_8(T,e,m,p,l,a,t,e);
136 AttributeResolver* SHIBSP_DLLLOCAL TemplateAttributeResolverFactory(const DOMElement* const & e)
138 return new TemplateAttributeResolver(e);
143 vector<opensaml::Assertion*> TemplateContext::m_assertions;
145 TemplateAttributeResolver::TemplateAttributeResolver(const DOMElement* e)
146 : m_log(Category::getInstance(SHIBSP_LOGCAT".AttributeResolver.Template")),
147 m_dest(1, XMLHelper::getAttrString(e, nullptr, dest))
149 if (m_dest.front().empty())
150 throw ConfigurationException("Template AttributeResolver requires dest attribute.");
152 string s(XMLHelper::getAttrString(e, nullptr, _sources));
153 split(m_sources, s, is_space(), algorithm::token_compress_on);
154 if (m_sources.empty())
155 throw ConfigurationException("Template AttributeResolver requires sources attribute.");
157 e = e ? XMLHelper::getFirstChildElement(e, Template) : nullptr;
158 auto_ptr_char t(e ? e->getTextContent() : nullptr);
160 m_template = t.get();
163 if (m_template.empty())
164 throw ConfigurationException("Template AttributeResolver requires <Template> child element.");
168 void TemplateAttributeResolver::resolveAttributes(ResolutionContext& ctx) const
170 TemplateContext& tctx = dynamic_cast<TemplateContext&>(ctx);
171 if (!tctx.getInputAttributes())
174 map<string,const Attribute*> attrmap;
175 for (vector<string>::const_iterator a = m_sources.begin(); a != m_sources.end(); ++a) {
176 static bool (*eq)(const string&, const char*) = operator==;
177 const Attribute* attr = find_if(*tctx.getInputAttributes(), boost::bind(eq, boost::cref(*a), boost::bind(&Attribute::getId, _1)));
179 m_log.warn("source attribute (%s) missing, cannot resolve attribute (%s)", a->c_str(), m_dest.front().c_str());
182 else if (!attrmap.empty() && attr->valueCount() != attrmap.begin()->second->valueCount()) {
183 m_log.warn("all source attributes must contain equal number of values, cannot resolve attribute (%s)", m_dest.front().c_str());
189 auto_ptr<SimpleAttribute> dest(new SimpleAttribute(m_dest));
191 for (size_t ix = 0; ix < attrmap.begin()->second->valueCount(); ++ix) {
192 static const char* legal="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890_-.[]";
194 dest->getValues().push_back(string());
195 string& processed = dest->getValues().back();
197 string::size_type i=0, start=0;
198 while (start != string::npos && start < m_template.length() && (i = m_template.find("$", start)) != string::npos) {
200 processed += m_template.substr(start, i - start); // append everything in between
201 start = i + 1; // move start to the beginning of the token name
202 i = m_template.find_first_not_of(legal, start); // find token delimiter
203 if (i == start) { // append a non legal character
204 processed += m_template[start++];
208 map<string,const Attribute*>::const_iterator iter = attrmap.find(m_template.substr(start, (i==string::npos) ? i : i - start));
209 if (iter != attrmap.end())
210 processed += iter->second->getSerializedValues()[ix];
213 if (start != string::npos && start < m_template.length())
214 processed += m_template.substr(start, i); // append rest of string
216 if (processed.empty())
217 dest->getValues().pop_back();
220 // Save off new object.
221 if (dest.get() && dest->valueCount()) {
222 ctx.getResolvedAttributes().push_back(dest.get());