SSPCPP-616 - clean up concatenated string literals
[shibboleth/cpp-sp.git] / plugins / CaseFoldingAttributeResolver.cpp
1 /**
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.
6  *
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
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
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.
19  */
20
21 /**
22  * CaseFoldingAttributeResolver.cpp
23  * 
24  * Attribute Resolver plugins for upcasing and downcasing.
25  */
26
27 #include "internal.h"
28
29 #include <algorithm>
30 #include <shibsp/exceptions.h>
31 #include <shibsp/SessionCache.h>
32 #include <shibsp/attribute/SimpleAttribute.h>
33 #include <shibsp/attribute/resolver/AttributeResolver.h>
34 #include <shibsp/attribute/resolver/ResolutionContext.h>
35 #include <xmltooling/XMLToolingConfig.h>
36 #include <xmltooling/util/XMLHelper.h>
37 #include <xercesc/util/XMLUniDefs.hpp>
38
39 using namespace shibsp;
40 using namespace xmltooling;
41 using namespace xercesc;
42 using namespace std;
43
44 namespace shibsp {
45
46     class SHIBSP_DLLLOCAL FoldingContext : public ResolutionContext
47     {
48     public:
49         FoldingContext(const vector<Attribute*>* attributes) : m_inputAttributes(attributes) {
50         }
51
52         ~FoldingContext() {
53             for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());
54         }
55
56         const vector<Attribute*>* getInputAttributes() const {
57             return m_inputAttributes;
58         }
59         vector<Attribute*>& getResolvedAttributes() {
60             return m_attributes;
61         }
62         vector<opensaml::Assertion*>& getResolvedAssertions() {
63             return m_assertions;
64         }
65
66     private:
67         const vector<Attribute*>* m_inputAttributes;
68         vector<Attribute*> m_attributes;
69         static vector<opensaml::Assertion*> m_assertions;   // empty dummy
70     };
71
72
73     class SHIBSP_DLLLOCAL CaseFoldingAttributeResolver : public AttributeResolver
74     {
75     public:
76         enum case_t {
77             _up,
78             _down
79         };
80
81         CaseFoldingAttributeResolver(const DOMElement* e, case_t direction);
82         virtual ~CaseFoldingAttributeResolver() {}
83
84         Lockable* lock() {
85             return this;
86         }
87         void unlock() {
88         }
89
90         ResolutionContext* createResolutionContext(
91             const Application& application,
92             const opensaml::saml2md::EntityDescriptor* issuer,
93             const XMLCh* protocol,
94             const opensaml::saml2::NameID* nameid=nullptr,
95             const XMLCh* authncontext_class=nullptr,
96             const XMLCh* authncontext_decl=nullptr,
97             const vector<const opensaml::Assertion*>* tokens=nullptr,
98             const vector<Attribute*>* attributes=nullptr
99             ) const {
100             // Make sure new method gets run.
101             return createResolutionContext(application, nullptr, issuer, protocol, nameid, authncontext_class, authncontext_decl, tokens, attributes);
102         }
103
104         ResolutionContext* createResolutionContext(
105             const Application& application,
106             const GenericRequest* request,
107             const opensaml::saml2md::EntityDescriptor* issuer,
108             const XMLCh* protocol,
109             const opensaml::saml2::NameID* nameid=nullptr,
110             const XMLCh* authncontext_class=nullptr,
111             const XMLCh* authncontext_decl=nullptr,
112             const vector<const opensaml::Assertion*>* tokens=nullptr,
113             const vector<Attribute*>* attributes=nullptr
114             ) const {
115             return new FoldingContext(attributes);
116         }
117
118         ResolutionContext* createResolutionContext(const Application& application, const Session& session) const {
119             return new FoldingContext(&session.getAttributes());
120         }
121
122         void resolveAttributes(ResolutionContext& ctx) const;
123
124         void getAttributeIds(vector<string>& attributes) const {
125             if (!m_dest.empty() && !m_dest.front().empty())
126                 attributes.push_back(m_dest.front());
127         }
128
129     private:
130         Category& m_log;
131         case_t m_direction;
132         string m_source;
133         vector<string> m_dest;
134     };
135
136     static const XMLCh dest[] =             UNICODE_LITERAL_4(d,e,s,t);
137     static const XMLCh source[] =           UNICODE_LITERAL_6(s,o,u,r,c,e);
138
139     AttributeResolver* SHIBSP_DLLLOCAL UpperCaseAttributeResolverFactory(const DOMElement* const & e)
140     {
141         return new CaseFoldingAttributeResolver(e, CaseFoldingAttributeResolver::_up);
142     }
143
144     AttributeResolver* SHIBSP_DLLLOCAL LowerCaseAttributeResolverFactory(const DOMElement* const & e)
145     {
146         return new CaseFoldingAttributeResolver(e, CaseFoldingAttributeResolver::_down);
147     }
148 };
149
150 vector<opensaml::Assertion*> FoldingContext::m_assertions;
151
152 CaseFoldingAttributeResolver::CaseFoldingAttributeResolver(const DOMElement* e, case_t direction)
153     : m_log(Category::getInstance(SHIBSP_LOGCAT ".AttributeResolver.CaseFolding")),
154         m_direction(direction),
155         m_source(XMLHelper::getAttrString(e, nullptr, source)),
156         m_dest(1, XMLHelper::getAttrString(e, nullptr, dest))
157 {
158     if (m_source.empty())
159         throw ConfigurationException("CaseFolding AttributeResolver requires source attribute.");
160 }
161
162
163 void CaseFoldingAttributeResolver::resolveAttributes(ResolutionContext& ctx) const
164 {
165     FoldingContext& fctx = dynamic_cast<FoldingContext&>(ctx);
166     if (!fctx.getInputAttributes())
167         return;
168
169     auto_ptr<SimpleAttribute> destwrapper;
170
171     for (vector<Attribute*>::const_iterator a = fctx.getInputAttributes()->begin(); a != fctx.getInputAttributes()->end(); ++a) {
172         if (m_source != (*a)->getId() || (*a)->valueCount() == 0) {
173             continue;
174         }
175
176         SimpleAttribute* dest = nullptr;
177         if (m_dest.empty() || m_dest.front().empty()) {
178             // Can we transform in-place?
179             dest = dynamic_cast<SimpleAttribute*>(*a);
180             if (!dest) {
181                 m_log.warn("can't %scase non-simple attribute (%s) 'in place'", (m_direction==_up ? "up" : "down"), m_source.c_str());
182                 continue;
183             }
184             m_log.debug("applying in-place transform to source attribute (%s)", m_source.c_str());
185         }
186         else if (!destwrapper.get()) {
187             // Create a destination attribute.
188             destwrapper.reset(new SimpleAttribute(m_dest));
189             m_log.debug("applying transform from source attribute (%s) to dest attribute (%s)", m_source.c_str(), m_dest.front().c_str());
190         }
191
192         for (size_t i = 0; i < (*a)->valueCount(); ++i) {
193             try {
194                 XMLCh* srcval = fromUTF8((*a)->getSerializedValues()[i].c_str());
195                 if (srcval) {
196                     auto_arrayptr<XMLCh> valjanitor(srcval);
197                     (m_direction == _up) ? XMLString::upperCase(srcval) : XMLString::lowerCase(srcval);
198                     auto_arrayptr<char> narrow(toUTF8(srcval));
199                     if (dest) {
200                         // Modify in place.
201                         dest->getValues()[i] = narrow.get();
202                     }
203                     else {
204                         // Add to new object.
205                         destwrapper->getValues().push_back(narrow.get());
206                     }
207                 }
208             }
209             catch (XMLException& ex) {
210                 auto_ptr_char msg(ex.getMessage());
211                 m_log.error("caught error performing conversion: %s", msg.get());
212             }
213         }
214     }
215
216     // Save off new object.
217     if (destwrapper.get()) {
218         ctx.getResolvedAttributes().push_back(destwrapper.get());
219         destwrapper.release();
220     }
221 }