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