Improve logging around XML processing of content.
[shibboleth/cpp-sp.git] / shibsp / attribute / NameIDAttributeDecoder.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  * NameIDAttributeDecoder.cpp
19  *
20  * Decodes SAML into NameIDAttributes.
21  */
22
23 #include "internal.h"
24 #include "attribute/AttributeDecoder.h"
25 #include "attribute/NameIDAttribute.h"
26
27 #include <saml/saml1/core/Assertions.h>
28 #include <saml/saml2/core/Assertions.h>
29
30 using namespace shibsp;
31 using namespace opensaml::saml1;
32 using namespace opensaml::saml2;
33 using namespace xmltooling;
34 using namespace std;
35
36 namespace shibsp {
37     static const XMLCh formatter[] = UNICODE_LITERAL_9(f,o,r,m,a,t,t,e,r);
38     static const XMLCh defaultQualifiers[] = UNICODE_LITERAL_17(d,e,f,a,u,l,t,Q,u,a,l,i,f,i,e,r,s);
39
40     class SHIBSP_DLLLOCAL NameIDAttributeDecoder : virtual public AttributeDecoder
41     {
42     public:
43         NameIDAttributeDecoder(const DOMElement* e)
44                 : AttributeDecoder(e), m_formatter(e ? e->getAttributeNS(nullptr, formatter) : nullptr), m_defaultQualifiers(false) {
45             const XMLCh* flag = e ? e->getAttributeNS(nullptr, defaultQualifiers) : nullptr;
46             if (flag && (*flag == chLatin_t || *flag == chDigit_1))
47                 m_defaultQualifiers = true;
48         }
49         ~NameIDAttributeDecoder() {}
50
51         shibsp::Attribute* decode(
52             const vector<string>& ids, const XMLObject* xmlObject, const char* assertingParty=nullptr, const char* relyingParty=nullptr
53             ) const;
54
55     private:
56         void extract(
57             const NameIDType* n, vector<NameIDAttribute::Value>& dest, const char* assertingParty, const char* relyingParty
58             ) const;
59         void extract(
60             const NameIdentifier* n, vector<NameIDAttribute::Value>& dest, const char* assertingParty, const char* relyingParty
61             ) const;
62         auto_ptr_char m_formatter;
63         bool m_defaultQualifiers;
64     };
65
66     AttributeDecoder* SHIBSP_DLLLOCAL NameIDAttributeDecoderFactory(const DOMElement* const & e)
67     {
68         return new NameIDAttributeDecoder(e);
69     }
70 };
71
72 shibsp::Attribute* NameIDAttributeDecoder::decode(
73     const vector<string>& ids, const XMLObject* xmlObject, const char* assertingParty, const char* relyingParty
74     ) const
75 {
76     auto_ptr<NameIDAttribute> nameid(
77         new NameIDAttribute(ids, (m_formatter.get() && *m_formatter.get()) ? m_formatter.get() : DEFAULT_NAMEID_FORMATTER)
78         );
79     vector<NameIDAttribute::Value>& dest = nameid->getValues();
80     vector<XMLObject*>::const_iterator v,stop;
81
82     Category& log = Category::getInstance(SHIBSP_LOGCAT".AttributeDecoder.NameID");
83
84     if (xmlObject && XMLString::equals(opensaml::saml1::Attribute::LOCAL_NAME,xmlObject->getElementQName().getLocalPart())) {
85         const opensaml::saml2::Attribute* saml2attr = dynamic_cast<const opensaml::saml2::Attribute*>(xmlObject);
86         if (saml2attr) {
87             const vector<XMLObject*>& values = saml2attr->getAttributeValues();
88             v = values.begin();
89             stop = values.end();
90             if (log.isDebugEnabled()) {
91                 auto_ptr_char n(saml2attr->getName());
92                 log.debug(
93                     "decoding NameIDAttribute (%s) from SAML 2 Attribute (%s) with %lu value(s)",
94                     ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size()
95                     );
96             }
97         }
98         else {
99             const opensaml::saml1::Attribute* saml1attr = dynamic_cast<const opensaml::saml1::Attribute*>(xmlObject);
100             if (saml1attr) {
101                 const vector<XMLObject*>& values = saml1attr->getAttributeValues();
102                 v = values.begin();
103                 stop = values.end();
104                 if (log.isDebugEnabled()) {
105                     auto_ptr_char n(saml1attr->getAttributeName());
106                     log.debug(
107                         "decoding NameIDAttribute (%s) from SAML 1 Attribute (%s) with %lu value(s)",
108                         ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size()
109                         );
110                 }
111             }
112             else {
113                 log.warn("XMLObject type not recognized by NameIDAttributeDecoder, no values returned");
114                 return nullptr;
115             }
116         }
117
118         for (; v!=stop; ++v) {
119             const NameIDType* n2 = dynamic_cast<const NameIDType*>(*v);
120             if (n2) {
121                 log.debug("decoding AttributeValue element of saml2:NameIDType type");
122                 extract(n2, dest, assertingParty, relyingParty);
123             }
124             else {
125                 const NameIdentifier* n1=dynamic_cast<const NameIdentifier*>(*v);
126                 if (n1) {
127                     log.debug("decoding AttributeValue element of saml1:NameIdentifier type");
128                     extract(n1, dest, assertingParty, relyingParty);
129                 }
130                 else if ((*v)->hasChildren()) {
131                     const list<XMLObject*>& values = (*v)->getOrderedChildren();
132                     for (list<XMLObject*>::const_iterator vv = values.begin(); vv!=values.end(); ++vv) {
133                         if (n2=dynamic_cast<const NameIDType*>(*vv)) {
134                             log.debug("decoding saml2:NameID child element of AttributeValue");
135                             extract(n2, dest, assertingParty, relyingParty);
136                         }
137                         else if (n1=dynamic_cast<const NameIdentifier*>(*vv)) {
138                             log.debug("decoding saml1:NameIdentifier child element of AttributeValue");
139                             extract(n1, dest, assertingParty, relyingParty);
140                         }
141                         else {
142                             log.warn("skipping AttributeValue child element not recognizable as NameID/NameIdentifier");
143                         }
144                     }
145                 }
146                 else {
147                     log.warn("AttributeValue was not of a supported type and contains no child elements");
148                 }
149             }
150         }
151
152         return dest.empty() ? nullptr : _decode(nameid.release());
153     }
154
155     const NameIDType* saml2name = dynamic_cast<const NameIDType*>(xmlObject);
156     if (saml2name) {
157         if (log.isDebugEnabled()) {
158             auto_ptr_char f(saml2name->getFormat());
159             log.debug("decoding NameIDAttribute (%s) from SAML 2 NameID with Format (%s)", ids.front().c_str(), f.get() ? f.get() : "unspecified");
160         }
161         extract(saml2name, dest, assertingParty, relyingParty);
162     }
163     else {
164         const NameIdentifier* saml1name = dynamic_cast<const NameIdentifier*>(xmlObject);
165         if (saml1name) {
166             if (log.isDebugEnabled()) {
167                 auto_ptr_char f(saml1name->getFormat());
168                 log.debug(
169                     "decoding NameIDAttribute (%s) from SAML 1 NameIdentifier with Format (%s)",
170                     ids.front().c_str(), f.get() ? f.get() : "unspecified"
171                     );
172             }
173             extract(saml1name, dest, assertingParty, relyingParty);
174         }
175         else {
176             log.warn("XMLObject type not recognized by NameIDAttributeDecoder, no values returned");
177             return nullptr;
178         }
179     }
180
181     return dest.empty() ? nullptr : _decode(nameid.release());
182 }
183
184 void NameIDAttributeDecoder::extract(
185     const NameIDType* n, vector<NameIDAttribute::Value>& dest, const char* assertingParty, const char* relyingParty
186     ) const
187 {
188     auto_arrayptr<char> name(toUTF8(n->getName()));
189     if (name.get() && *name.get()) {
190         dest.push_back(NameIDAttribute::Value());
191         NameIDAttribute::Value& val = dest.back();
192         val.m_Name = name.get();
193         char* str = toUTF8(n->getFormat());
194         if (str) {
195             val.m_Format = str;
196             delete[] str;
197         }
198
199         str = toUTF8(n->getNameQualifier());
200         if (str && *str)
201             val.m_NameQualifier = str;
202         else if (m_defaultQualifiers && assertingParty)
203             val.m_NameQualifier = assertingParty;
204         delete[] str;
205
206         str = toUTF8(n->getSPNameQualifier());
207         if (str && *str)
208             val.m_SPNameQualifier = str;
209         else if (m_defaultQualifiers && relyingParty)
210             val.m_SPNameQualifier = relyingParty;
211         delete[] str;
212
213         str = toUTF8(n->getSPProvidedID());
214         if (str) {
215             val.m_SPProvidedID = str;
216             delete[] str;
217         }
218     }
219 }
220
221 void NameIDAttributeDecoder::extract(
222     const NameIdentifier* n, vector<NameIDAttribute::Value>& dest, const char* assertingParty, const char* relyingParty
223     ) const
224 {
225     auto_arrayptr<char> name(toUTF8(n->getName()));
226     if (name.get() && *name.get()) {
227         dest.push_back(NameIDAttribute::Value());
228         NameIDAttribute::Value& val = dest.back();
229         val.m_Name = name.get();
230         char* str = toUTF8(n->getFormat());
231         if (str) {
232             val.m_Format = str;
233             delete[] str;
234         }
235
236         str = toUTF8(n->getNameQualifier());
237         if (str && *str)
238             val.m_NameQualifier = str;
239         else if (m_defaultQualifiers && assertingParty)
240             val.m_NameQualifier = assertingParty;
241         delete[] str;
242
243         if (m_defaultQualifiers && relyingParty)
244             val.m_SPNameQualifier = relyingParty;
245     }
246 }