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 * An object that checks one or more appIds' contents against an origin.
77 function AppIdChecker() {}
80 * Checks whether the given origin is allowed by all of the given appIds.
81 * @param {!Countdown} timer A timer by which to resolve all provided app ids.
82 * @param {string} origin The origin to check.
83 * @param {!Array<string>} appIds The app ids to check.
84 * @param {boolean} allowHttp Whether to allow http:// URLs.
85 * @param {string=} opt_logMsgUrl A log message URL.
86 * @return {Promise<boolean>} A promise for the result of the check
88 AppIdChecker.prototype.checkAppIds =
89 function(timer, origin, appIds, allowHttp, opt_logMsgUrl) {};
92 * An interface to create an AppIdChecker.
95 function AppIdCheckerFactory() {}
98 * @return {!AppIdChecker} A new AppIdChecker.
100 AppIdCheckerFactory.prototype.create = function() {};
103 * Provides an object to track checking a list of appIds.
104 * @param {!TextFetcher} fetcher A URL fetcher.
106 * @implements AppIdChecker
108 function XhrAppIdChecker(fetcher) {
109 /** @private {!TextFetcher} */
110 this.fetcher_ = fetcher;
114 * Checks whether all the app ids provided can be asserted by the given origin.
115 * @param {!Countdown} timer A timer by which to resolve all provided app ids.
116 * @param {string} origin The origin to check.
117 * @param {!Array<string>} appIds The app ids to check.
118 * @param {boolean} allowHttp Whether to allow http:// URLs.
119 * @param {string=} opt_logMsgUrl A log message URL.
120 * @return {Promise<boolean>} A promise for the result of the check
122 XhrAppIdChecker.prototype.checkAppIds =
123 function(timer, origin, appIds, allowHttp, opt_logMsgUrl) {
125 // Can't use the same object to check appIds more than once.
126 return Promise.resolve(false);
129 /** @private {!Countdown} */
131 /** @private {string} */
132 this.origin_ = origin;
135 for (var i = 0; i < appIds.length; i++) {
136 appIdsMap[appIds[i]] = appIds[i];
139 /** @private {Array<string>} */
140 this.distinctAppIds_ = Object.keys(appIdsMap);
141 /** @private {boolean} */
142 this.allowHttp_ = allowHttp;
143 /** @private {string|undefined} */
144 this.logMsgUrl_ = opt_logMsgUrl;
145 if (!this.distinctAppIds_.length)
146 return Promise.resolve(false);
148 if (this.allAppIdsEqualOrigin_()) {
149 // Trivially allowed.
150 return Promise.resolve(true);
153 // Begin checking remaining app ids.
154 var appIdChecks = self.distinctAppIds_.map(self.checkAppId_.bind(self));
155 return Promise.all(appIdChecks).then(function(results) {
156 return results.every(function(result) {
164 * Checks if a single appId can be asserted by the given origin.
165 * @param {string} appId The appId to check
166 * @return {Promise<boolean>} A promise for the result of the check
169 XhrAppIdChecker.prototype.checkAppId_ = function(appId) {
170 if (appId == this.origin_) {
172 return Promise.resolve(true);
174 var p = this.fetchAllowedOriginsForAppId_(appId);
176 return p.then(function(allowedOrigins) {
177 if (allowedOrigins.indexOf(self.origin_) == -1) {
178 console.warn(UTIL_fmt('Origin ' + self.origin_ +
179 ' not allowed by app id ' + appId));
187 * @return {boolean} Whether all the app ids being checked are equal to the
191 XhrAppIdChecker.prototype.allAppIdsEqualOrigin_ = function() {
193 return this.distinctAppIds_.every(function(appId) {
194 return appId == self.origin_;
199 * Fetches the allowed origins for an appId.
200 * @param {string} appId Application id
201 * @return {Promise<!Array<string>>} A promise for a list of allowed origins
205 XhrAppIdChecker.prototype.fetchAllowedOriginsForAppId_ = function(appId) {
207 return Promise.resolve([]);
210 if (appId.indexOf('http://') == 0 && !this.allowHttp_) {
211 console.log(UTIL_fmt('http app ids disallowed, ' + appId + ' requested'));
212 return Promise.resolve([]);
215 var origin = getOriginFromUrl(appId);
217 return Promise.resolve([]);
220 var p = this.fetcher_.fetch(appId);
222 return p.then(getOriginsFromJson, function(rc_) {
223 var rc = /** @type {number} */(rc_);
224 console.log(UTIL_fmt('fetching ' + appId + ' failed: ' + rc));
225 if (!(rc >= 400 && rc < 500) && !self.timer_.expired()) {
227 return self.fetchAllowedOriginsForAppId_(appId);
234 * A factory to create an XhrAppIdChecker.
235 * @implements AppIdCheckerFactory
236 * @param {!TextFetcher} fetcher
239 function XhrAppIdCheckerFactory(fetcher) {
240 /** @private {!TextFetcher} */
241 this.fetcher_ = fetcher;
245 * @return {!AppIdChecker} A new AppIdChecker.
247 XhrAppIdCheckerFactory.prototype.create = function() {
248 return new XhrAppIdChecker(this.fetcher_);