 8cdc76ba4c
			
		
	
	8cdc76ba4c
	
	
	
		
			
			These tags are preserved by the Closure compiler and vulcanize in order to serve the license notices embedded in the outputs. In a standalone Gerrit server, these license are also covered in the LICENSES.txt served with the documentation. When serving PG assets from a CDN, it's less obvious what the corresponding LICENSES.txt file is, since the CDN is not directly linked to a running Gerrit server. Safer to embed the licenses in the assets themselves. Change-Id: Id1add1451fad1baa7916882a6bda02c326ccc988
		
			
				
	
	
		
			187 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			187 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * @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);
 |