Imported Upstream version 2.2.1+dfsg
[shibboleth/sp.git] / shibsp / attribute / DOMAttributeDecoder.cpp
1 /*\r
2  *  Copyright 2009 Internet2\r
3  *\r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *\r
8  *     http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  */\r
16 \r
17 /**\r
18  * DOMAttributeDecoder.cpp\r
19  *\r
20  * Decodes a DOM into an ExtensibleAttribute.\r
21  */\r
22 \r
23 #include "internal.h"\r
24 #include "attribute/AttributeDecoder.h"\r
25 #include "attribute/ExtensibleAttribute.h"\r
26 \r
27 #include <saml/saml1/core/Assertions.h>\r
28 #include <saml/saml2/core/Assertions.h>\r
29 #include <xmltooling/util/XMLHelper.h>\r
30 \r
31 using namespace shibsp;\r
32 using namespace opensaml;\r
33 using namespace xmltooling;\r
34 using namespace std;\r
35 \r
36 namespace shibsp {\r
37     class SHIBSP_DLLLOCAL DOMAttributeDecoder : virtual public AttributeDecoder\r
38     {\r
39     public:\r
40         DOMAttributeDecoder(const DOMElement* e);\r
41         ~DOMAttributeDecoder() {}\r
42 \r
43         Attribute* decode(\r
44             const vector<string>& ids, const XMLObject* xmlObject, const char* assertingParty=NULL, const char* relyingParty=NULL\r
45             ) const;\r
46 \r
47     private:\r
48         DDF convert(DOMElement* e, bool nameit=true) const;\r
49         auto_ptr_char m_formatter;\r
50         map<pair<xstring,xstring>,string> m_tagMap;\r
51     };\r
52 \r
53     AttributeDecoder* SHIBSP_DLLLOCAL DOMAttributeDecoderFactory(const DOMElement* const & e)\r
54     {\r
55         return new DOMAttributeDecoder(e);\r
56     }\r
57 \r
58     static const XMLCh Mapping[] =  UNICODE_LITERAL_7(M,a,p,p,i,n,g);\r
59     static const XMLCh _from[] =    UNICODE_LITERAL_4(f,r,o,m);\r
60     static const XMLCh _to[] =      UNICODE_LITERAL_2(t,o);\r
61     static const XMLCh formatter[] =      UNICODE_LITERAL_9(f,o,r,m,a,t,t,e,r);\r
62 };\r
63 \r
64 DOMAttributeDecoder::DOMAttributeDecoder(const DOMElement* e)\r
65     : AttributeDecoder(e), m_formatter(e ? e->getAttributeNS(NULL,formatter) : NULL)\r
66 {\r
67     Category& log = Category::getInstance(SHIBSP_LOGCAT".AttributeDecoder.DOM");\r
68 \r
69     e = e ? XMLHelper::getFirstChildElement(e, Mapping) : NULL;\r
70     while (e) {\r
71         if (e->hasAttributeNS(NULL, _from) && e->hasAttributeNS(NULL, _to)) {\r
72             auto_ptr<xmltooling::QName> f(XMLHelper::getNodeValueAsQName(e->getAttributeNodeNS(NULL, _from)));\r
73             auto_ptr_char t(e->getAttributeNS(NULL, _to));\r
74             if (f.get() && t.get() && *t.get()) {\r
75                 if (log.isDebugEnabled())\r
76                     log.debug("mapping (%s) to (%s)", f->toString().c_str(), t.get());\r
77                 m_tagMap.insert(\r
78                     pair< const pair<xstring,xstring>,string>(\r
79                         pair<xstring,xstring>(f->getLocalPart(), f->hasNamespaceURI() ? f->getNamespaceURI() : &chNull),\r
80                         t.get()\r
81                         )\r
82                     );\r
83             }\r
84         }\r
85         e = XMLHelper::getNextSiblingElement(e, Mapping);\r
86     }\r
87 }\r
88 \r
89 Attribute* DOMAttributeDecoder::decode(\r
90     const vector<string>& ids, const XMLObject* xmlObject, const char* assertingParty, const char* relyingParty\r
91     ) const\r
92 {\r
93     Category& log = Category::getInstance(SHIBSP_LOGCAT".AttributeDecoder.DOM");\r
94 \r
95     if (!xmlObject || !XMLString::equals(saml1::Attribute::LOCAL_NAME, xmlObject->getElementQName().getLocalPart())) {\r
96         log.warn("XMLObject type not recognized by DOMAttributeDecoder, no values returned");\r
97         return NULL;\r
98     }\r
99 \r
100     auto_ptr<ExtensibleAttribute> attr(new ExtensibleAttribute(ids, m_formatter.get()));\r
101     DDF dest = attr->getValues();\r
102     vector<XMLObject*>::const_iterator v,stop;\r
103 \r
104     const saml2::Attribute* saml2attr = dynamic_cast<const saml2::Attribute*>(xmlObject);\r
105     if (saml2attr) {\r
106         const vector<XMLObject*>& values = saml2attr->getAttributeValues();\r
107         v = values.begin();\r
108         stop = values.end();\r
109         if (log.isDebugEnabled()) {\r
110             auto_ptr_char n(saml2attr->getName());\r
111             log.debug(\r
112                 "decoding ExtensibleAttribute (%s) from SAML 2 Attribute (%s) with %lu value(s)",\r
113                 ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size()\r
114                 );\r
115         }\r
116     }\r
117     else {\r
118         const saml1::Attribute* saml1attr = dynamic_cast<const saml1::Attribute*>(xmlObject);\r
119         if (saml1attr) {\r
120             const vector<XMLObject*>& values = saml1attr->getAttributeValues();\r
121             v = values.begin();\r
122             stop = values.end();\r
123             if (log.isDebugEnabled()) {\r
124                 auto_ptr_char n(saml1attr->getAttributeName());\r
125                 log.debug(\r
126                     "decoding ExtensibleAttribute (%s) from SAML 1 Attribute (%s) with %lu value(s)",\r
127                     ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size()\r
128                     );\r
129             }\r
130         }\r
131         else {\r
132             log.warn("XMLObject type not recognized by DOMAttributeDecoder, no values returned");\r
133             return NULL;\r
134         }\r
135     }\r
136 \r
137     for (; v!=stop; ++v) {\r
138         DOMElement* e = (*v)->getDOM();\r
139         if (e) {\r
140             DDF converted = convert(e, false);\r
141             if (!converted.isnull())\r
142                 dest.add(converted);\r
143         }\r
144         else\r
145             log.warn("skipping AttributeValue without a backing DOM");\r
146     }\r
147 \r
148     return dest.integer() ? _decode(attr.release()) : NULL;\r
149 }\r
150 \r
151 DDF DOMAttributeDecoder::convert(DOMElement* e, bool nameit) const\r
152 {\r
153     const XMLCh* nsURI;\r
154     const XMLCh* local;\r
155     map<pair<xstring,xstring>,string>::const_iterator mapping;\r
156     DDF obj = DDF(NULL).structure();\r
157 \r
158     if (nameit) {\r
159         // Name this structure.\r
160         nsURI = e->getNamespaceURI();\r
161         local = e->getLocalName();\r
162         mapping = m_tagMap.find(pair<xstring,xstring>(local,nsURI));\r
163         if (mapping == m_tagMap.end()) {\r
164             auto_ptr_char temp(local);\r
165             obj.name(temp.get());\r
166         }\r
167         else {\r
168             obj.name(mapping->second.c_str());\r
169         }\r
170     }\r
171 \r
172     // Process non-xmlns attributes.\r
173     DOMNamedNodeMap* attrs = e->getAttributes();\r
174     for (XMLSize_t a = attrs->getLength(); a > 0; --a) {\r
175         DOMNode* attr = attrs->item(a-1);\r
176         nsURI = attr->getNamespaceURI();\r
177         if (XMLString::equals(nsURI, xmlconstants::XMLNS_NS))\r
178             continue;\r
179         local = attr->getLocalName();\r
180         mapping = m_tagMap.find(pair<xstring,xstring>(local, nsURI ? nsURI : &chNull));\r
181         if (mapping == m_tagMap.end()) {\r
182             auto_ptr_char temp(local);\r
183             obj.addmember(temp.get()).string(toUTF8(attr->getNodeValue(), true), false);\r
184         }\r
185         else {\r
186             obj.addmember(mapping->second.c_str()).string(toUTF8(attr->getNodeValue(), true), false);\r
187         }\r
188     }\r
189 \r
190     DOMElement* child = XMLHelper::getFirstChildElement(e);\r
191     if (!child && e->hasChildNodes() && e->getFirstChild()->getNodeType() == DOMNode::TEXT_NODE) {\r
192         // Attach a _text member if a text node is present.\r
193         obj.addmember("_string").string(toUTF8(e->getFirstChild()->getNodeValue(), true), false);\r
194     }\r
195     else {\r
196         while (child) {\r
197             // Convert the child element.\r
198             DDF converted = convert(child);\r
199             if (!converted.isnull()) {\r
200                 // Now identify it and attach it.\r
201                 if (obj[converted.name()].isnull()) {\r
202                     // We're a new child, so just attach as a structure member.\r
203                     obj.add(converted);\r
204                 }\r
205                 else if (obj[converted.name()].islist()) {\r
206                     // We're already a repeating child, so add it to the list.\r
207                     obj[converted.name()].add(converted);\r
208                 }\r
209                 else if (obj[converted.name()].isstruct()) {\r
210                     // This is the complex case where we see a child for the second\r
211                     // time and have to convert a structure member into a named list.\r
212                     DDF newlist = DDF(converted.name()).list();\r
213                     newlist.add(obj[converted.name()].remove());\r
214                     newlist.add(converted);\r
215                     obj.add(newlist);\r
216                 }\r
217             }\r
218             child = XMLHelper::getNextSiblingElement(child);\r
219         }\r
220     }\r
221 \r
222     // If we're empty, just delete.\r
223     if (obj.integer() == 0)\r
224         obj.destroy();\r
225     return obj;\r
226 }\r