Move scoped attribute subclass, add TargetedID plugin.
[shibboleth/cpp-sp.git] / xmlproviders / TargetedID.cpp
1 /*
2  * The Shibboleth License, Version 1.
3  * Copyright (c) 2002
4  * University Corporation for Advanced Internet Development, Inc.
5  * All rights reserved
6  *
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * Redistributions of source code must retain the above copyright notice, this
12  * list of conditions and the following disclaimer.
13  *
14  * Redistributions in binary form must reproduce the above copyright notice,
15  * this list of conditions and the following disclaimer in the documentation
16  * and/or other materials provided with the distribution, if any, must include
17  * the following acknowledgment: "This product includes software developed by
18  * the University Corporation for Advanced Internet Development
19  * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
20  * may appear in the software itself, if and wherever such third-party
21  * acknowledgments normally appear.
22  *
23  * Neither the name of Shibboleth nor the names of its contributors, nor
24  * Internet2, nor the University Corporation for Advanced Internet Development,
25  * Inc., nor UCAID may be used to endorse or promote products derived from this
26  * software without specific prior written permission. For written permission,
27  * please contact shibboleth@shibboleth.org
28  *
29  * Products derived from this software may not be called Shibboleth, Internet2,
30  * UCAID, or the University Corporation for Advanced Internet Development, nor
31  * may Shibboleth appear in their name, without prior written permission of the
32  * University Corporation for Advanced Internet Development.
33  *
34  *
35  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36  * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
38  * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
39  * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
40  * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
41  * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
42  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
43  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
47  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  */
49
50
51 /* TargetedID.cpp - eduPersonTargetedID custom attribute handling
52
53    Scott Cantor
54    4/30/05
55
56    $History:$
57 */
58
59 #include "internal.h"
60 #include <xercesc/util/Base64.hpp>
61
62 using namespace shibboleth;
63 using namespace saml;
64 using namespace std;
65
66 namespace {
67     class TargetedID : public SAMLAttribute
68     {
69     public:
70         TargetedID(
71             const XMLCh* name=NULL,
72             const XMLCh* ns=NULL,
73             const saml::QName* type=NULL,
74             long lifetime=0,
75             const Iterator<const XMLCh*>& values=EMPTY(const XMLCh*),
76             const Iterator<const XMLCh*>& nameQualifiers=EMPTY(const XMLCh*),
77             const Iterator<const XMLCh*>& spNameQualifiers=EMPTY(const XMLCh*)
78             );
79         TargetedID(DOMElement* e);
80         TargetedID(istream& in);
81         ~TargetedID();
82     
83         saml::SAMLObject* clone() const;
84         
85         saml::Iterator<const XMLCh*> getValues() const;
86         saml::Iterator<std::string> getSingleByteValues() const;
87
88         void setValues(const saml::Iterator<const XMLCh*>& values=EMPTY(const XMLCh*)) {
89             throw SAMLException("unsupported operation");
90         }
91         void addValue(const XMLCh* value) {
92             throw SAMLException("unsupported operation");
93         }
94         void removeValue(unsigned int index);
95         
96         static const XMLCh NameID[];
97         static const XMLCh SPNameQualifier[];
98         static const XMLCh FORMAT_PERSISTENT[];
99     protected:
100         void valueToDOM(unsigned int index, DOMElement* e) const;
101         void valueFromDOM(DOMElement* e);
102         void ownStrings();
103     
104     private:
105         vector<const XMLCh*> m_nameQualifiers;
106         vector<const XMLCh*> m_spNameQualifiers;
107         mutable vector<const XMLCh*> m_encodedValues;
108     };
109
110     struct TargetedIDBuilder : public virtual IAttributeFactory
111     {
112         TargetedIDBuilder(const DOMElement* e) {}
113         SAMLAttribute* build(DOMElement* e) const {
114             return new TargetedID(e);
115         }
116     };
117 }
118
119 IPlugIn* TargetedIDFactory(const DOMElement* e)
120 {
121     return new TargetedIDBuilder(e);
122 }
123
124 TargetedID::TargetedID(
125     const XMLCh* name,
126     const XMLCh* ns,
127     const saml::QName* type,
128     long lifetime,
129     const Iterator<const XMLCh*>& values,
130     const Iterator<const XMLCh*>& nameQualifiers,
131     const Iterator<const XMLCh*>& spNameQualifiers
132     ) : SAMLAttribute(name,ns,NULL,lifetime,values)
133 {
134     RTTI(TargetedID);
135     if (values.size()!=nameQualifiers.size() || values.size()!=spNameQualifiers.size())
136         throw MalformedException("TargetedID() requires the number of qualifiers to equal the number of values");
137
138     while (nameQualifiers.hasNext())
139         m_nameQualifiers.push_back(saml::XML::assign(nameQualifiers.next()));
140     while (spNameQualifiers.hasNext())
141         m_spNameQualifiers.push_back(saml::XML::assign(spNameQualifiers.next()));
142 }
143
144 TargetedID::TargetedID(DOMElement* e) : SAMLAttribute(e,false)
145 {
146     RTTI(TargetedID);
147     fromDOM(e);
148 }
149
150 TargetedID::TargetedID(istream& in) : SAMLAttribute(in,false)
151 {
152     RTTI(TargetedID);
153     fromDOM(m_document->getDocumentElement());
154 }
155
156 TargetedID::~TargetedID()
157 {
158     if (m_bOwnStrings) {
159         for (vector<const XMLCh*>::iterator i=m_nameQualifiers.begin(); i!=m_nameQualifiers.end(); i++) {
160             XMLCh* p = const_cast<XMLCh*>(*i);
161             XMLString::release(&p);
162         }
163         for (vector<const XMLCh*>::iterator j=m_spNameQualifiers.begin(); j!=m_spNameQualifiers.end(); j++) {
164             XMLCh* p = const_cast<XMLCh*>(*j);
165             XMLString::release(&p);
166         }
167     }
168
169     // We always own any encoded values we've built.
170     for (vector<const XMLCh*>::iterator i=m_encodedValues.begin(); i!=m_encodedValues.end(); i++) {
171         XMLCh* p = const_cast<XMLCh*>(*i);
172         XMLString::release(&p);
173     }
174 }
175
176 void TargetedID::ownStrings()
177 {
178     if (!m_bOwnStrings) {
179         for (vector<const XMLCh*>::iterator i=m_nameQualifiers.begin(); i!=m_nameQualifiers.end(); i++)
180             (*i)=saml::XML::assign(*i);
181         for (vector<const XMLCh*>::iterator j=m_spNameQualifiers.begin(); j!=m_spNameQualifiers.end(); j++)
182             (*j)=saml::XML::assign(*j);
183         SAMLAttribute::ownStrings();
184     }
185 }
186
187 Iterator<const XMLCh*> TargetedID::getValues() const
188 {
189     if (m_encodedValues.empty()) {
190         getSingleByteValues();
191         for (vector<string>::const_iterator i=m_sbValues.begin(); i!=m_sbValues.end(); i++)
192             m_encodedValues.push_back(XMLString::transcode(i->c_str()));
193     }
194     return m_encodedValues;
195 }
196
197 Iterator<string> TargetedID::getSingleByteValues() const
198 {
199     if (m_sbValues.empty()) {
200         for (unsigned long i=0; i<m_values.size(); i++) {
201             auto_ptr_char a(m_nameQualifiers[i]);
202             auto_ptr_char b(m_spNameQualifiers[i]);
203             auto_ptr_char c(m_values[i]);
204             if (a.get() && *(a.get()) && b.get() && *(b.get()) && c.get() && *(c.get())) {
205                 string cat(a.get()); cat+="!!"; cat+=b.get(); cat+="!!"; cat+=c.get();
206                 unsigned int outlen;
207                 XMLByte* encoded = Base64::encode(reinterpret_cast<XMLByte*>((char*)cat.c_str()), cat.length(), &outlen);
208                 XMLByte *pos, *pos2;
209                 for (pos=encoded, pos2=encoded; *pos2; pos2++)
210                     if (isgraph(*pos2))
211                         *pos++=*pos2;
212                 *pos=0;
213                 m_sbValues.push_back(reinterpret_cast<char*>(encoded));
214                 XMLString::release(&encoded);
215             }
216             else
217                 m_sbValues.push_back("");
218         }
219     }
220     return m_sbValues;
221 }
222
223 void TargetedID::removeValue(unsigned int index)
224 {
225     if (m_bOwnStrings) {
226         XMLCh* p=const_cast<XMLCh*>(m_nameQualifiers[index]);
227         XMLString::release(&p);
228         p=const_cast<XMLCh*>(m_spNameQualifiers[index]);
229         XMLString::release(&p);
230     }
231     m_nameQualifiers.erase(m_nameQualifiers.begin()+index);
232     m_spNameQualifiers.erase(m_spNameQualifiers.begin()+index);
233
234     if (!m_encodedValues.empty()) {
235         XMLCh* p=const_cast<XMLCh*>(m_encodedValues[index]);
236         XMLString::release(&p);
237         m_encodedValues.erase(m_encodedValues.begin()+index);
238     }
239     
240     SAMLAttribute::removeValue(index);
241 }
242
243 void TargetedID::valueFromDOM(DOMElement* e)
244 {
245     // Look for a SAML2 NameID.
246     e=saml::XML::getFirstChildElement(e,::XML::SAML2ASSERT_NS,NameID);
247     if (e && !XMLString::compareString(FORMAT_PERSISTENT,e->getAttributeNS(NULL,L(Format)))) {
248         m_nameQualifiers.push_back(e->getAttributeNS(NULL,L(NameQualifier)));
249         m_spNameQualifiers.push_back(e->getAttributeNS(NULL,SPNameQualifier));
250         if (e->hasChildNodes() && e->getFirstChild()->getNodeType()==DOMNode::TEXT_NODE)
251             m_values.push_back(e->getFirstChild()->getNodeValue());
252         else
253             m_values.push_back(&chNull);
254         return;
255     }
256
257     // Insert a null value placeholder.
258     m_nameQualifiers.push_back(&chNull);    
259     m_spNameQualifiers.push_back(&chNull);    
260     m_values.push_back(&chNull);
261 }
262
263 void TargetedID::valueToDOM(unsigned int index, DOMElement* e) const
264 {
265     const XMLCh* nq=m_nameQualifiers[index];
266     const XMLCh* spnq=m_spNameQualifiers[index];
267     const XMLCh* val=m_values[index];
268     if (!saml::XML::isEmpty(nq) && !saml::XML::isEmpty(spnq) && !saml::XML::isEmpty(val)) {
269         // Build a SAML2 NameID.
270         DOMElement* nameid=e->getOwnerDocument()->createElementNS(::XML::SAML2ASSERT_NS,NameID);
271         nameid->setAttributeNS(NULL,L(Format),FORMAT_PERSISTENT);    
272         nameid->setAttributeNS(NULL,L(NameQualifier),nq);
273         nameid->setAttributeNS(NULL,SPNameQualifier,spnq);
274         nameid->appendChild(e->getOwnerDocument()->createTextNode(val));
275         e->appendChild(nameid);
276     }
277 }
278
279 SAMLObject* TargetedID::clone() const
280 {
281     return new TargetedID(m_name,m_namespace,m_type,m_lifetime,m_values,m_nameQualifiers,m_spNameQualifiers);
282 }
283
284 const XMLCh TargetedID::NameID[] =
285 { chLatin_N, chLatin_a, chLatin_m, chLatin_e, chLatin_I, chLatin_D, chNull };
286
287 const XMLCh TargetedID::SPNameQualifier[] =
288 { chLatin_S, chLatin_P, chLatin_N, chLatin_a, chLatin_m, chLatin_e,
289   chLatin_Q, chLatin_u, chLatin_a, chLatin_l, chLatin_i, chLatin_f, chLatin_i, chLatin_e, chLatin_r, chNull
290 };
291
292 const XMLCh TargetedID::FORMAT_PERSISTENT[] =
293 {
294     chLatin_u, chLatin_r, chLatin_n, chColon, chLatin_o, chLatin_a, chLatin_s, chLatin_i, chLatin_s, chColon,
295     chLatin_n, chLatin_a, chLatin_m, chLatin_e, chLatin_s, chColon, chLatin_t, chLatin_c, chColon,
296     chLatin_S, chLatin_A, chLatin_M, chLatin_L, chColon, chDigit_2, chPeriod, chDigit_0, chColon,
297     chLatin_n, chLatin_a, chLatin_m, chLatin_e, chLatin_i, chLatin_d, chDash,
298     chLatin_f, chLatin_o, chLatin_r, chLatin_m, chLatin_a, chLatin_t, chColon,
299     chLatin_p, chLatin_e, chLatin_r, chLatin_s, chLatin_i, chLatin_s, chLatin_t, chLatin_e, chLatin_n, chLatin_t, chNull
300 };