QUIC - cleanup changes to sync chromium tree with internal source.
[chromium-blink-merge.git] / chrome / browser / resources / cryptotoken / appid.js
blobb5f8ed5fabaf53ecaccb613953f05960a8ae7364
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.
5 /**
6 * @fileoverview Implements a check whether an app id lists an origin.
7 */
8 'use strict';
10 /**
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) {
16 try {
17 var urls, i;
18 var appIdData = JSON.parse(text);
19 if (Array.isArray(appIdData)) {
20 // Older format where it is a simple list of facets
21 urls = appIdData;
22 } else {
23 var trustedFacets = appIdData['trustedFacets'];
24 if (trustedFacets) {
25 var versionBlock;
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'];
31 break;
35 if (typeof urls == 'undefined') {
36 throw Error('Could not find trustedFacets for version 1.0');
39 var origins = {};
40 var url;
41 for (i = 0; url = urls[i]; i++) {
42 var origin = getOriginFromUrl(url);
43 if (origin) {
44 origins[origin] = origin;
47 return Object.keys(origins);
48 } catch (e) {
49 console.error(UTIL_fmt('could not parse ' + text));
50 return [];
54 /**
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) {
61 return [];
63 var appIds = {};
64 for (var i = 0, request; request = signChallenges[i]; i++) {
65 var appId = request['appId'];
66 if (appId) {
67 appIds[appId] = appId;
70 return Object.keys(appIds);
73 /**
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.
81 * @constructor
83 function AppIdChecker(fetcher, timer, origin, appIds, allowHttp, opt_logMsgUrl)
85 /** @private {!TextFetcher} */
86 this.fetcher_ = fetcher;
87 /** @private {!Countdown} */
88 this.timer_ = timer;
89 /** @private {string} */
90 this.origin_ = origin;
91 var appIdsMap = {};
92 if (appIds) {
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);
123 } else {
124 var self = this;
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) {
129 if (!result)
130 self.anyInvalidAppIds_ = true;
131 return result;
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
141 * @private
143 AppIdChecker.prototype.checkAppId_ = function(appId) {
144 if (appId == this.origin_) {
145 // Trivially allowed
146 return Promise.resolve(true);
148 var p = this.fetchAllowedOriginsForAppId_(appId);
149 var self = this;
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));
154 return false;
156 return true;
161 * Closes this checker. No callback will be called after this checker is closed.
163 AppIdChecker.prototype.close = function() {
164 this.closed_ = true;
168 * @return {boolean} Whether all the app ids being checked are equal to the
169 * calling origin.
170 * @private
172 AppIdChecker.prototype.allAppIdsEqualOrigin_ = function() {
173 var self = this;
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
183 * for appId
184 * @private
186 AppIdChecker.prototype.fetchAllowedOriginsForAppId_ = function(appId) {
187 if (!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);
197 if (!origin) {
198 return Promise.resolve([]);
201 var p = this.fetcher_.fetch(appId);
202 var self = this;
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()) {
207 // Retry
208 return self.fetchAllowedOriginsForAppId_(appId);
210 return [];