Add back alias support for attributes.
[shibboleth/cpp-sp.git] / shibsp / attribute / ScopedAttributeDecoder.cpp
1 /*\r
2  *  Copyright 2001-2007 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  * ScopedAttributeDecoder.cpp\r
19  * \r
20  * Decodes SAML into ScopedAttributes\r
21  */\r
22 \r
23 #include "internal.h"\r
24 #include "attribute/AttributeDecoder.h"\r
25 #include "attribute/ScopedAttribute.h"\r
26 \r
27 #include <saml/saml1/core/Assertions.h>\r
28 #include <saml/saml2/core/Assertions.h>\r
29 \r
30 using namespace shibsp;\r
31 using namespace opensaml::saml1;\r
32 using namespace opensaml::saml2;\r
33 using namespace xmltooling;\r
34 using namespace std;\r
35 \r
36 namespace shibsp {\r
37     static const XMLCh Scope[] =            UNICODE_LITERAL_5(S,c,o,p,e);\r
38     static const XMLCh scopeDelimeter[] =   UNICODE_LITERAL_14(s,c,o,p,e,D,e,l,i,m,e,t,e,r);\r
39 \r
40     class SHIBSP_DLLLOCAL ScopedAttributeDecoder : virtual public AttributeDecoder\r
41     {\r
42     public:\r
43         ScopedAttributeDecoder(const DOMElement* e) : AttributeDecoder(e), m_delimeter('@') {\r
44             if (e) {\r
45                 if (e->hasAttributeNS(NULL,scopeDelimeter)) {\r
46                     auto_ptr_char d(e->getAttributeNS(NULL,scopeDelimeter));\r
47                     m_delimeter = *(d.get());\r
48                 }\r
49             }\r
50         }\r
51         ~ScopedAttributeDecoder() {}\r
52 \r
53         shibsp::Attribute* decode(\r
54             const vector<string>& ids, const XMLObject* xmlObject, const char* assertingParty=NULL, const char* relyingParty=NULL\r
55             ) const;\r
56 \r
57     private:\r
58         char m_delimeter;\r
59     };\r
60 \r
61     AttributeDecoder* SHIBSP_DLLLOCAL ScopedAttributeDecoderFactory(const DOMElement* const & e)\r
62     {\r
63         return new ScopedAttributeDecoder(e);\r
64     }\r
65 };\r
66 \r
67 shibsp::Attribute* ScopedAttributeDecoder::decode(\r
68     const vector<string>& ids, const XMLObject* xmlObject, const char* assertingParty, const char* relyingParty\r
69     ) const\r
70 {\r
71     char* val;\r
72     char* scope;\r
73     const XMLCh* xmlscope;\r
74     QName scopeqname(NULL,Scope);\r
75     auto_ptr<ScopedAttribute> scoped(new ScopedAttribute(ids, m_delimeter));\r
76     scoped->setCaseSensitive(m_caseSensitive);\r
77     vector< pair<string,string> >& dest = scoped->getValues();\r
78     vector<XMLObject*>::const_iterator v,stop;\r
79 \r
80     Category& log = Category::getInstance(SHIBSP_LOGCAT".AttributeDecoder");\r
81     \r
82     if (xmlObject && XMLString::equals(opensaml::saml1::Attribute::LOCAL_NAME,xmlObject->getElementQName().getLocalPart())) {\r
83         const opensaml::saml2::Attribute* saml2attr = dynamic_cast<const opensaml::saml2::Attribute*>(xmlObject);\r
84         if (saml2attr) {\r
85             const vector<XMLObject*>& values = saml2attr->getAttributeValues();\r
86             v = values.begin();\r
87             stop = values.end();\r
88             if (log.isDebugEnabled()) {\r
89                 auto_ptr_char n(saml2attr->getName());\r
90                 log.debug(\r
91                     "decoding ScopedAttribute (%s) from SAML 2 Attribute (%s) with %lu value(s)",\r
92                     ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size()\r
93                     );\r
94             }\r
95         }\r
96         else {\r
97             const opensaml::saml1::Attribute* saml1attr = dynamic_cast<const opensaml::saml1::Attribute*>(xmlObject);\r
98             if (saml1attr) {\r
99                 const vector<XMLObject*>& values = saml1attr->getAttributeValues();\r
100                 v = values.begin();\r
101                 stop = values.end();\r
102                 if (log.isDebugEnabled()) {\r
103                     auto_ptr_char n(saml1attr->getAttributeName());\r
104                     log.debug(\r
105                         "decoding ScopedAttribute (%s) from SAML 1 Attribute (%s) with %lu value(s)",\r
106                         ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size()\r
107                         );\r
108                 }\r
109             }\r
110             else {\r
111                 log.warn("XMLObject type not recognized by ScopedAttributeDecoder, no values returned");\r
112                 return NULL;\r
113             }\r
114         }\r
115 \r
116         for (; v!=stop; ++v) {\r
117             if (!(*v)->hasChildren()) {\r
118                 val = toUTF8((*v)->getTextContent());\r
119                 if (val && *val) {\r
120                     const AttributeExtensibleXMLObject* aexo=dynamic_cast<const AttributeExtensibleXMLObject*>(*v);\r
121                     xmlscope = aexo->getAttribute(scopeqname);\r
122                     if (xmlscope && *xmlscope) {\r
123                         scope = toUTF8(xmlscope);\r
124                         dest.push_back(make_pair(val,scope));\r
125                         delete[] scope;\r
126                     }\r
127                     else {\r
128                         scope = strchr(val, m_delimeter);\r
129                         if (scope) {\r
130                             *scope++ = 0;\r
131                             if (*scope)\r
132                                 dest.push_back(make_pair(val,scope));\r
133                             else\r
134                                 log.warn("ignoring unscoped AttributeValue");\r
135                         }\r
136                         else {\r
137                             log.warn("ignoring unscoped AttributeValue");\r
138                         }\r
139                     }\r
140                 }\r
141                 else {\r
142                     log.warn("skipping empty AttributeValue");\r
143                 }\r
144                 delete[] val;\r
145             }\r
146             else {\r
147                 log.warn("skipping complex AttributeValue");\r
148             }\r
149         }\r
150 \r
151         return dest.empty() ? NULL : scoped.release();\r
152     }\r
153 \r
154     const NameID* saml2name = dynamic_cast<const NameID*>(xmlObject);\r
155     if (saml2name) {\r
156         if (log.isDebugEnabled()) {\r
157             auto_ptr_char f(saml2name->getFormat());\r
158             log.debug("decoding ScopedAttribute (%s) from SAML 2 NameID with Format (%s)", ids.front().c_str(), f.get() ? f.get() : "unspecified");\r
159         }\r
160         val = toUTF8(saml2name->getName());\r
161     }\r
162     else {\r
163         const NameIdentifier* saml1name = dynamic_cast<const NameIdentifier*>(xmlObject);\r
164         if (saml1name) {\r
165             if (log.isDebugEnabled()) {\r
166                 auto_ptr_char f(saml1name->getFormat());\r
167                 log.debug(\r
168                     "decoding ScopedAttribute (%s) from SAML 1 NameIdentifier with Format (%s)",\r
169                     ids.front().c_str(), f.get() ? f.get() : "unspecified"\r
170                     );\r
171             }\r
172             val = toUTF8(saml1name->getName());\r
173         }\r
174         else {\r
175             log.warn("XMLObject type not recognized by ScopedAttributeDecoder, no values returned");\r
176             return NULL;\r
177         }\r
178     }\r
179 \r
180     if (val && *val && *val!=m_delimeter) {\r
181         scope = strchr(val, m_delimeter);\r
182         if (scope) {\r
183             *scope++ = 0;\r
184             if (*scope)\r
185                 dest.push_back(make_pair(val,scope));\r
186             else\r
187                 log.warn("ignoring NameID with no scope");\r
188         }\r
189         else {\r
190             log.warn("ignoring NameID with no scope delimiter (%c)", m_delimeter);\r
191         }\r
192     }\r
193     else {\r
194         log.warn("ignoring empty NameID");\r
195     }\r
196     delete[] val;\r
197     return dest.empty() ? NULL : scoped.release();\r
198 }\r