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