https://issues.shibboleth.net/jira/browse/SSPCPP-469
[shibboleth/cpp-sp.git] / shibsp / attribute / Attribute.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  * shibsp/attribute/Attribute.cpp
23  *
24  * A resolved attribute.
25  */
26
27 #include "internal.h"
28 #include "exceptions.h"
29 #include "SPConfig.h"
30 #include "attribute/SimpleAttribute.h"
31 #ifndef SHIBSP_LITE
32 # include "attribute/AttributeDecoder.h"
33 #endif
34 #include "util/SPConstants.h"
35
36 #include <xmltooling/XMLObject.h>
37 #include <xmltooling/security/SecurityHelper.h>
38 #include <xmltooling/util/XMLHelper.h>
39 #include <xercesc/util/XMLUniDefs.hpp>
40
41 using namespace shibsp;
42 using namespace xmltooling;
43 using namespace std;
44
45 namespace shibsp {
46     SHIBSP_DLLLOCAL Attribute* SimpleAttributeFactory(DDF& in);
47     SHIBSP_DLLLOCAL Attribute* ScopedAttributeFactory(DDF& in);
48     SHIBSP_DLLLOCAL Attribute* NameIDAttributeFactory(DDF& in);
49     SHIBSP_DLLLOCAL Attribute* ExtensibleAttributeFactory(DDF& in);
50     SHIBSP_DLLLOCAL Attribute* XMLAttributeFactory(DDF& in);
51     SHIBSP_DLLLOCAL Attribute* BinaryAttributeFactory(DDF& in);
52
53 #ifndef SHIBSP_LITE
54     SHIBSP_DLLLOCAL PluginManager<AttributeDecoder,xmltooling::QName,const DOMElement*>::Factory StringAttributeDecoderFactory;
55     SHIBSP_DLLLOCAL PluginManager<AttributeDecoder,xmltooling::QName,const DOMElement*>::Factory ScopedAttributeDecoderFactory;
56     SHIBSP_DLLLOCAL PluginManager<AttributeDecoder,xmltooling::QName,const DOMElement*>::Factory NameIDAttributeDecoderFactory;
57     SHIBSP_DLLLOCAL PluginManager<AttributeDecoder,xmltooling::QName,const DOMElement*>::Factory NameIDFromScopedAttributeDecoderFactory;
58     SHIBSP_DLLLOCAL PluginManager<AttributeDecoder,xmltooling::QName,const DOMElement*>::Factory KeyInfoAttributeDecoderFactory;
59     SHIBSP_DLLLOCAL PluginManager<AttributeDecoder,xmltooling::QName,const DOMElement*>::Factory DOMAttributeDecoderFactory;
60     SHIBSP_DLLLOCAL PluginManager<AttributeDecoder,xmltooling::QName,const DOMElement*>::Factory XMLAttributeDecoderFactory;
61     SHIBSP_DLLLOCAL PluginManager<AttributeDecoder,xmltooling::QName,const DOMElement*>::Factory Base64AttributeDecoderFactory;
62
63     static const XMLCh _StringAttributeDecoder[] = UNICODE_LITERAL_22(S,t,r,i,n,g,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r);
64     static const XMLCh _ScopedAttributeDecoder[] = UNICODE_LITERAL_22(S,c,o,p,e,d,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r);
65     static const XMLCh _NameIDAttributeDecoder[] = UNICODE_LITERAL_22(N,a,m,e,I,D,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r);
66     static const XMLCh _NameIDFromScopedAttributeDecoder[] = UNICODE_LITERAL_32(N,a,m,e,I,D,F,r,o,m,S,c,o,p,e,d,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r);
67     static const XMLCh _KeyInfoAttributeDecoder[] =UNICODE_LITERAL_23(K,e,y,I,n,f,o,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r);
68     static const XMLCh _DOMAttributeDecoder[] =    UNICODE_LITERAL_19(D,O,M,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r);
69     static const XMLCh _XMLAttributeDecoder[] =    UNICODE_LITERAL_19(X,M,L,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r);
70     static const XMLCh _Base64AttributeDecoder[] = {
71         chLatin_B, chLatin_a, chLatin_s, chLatin_e, chDigit_6, chDigit_4,
72         chLatin_A, chLatin_t, chLatin_t, chLatin_r, chLatin_i, chLatin_b, chLatin_u, chLatin_t, chLatin_e,
73         chLatin_D, chLatin_e, chLatin_c, chLatin_o, chLatin_d, chLatin_e, chLatin_r, chNull
74     };
75
76     static const XMLCh caseSensitive[] =    UNICODE_LITERAL_13(c,a,s,e,S,e,n,s,i,t,i,v,e);
77     static const XMLCh hashAlg[] =          UNICODE_LITERAL_7(h,a,s,h,A,l,g);
78     static const XMLCh internal[] =         UNICODE_LITERAL_8(i,n,t,e,r,n,a,l);
79     static const XMLCh langAware[] =        UNICODE_LITERAL_9(l,a,n,g,A,w,a,r,e);
80 #endif
81 };
82
83 #ifndef SHIBSP_LITE
84 xmltooling::QName shibsp::StringAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _StringAttributeDecoder);
85 xmltooling::QName shibsp::ScopedAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _ScopedAttributeDecoder);
86 xmltooling::QName shibsp::NameIDAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _NameIDAttributeDecoder);
87 xmltooling::QName shibsp::NameIDFromScopedAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _NameIDFromScopedAttributeDecoder);
88 xmltooling::QName shibsp::KeyInfoAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _KeyInfoAttributeDecoder);
89 xmltooling::QName shibsp::DOMAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _DOMAttributeDecoder);
90 xmltooling::QName shibsp::XMLAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _XMLAttributeDecoder);
91 xmltooling::QName shibsp::Base64AttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _Base64AttributeDecoder);
92
93 void shibsp::registerAttributeDecoders()
94 {
95     SPConfig& conf = SPConfig::getConfig();
96     conf.AttributeDecoderManager.registerFactory(StringAttributeDecoderType, StringAttributeDecoderFactory);
97     conf.AttributeDecoderManager.registerFactory(ScopedAttributeDecoderType, ScopedAttributeDecoderFactory);
98     conf.AttributeDecoderManager.registerFactory(NameIDAttributeDecoderType, NameIDAttributeDecoderFactory);
99     conf.AttributeDecoderManager.registerFactory(NameIDFromScopedAttributeDecoderType, NameIDFromScopedAttributeDecoderFactory);
100     conf.AttributeDecoderManager.registerFactory(KeyInfoAttributeDecoderType, KeyInfoAttributeDecoderFactory);
101     conf.AttributeDecoderManager.registerFactory(DOMAttributeDecoderType, DOMAttributeDecoderFactory);
102     conf.AttributeDecoderManager.registerFactory(XMLAttributeDecoderType, XMLAttributeDecoderFactory);
103     conf.AttributeDecoderManager.registerFactory(Base64AttributeDecoderType, Base64AttributeDecoderFactory);
104 }
105
106 AttributeDecoder::AttributeDecoder(const DOMElement *e)
107     : m_caseSensitive(XMLHelper::getAttrBool(e, true, caseSensitive)),
108         m_internal(XMLHelper::getAttrBool(e, false, internal)),
109         m_langAware(XMLHelper::getAttrBool(e, false, langAware)),
110         m_hashAlg(XMLHelper::getAttrString(e, nullptr, hashAlg))
111 {
112 }
113
114 AttributeDecoder::~AttributeDecoder()
115 {
116 }
117
118 Attribute* AttributeDecoder::decode(
119     const GenericRequest* request,
120     const std::vector<std::string>& ids,
121     const xmltooling::XMLObject* xmlObject,
122     const char* assertingParty,
123     const char* relyingParty
124     ) const
125 {
126     // Default call into deprecated method.
127     return decode(ids, xmlObject, assertingParty, relyingParty);
128 }
129
130 Attribute* AttributeDecoder::decode(
131     const std::vector<std::string>& ids,
132     const xmltooling::XMLObject* xmlObject,
133     const char* assertingParty,
134     const char* relyingParty
135     ) const
136 {
137     // Default for deprecated method.
138     return nullptr;
139 }
140
141 Attribute* AttributeDecoder::_decode(Attribute* attr) const
142 {
143     if (attr) {
144         attr->setCaseSensitive(m_caseSensitive);
145         attr->setInternal(m_internal);
146
147         if (!m_hashAlg.empty()) {
148             // We turn the values into strings using the supplied hash algorithm and return a SimpleAttribute instead.
149             auto_ptr<SimpleAttribute> simple(new SimpleAttribute(attr->getAliases()));
150             simple->setCaseSensitive(false);
151             simple->setInternal(m_internal);
152             vector<string>& newdest = simple->getValues();
153             const vector<string>& serialized = attr->getSerializedValues();
154             for (vector<string>::const_iterator ser = serialized.begin(); ser != serialized.end(); ++ser) {
155                 newdest.push_back(SecurityHelper::doHash(m_hashAlg.c_str(), ser->data(), ser->length()));
156                 if (newdest.back().empty())
157                     newdest.pop_back();
158             }
159             delete attr;
160             return newdest.empty() ? nullptr : simple.release();
161         }
162
163     }
164     return attr;
165 }
166
167 pair<vector<XMLObject*>::const_iterator,vector<XMLObject*>::const_iterator> AttributeDecoder::valueRange(
168     const GenericRequest* request, const vector<XMLObject*>& objects
169     ) const
170 {
171     if (!m_langAware || !request || objects.empty()) {
172         return make_pair(objects.begin(), objects.end());
173     }
174     else if (request && request->startLangMatching()) {
175         do {
176             for (vector<XMLObject*>::const_iterator i = objects.begin(); i != objects.end(); ++i) {
177                 if (request->matchLang((*i)->getLang())) {
178                     return make_pair(i, i + 1);
179                 }
180             }
181         } while (request->continueLangMatching());
182     }
183
184     return make_pair(objects.begin(), objects.begin() + 1);
185 }
186 #endif
187
188 void shibsp::registerAttributeFactories()
189 {
190     Attribute::registerFactory("", SimpleAttributeFactory);
191     Attribute::registerFactory("Simple", SimpleAttributeFactory);
192     Attribute::registerFactory("Binary", BinaryAttributeFactory);
193     Attribute::registerFactory("Scoped", ScopedAttributeFactory);
194     Attribute::registerFactory("NameID", NameIDAttributeFactory);
195     Attribute::registerFactory("Extensible", ExtensibleAttributeFactory);
196     Attribute::registerFactory("XML", XMLAttributeFactory);
197 }
198
199 map<string,Attribute::AttributeFactory*> Attribute::m_factoryMap;
200
201 void Attribute::registerFactory(const char* type, AttributeFactory* factory)
202 {
203     m_factoryMap[type] = factory;
204 }
205
206 void Attribute::deregisterFactory(const char* type)
207 {
208     m_factoryMap.erase(type);
209 }
210
211 void Attribute::deregisterFactories()
212 {
213     m_factoryMap.clear();
214 }
215
216 Attribute::Attribute(const vector<string>& ids) : m_id(ids), m_caseSensitive(true), m_internal(false)
217 {
218 }
219
220 Attribute::Attribute(DDF& in) : m_caseSensitive(in["case_insensitive"].isnull()), m_internal(!in["internal"].isnull())
221 {
222     const char* id = in.first().name();
223     if (id && *id)
224         m_id.push_back(id);
225     else
226         throw AttributeException("No id found in marshalled attribute content.");
227     DDF aliases = in["aliases"];
228     if (aliases.islist()) {
229         DDF alias = aliases.first();
230         while (alias.isstring()) {
231             m_id.push_back(alias.string());
232             alias = aliases.next();
233         }
234     }
235 }
236
237 Attribute::~Attribute()
238 {
239 }
240
241 const char* Attribute::getId() const
242 {
243     return m_id.front().c_str();
244 }
245
246 const vector<string>& Attribute::getAliases() const
247 {
248     return m_id;
249 }
250
251 vector<string>& Attribute::getAliases()
252 {
253     return m_id;
254 }
255
256 void Attribute::setCaseSensitive(bool caseSensitive)
257 {
258     m_caseSensitive = caseSensitive;
259 }
260
261 void Attribute::setInternal(bool internal)
262 {
263     m_internal = internal;
264 }
265
266 bool Attribute::isCaseSensitive() const
267 {
268     return m_caseSensitive;
269 }
270
271 bool Attribute::isInternal() const
272 {
273     return m_internal;
274 }
275
276 size_t Attribute::valueCount() const
277 {
278     return m_serialized.size();
279 }
280
281 const vector<string>& Attribute::getSerializedValues() const
282 {
283     return m_serialized;
284 }
285
286 const char* Attribute::getString(size_t index) const
287 {
288     return m_serialized[index].c_str();
289 }
290
291 const char* Attribute::getScope(size_t index) const
292 {
293     return nullptr;
294 }
295
296 void Attribute::removeValue(size_t index)
297 {
298     if (index < m_serialized.size())
299         m_serialized.erase(m_serialized.begin() + index);
300 }
301
302 DDF Attribute::marshall() const
303 {
304     DDF ddf(nullptr);
305     ddf.structure().addmember(m_id.front().c_str()).list();
306     if (!m_caseSensitive)
307         ddf.addmember("case_insensitive");
308     if (m_internal)
309         ddf.addmember("internal");
310     if (m_id.size() > 1) {
311         DDF alias;
312         DDF aliases = ddf.addmember("aliases").list();
313         for (std::vector<std::string>::const_iterator a = m_id.begin() + 1; a != m_id.end(); ++a) {
314             alias = DDF(nullptr).string(a->c_str());
315             aliases.add(alias);
316         }
317     }
318     return ddf;
319 }
320
321 Attribute* Attribute::unmarshall(DDF& in)
322 {
323     map<string,AttributeFactory*>::const_iterator i = m_factoryMap.find(in.name() ? in.name() : "");
324     if (i == m_factoryMap.end())
325         throw AttributeException("No registered factory for Attribute of type ($1).", params(1,in.name()));
326     return (i->second)(in);
327 }