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