ce5950b7c50bf00d98850b91685055ef917b5eb8
[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 <xmltooling/logging.h>
35 #include <xmltooling/XMLToolingConfig.h>
36
37 using namespace opensaml::saml2md;
38 using namespace xmltooling;
39 using namespace std;
40
41 DiscoverableMetadataProvider::DiscoverableMetadataProvider(const DOMElement* e) : MetadataProvider(e), m_legacyOrgNames(false)
42 {
43     static const XMLCh legacyOrgNames[] = UNICODE_LITERAL_14(l,e,g,a,c,y,O,r,g,N,a,m,e,s);
44     m_legacyOrgNames = XMLHelper::getAttrBool(e, false, legacyOrgNames);
45 }
46
47 DiscoverableMetadataProvider::~DiscoverableMetadataProvider()
48 {
49 }
50
51 void DiscoverableMetadataProvider::generateFeed()
52 {
53     m_feed.erase();
54     bool first = true;
55     const XMLObject* object = getMetadata();
56     disco(m_feed, dynamic_cast<const EntitiesDescriptor*>(object), first);
57     disco(m_feed, dynamic_cast<const EntityDescriptor*>(object), first);
58
59     SAMLConfig::getConfig().generateRandomBytes(m_feedTag, 4);
60     m_feedTag = SAMLArtifact::toHex(m_feedTag);
61 }
62
63 string DiscoverableMetadataProvider::getCacheTag() const
64 {
65     return m_feedTag;
66 }
67
68 void DiscoverableMetadataProvider::outputFeed(ostream& os, bool& first, bool wrapArray) const
69 {
70     if (wrapArray)
71         os << '[';
72     if (!m_feed.empty()) {
73         if (first)
74             first = false;
75         else
76             os << ",\n";
77         os << m_feed;
78     }
79     if (wrapArray)
80         os << "\n]";
81 }
82
83 static string& json_safe(string& s, const char* buf)
84 {
85     for (; *buf; ++buf) {
86         switch (*buf) {
87             case '\\':
88             case '"':
89                 s += '\\';
90                 s += *buf;
91                 break;
92             case '\b':
93                 s += "\\b";
94                 break;
95             case '\t':
96                 s += "\\t";
97                 break;
98             case '\n':
99                 s += "\\n";
100                 break;
101             case '\f':
102                 s += "\\f";
103                 break;
104             case '\r':
105                 s += "\\r";
106                 break;
107             default:
108                 s += *buf;
109         }
110     }
111     return s;
112 }
113
114 void DiscoverableMetadataProvider::disco(string& s, const EntityDescriptor* entity, bool& first) const
115 {
116     time_t now = time(nullptr);
117     if (entity && entity->isValid(now)) {
118         const vector<IDPSSODescriptor*>& idps = entity->getIDPSSODescriptors();
119         if (!idps.empty()) {
120             auto_ptr_char entityid(entity->getEntityID());
121             // Open a struct and output id: entityID.
122             if (first)
123                 first = false;
124             else
125                 s += ',';
126             s += "\n{\n \"entityID\": \"";
127             json_safe(s, entityid.get());
128             s += '\"';
129             bool extFound = false;
130             for (vector<IDPSSODescriptor*>::const_iterator idp = idps.begin(); !extFound && idp != idps.end(); ++idp) {
131                 if ((*idp)->isValid(now) && (*idp)->getExtensions()) {
132                     const vector<XMLObject*>& exts =  const_cast<const Extensions*>((*idp)->getExtensions())->getUnknownXMLObjects();
133                     for (vector<XMLObject*>::const_iterator ext = exts.begin(); !extFound && ext != exts.end(); ++ext) {
134                         const UIInfo* info = dynamic_cast<UIInfo*>(*ext);
135                         if (info) {
136                             extFound = true;
137                             const vector<DisplayName*>& dispnames = info->getDisplayNames();
138                             if (!dispnames.empty()) {
139                                 s += ",\n \"DisplayNames\": [";
140                                 for (vector<DisplayName*>::const_iterator dispname = dispnames.begin(); dispname != dispnames.end(); ++dispname) {
141                                     if (dispname != dispnames.begin())
142                                         s += ',';
143                                     auto_arrayptr<char> val(toUTF8((*dispname)->getName()));
144                                     auto_ptr_char lang((*dispname)->getLang());
145                                     s += "\n  {\n  \"value\": \"";
146                                     json_safe(s, val.get());
147                                     s += "\",\n  \"lang\": \"";
148                                     s += lang.get();
149                                     s += "\"\n  }";
150                                 }
151                                 s += "\n ]";
152                             }
153
154                             const vector<Description*>& descs = info->getDescriptions();
155                             if (!descs.empty()) {
156                                 s += ",\n \"Descriptions\": [";
157                                 for (vector<Description*>::const_iterator desc = descs.begin(); desc != descs.end(); ++desc) {
158                                     if (desc != descs.begin())
159                                         s += ',';
160                                     auto_arrayptr<char> val(toUTF8((*desc)->getDescription()));
161                                     auto_ptr_char lang((*desc)->getLang());
162                                     s += "\n  {\n  \"value\": \"";
163                                     json_safe(s, val.get());
164                                     s += "\",\n  \"lang\": \"";
165                                     s += lang.get();
166                                     s += "\"\n  }";
167                                 }
168                                 s += "\n ]";
169                             }
170
171                             const vector<Keywords*>& keywords = info->getKeywordss();
172                             if (!keywords.empty()) {
173                                 s += ",\n \"Keywords\": [";
174                                 for (vector<Keywords*>::const_iterator words = keywords.begin(); words != keywords.end(); ++words) {
175                                     if (words != keywords.begin())
176                                         s += ',';
177                                     auto_arrayptr<char> val(toUTF8((*words)->getValues()));
178                                     auto_ptr_char lang((*words)->getLang());
179                                     s += "\n  {\n  \"value\": \"";
180                                     json_safe(s, val.get());
181                                     s += "\",\n  \"lang\": \"";
182                                     s += lang.get();
183                                     s += "\"\n  }";
184                                 }
185                                 s += "\n ]";
186                             }
187
188                             const vector<InformationURL*>& infurls = info->getInformationURLs();
189                             if (!infurls.empty()) {
190                                 s += ",\n \"InformationURLs\": [";
191                                 for (vector<InformationURL*>::const_iterator infurl = infurls.begin(); infurl != infurls.end(); ++infurl) {
192                                     if (infurl != infurls.begin())
193                                         s += ',';
194                                     auto_ptr_char val((*infurl)->getURL());
195                                     auto_ptr_char lang((*infurl)->getLang());
196                                     s += "\n  {\n  \"value\": \"";
197                                     json_safe(s, val.get());
198                                     s += "\",\n  \"lang\": \"";
199                                     s += lang.get();
200                                     s += "\"\n  }";
201                                 }
202                                 s += "\n ]";
203                             }
204
205                             const vector<PrivacyStatementURL*>& privs = info->getPrivacyStatementURLs();
206                             if (!privs.empty()) {
207                                 s += ",\n \"PrivacyStatementURLs\": [";
208                                 for (vector<PrivacyStatementURL*>::const_iterator priv = privs.begin(); priv != privs.end(); ++priv) {
209                                     if (priv != privs.begin())
210                                         s += ',';
211                                     auto_ptr_char val((*priv)->getURL());
212                                     auto_ptr_char lang((*priv)->getLang());
213                                     s += "\n  {\n  \"value\": \"";
214                                     json_safe(s, val.get());
215                                     s += "\",\n  \"lang\": \"";
216                                     s += lang.get();
217                                     s += "\"\n  }";
218                                 }
219                                 s += "\n ]";
220                             }
221
222                             const vector<Logo*>& logos = info->getLogos();
223                             if (!logos.empty()) {
224                                 s += ",\n \"Logos\": [";
225                                 for (vector<Logo*>::const_iterator logo = logos.begin(); logo != logos.end(); ++logo) {
226                                     if (logo != logos.begin())
227                                         s += ',';
228                                     s += "\n  {\n";
229                                     auto_ptr_char val((*logo)->getURL());
230                                     s += "  \"value\": \"";
231                                     json_safe(s, val.get());
232                                     ostringstream ht;
233                                     ht << (*logo)->getHeight().second;
234                                     s += "\",\n  \"height\": \"";
235                                     s += ht.str();
236                                     ostringstream wt;
237                                     wt << (*logo)->getWidth().second;
238                                     s += "\",\n  \"width\": \"";
239                                     s += wt.str();
240                                     s += '\"';
241                                     if ((*logo)->getLang()) {
242                                         auto_ptr_char lang((*logo)->getLang());
243                                         s += ",\n  \"lang\": \"";
244                                         s += lang.get();
245                                         s += '\"';
246                                     }
247                                     s += "\n  }";
248                                 }
249                                 s += "\n ]";
250                             }
251                         }
252                     }
253                 }
254             }
255
256             if (m_legacyOrgNames && !extFound) {
257                 const Organization* org = nullptr;
258                 for (vector<IDPSSODescriptor*>::const_iterator idp = idps.begin(); !org && idp != idps.end(); ++idp) {
259                     if ((*idp)->isValid(now))
260                         org = (*idp)->getOrganization();
261                 }
262                 if (!org)
263                     org = entity->getOrganization();
264                 if (org) {
265                     const vector<OrganizationDisplayName*>& odns = org->getOrganizationDisplayNames();
266                     if (!odns.empty()) {
267                         s += ",\n \"DisplayNames\": [";
268                         for (vector<OrganizationDisplayName*>::const_iterator dispname = odns.begin(); dispname != odns.end(); ++dispname) {
269                             if (dispname != odns.begin())
270                                 s += ',';
271                             auto_arrayptr<char> val(toUTF8((*dispname)->getName()));
272                             auto_ptr_char lang((*dispname)->getLang());
273                             s += "\n  {\n  \"value\": \"";
274                             json_safe(s, val.get());
275                             s += "\",\n  \"lang\": \"";
276                             s += lang.get();
277                             s += "\"\n  }";
278                         }
279                         s += "\n ]";
280                     }
281                 }
282             }
283
284             // Close the struct;
285             s += "\n}";
286         }
287     }
288 }
289
290 void DiscoverableMetadataProvider::disco(string& s, const EntitiesDescriptor* group, bool& first) const
291 {
292     if (group) {
293         const vector<EntitiesDescriptor*>& groups = group->getEntitiesDescriptors();
294         for (vector<EntitiesDescriptor*>::const_iterator i = groups.begin(); i != groups.end(); ++i)
295             disco(s, *i, first);
296
297         const vector<EntityDescriptor*>& sites = group->getEntityDescriptors();
298         for (vector<EntityDescriptor*>::const_iterator j = sites.begin(); j != sites.end(); ++j)
299             disco(s, *j, first);
300     }
301 }