/** * @license * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ (function(window) { 'use strict'; // Prevent redefinition. if (window.Gerrit.Auth) { return; } const MAX_GET_TOKEN_RETRIES = 2; Gerrit.Auth = { TYPE: { XSRF_TOKEN: 'xsrf_token', ACCESS_TOKEN: 'access_token', }, _type: null, _cachedTokenPromise: null, _defaultOptions: {}, _retriesLeft: MAX_GET_TOKEN_RETRIES, _getToken() { return Promise.resolve(this._cachedTokenPromise); }, /** * Enable cross-domain authentication using OAuth access token. * * @param { * function(): !Promise<{ * access_token: string, * expires_at: number * }> * } getToken * @param {?{credentials:string}} defaultOptions */ setup(getToken, defaultOptions) { this._retriesLeft = MAX_GET_TOKEN_RETRIES; if (getToken) { this._type = Gerrit.Auth.TYPE.ACCESS_TOKEN; this._cachedTokenPromise = null; this._getToken = getToken; } this._defaultOptions = {}; if (defaultOptions) { for (const p of ['credentials']) { this._defaultOptions[p] = defaultOptions[p]; } } }, /** * Perform network fetch with authentication. * * @param {string} url * @param {Object=} opt_options * @return {!Promise<!Response>} */ fetch(url, opt_options) { const options = Object.assign({ headers: new Headers(), }, this._defaultOptions, opt_options); if (this._type === Gerrit.Auth.TYPE.ACCESS_TOKEN) { return this._getAccessToken().then( accessToken => this._fetchWithAccessToken(url, options, accessToken) ); } else { return this._fetchWithXsrfToken(url, options); } }, _getCookie(name) { const key = name + '='; let result = ''; document.cookie.split(';').some(c => { c = c.trim(); if (c.startsWith(key)) { result = c.substring(key.length); return true; } }); return result; }, _isTokenValid(token) { if (!token) { return false; } if (!token.access_token || !token.expires_at) { return false; } const expiration = new Date(parseInt(token.expires_at, 10) * 1000); if (Date.now() >= expiration.getTime()) { return false; } return true; }, _fetchWithXsrfToken(url, options) { if (options.method && options.method !== 'GET') { const token = this._getCookie('XSRF_TOKEN'); if (token) { options.headers.append('X-Gerrit-Auth', token); } } options.credentials = 'same-origin'; return fetch(url, options); }, /** * @return {!Promise<string>} */ _getAccessToken() { if (!this._cachedTokenPromise) { this._cachedTokenPromise = this._getToken(); } return this._cachedTokenPromise.then(token => { if (this._isTokenValid(token)) { this._retriesLeft = MAX_GET_TOKEN_RETRIES; return token.access_token; } if (this._retriesLeft > 0) { this._retriesLeft--; this._cachedTokenPromise = null; return this._getAccessToken(); } // Fall back to anonymous access. return null; }); }, _fetchWithAccessToken(url, options, accessToken) { const params = []; if (accessToken) { params.push(`access_token=${accessToken}`); const baseUrl = Gerrit.BaseUrlBehavior.getBaseUrl(); const pathname = baseUrl ? url.substring(url.indexOf(baseUrl) + baseUrl.length) : url; if (!pathname.startsWith('/a/')) { url = url.replace(pathname, '/a' + pathname); } } const method = options.method || 'GET'; let contentType = options.headers.get('Content-Type'); // For all requests with body, ensure json content type. if (!contentType && options.body) { contentType = 'application/json'; } if (method !== 'GET') { options.method = 'POST'; params.push(`$m=${method}`); // If a request is not GET, and does not have a body, ensure text/plain // content type. if (!contentType) { contentType = 'text/plain'; } } if (contentType) { options.headers.set('Content-Type', 'text/plain'); params.push(`$ct=${encodeURIComponent(contentType)}`); } if (params.length) { url = url + (url.indexOf('?') === -1 ? '?' : '&') + params.join('&'); } return fetch(url, options); }, }; window.Gerrit.Auth = Gerrit.Auth; })(window);