Corrected removeValue signature.
[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 long 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                 m_sbValues.push_back(cat);
207             }
208             else
209                 m_sbValues.push_back("");
210         }
211     }
212     return m_sbValues;
213 }
214
215 void TargetedID::removeValue(unsigned long index)
216 {
217     if (m_bOwnStrings) {
218         XMLCh* p=const_cast<XMLCh*>(m_nameQualifiers[index]);
219         XMLString::release(&p);
220         p=const_cast<XMLCh*>(m_spNameQualifiers[index]);
221         XMLString::release(&p);
222     }
223     m_nameQualifiers.erase(m_nameQualifiers.begin()+index);
224     m_spNameQualifiers.erase(m_spNameQualifiers.begin()+index);
225
226     if (!m_encodedValues.empty()) {
227         XMLCh* p=const_cast<XMLCh*>(m_encodedValues[index]);
228         XMLString::release(&p);
229         m_encodedValues.erase(m_encodedValues.begin()+index);
230     }
231     
232     SAMLAttribute::removeValue(index);
233 }
234
235 void TargetedID::valueFromDOM(DOMElement* e)
236 {
237     // Look for a SAML2 NameID.
238     e=saml::XML::getFirstChildElement(e,::XML::SAML2ASSERT_NS,NameID);
239     if (e && !XMLString::compareString(FORMAT_PERSISTENT,e->getAttributeNS(NULL,L(Format)))) {
240         m_nameQualifiers.push_back(e->getAttributeNS(NULL,L(NameQualifier)));
241         m_spNameQualifiers.push_back(e->getAttributeNS(NULL,SPNameQualifier));
242         if (e->hasChildNodes() && e->getFirstChild()->getNodeType()==DOMNode::TEXT_NODE)
243             m_values.push_back(e->getFirstChild()->getNodeValue());
244         else
245             m_values.push_back(&chNull);
246         return;
247     }
248
249     // Insert a null value placeholder.
250     m_nameQualifiers.push_back(&chNull);    
251     m_spNameQualifiers.push_back(&chNull);    
252     m_values.push_back(&chNull);
253 }
254
255 void TargetedID::valueToDOM(unsigned int index, DOMElement* e) const
256 {
257     const XMLCh* nq=m_nameQualifiers[index];
258     const XMLCh* spnq=m_spNameQualifiers[index];
259     const XMLCh* val=m_values[index];
260     if (!saml::XML::isEmpty(nq) && !saml::XML::isEmpty(spnq) && !saml::XML::isEmpty(val)) {
261         // Build a SAML2 NameID.
262         DOMElement* nameid=e->getOwnerDocument()->createElementNS(::XML::SAML2ASSERT_NS,NameID);
263         nameid->setAttributeNS(NULL,L(Format),FORMAT_PERSISTENT);    
264         nameid->setAttributeNS(NULL,L(NameQualifier),nq);
265         nameid->setAttributeNS(NULL,SPNameQualifier,spnq);
266         nameid->appendChild(e->getOwnerDocument()->createTextNode(val));
267         e->appendChild(nameid);
268     }
269 }
270
271 SAMLObject* TargetedID::clone() const
272 {
273     return new TargetedID(m_name,m_namespace,m_type,m_lifetime,m_values,m_nameQualifiers,m_spNameQualifiers);
274 }
275
276 const XMLCh TargetedID::NameID[] =
277 { chLatin_N, chLatin_a, chLatin_m, chLatin_e, chLatin_I, chLatin_D, chNull };
278
279 const XMLCh TargetedID::SPNameQualifier[] =
280 { chLatin_S, chLatin_P, chLatin_N, chLatin_a, chLatin_m, chLatin_e,
281   chLatin_Q, chLatin_u, chLatin_a, chLatin_l, chLatin_i, chLatin_f, chLatin_i, chLatin_e, chLatin_r, chNull
282 };
283
284 const XMLCh TargetedID::FORMAT_PERSISTENT[] =
285 {
286     chLatin_u, chLatin_r, chLatin_n, chColon, chLatin_o, chLatin_a, chLatin_s, chLatin_i, chLatin_s, chColon,
287     chLatin_n, chLatin_a, chLatin_m, chLatin_e, chLatin_s, chColon, chLatin_t, chLatin_c, chColon,
288     chLatin_S, chLatin_A, chLatin_M, chLatin_L, chColon, chDigit_2, chPeriod, chDigit_0, chColon,
289     chLatin_n, chLatin_a, chLatin_m, chLatin_e, chLatin_i, chLatin_d, chDash,
290     chLatin_f, chLatin_o, chLatin_r, chLatin_m, chLatin_a, chLatin_t, chColon,
291     chLatin_p, chLatin_e, chLatin_r, chLatin_s, chLatin_i, chLatin_s, chLatin_t, chLatin_e, chLatin_n, chLatin_t, chNull
292 };