Boost changes
[shibboleth/cpp-opensaml.git] / saml / saml2 / metadata / impl / DiscoverableMetadataProvider.cpp
1 /**
2  * Licensed to the University Corporation for Advanced Internet
3  * Development, Inc. (UCAID) under one or more contributor license
4  * agreements. See the NOTICE file distributed with this work for
5  * additional information regarding copyright ownership.
6  *
7  * UCAID licenses this file to you under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17  * either express or implied. See the License for the specific
18  * language governing permissions and limitations under the License.
19  */
20
21 /**
22  * DiscoverableMetadataProvider.cpp
23  *
24  * A metadata provider that provides a JSON feed of IdP discovery information.
25  */
26
27 #include "internal.h"
28 #include "binding/SAMLArtifact.h"
29 #include "saml2/metadata/Metadata.h"
30 #include "saml2/metadata/DiscoverableMetadataProvider.h"
31
32 #include <fstream>
33 #include <sstream>
34 #include <boost/bind.hpp>
35 #include <boost/iterator/indirect_iterator.hpp>
36 #include <xmltooling/logging.h>
37 #include <xmltooling/XMLToolingConfig.h>
38
39 using namespace opensaml::saml2md;
40 using namespace xmltooling;
41 using namespace boost;
42 using namespace std;
43
44 DiscoverableMetadataProvider::DiscoverableMetadataProvider(const DOMElement* e) : MetadataProvider(e), m_legacyOrgNames(false)
45 {
46     static const XMLCh legacyOrgNames[] = UNICODE_LITERAL_14(l,e,g,a,c,y,O,r,g,N,a,m,e,s);
47     m_legacyOrgNames = XMLHelper::getAttrBool(e, false, legacyOrgNames);
48 }
49
50 DiscoverableMetadataProvider::~DiscoverableMetadataProvider()
51 {
52 }
53
54 void DiscoverableMetadataProvider::generateFeed()
55 {
56     m_feed.erase();
57     bool first = true;
58     const XMLObject* object = getMetadata();
59     discoGroup(m_feed, dynamic_cast<const EntitiesDescriptor*>(object), first);
60     discoEntity(m_feed, dynamic_cast<const EntityDescriptor*>(object), first);
61
62     SAMLConfig::getConfig().generateRandomBytes(m_feedTag, 4);
63     m_feedTag = SAMLArtifact::toHex(m_feedTag);
64 }
65
66 string DiscoverableMetadataProvider::getCacheTag() const
67 {
68     return m_feedTag;
69 }
70
71 void DiscoverableMetadataProvider::outputFeed(ostream& os, bool& first, bool wrapArray) const
72 {
73     if (wrapArray)
74         os << '[';
75     if (!m_feed.empty()) {
76         if (first)
77             first = false;
78         else
79             os << ",\n";
80         os << m_feed;
81     }
82     if (wrapArray)
83         os << "\n]";
84 }
85
86 static string& json_safe(string& s, const char* buf)
87 {
88     for (; *buf; ++buf) {
89         switch (*buf) {
90             case '\\':
91             case '"':
92                 s += '\\';
93                 s += *buf;
94                 break;
95             case '\b':
96                 s += "\\b";
97                 break;
98             case '\t':
99                 s += "\\t";
100                 break;
101             case '\n':
102                 s += "\\n";
103                 break;
104             case '\f':
105                 s += "\\f";
106                 break;
107             case '\r':
108                 s += "\\r";
109                 break;
110             default:
111                 s += *buf;
112         }
113     }
114     return s;
115 }
116
117 void DiscoverableMetadataProvider::discoEntity(string& s, const EntityDescriptor* entity, bool& first) const
118 {
119     time_t now = time(nullptr);
120     if (entity && entity->isValid(now)) {
121         const vector<IDPSSODescriptor*>& idps = entity->getIDPSSODescriptors();
122         if (!idps.empty()) {
123             auto_ptr_char entityid(entity->getEntityID());
124             // Open a struct and output id: entityID.
125             if (first)
126                 first = false;
127             else
128                 s += ',';
129             s += "\n{\n \"entityID\": \"";
130             json_safe(s, entityid.get());
131             s += '\"';
132             bool extFound = false;
133             for (indirect_iterator<vector<IDPSSODescriptor*>::const_iterator> idp = make_indirect_iterator(idps.begin());
134                     !extFound && idp != make_indirect_iterator(idps.end()); ++idp) {
135                 if (idp->isValid(now) && idp->getExtensions()) {
136                     const vector<XMLObject*>& exts =  const_cast<const Extensions*>(idp->getExtensions())->getUnknownXMLObjects();
137                     for (vector<XMLObject*>::const_iterator ext = exts.begin(); !extFound && ext != exts.end(); ++ext) {
138                         const UIInfo* info = dynamic_cast<UIInfo*>(*ext);
139                         if (info) {
140                             extFound = true;
141                             const vector<DisplayName*>& dispnames = info->getDisplayNames();
142                             if (!dispnames.empty()) {
143                                 s += ",\n \"DisplayNames\": [";
144                                 for (indirect_iterator<vector<DisplayName*>::const_iterator> dispname = make_indirect_iterator(dispnames.begin());
145                                         dispname != make_indirect_iterator(dispnames.end()); ++dispname) {
146                                     if (dispname.base() != dispnames.begin())
147                                         s += ',';
148                                     auto_arrayptr<char> val(toUTF8(dispname->getName()));
149                                     auto_ptr_char lang(dispname->getLang());
150                                     s += "\n  {\n  \"value\": \"";
151                                     json_safe(s, val.get());
152                                     s += "\",\n  \"lang\": \"";
153                                     s += lang.get();
154                                     s += "\"\n  }";
155                                 }
156                                 s += "\n ]";
157                             }
158
159                             const vector<Description*>& descs = info->getDescriptions();
160                             if (!descs.empty()) {
161                                 s += ",\n \"Descriptions\": [";
162                                 for (indirect_iterator<vector<Description*>::const_iterator> desc = make_indirect_iterator(descs.begin());
163                                         desc != make_indirect_iterator(descs.end()); ++desc) {
164                                     if (desc.base() != descs.begin())
165                                         s += ',';
166                                     auto_arrayptr<char> val(toUTF8(desc->getDescription()));
167                                     auto_ptr_char lang(desc->getLang());
168                                     s += "\n  {\n  \"value\": \"";
169                                     json_safe(s, val.get());
170                                     s += "\",\n  \"lang\": \"";
171                                     s += lang.get();
172                                     s += "\"\n  }";
173                                 }
174                                 s += "\n ]";
175                             }
176
177                             const vector<Keywords*>& keywords = info->getKeywordss();
178                             if (!keywords.empty()) {
179                                 s += ",\n \"Keywords\": [";
180                                 for (indirect_iterator<vector<Keywords*>::const_iterator> words = make_indirect_iterator(keywords.begin());
181                                         words != make_indirect_iterator(keywords.end()); ++words) {
182                                     if (words.base() != keywords.begin())
183                                         s += ',';
184                                     auto_arrayptr<char> val(toUTF8(words->getValues()));
185                                     auto_ptr_char lang(words->getLang());
186                                     s += "\n  {\n  \"value\": \"";
187                                     json_safe(s, val.get());
188                                     s += "\",\n  \"lang\": \"";
189                                     s += lang.get();
190                                     s += "\"\n  }";
191                                 }
192                                 s += "\n ]";
193                             }
194
195                             const vector<InformationURL*>& infurls = info->getInformationURLs();
196                             if (!infurls.empty()) {
197                                 s += ",\n \"InformationURLs\": [";
198                                 for (indirect_iterator<vector<InformationURL*>::const_iterator> infurl = make_indirect_iterator(infurls.begin());
199                                         infurl != make_indirect_iterator(infurls.end()); ++infurl) {
200                                     if (infurl.base() != infurls.begin())
201                                         s += ',';
202                                     auto_ptr_char val(infurl->getURL());
203                                     auto_ptr_char lang(infurl->getLang());
204                                     s += "\n  {\n  \"value\": \"";
205                                     json_safe(s, val.get());
206                                     s += "\",\n  \"lang\": \"";
207                                     s += lang.get();
208                                     s += "\"\n  }";
209                                 }
210                                 s += "\n ]";
211                             }
212
213                             const vector<PrivacyStatementURL*>& privs = info->getPrivacyStatementURLs();
214                             if (!privs.empty()) {
215                                 s += ",\n \"PrivacyStatementURLs\": [";
216                                 for (indirect_iterator<vector<PrivacyStatementURL*>::const_iterator> priv = make_indirect_iterator(privs.begin());
217                                         priv != make_indirect_iterator(privs.end()); ++priv) {
218                                     if (priv.base() != privs.begin())
219                                         s += ',';
220                                     auto_ptr_char val(priv->getURL());
221                                     auto_ptr_char lang(priv->getLang());
222                                     s += "\n  {\n  \"value\": \"";
223                                     json_safe(s, val.get());
224                                     s += "\",\n  \"lang\": \"";
225                                     s += lang.get();
226                                     s += "\"\n  }";
227                                 }
228                                 s += "\n ]";
229                             }
230
231                             const vector<Logo*>& logos = info->getLogos();
232                             if (!logos.empty()) {
233                                 s += ",\n \"Logos\": [";
234                                 for (indirect_iterator<vector<Logo*>::const_iterator> logo = make_indirect_iterator(logos.begin());
235                                         logo != make_indirect_iterator(logos.end()); ++logo) {
236                                     if (logo.base() != logos.begin())
237                                         s += ',';
238                                     s += "\n  {\n";
239                                     auto_ptr_char val(logo->getURL());
240                                     s += "  \"value\": \"";
241                                     json_safe(s, val.get());
242                                     ostringstream ht;
243                                     ht << logo->getHeight().second;
244                                     s += "\",\n  \"height\": \"";
245                                     s += ht.str();
246                                     ostringstream wt;
247                                     wt << logo->getWidth().second;
248                                     s += "\",\n  \"width\": \"";
249                                     s += wt.str();
250                                     s += '\"';
251                                     if (logo->getLang()) {
252                                         auto_ptr_char lang(logo->getLang());
253                                         s += ",\n  \"lang\": \"";
254                                         s += lang.get();
255                                         s += '\"';
256                                     }
257                                     s += "\n  }";
258                                 }
259                                 s += "\n ]";
260                             }
261                         }
262                     }
263                 }
264             }
265
266             if (m_legacyOrgNames && !extFound) {
267                 const Organization* org = nullptr;
268                 for (indirect_iterator<vector<IDPSSODescriptor*>::const_iterator> idp = make_indirect_iterator(idps.begin());
269                         !org && idp != make_indirect_iterator(idps.end()); ++idp) {
270                     if (idp->isValid(now))
271                         org = idp->getOrganization();
272                 }
273                 if (!org)
274                     org = entity->getOrganization();
275                 if (org) {
276                     const vector<OrganizationDisplayName*>& odns = org->getOrganizationDisplayNames();
277                     if (!odns.empty()) {
278                         s += ",\n \"DisplayNames\": [";
279                         for (indirect_iterator<vector<OrganizationDisplayName*>::const_iterator> dispname = make_indirect_iterator(odns.begin());
280                                 dispname != make_indirect_iterator(odns.end()); ++dispname) {
281                             if (dispname.base() != odns.begin())
282                                 s += ',';
283                             auto_arrayptr<char> val(toUTF8(dispname->getName()));
284                             auto_ptr_char lang(dispname->getLang());
285                             s += "\n  {\n  \"value\": \"";
286                             json_safe(s, val.get());
287                             s += "\",\n  \"lang\": \"";
288                             s += lang.get();
289                             s += "\"\n  }";
290                         }
291                         s += "\n ]";
292                     }
293                 }
294             }
295
296             // Close the struct;
297             s += "\n}";
298         }
299     }
300 }
301
302 void DiscoverableMetadataProvider::discoGroup(string& s, const EntitiesDescriptor* group, bool& first) const
303 {
304     if (group) {
305         for_each(
306             group->getEntitiesDescriptors().begin(), group->getEntitiesDescriptors().end(),
307             boost::bind(&DiscoverableMetadataProvider::discoGroup, boost::ref(this), boost::ref(s), _1, boost::ref(first))
308             );
309         for_each(
310             group->getEntityDescriptors().begin(), group->getEntityDescriptors().end(),
311             boost::bind(&DiscoverableMetadataProvider::discoEntity, boost::ref(this), boost::ref(s), _1, boost::ref(first))
312             );
313     }
314 }