UNPKG

@theia/core

Version:

Theia is a cloud & desktop IDE framework implemented in TypeScript.

333 lines • 16.2 kB
"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