[sql] Remove _HAS_EXCEPTIONS=0 from build info.
[chromium-blink-merge.git] / chrome / browser / resources / cryptotoken / appid.js
blobb5f8ed5fabaf53ecaccb613953f05960a8ae7364
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 /**
6  * @fileoverview Implements a check whether an app id lists an origin.
7  */
8 'use strict';
10 /**
11  * Parses the text as JSON and returns it as an array of strings.
12  * @param {string} text Input JSON
13  * @return {!Array<string>} Array of origins
14  */
15 function getOriginsFromJson(text) {
16   try {
17     var urls, i;
18     var appIdData = JSON.parse(text);
19     if (Array.isArray(appIdData)) {
20       // Older format where it is a simple list of facets
21       urls = appIdData;
22     } else {
23       var trustedFacets = appIdData['trustedFacets'];
24       if (trustedFacets) {
25         var versionBlock;
26         for (i = 0; versionBlock = trustedFacets[i]; i++) {
27           if (versionBlock['version'] &&
28               versionBlock['version']['major'] == 1 &&
29               versionBlock['version']['minor'] == 0) {
30             urls = versionBlock['ids'];
31             break;
32           }
33         }
34       }
35       if (typeof urls == 'undefined') {
36         throw Error('Could not find trustedFacets for version 1.0');
37       }
38     }
39     var origins = {};
40     var url;
41     for (i = 0; url = urls[i]; i++) {
42       var origin = getOriginFromUrl(url);
43       if (origin) {
44         origins[origin] = origin;
45       }
46     }
47     return Object.keys(origins);
48   } catch (e) {
49     console.error(UTIL_fmt('could not parse ' + text));
50     return [];
51   }
54 /**
55  * Retrieves a set of distinct app ids from the sign challenges.
56  * @param {Array<SignChallenge>=} signChallenges Input sign challenges.
57  * @return {Array<string>} array of distinct app ids.
58  */
59 function getDistinctAppIds(signChallenges) {
60   if (!signChallenges) {
61     return [];
62   }
63   var appIds = {};
64   for (var i = 0, request; request = signChallenges[i]; i++) {
65     var appId = request['appId'];
66     if (appId) {
67       appIds[appId] = appId;
68     }
69   }
70   return Object.keys(appIds);
73 /**
74  * Provides an object to track checking a list of appIds.
75  * @param {!TextFetcher} fetcher A URL fetcher.
76  * @param {!Countdown} timer A timer by which to resolve all provided app ids.
77  * @param {string} origin The origin to check.
78  * @param {!Array<string>} appIds The app ids to check.
79  * @param {boolean} allowHttp Whether to allow http:// URLs.
80  * @param {string=} opt_logMsgUrl A log message URL.
81  * @constructor
82  */
83 function AppIdChecker(fetcher, timer, origin, appIds, allowHttp, opt_logMsgUrl)
84     {
85   /** @private {!TextFetcher} */
86   this.fetcher_ = fetcher;
87   /** @private {!Countdown} */
88   this.timer_ = timer;
89   /** @private {string} */
90   this.origin_ = origin;
91   var appIdsMap = {};
92   if (appIds) {
93     for (var i = 0; i < appIds.length; i++) {
94       appIdsMap[appIds[i]] = appIds[i];
95     }
96   }
97   /** @private {Array<string>} */
98   this.distinctAppIds_ = Object.keys(appIdsMap);
99   /** @private {boolean} */
100   this.allowHttp_ = allowHttp;
101   /** @private {string|undefined} */
102   this.logMsgUrl_ = opt_logMsgUrl;
104   /** @private {boolean} */
105   this.closed_ = false;
106   /** @private {boolean} */
107   this.anyInvalidAppIds_ = false;
108   /** @private {number} */
109   this.fetchedAppIds_ = 0;
113  * Checks whether all the app ids provided can be asserted by the given origin.
114  * @return {Promise<boolean>} A promise for the result of the check
115  */
116 AppIdChecker.prototype.doCheck = function() {
117   if (!this.distinctAppIds_.length)
118     return Promise.resolve(false);
120   if (this.allAppIdsEqualOrigin_()) {
121     // Trivially allowed.
122     return Promise.resolve(true);
123   } else {
124     var self = this;
125     // Begin checking remaining app ids.
126     var appIdChecks = self.distinctAppIds_.map(self.checkAppId_.bind(self));
127     return Promise.all(appIdChecks).then(function(results) {
128       return results.every(function(result) {
129         if (!result)
130           self.anyInvalidAppIds_ = true;
131         return result;
132       });
133     });
134   }
138  * Checks if a single appId can be asserted by the given origin.
139  * @param {string} appId The appId to check
140  * @return {Promise<boolean>} A promise for the result of the check
141  * @private
142  */
143 AppIdChecker.prototype.checkAppId_ = function(appId) {
144   if (appId == this.origin_) {
145     // Trivially allowed
146     return Promise.resolve(true);
147   }
148   var p = this.fetchAllowedOriginsForAppId_(appId);
149   var self = this;
150   return p.then(function(allowedOrigins) {
151     if (allowedOrigins.indexOf(self.origin_) == -1) {
152       console.warn(UTIL_fmt('Origin ' + self.origin_ +
153             ' not allowed by app id ' + appId));
154       return false;
155     }
156     return true;
157   });
161  * Closes this checker. No callback will be called after this checker is closed.
162  */
163 AppIdChecker.prototype.close = function() {
164   this.closed_ = true;
168  * @return {boolean} Whether all the app ids being checked are equal to the
169  * calling origin.
170  * @private
171  */
172 AppIdChecker.prototype.allAppIdsEqualOrigin_ = function() {
173   var self = this;
174   return this.distinctAppIds_.every(function(appId) {
175     return appId == self.origin_;
176   });
180  * Fetches the allowed origins for an appId.
181  * @param {string} appId Application id
182  * @return {Promise<!Array<string>>} A promise for a list of allowed origins
183  *     for appId
184  * @private
185  */
186 AppIdChecker.prototype.fetchAllowedOriginsForAppId_ = function(appId) {
187   if (!appId) {
188     return Promise.resolve([]);
189   }
191   if (appId.indexOf('http://') == 0 && !this.allowHttp_) {
192     console.log(UTIL_fmt('http app ids disallowed, ' + appId + ' requested'));
193     return Promise.resolve([]);
194   }
196   var origin = getOriginFromUrl(appId);
197   if (!origin) {
198     return Promise.resolve([]);
199   }
201   var p = this.fetcher_.fetch(appId);
202   var self = this;
203   return p.then(getOriginsFromJson, function(rc_) {
204     var rc = /** @type {number} */(rc_);
205     console.log(UTIL_fmt('fetching ' + appId + ' failed: ' + rc));
206     if (!(rc >= 400 && rc < 500) && !self.timer_.expired()) {
207       // Retry
208       return self.fetchAllowedOriginsForAppId_(appId);
209     }
210     return [];
211   });