Add option to make attributes internal only.
[shibboleth/cpp-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                     make_pair(\r
79                         pair<xstring,xstring>(f->getLocalPart(), f->hasNamespaceURI() ? f->getNamespaceURI() : &chNull),\r
80                         t.get())\r
81                     );\r
82             }\r
83         }\r
84         e = XMLHelper::getNextSiblingElement(e, Mapping);\r
85     }\r
86 }\r
87 \r
88 Attribute* DOMAttributeDecoder::decode(\r
89     const vector<string>& ids, const XMLObject* xmlObject, const char* assertingParty, const char* relyingParty\r
90     ) const\r
91 {\r
92     Category& log = Category::getInstance(SHIBSP_LOGCAT".AttributeDecoder.DOM");\r
93 \r
94     if (!xmlObject || !XMLString::equals(saml1::Attribute::LOCAL_NAME, xmlObject->getElementQName().getLocalPart())) {\r
95         log.warn("XMLObject type not recognized by DOMAttributeDecoder, no values returned");\r
96         return NULL;\r
97     }\r
98 \r
99     auto_ptr<ExtensibleAttribute> attr(new ExtensibleAttribute(ids, m_formatter.get()));\r
100     DDF dest = attr->getValues();\r
101     vector<XMLObject*>::const_iterator v,stop;\r
102 \r
103     const saml2::Attribute* saml2attr = dynamic_cast<const saml2::Attribute*>(xmlObject);\r
104     if (saml2attr) {\r
105         const vector<XMLObject*>& values = saml2attr->getAttributeValues();\r
106         v = values.begin();\r
107         stop = values.end();\r
108         if (log.isDebugEnabled()) {\r
109             auto_ptr_char n(saml2attr->getName());\r
110             log.debug(\r
111                 "decoding ExtensibleAttribute (%s) from SAML 2 Attribute (%s) with %lu value(s)",\r
112                 ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size()\r
113                 );\r
114         }\r
115     }\r
116     else {\r
117         const saml1::Attribute* saml1attr = dynamic_cast<const saml1::Attribute*>(xmlObject);\r
118         if (saml1attr) {\r
119             const vector<XMLObject*>& values = saml1attr->getAttributeValues();\r
120             v = values.begin();\r
121             stop = values.end();\r
122             if (log.isDebugEnabled()) {\r
123                 auto_ptr_char n(saml1attr->getAttributeName());\r
124                 log.debug(\r
125                     "decoding ExtensibleAttribute (%s) from SAML 1 Attribute (%s) with %lu value(s)",\r
126                     ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size()\r
127                     );\r
128             }\r
129         }\r
130         else {\r
131             log.warn("XMLObject type not recognized by DOMAttributeDecoder, no values returned");\r
132             return NULL;\r
133         }\r
134     }\r
135 \r
136     for (; v!=stop; ++v) {\r
137         DOMElement* e = (*v)->getDOM();\r
138         if (e) {\r
139             DDF converted = convert(e, false);\r
140             if (!converted.isnull())\r
141                 dest.add(converted);\r
142         }\r
143         else\r
144             log.warn("skipping AttributeValue without a backing DOM");\r
145     }\r
146 \r
147     return dest.integer() ? _decode(attr.release()) : NULL;\r
148 }\r
149 \r
150 DDF DOMAttributeDecoder::convert(DOMElement* e, bool nameit) const\r
151 {\r
152     const XMLCh* nsURI;\r
153     const XMLCh* local;\r
154     map<pair<xstring,xstring>,string>::const_iterator mapping;\r
155     DDF obj = DDF(NULL).structure();\r
156 \r
157     if (nameit) {\r
158         // Name this structure.\r
159         nsURI = e->getNamespaceURI();\r
160         local = e->getLocalName();\r
161         mapping = m_tagMap.find(pair<xstring,xstring>(local,nsURI));\r
162         if (mapping == m_tagMap.end()) {\r
163             auto_ptr_char temp(local);\r
164             obj.name(temp.get());\r
165         }\r
166         else {\r
167             obj.name(mapping->second.c_str());\r
168         }\r
169     }\r
170 \r
171     // Process non-xmlns attributes.\r
172     DOMNamedNodeMap* attrs = e->getAttributes();\r
173     for (XMLSize_t a = attrs->getLength(); a > 0; --a) {\r
174         DOMNode* attr = attrs->item(a-1);\r
175         nsURI = attr->getNamespaceURI();\r
176         if (XMLString::equals(nsURI, xmlconstants::XMLNS_NS))\r
177             continue;\r
178         local = attr->getLocalName();\r
179         mapping = m_tagMap.find(pair<xstring,xstring>(local, nsURI ? nsURI : &chNull));\r
180         if (mapping == m_tagMap.end()) {\r
181             auto_ptr_char temp(local);\r
182             obj.addmember(temp.get()).string(toUTF8(attr->getNodeValue(), true), false);\r
183         }\r
184         else {\r
185             obj.addmember(mapping->second.c_str()).string(toUTF8(attr->getNodeValue(), true), false);\r
186         }\r
187     }\r
188 \r
189     DOMElement* child = XMLHelper::getFirstChildElement(e);\r
190     if (!child && e->hasChildNodes() && e->getFirstChild()->getNodeType() == DOMNode::TEXT_NODE) {\r
191         // Attach a _text member if a text node is present.\r
192         obj.addmember("_string").string(toUTF8(e->getFirstChild()->getNodeValue(), true), false);\r
193     }\r
194     else {\r
195         while (child) {\r
196             // Convert the child element.\r
197             DDF converted = convert(child);\r
198             if (!converted.isnull()) {\r
199                 // Now identify it and attach it.\r
200                 if (obj[converted.name()].isnull()) {\r
201                     // We're a new child, so just attach as a structure member.\r
202                     obj.add(converted);\r
203                 }\r
204                 else if (obj[converted.name()].islist()) {\r
205                     // We're already a repeating child, so add it to the list.\r
206                     obj[converted.name()].add(converted);\r
207                 }\r
208                 else if (obj[converted.name()].isstruct()) {\r
209                     // This is the complex case where we see a child for the second\r
210                     // time and have to convert a structure member into a named list.\r
211                     DDF newlist = DDF(converted.name()).list();\r
212                     newlist.add(obj[converted.name()].remove());\r
213                     newlist.add(converted);\r
214                     obj.add(newlist);\r
215                 }\r
216             }\r
217             child = XMLHelper::getNextSiblingElement(child);\r
218         }\r
219     }\r
220 \r
221     // If we're empty, just delete.\r
222     if (obj.integer() == 0)\r
223         obj.destroy();\r
224     return obj;\r
225 }\r