Files
gerrit/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-gapi-auth.js
Viktar Donich 9ce15cd204 Take CANONICAL_PATH into account with GrGapiAuth
Change-Id: I7c38e0496bb33a7dd57b190d0b2c888afa772349
2017-07-10 11:23:42 -07:00

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