@theia/core
Version:
Theia is a cloud & desktop IDE framework implemented in TypeScript.
333 lines • 16.2 kB
JavaScript
"use strict";
// *****************************************************************************
// Copyright (C) 2020 Red Hat, Inc. and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// *****************************************************************************
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.readAllowedExtensions = exports.AuthenticationServiceImpl = exports.AuthenticationService = void 0;
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// code copied and modified from https://github.com/microsoft/vscode/blob/1.47.3/src/vs/workbench/services/authentication/browser/authenticationService.ts
const inversify_1 = require("inversify");
const event_1 = require("../common/event");
const storage_service_1 = require("../browser/storage-service");
const disposable_1 = require("../common/disposable");
const menu_1 = require("../common/menu");
const command_1 = require("../common/command");
const nls_1 = require("../common/nls");
exports.AuthenticationService = Symbol('AuthenticationService');
let AuthenticationServiceImpl = class AuthenticationServiceImpl {
constructor() {
this.noAccountsCommand = { id: 'noAccounts' };
this.signInRequestItems = new Map();
this.sessionMap = new Map();
this.authenticationProviders = new Map();
this.onDidRegisterAuthenticationProviderEmitter = new event_1.Emitter();
this.onDidRegisterAuthenticationProvider = this.onDidRegisterAuthenticationProviderEmitter.event;
this.onDidUnregisterAuthenticationProviderEmitter = new event_1.Emitter();
this.onDidUnregisterAuthenticationProvider = this.onDidUnregisterAuthenticationProviderEmitter.event;
this.onDidChangeSessionsEmitter = new event_1.Emitter();
this.onDidChangeSessions = this.onDidChangeSessionsEmitter.event;
}
init() {
this.onDidChangeSessions(event => this.handleSessionChange(event));
this.commands.registerCommand(this.noAccountsCommand, {
execute: () => { },
isEnabled: () => false
});
}
async handleSessionChange(changeEvent) {
var _a;
if (changeEvent.event.added && changeEvent.event.added.length > 0) {
const sessions = await this.getSessions(changeEvent.providerId);
sessions.forEach(session => {
if (!this.sessionMap.get(session.id)) {
this.sessionMap.set(session.id, this.createAccountUi(changeEvent.providerId, changeEvent.label, session));
}
});
}
for (const removed of changeEvent.event.removed || []) {
const sessionId = typeof removed === 'string' ? removed : removed === null || removed === void 0 ? void 0 : removed.id;
if (sessionId) {
(_a = this.sessionMap.get(sessionId)) === null || _a === void 0 ? void 0 : _a.dispose();
this.sessionMap.delete(sessionId);
}
}
}
createAccountUi(providerId, providerLabel, session) {
// unregister old commands and menus if present (there is only one per account but there may be several sessions per account)
const providerAccountId = `account-sign-out-${providerId}-${session.account.id}`;
this.commands.unregisterCommand(providerAccountId);
const providerAccountSubmenu = [...menu_1.ACCOUNTS_SUBMENU, providerAccountId];
this.menus.unregisterMenuAction({ commandId: providerAccountId }, providerAccountSubmenu);
// register new command and menu entry for the sessions account
const disposables = new disposable_1.DisposableCollection();
disposables.push(this.commands.registerCommand({ id: providerAccountId }, {
execute: async () => {
this.signOutOfAccount(providerId, session.account.label);
}
}));
this.menus.registerSubmenu(providerAccountSubmenu, `${session.account.label} (${providerLabel})`);
disposables.push(this.menus.registerMenuAction(providerAccountSubmenu, {
label: nls_1.nls.localizeByDefault('Sign Out'),
commandId: providerAccountId
}));
return disposables;
}
getProviderIds() {
const providerIds = [];
this.authenticationProviders.forEach(provider => {
providerIds.push(provider.id);
});
return providerIds;
}
isAuthenticationProviderRegistered(id) {
return this.authenticationProviders.has(id);
}
updateAccountsMenuItem() {
let hasSession = false;
this.authenticationProviders.forEach(async (provider) => {
hasSession = hasSession || provider.hasSessions();
});
if (hasSession && this.noAccountsMenuItem) {
this.noAccountsMenuItem.dispose();
this.noAccountsMenuItem = undefined;
}
if (!hasSession && !this.noAccountsMenuItem) {
this.noAccountsMenuItem = this.menus.registerMenuAction(menu_1.ACCOUNTS_MENU, {
label: 'You are not signed in to any accounts',
order: '0',
commandId: this.noAccountsCommand.id
});
}
}
registerAuthenticationProvider(id, authenticationProvider) {
if (this.authenticationProviders.get(id)) {
throw new Error(`An authentication provider with id '${id}' is already registered.`);
}
this.authenticationProviders.set(id, authenticationProvider);
this.onDidRegisterAuthenticationProviderEmitter.fire({ id, label: authenticationProvider.label });
this.updateAccountsMenuItem();
console.log(`An authentication provider with id '${id}' was registered.`);
}
unregisterAuthenticationProvider(id) {
const provider = this.authenticationProviders.get(id);
if (provider) {
this.authenticationProviders.delete(id);
this.onDidUnregisterAuthenticationProviderEmitter.fire({ id, label: provider.label });
this.updateAccountsMenuItem();
}
else {
console.error(`Failed to unregister an authentication provider. A provider with id '${id}' was not found.`);
}
}
async updateSessions(id, event) {
const provider = this.authenticationProviders.get(id);
if (provider) {
await provider.updateSessionItems(event);
this.onDidChangeSessionsEmitter.fire({ providerId: id, label: provider.label, event: event });
this.updateAccountsMenuItem();
if (event.added) {
await this.updateNewSessionRequests(provider);
}
}
else {
console.error(`Failed to update an authentication session. An authentication provider with id '${id}' was not found.`);
}
}
async updateNewSessionRequests(provider) {
const existingRequestsForProvider = this.signInRequestItems.get(provider.id);
if (!existingRequestsForProvider) {
return;
}
const sessions = await provider.getSessions();
Object.keys(existingRequestsForProvider).forEach(requestedScopes => {
if (sessions.some(session => session.scopes.slice().sort().join('') === requestedScopes)) {
const sessionRequest = existingRequestsForProvider[requestedScopes];
if (sessionRequest) {
sessionRequest.disposables.forEach(item => item.dispose());
}
delete existingRequestsForProvider[requestedScopes];
if (Object.keys(existingRequestsForProvider).length === 0) {
this.signInRequestItems.delete(provider.id);
}
else {
this.signInRequestItems.set(provider.id, existingRequestsForProvider);
}
}
});
}
async requestNewSession(providerId, scopes, extensionId, extensionName) {
let provider = this.authenticationProviders.get(providerId);
if (!provider) {
// Activate has already been called for the authentication provider, but it cannot block on registering itself
// since this is sync and returns a disposable. So, wait for registration event to fire that indicates the
// provider is now in the map.
await new Promise((resolve, _) => {
this.onDidRegisterAuthenticationProvider(e => {
if (e.id === providerId) {
provider = this.authenticationProviders.get(providerId);
resolve(undefined);
}
});
});
}
if (provider) {
const providerRequests = this.signInRequestItems.get(providerId);
const scopesList = scopes.sort().join('');
const extensionHasExistingRequest = providerRequests
&& providerRequests[scopesList]
&& providerRequests[scopesList].requestingExtensionIds.indexOf(extensionId) > -1;
if (extensionHasExistingRequest) {
return;
}
const menuItem = this.menus.registerMenuAction(menu_1.ACCOUNTS_SUBMENU, {
label: `Sign in to use ${extensionName} (1)`,
order: '1',
commandId: `${extensionId}signIn`,
});
const signInCommand = this.commands.registerCommand({ id: `${extensionId}signIn` }, {
execute: async () => {
const session = await this.login(providerId, scopes);
// Add extension to allow list since user explicitly signed in on behalf of it
const allowList = await readAllowedExtensions(this.storageService, providerId, session.account.label);
if (!allowList.find(allowed => allowed.id === extensionId)) {
allowList.push({ id: extensionId, name: extensionName });
this.storageService.setData(`authentication-trusted-extensions-${providerId}-${session.account.label}`, JSON.stringify(allowList));
}
// And also set it as the preferred account for the extension
this.storageService.setData(`authentication-session-${extensionName}-${providerId}`, session.id);
}
});
if (providerRequests) {
const existingRequest = providerRequests[scopesList] || { disposables: [], requestingExtensionIds: [] };
providerRequests[scopesList] = {
disposables: [...existingRequest.disposables, menuItem, signInCommand],
requestingExtensionIds: [...existingRequest.requestingExtensionIds, extensionId]
};
this.signInRequestItems.set(providerId, providerRequests);
}
else {
this.signInRequestItems.set(providerId, {
[scopesList]: {
disposables: [menuItem, signInCommand],
requestingExtensionIds: [extensionId]
}
});
}
}
}
getLabel(id) {
const authProvider = this.authenticationProviders.get(id);
if (authProvider) {
return authProvider.label;
}
else {
throw new Error(`No authentication provider '${id}' is currently registered.`);
}
}
supportsMultipleAccounts(id) {
const authProvider = this.authenticationProviders.get(id);
if (authProvider) {
return authProvider.supportsMultipleAccounts;
}
else {
throw new Error(`No authentication provider '${id}' is currently registered.`);
}
}
async getSessions(id, scopes) {
const authProvider = this.authenticationProviders.get(id);
if (authProvider) {
return authProvider.getSessions(scopes);
}
else {
throw new Error(`No authentication provider '${id}' is currently registered.`);
}
}
async login(id, scopes) {
const authProvider = this.authenticationProviders.get(id);
if (authProvider) {
return authProvider.createSession(scopes);
}
else {
throw new Error(`No authentication provider '${id}' is currently registered.`);
}
}
async logout(id, sessionId) {
const authProvider = this.authenticationProviders.get(id);
if (authProvider) {
return authProvider.removeSession(sessionId);
}
else {
throw new Error(`No authentication provider '${id}' is currently registered.`);
}
}
async signOutOfAccount(id, accountName) {
const authProvider = this.authenticationProviders.get(id);
if (authProvider) {
return authProvider.signOut(accountName);
}
else {
throw new Error(`No authentication provider '${id}' is currently registered.`);
}
}
};
__decorate([
(0, inversify_1.inject)(menu_1.MenuModelRegistry),
__metadata("design:type", menu_1.MenuModelRegistry)
], AuthenticationServiceImpl.prototype, "menus", void 0);
__decorate([
(0, inversify_1.inject)(command_1.CommandRegistry),
__metadata("design:type", command_1.CommandRegistry)
], AuthenticationServiceImpl.prototype, "commands", void 0);
__decorate([
(0, inversify_1.inject)(storage_service_1.StorageService),
__metadata("design:type", Object)
], AuthenticationServiceImpl.prototype, "storageService", void 0);
__decorate([
(0, inversify_1.postConstruct)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", void 0)
], AuthenticationServiceImpl.prototype, "init", null);
AuthenticationServiceImpl = __decorate([
(0, inversify_1.injectable)()
], AuthenticationServiceImpl);
exports.AuthenticationServiceImpl = AuthenticationServiceImpl;
async function readAllowedExtensions(storageService, providerId, accountName) {
let trustedExtensions = [];
try {
const trustedExtensionSrc = await storageService.getData(`authentication-trusted-extensions-${providerId}-${accountName}`);
if (trustedExtensionSrc) {
trustedExtensions = JSON.parse(trustedExtensionSrc);
}
}
catch (err) {
console.error(err);
}
return trustedExtensions;
}
exports.readAllowedExtensions = readAllowedExtensions;
//# sourceMappingURL=authentication-service.js.map