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_
);