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.
6 * @fileoverview Implements a check whether an app id lists an origin.
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
15 function getOriginsFromJson(text) {
18 var appIdData = JSON.parse(text);
19 if (Array.isArray(appIdData)) {
20 // Older format where it is a simple list of facets
23 var trustedFacets = appIdData['trustedFacets'];
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'];
35 if (typeof urls == 'undefined') {
36 throw Error('Could not find trustedFacets for version 1.0');
41 for (i = 0; url = urls[i]; i++) {
42 var origin = getOriginFromUrl(url);
44 origins[origin] = origin;
47 return Object.keys(origins);
49 console.error(UTIL_fmt('could not parse ' + text));
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.
59 function getDistinctAppIds(signChallenges) {
60 if (!signChallenges) {
64 for (var i = 0, request; request = signChallenges[i]; i++) {
65 var appId = request['appId'];
67 appIds[appId] = appId;
70 return Object.keys(appIds);
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.
83 function AppIdChecker(fetcher, timer, origin, appIds, allowHttp, opt_logMsgUrl)
85 /** @private {!TextFetcher} */
86 this.fetcher_ = fetcher;
87 /** @private {!Countdown} */
89 /** @private {string} */
90 this.origin_ = origin;
93 for (var i = 0; i < appIds.length; i++) {
94 appIdsMap[appIds[i]] = appIds[i];
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
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);
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) {
130 self.anyInvalidAppIds_ = true;
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
143 AppIdChecker.prototype.checkAppId_ = function(appId) {
144 if (appId == this.origin_) {
146 return Promise.resolve(true);
148 var p = this.fetchAllowedOriginsForAppId_(appId);
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));
161 * Closes this checker. No callback will be called after this checker is closed.
163 AppIdChecker.prototype.close = function() {
168 * @return {boolean} Whether all the app ids being checked are equal to the
172 AppIdChecker.prototype.allAppIdsEqualOrigin_ = function() {
174 return this.distinctAppIds_.every(function(appId) {
175 return appId == self.origin_;
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
186 AppIdChecker.prototype.fetchAllowedOriginsForAppId_ = function(appId) {
188 return Promise.resolve([]);
191 if (appId.indexOf('http://') == 0 && !this.allowHttp_) {
192 console.log(UTIL_fmt('http app ids disallowed, ' + appId + ' requested'));
193 return Promise.resolve([]);
196 var origin = getOriginFromUrl(appId);
198 return Promise.resolve([]);
201 var p = this.fetcher_.fetch(appId);
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()) {
208 return self.fetchAllowedOriginsForAppId_(appId);