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