Rework decoder handling in simple resolver, add IdP/SP names to decoder API, hook...
[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 <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     class SHIBSP_DLLLOCAL ScopedAttributeDecoder : virtual public AttributeDecoder\r
40     {\r
41     public:\r
42         ScopedAttributeDecoder(const DOMElement* e) {}\r
43         ~ScopedAttributeDecoder() {}\r
44 \r
45         shibsp::Attribute* decode(\r
46             const char* id, const XMLObject* xmlObject, const char* assertingParty=NULL, const char* relyingParty=NULL\r
47             ) const;\r
48     };\r
49 \r
50     AttributeDecoder* SHIBSP_DLLLOCAL ScopedAttributeDecoderFactory(const DOMElement* const & e)\r
51     {\r
52         return new ScopedAttributeDecoder(e);\r
53     }\r
54 \r
55     static const XMLCh Scope[] = UNICODE_LITERAL_5(S,c,o,p,e);\r
56 };\r
57 \r
58 shibsp::Attribute* ScopedAttributeDecoder::decode(\r
59     const char* id, const XMLObject* xmlObject, const char* assertingParty, const char* relyingParty\r
60     ) const\r
61 {\r
62     char* val;\r
63     char* scope;\r
64     const XMLCh* xmlscope;\r
65     QName scopeqname(NULL,Scope);\r
66     auto_ptr<ScopedAttribute> scoped(new ScopedAttribute(id));\r
67     vector< pair<string,string> >& dest = scoped->getValues();\r
68     vector<XMLObject*>::const_iterator v,stop;\r
69 \r
70     Category& log = Category::getInstance(SHIBSP_LOGCAT".AttributeDecoder");\r
71     \r
72     if (xmlObject && XMLString::equals(opensaml::saml1::Attribute::LOCAL_NAME,xmlObject->getElementQName().getLocalPart())) {\r
73         const opensaml::saml2::Attribute* saml2attr = dynamic_cast<const opensaml::saml2::Attribute*>(xmlObject);\r
74         if (saml2attr) {\r
75             const vector<XMLObject*>& values = saml2attr->getAttributeValues();\r
76             v = values.begin();\r
77             stop = values.end();\r
78             if (log.isDebugEnabled()) {\r
79                 auto_ptr_char n(saml2attr->getName());\r
80                 log.debug("decoding ScopedAttribute (%s) from SAML 2 Attribute (%s) with %lu value(s)", id, n.get() ? n.get() : "unnamed", values.size());\r
81             }\r
82         }\r
83         else {\r
84             const opensaml::saml1::Attribute* saml1attr = dynamic_cast<const opensaml::saml1::Attribute*>(xmlObject);\r
85             if (saml1attr) {\r
86                 const vector<XMLObject*>& values = saml2attr->getAttributeValues();\r
87                 v = values.begin();\r
88                 stop = values.end();\r
89                 if (log.isDebugEnabled()) {\r
90                     auto_ptr_char n(saml1attr->getAttributeName());\r
91                     log.debug("decoding ScopedAttribute (%s) from SAML 1 Attribute (%s) with %lu value(s)", id, n.get() ? n.get() : "unnamed", values.size());\r
92                 }\r
93             }\r
94             else {\r
95                 log.warn("XMLObject type not recognized by ScopedAttributeDecoder, no values returned");\r
96                 return NULL;\r
97             }\r
98         }\r
99 \r
100         for (; v!=stop; ++v) {\r
101             if (!(*v)->hasChildren()) {\r
102                 val = toUTF8((*v)->getTextContent());\r
103                 if (val && *val) {\r
104                     const AttributeExtensibleXMLObject* aexo=dynamic_cast<const AttributeExtensibleXMLObject*>(*v);\r
105                     xmlscope = aexo->getAttribute(scopeqname);\r
106                     if (xmlscope && *xmlscope) {\r
107                         scope = toUTF8(xmlscope);\r
108                         dest.push_back(make_pair(val,scope));\r
109                         delete[] scope;\r
110                     }\r
111                     else {\r
112                         scope = strchr(val, '@');\r
113                         if (scope) {\r
114                             *scope++ = 0;\r
115                             if (*scope)\r
116                                 dest.push_back(make_pair(val,scope));\r
117                             else\r
118                                 log.warn("ignoring unscoped AttributeValue");\r
119                         }\r
120                         else {\r
121                             log.warn("ignoring unscoped AttributeValue");\r
122                         }\r
123                     }\r
124                 }\r
125                 else {\r
126                     log.warn("skipping empty AttributeValue");\r
127                 }\r
128                 delete[] val;\r
129             }\r
130             else {\r
131                 log.warn("skipping complex AttributeValue");\r
132             }\r
133         }\r
134 \r
135         return dest.empty() ? NULL : scoped.release();\r
136     }\r
137 \r
138     const NameID* saml2name = dynamic_cast<const NameID*>(xmlObject);\r
139     if (saml2name) {\r
140         if (log.isDebugEnabled()) {\r
141             auto_ptr_char f(saml2name->getFormat());\r
142             log.debug("decoding ScopedAttribute (%s) from SAML 2 NameID with Format (%s)", id, f.get() ? f.get() : "unspecified");\r
143         }\r
144         val = toUTF8(saml2name->getName());\r
145     }\r
146     else {\r
147         const NameIdentifier* saml1name = dynamic_cast<const NameIdentifier*>(xmlObject);\r
148         if (saml1name) {\r
149             if (log.isDebugEnabled()) {\r
150                 auto_ptr_char f(saml1name->getFormat());\r
151                 log.debug("decoding ScopedAttribute (%s) from SAML 1 NameIdentifier with Format (%s)", id, f.get() ? f.get() : "unspecified");\r
152             }\r
153             val = toUTF8(saml1name->getName());\r
154         }\r
155         else {\r
156             log.warn("XMLObject type not recognized by ScopedAttributeDecoder, no values returned");\r
157             return NULL;\r
158         }\r
159     }\r
160 \r
161     if (val && *val && *val!='@') {\r
162         scope = strchr(val, '@');\r
163         if (scope) {\r
164             *scope++ = 0;\r
165             if (*scope)\r
166                 dest.push_back(make_pair(val,scope));\r
167             else\r
168                 log.warn("ignoring NameID with no scope");\r
169         }\r
170         else {\r
171             log.warn("ignoring NameID with no scope delimiter (@)");\r
172         }\r
173     }\r
174     else {\r
175         log.warn("ignoring empty NameID");\r
176     }\r
177     delete[] val;\r
178     return dest.empty() ? NULL : scoped.release();\r
179 }\r