72039b340bb98b8e2fc495326028387f5cecefab
[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<InformationURL*>& infurls = info->getInformationURLs();
167                             if (!infurls.empty()) {
168                                 s += ",\n \"InformationURLs\": [";
169                                 for (vector<InformationURL*>::const_iterator infurl = infurls.begin(); infurl != infurls.end(); ++infurl) {
170                                     if (infurl != infurls.begin())
171                                         s += ',';
172                                     auto_ptr_char val((*infurl)->getURL());
173                                     auto_ptr_char lang((*infurl)->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<PrivacyStatementURL*>& privs = info->getPrivacyStatementURLs();
184                             if (!privs.empty()) {
185                                 s += ",\n \"PrivacyStatementURLs\": [";
186                                 for (vector<PrivacyStatementURL*>::const_iterator priv = privs.begin(); priv != privs.end(); ++priv) {
187                                     if (priv != privs.begin())
188                                         s += ',';
189                                     auto_ptr_char val((*priv)->getURL());
190                                     auto_ptr_char lang((*priv)->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<Logo*>& logos = info->getLogos();
201                             if (!logos.empty()) {
202                                 s += ",\n \"Logos\": [";
203                                 for (vector<Logo*>::const_iterator logo = logos.begin(); logo != logos.end(); ++logo) {
204                                     if (logo != logos.begin())
205                                         s += ',';
206                                     s += "\n  {\n";
207                                     auto_ptr_char val((*logo)->getURL());
208                                     s += "  \"value\": \"";
209                                     json_safe(s, val.get());
210                                     ostringstream ht;
211                                     ht << (*logo)->getHeight().second;
212                                     s += "\",\n  \"height\": \"";
213                                     s += ht.str();
214                                     ostringstream wt;
215                                     wt << (*logo)->getWidth().second;
216                                     s += "\",\n  \"width\": \"";
217                                     s += wt.str();
218                                     s += '\"';
219                                     if ((*logo)->getLang()) {
220                                         auto_ptr_char lang((*logo)->getLang());
221                                         s += ",\n  \"lang\": \"";
222                                         s += lang.get();
223                                         s += '\"';
224                                     }
225                                     s += "\n  }";
226                                 }
227                                 s += "\n ]";
228                             }
229                         }
230                     }
231                 }
232             }
233
234             if (m_legacyOrgNames && !extFound) {
235                 const Organization* org = nullptr;
236                 for (vector<IDPSSODescriptor*>::const_iterator idp = idps.begin(); !org && idp != idps.end(); ++idp) {
237                     if ((*idp)->isValid(now))
238                         org = (*idp)->getOrganization();
239                 }
240                 if (!org)
241                     org = entity->getOrganization();
242                 if (org) {
243                     const vector<OrganizationDisplayName*>& odns = org->getOrganizationDisplayNames();
244                     if (!odns.empty()) {
245                         s += ",\n \"DisplayNames\": [";
246                         for (vector<OrganizationDisplayName*>::const_iterator dispname = odns.begin(); dispname != odns.end(); ++dispname) {
247                             if (dispname != odns.begin())
248                                 s += ',';
249                             auto_arrayptr<char> val(toUTF8((*dispname)->getName()));
250                             auto_ptr_char lang((*dispname)->getLang());
251                             s += "\n  {\n  \"value\": \"";
252                             json_safe(s, val.get());
253                             s += "\",\n  \"lang\": \"";
254                             s += lang.get();
255                             s += "\"\n  }";
256                         }
257                         s += "\n ]";
258                     }
259                 }
260             }
261
262             // Close the struct;
263             s += "\n}";
264         }
265     }
266 }
267
268 void DiscoverableMetadataProvider::disco(string& s, const EntitiesDescriptor* group, bool& first) const
269 {
270     if (group) {
271         const vector<EntitiesDescriptor*>& groups = group->getEntitiesDescriptors();
272         for (vector<EntitiesDescriptor*>::const_iterator i = groups.begin(); i != groups.end(); ++i)
273             disco(s, *i, first);
274
275         const vector<EntityDescriptor*>& sites = group->getEntityDescriptors();
276         for (vector<EntityDescriptor*>::const_iterator j = sites.begin(); j != sites.end(); ++j)
277             disco(s, *j, first);
278     }
279 }