198 lines
6.4 KiB
JavaScript
198 lines
6.4 KiB
JavaScript
// 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.GrGapiAuth) { return; }
|
|
|
|
const EMAIL_SCOPE = 'email';
|
|
|
|
function GrGapiAuth() {}
|
|
|
|
GrGapiAuth._loadGapiPromise = null;
|
|
GrGapiAuth._setupPromise = null;
|
|
GrGapiAuth._refreshTokenPromise = null;
|
|
GrGapiAuth._sharedAuthToken = null;
|
|
GrGapiAuth._oauthClientId = null;
|
|
GrGapiAuth._oauthEmail = null;
|
|
|
|
GrGapiAuth.prototype.fetch = function(url, options) {
|
|
options = Object.assign({}, options);
|
|
return this._getAccessToken().then(
|
|
token => window.FASTER_GERRIT_CORS ?
|
|
this._fasterGerritCors(url, options, token) :
|
|
this._defaultFetch(url, options, token)
|
|
);
|
|
};
|
|
|
|
GrGapiAuth.prototype._defaultFetch = function(url, options, token) {
|
|
if (token) {
|
|
options.headers = options.headers || new Headers();
|
|
options.headers.append('Authorization', `Bearer ${token}`);
|
|
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);
|
|
}
|
|
}
|
|
return fetch(url, options);
|
|
};
|
|
|
|
GrGapiAuth.prototype._fasterGerritCors = function(url, options, token) {
|
|
const method = options.method || 'GET';
|
|
if (method === 'GET') {
|
|
return fetch(url, options);
|
|
}
|
|
const params = [];
|
|
if (token) {
|
|
params.push(`access_token=${token}`);
|
|
}
|
|
const contentType = options.headers && options.headers.get('Content-Type');
|
|
if (contentType) {
|
|
options.headers.set('Content-Type', 'text/plain');
|
|
params.push(`$ct=${encodeURIComponent(contentType)}`);
|
|
}
|
|
params.push(`$m=${method}`);
|
|
url = url + (url.indexOf('?') === -1 ? '?' : '') + params.join('&');
|
|
options.method = 'POST';
|
|
return fetch(url, options);
|
|
};
|
|
|
|
GrGapiAuth.prototype._getAccessToken = function() {
|
|
if (this._isTokenValid(GrGapiAuth._sharedAuthToken)) {
|
|
return Promise.resolve(GrGapiAuth._sharedAuthToken.access_token);
|
|
}
|
|
if (!GrGapiAuth._refreshTokenPromise) {
|
|
GrGapiAuth._refreshTokenPromise = this._loadGapi()
|
|
.then(() => this._configureOAuthLibrary())
|
|
.then(() => this._refreshToken())
|
|
.then(token => {
|
|
GrGapiAuth._sharedAuthToken = token;
|
|
GrGapiAuth._refreshTokenPromise = null;
|
|
return this._getAccessToken();
|
|
}).catch(err => {
|
|
console.error(err);
|
|
});
|
|
}
|
|
return GrGapiAuth._refreshTokenPromise;
|
|
};
|
|
|
|
GrGapiAuth.prototype._isTokenValid = function(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) { return false; }
|
|
|
|
return true;
|
|
};
|
|
|
|
GrGapiAuth.prototype._loadGapi = function() {
|
|
if (!GrGapiAuth._loadGapiPromise) {
|
|
GrGapiAuth._loadGapiPromise = new Promise((resolve, reject) => {
|
|
const scriptEl = document.createElement('script');
|
|
scriptEl.defer = true;
|
|
scriptEl.async = true;
|
|
scriptEl.src = 'https://apis.google.com/js/platform.js';
|
|
scriptEl.onerror = reject;
|
|
scriptEl.onload = resolve;
|
|
document.body.appendChild(scriptEl);
|
|
});
|
|
}
|
|
return GrGapiAuth._loadGapiPromise;
|
|
};
|
|
|
|
GrGapiAuth.prototype._configureOAuthLibrary = function() {
|
|
if (!GrGapiAuth._setupPromise) {
|
|
GrGapiAuth._setupPromise = new Promise(
|
|
resolve => gapi.load('config_min', resolve)
|
|
)
|
|
.then(() => this._getOAuthConfig())
|
|
.then(config => {
|
|
if (config.hasOwnProperty('auth_url') && config.auth_url) {
|
|
gapi.config.update('oauth-flow/authUrl', config.auth_url);
|
|
}
|
|
if (config.hasOwnProperty('proxy_url') && config.proxy_url) {
|
|
gapi.config.update('oauth-flow/proxyUrl', config.proxy_url);
|
|
}
|
|
GrGapiAuth._oauthClientId = config.client_id;
|
|
GrGapiAuth._oauthEmail = config.email;
|
|
|
|
// Loading auth has a side-effect. The URLs should be set before
|
|
// loading it.
|
|
return new Promise(
|
|
resolve => gapi.load('auth', () => gapi.auth.init(resolve))
|
|
);
|
|
});
|
|
}
|
|
return GrGapiAuth._setupPromise;
|
|
};
|
|
|
|
GrGapiAuth.prototype._refreshToken = function() {
|
|
const opts = {
|
|
client_id: GrGapiAuth._oauthClientId,
|
|
immediate: true,
|
|
scope: EMAIL_SCOPE,
|
|
login_hint: GrGapiAuth._oauthEmail,
|
|
};
|
|
return new Promise((resolve, reject) => {
|
|
gapi.auth.authorize(opts, token => {
|
|
if (!token) {
|
|
reject('No token returned');
|
|
} else if (token.error) {
|
|
reject(token.error);
|
|
} else {
|
|
resolve(token);
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
GrGapiAuth.prototype._getOAuthConfig = function() {
|
|
const baseUrl = Gerrit.BaseUrlBehavior.getBaseUrl();
|
|
const authConfigURL = baseUrl + '/accounts/self/oauthconfig';
|
|
const opts = {
|
|
headers: new Headers({Accept: 'application/json'}),
|
|
credentials: 'same-origin',
|
|
};
|
|
return fetch(authConfigURL, opts).then(response => {
|
|
if (!response.ok) {
|
|
console.error(response.statusText);
|
|
if (response.body && response.body.then) {
|
|
return response.body.then(text => {
|
|
return Promise.reject(text);
|
|
});
|
|
}
|
|
if (response.statusText) {
|
|
return Promise.reject(response.statusText);
|
|
} else {
|
|
return Promise.reject('_getOAuthConfig' + response.status);
|
|
}
|
|
}
|
|
return this._getResponseObject(response);
|
|
});
|
|
};
|
|
|
|
GrGapiAuth.prototype._getResponseObject = function(response) {
|
|
const JSON_PREFIX = ')]}\'';
|
|
return response.text().then(text => {
|
|
return JSON.parse(text.substring(JSON_PREFIX.length));
|
|
});
|
|
},
|
|
|
|
window.GrGapiAuth = GrGapiAuth;
|
|
})(window);
|