| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @fileoverview Implements a check whether an app id lists an origin. | 6 * @fileoverview Implements a check whether an app id lists an origin. |
| 7 */ | 7 */ |
| 8 'use strict'; | 8 'use strict'; |
| 9 | 9 |
| 10 /** | 10 /** |
| 11 * Parses the text as JSON and returns it as an array of strings. | 11 * Parses the text as JSON and returns it as an array of strings. |
| 12 * @param {string} text Input JSON | 12 * @param {string} text Input JSON |
| 13 * @return {!Array.<string>} Array of origins | 13 * @return {!Array<string>} Array of origins |
| 14 */ | 14 */ |
| 15 function getOriginsFromJson(text) { | 15 function getOriginsFromJson(text) { |
| 16 try { | 16 try { |
| 17 var urls = JSON.parse(text); | 17 var urls = JSON.parse(text); |
| 18 var origins = {}; | 18 var origins = {}; |
| 19 for (var i = 0, url; url = urls[i]; i++) { | 19 for (var i = 0, url; url = urls[i]; i++) { |
| 20 var origin = getOriginFromUrl(url); | 20 var origin = getOriginFromUrl(url); |
| 21 if (origin) { | 21 if (origin) { |
| 22 origins[origin] = origin; | 22 origins[origin] = origin; |
| 23 } | 23 } |
| 24 } | 24 } |
| 25 return Object.keys(origins); | 25 return Object.keys(origins); |
| 26 } catch (e) { | 26 } catch (e) { |
| 27 console.log(UTIL_fmt('could not parse ' + text)); | 27 console.log(UTIL_fmt('could not parse ' + text)); |
| 28 return []; | 28 return []; |
| 29 } | 29 } |
| 30 } | 30 } |
| 31 | 31 |
| 32 /** | 32 /** |
| 33 * Retrieves a set of distinct app ids from the sign challenges. | 33 * Retrieves a set of distinct app ids from the sign challenges. |
| 34 * @param {Array.<SignChallenge>=} signChallenges Input sign challenges. | 34 * @param {Array<SignChallenge>=} signChallenges Input sign challenges. |
| 35 * @return {Array.<string>} array of distinct app ids. | 35 * @return {Array<string>} array of distinct app ids. |
| 36 */ | 36 */ |
| 37 function getDistinctAppIds(signChallenges) { | 37 function getDistinctAppIds(signChallenges) { |
| 38 if (!signChallenges) { | 38 if (!signChallenges) { |
| 39 return []; | 39 return []; |
| 40 } | 40 } |
| 41 var appIds = {}; | 41 var appIds = {}; |
| 42 for (var i = 0, request; request = signChallenges[i]; i++) { | 42 for (var i = 0, request; request = signChallenges[i]; i++) { |
| 43 var appId = request['appId']; | 43 var appId = request['appId']; |
| 44 if (appId) { | 44 if (appId) { |
| 45 appIds[appId] = appId; | 45 appIds[appId] = appId; |
| 46 } | 46 } |
| 47 } | 47 } |
| 48 return Object.keys(appIds); | 48 return Object.keys(appIds); |
| 49 } | 49 } |
| 50 | 50 |
| 51 /** | 51 /** |
| 52 * Provides an object to track checking a list of appIds. | 52 * Provides an object to track checking a list of appIds. |
| 53 * @param {!TextFetcher} fetcher A URL fetcher. | 53 * @param {!TextFetcher} fetcher A URL fetcher. |
| 54 * @param {!Countdown} timer A timer by which to resolve all provided app ids. | 54 * @param {!Countdown} timer A timer by which to resolve all provided app ids. |
| 55 * @param {string} origin The origin to check. | 55 * @param {string} origin The origin to check. |
| 56 * @param {!Array.<string>} appIds The app ids to check. | 56 * @param {!Array<string>} appIds The app ids to check. |
| 57 * @param {boolean} allowHttp Whether to allow http:// URLs. | 57 * @param {boolean} allowHttp Whether to allow http:// URLs. |
| 58 * @param {string=} opt_logMsgUrl A log message URL. | 58 * @param {string=} opt_logMsgUrl A log message URL. |
| 59 * @constructor | 59 * @constructor |
| 60 */ | 60 */ |
| 61 function AppIdChecker(fetcher, timer, origin, appIds, allowHttp, opt_logMsgUrl) | 61 function AppIdChecker(fetcher, timer, origin, appIds, allowHttp, opt_logMsgUrl) |
| 62 { | 62 { |
| 63 /** @private {!TextFetcher} */ | 63 /** @private {!TextFetcher} */ |
| 64 this.fetcher_ = fetcher; | 64 this.fetcher_ = fetcher; |
| 65 /** @private {!Countdown} */ | 65 /** @private {!Countdown} */ |
| 66 this.timer_ = timer; | 66 this.timer_ = timer; |
| 67 /** @private {string} */ | 67 /** @private {string} */ |
| 68 this.origin_ = origin; | 68 this.origin_ = origin; |
| 69 var appIdsMap = {}; | 69 var appIdsMap = {}; |
| 70 if (appIds) { | 70 if (appIds) { |
| 71 for (var i = 0; i < appIds.length; i++) { | 71 for (var i = 0; i < appIds.length; i++) { |
| 72 appIdsMap[appIds[i]] = appIds[i]; | 72 appIdsMap[appIds[i]] = appIds[i]; |
| 73 } | 73 } |
| 74 } | 74 } |
| 75 /** @private {Array.<string>} */ | 75 /** @private {Array<string>} */ |
| 76 this.distinctAppIds_ = Object.keys(appIdsMap); | 76 this.distinctAppIds_ = Object.keys(appIdsMap); |
| 77 /** @private {boolean} */ | 77 /** @private {boolean} */ |
| 78 this.allowHttp_ = allowHttp; | 78 this.allowHttp_ = allowHttp; |
| 79 /** @private {string|undefined} */ | 79 /** @private {string|undefined} */ |
| 80 this.logMsgUrl_ = opt_logMsgUrl; | 80 this.logMsgUrl_ = opt_logMsgUrl; |
| 81 | 81 |
| 82 /** @private {boolean} */ | 82 /** @private {boolean} */ |
| 83 this.closed_ = false; | 83 this.closed_ = false; |
| 84 /** @private {boolean} */ | 84 /** @private {boolean} */ |
| 85 this.anyInvalidAppIds_ = false; | 85 this.anyInvalidAppIds_ = false; |
| 86 /** @private {number} */ | 86 /** @private {number} */ |
| 87 this.fetchedAppIds_ = 0; | 87 this.fetchedAppIds_ = 0; |
| 88 } | 88 } |
| 89 | 89 |
| 90 /** | 90 /** |
| 91 * Checks whether all the app ids provided can be asserted by the given origin. | 91 * Checks whether all the app ids provided can be asserted by the given origin. |
| 92 * @return {Promise.<boolean>} A promise for the result of the check | 92 * @return {Promise<boolean>} A promise for the result of the check |
| 93 */ | 93 */ |
| 94 AppIdChecker.prototype.doCheck = function() { | 94 AppIdChecker.prototype.doCheck = function() { |
| 95 if (!this.distinctAppIds_.length) | 95 if (!this.distinctAppIds_.length) |
| 96 return Promise.resolve(false); | 96 return Promise.resolve(false); |
| 97 | 97 |
| 98 if (this.allAppIdsEqualOrigin_()) { | 98 if (this.allAppIdsEqualOrigin_()) { |
| 99 // Trivially allowed. | 99 // Trivially allowed. |
| 100 return Promise.resolve(true); | 100 return Promise.resolve(true); |
| 101 } else { | 101 } else { |
| 102 var self = this; | 102 var self = this; |
| 103 // Begin checking remaining app ids. | 103 // Begin checking remaining app ids. |
| 104 var appIdChecks = self.distinctAppIds_.map(self.checkAppId_.bind(self)); | 104 var appIdChecks = self.distinctAppIds_.map(self.checkAppId_.bind(self)); |
| 105 return Promise.all(appIdChecks).then(function(results) { | 105 return Promise.all(appIdChecks).then(function(results) { |
| 106 return results.every(function(result) { | 106 return results.every(function(result) { |
| 107 if (!result) | 107 if (!result) |
| 108 self.anyInvalidAppIds_ = true; | 108 self.anyInvalidAppIds_ = true; |
| 109 return result; | 109 return result; |
| 110 }); | 110 }); |
| 111 }); | 111 }); |
| 112 } | 112 } |
| 113 }; | 113 }; |
| 114 | 114 |
| 115 /** | 115 /** |
| 116 * Checks if a single appId can be asserted by the given origin. | 116 * Checks if a single appId can be asserted by the given origin. |
| 117 * @param {string} appId The appId to check | 117 * @param {string} appId The appId to check |
| 118 * @return {Promise.<boolean>} A promise for the result of the check | 118 * @return {Promise<boolean>} A promise for the result of the check |
| 119 * @private | 119 * @private |
| 120 */ | 120 */ |
| 121 AppIdChecker.prototype.checkAppId_ = function(appId) { | 121 AppIdChecker.prototype.checkAppId_ = function(appId) { |
| 122 if (appId == this.origin_) { | 122 if (appId == this.origin_) { |
| 123 // Trivially allowed | 123 // Trivially allowed |
| 124 return Promise.resolve(true); | 124 return Promise.resolve(true); |
| 125 } | 125 } |
| 126 var p = this.fetchAllowedOriginsForAppId_(appId); | 126 var p = this.fetchAllowedOriginsForAppId_(appId); |
| 127 var self = this; | 127 var self = this; |
| 128 return p.then(function(allowedOrigins) { | 128 return p.then(function(allowedOrigins) { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 150 AppIdChecker.prototype.allAppIdsEqualOrigin_ = function() { | 150 AppIdChecker.prototype.allAppIdsEqualOrigin_ = function() { |
| 151 var self = this; | 151 var self = this; |
| 152 return this.distinctAppIds_.every(function(appId) { | 152 return this.distinctAppIds_.every(function(appId) { |
| 153 return appId == self.origin_; | 153 return appId == self.origin_; |
| 154 }); | 154 }); |
| 155 }; | 155 }; |
| 156 | 156 |
| 157 /** | 157 /** |
| 158 * Fetches the allowed origins for an appId. | 158 * Fetches the allowed origins for an appId. |
| 159 * @param {string} appId Application id | 159 * @param {string} appId Application id |
| 160 * @return {Promise.<!Array.<string>>} A promise for a list of allowed origins | 160 * @return {Promise<!Array<string>>} A promise for a list of allowed origins |
| 161 * for appId | 161 * for appId |
| 162 * @private | 162 * @private |
| 163 */ | 163 */ |
| 164 AppIdChecker.prototype.fetchAllowedOriginsForAppId_ = function(appId) { | 164 AppIdChecker.prototype.fetchAllowedOriginsForAppId_ = function(appId) { |
| 165 if (!appId) { | 165 if (!appId) { |
| 166 return Promise.resolve([]); | 166 return Promise.resolve([]); |
| 167 } | 167 } |
| 168 | 168 |
| 169 if (appId.indexOf('http://') == 0 && !this.allowHttp_) { | 169 if (appId.indexOf('http://') == 0 && !this.allowHttp_) { |
| 170 console.log(UTIL_fmt('http app ids disallowed, ' + appId + ' requested')); | 170 console.log(UTIL_fmt('http app ids disallowed, ' + appId + ' requested')); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 181 return p.then(getOriginsFromJson, function(rc_) { | 181 return p.then(getOriginsFromJson, function(rc_) { |
| 182 var rc = /** @type {number} */(rc_); | 182 var rc = /** @type {number} */(rc_); |
| 183 console.log(UTIL_fmt('fetching ' + appId + ' failed: ' + rc)); | 183 console.log(UTIL_fmt('fetching ' + appId + ' failed: ' + rc)); |
| 184 if (!(rc >= 400 && rc < 500) && !self.timer_.expired()) { | 184 if (!(rc >= 400 && rc < 500) && !self.timer_.expired()) { |
| 185 // Retry | 185 // Retry |
| 186 return self.fetchAllowedOriginsForAppId_(appId); | 186 return self.fetchAllowedOriginsForAppId_(appId); |
| 187 } | 187 } |
| 188 return []; | 188 return []; |
| 189 }); | 189 }); |
| 190 }; | 190 }; |
| OLD | NEW |