UNPKG

16.2 kBJavaScriptView Raw
1"use strict";
2// *****************************************************************************
3// Copyright (C) 2020 Red Hat, Inc. and others.
4//
5// This program and the accompanying materials are made available under the
6// terms of the Eclipse Public License v. 2.0 which is available at
7// http://www.eclipse.org/legal/epl-2.0.
8//
9// This Source Code may also be made available under the following Secondary
10// Licenses when the conditions for such availability set forth in the Eclipse
11// Public License v. 2.0 are satisfied: GNU General Public License, version 2
12// with the GNU Classpath Exception which is available at
13// https://www.gnu.org/software/classpath/license.html.
14//
15// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
16// *****************************************************************************
17var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
18 var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
19 if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
20 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;
21 return c > 3 && r && Object.defineProperty(target, key, r), r;
22};
23var __metadata = (this && this.__metadata) || function (k, v) {
24 if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
25};
26Object.defineProperty(exports, "__esModule", { value: true });
27exports.readAllowedExtensions = exports.AuthenticationServiceImpl = exports.AuthenticationService = void 0;
28/*---------------------------------------------------------------------------------------------
29 * Copyright (c) Microsoft Corporation. All rights reserved.
30 * Licensed under the MIT License. See License.txt in the project root for license information.
31 *--------------------------------------------------------------------------------------------*/
32// code copied and modified from https://github.com/microsoft/vscode/blob/1.47.3/src/vs/workbench/services/authentication/browser/authenticationService.ts
33const inversify_1 = require("inversify");
34const event_1 = require("../common/event");
35const storage_service_1 = require("../browser/storage-service");
36const disposable_1 = require("../common/disposable");
37const menu_1 = require("../common/menu");
38const command_1 = require("../common/command");
39const nls_1 = require("../common/nls");
40exports.AuthenticationService = Symbol('AuthenticationService');
41let AuthenticationServiceImpl = class AuthenticationServiceImpl {
42 constructor() {
43 this.noAccountsCommand = { id: 'noAccounts' };
44 this.signInRequestItems = new Map();
45 this.sessionMap = new Map();
46 this.authenticationProviders = new Map();
47 this.onDidRegisterAuthenticationProviderEmitter = new event_1.Emitter();
48 this.onDidRegisterAuthenticationProvider = this.onDidRegisterAuthenticationProviderEmitter.event;
49 this.onDidUnregisterAuthenticationProviderEmitter = new event_1.Emitter();
50 this.onDidUnregisterAuthenticationProvider = this.onDidUnregisterAuthenticationProviderEmitter.event;
51 this.onDidChangeSessionsEmitter = new event_1.Emitter();
52 this.onDidChangeSessions = this.onDidChangeSessionsEmitter.event;
53 }
54 init() {
55 this.onDidChangeSessions(event => this.handleSessionChange(event));
56 this.commands.registerCommand(this.noAccountsCommand, {
57 execute: () => { },
58 isEnabled: () => false
59 });
60 }
61 async handleSessionChange(changeEvent) {
62 var _a;
63 if (changeEvent.event.added && changeEvent.event.added.length > 0) {
64 const sessions = await this.getSessions(changeEvent.providerId);
65 sessions.forEach(session => {
66 if (!this.sessionMap.get(session.id)) {
67 this.sessionMap.set(session.id, this.createAccountUi(changeEvent.providerId, changeEvent.label, session));
68 }
69 });
70 }
71 for (const removed of changeEvent.event.removed || []) {
72 const sessionId = typeof removed === 'string' ? removed : removed === null || removed === void 0 ? void 0 : removed.id;
73 if (sessionId) {
74 (_a = this.sessionMap.get(sessionId)) === null || _a === void 0 ? void 0 : _a.dispose();
75 this.sessionMap.delete(sessionId);
76 }
77 }
78 }
79 createAccountUi(providerId, providerLabel, session) {
80 // unregister old commands and menus if present (there is only one per account but there may be several sessions per account)
81 const providerAccountId = `account-sign-out-${providerId}-${session.account.id}`;
82 this.commands.unregisterCommand(providerAccountId);
83 const providerAccountSubmenu = [...menu_1.ACCOUNTS_SUBMENU, providerAccountId];
84 this.menus.unregisterMenuAction({ commandId: providerAccountId }, providerAccountSubmenu);
85 // register new command and menu entry for the sessions account
86 const disposables = new disposable_1.DisposableCollection();
87 disposables.push(this.commands.registerCommand({ id: providerAccountId }, {
88 execute: async () => {
89 this.signOutOfAccount(providerId, session.account.label);
90 }
91 }));
92 this.menus.registerSubmenu(providerAccountSubmenu, `${session.account.label} (${providerLabel})`);
93 disposables.push(this.menus.registerMenuAction(providerAccountSubmenu, {
94 label: nls_1.nls.localizeByDefault('Sign Out'),
95 commandId: providerAccountId
96 }));
97 return disposables;
98 }
99 getProviderIds() {
100 const providerIds = [];
101 this.authenticationProviders.forEach(provider => {
102 providerIds.push(provider.id);
103 });
104 return providerIds;
105 }
106 isAuthenticationProviderRegistered(id) {
107 return this.authenticationProviders.has(id);
108 }
109 updateAccountsMenuItem() {
110 let hasSession = false;
111 this.authenticationProviders.forEach(async (provider) => {
112 hasSession = hasSession || provider.hasSessions();
113 });
114 if (hasSession && this.noAccountsMenuItem) {
115 this.noAccountsMenuItem.dispose();
116 this.noAccountsMenuItem = undefined;
117 }
118 if (!hasSession && !this.noAccountsMenuItem) {
119 this.noAccountsMenuItem = this.menus.registerMenuAction(menu_1.ACCOUNTS_MENU, {
120 label: 'You are not signed in to any accounts',
121 order: '0',
122 commandId: this.noAccountsCommand.id
123 });
124 }
125 }
126 registerAuthenticationProvider(id, authenticationProvider) {
127 if (this.authenticationProviders.get(id)) {
128 throw new Error(`An authentication provider with id '${id}' is already registered.`);
129 }
130 this.authenticationProviders.set(id, authenticationProvider);
131 this.onDidRegisterAuthenticationProviderEmitter.fire({ id, label: authenticationProvider.label });
132 this.updateAccountsMenuItem();
133 console.log(`An authentication provider with id '${id}' was registered.`);
134 }
135 unregisterAuthenticationProvider(id) {
136 const provider = this.authenticationProviders.get(id);
137 if (provider) {
138 this.authenticationProviders.delete(id);
139 this.onDidUnregisterAuthenticationProviderEmitter.fire({ id, label: provider.label });
140 this.updateAccountsMenuItem();
141 }
142 else {
143 console.error(`Failed to unregister an authentication provider. A provider with id '${id}' was not found.`);
144 }
145 }
146 async updateSessions(id, event) {
147 const provider = this.authenticationProviders.get(id);
148 if (provider) {
149 await provider.updateSessionItems(event);
150 this.onDidChangeSessionsEmitter.fire({ providerId: id, label: provider.label, event: event });
151 this.updateAccountsMenuItem();
152 if (event.added) {
153 await this.updateNewSessionRequests(provider);
154 }
155 }
156 else {
157 console.error(`Failed to update an authentication session. An authentication provider with id '${id}' was not found.`);
158 }
159 }
160 async updateNewSessionRequests(provider) {
161 const existingRequestsForProvider = this.signInRequestItems.get(provider.id);
162 if (!existingRequestsForProvider) {
163 return;
164 }
165 const sessions = await provider.getSessions();
166 Object.keys(existingRequestsForProvider).forEach(requestedScopes => {
167 if (sessions.some(session => session.scopes.slice().sort().join('') === requestedScopes)) {
168 const sessionRequest = existingRequestsForProvider[requestedScopes];
169 if (sessionRequest) {
170 sessionRequest.disposables.forEach(item => item.dispose());
171 }
172 delete existingRequestsForProvider[requestedScopes];
173 if (Object.keys(existingRequestsForProvider).length === 0) {
174 this.signInRequestItems.delete(provider.id);
175 }
176 else {
177 this.signInRequestItems.set(provider.id, existingRequestsForProvider);
178 }
179 }
180 });
181 }
182 async requestNewSession(providerId, scopes, extensionId, extensionName) {
183 let provider = this.authenticationProviders.get(providerId);
184 if (!provider) {
185 // Activate has already been called for the authentication provider, but it cannot block on registering itself
186 // since this is sync and returns a disposable. So, wait for registration event to fire that indicates the
187 // provider is now in the map.
188 await new Promise((resolve, _) => {
189 this.onDidRegisterAuthenticationProvider(e => {
190 if (e.id === providerId) {
191 provider = this.authenticationProviders.get(providerId);
192 resolve(undefined);
193 }
194 });
195 });
196 }
197 if (provider) {
198 const providerRequests = this.signInRequestItems.get(providerId);
199 const scopesList = scopes.sort().join('');
200 const extensionHasExistingRequest = providerRequests
201 && providerRequests[scopesList]
202 && providerRequests[scopesList].requestingExtensionIds.indexOf(extensionId) > -1;
203 if (extensionHasExistingRequest) {
204 return;
205 }
206 const menuItem = this.menus.registerMenuAction(menu_1.ACCOUNTS_SUBMENU, {
207 label: `Sign in to use ${extensionName} (1)`,
208 order: '1',
209 commandId: `${extensionId}signIn`,
210 });
211 const signInCommand = this.commands.registerCommand({ id: `${extensionId}signIn` }, {
212 execute: async () => {
213 const session = await this.login(providerId, scopes);
214 // Add extension to allow list since user explicitly signed in on behalf of it
215 const allowList = await readAllowedExtensions(this.storageService, providerId, session.account.label);
216 if (!allowList.find(allowed => allowed.id === extensionId)) {
217 allowList.push({ id: extensionId, name: extensionName });
218 this.storageService.setData(`authentication-trusted-extensions-${providerId}-${session.account.label}`, JSON.stringify(allowList));
219 }
220 // And also set it as the preferred account for the extension
221 this.storageService.setData(`authentication-session-${extensionName}-${providerId}`, session.id);
222 }
223 });
224 if (providerRequests) {
225 const existingRequest = providerRequests[scopesList] || { disposables: [], requestingExtensionIds: [] };
226 providerRequests[scopesList] = {
227 disposables: [...existingRequest.disposables, menuItem, signInCommand],
228 requestingExtensionIds: [...existingRequest.requestingExtensionIds, extensionId]
229 };
230 this.signInRequestItems.set(providerId, providerRequests);
231 }
232 else {
233 this.signInRequestItems.set(providerId, {
234 [scopesList]: {
235 disposables: [menuItem, signInCommand],
236 requestingExtensionIds: [extensionId]
237 }
238 });
239 }
240 }
241 }
242 getLabel(id) {
243 const authProvider = this.authenticationProviders.get(id);
244 if (authProvider) {
245 return authProvider.label;
246 }
247 else {
248 throw new Error(`No authentication provider '${id}' is currently registered.`);
249 }
250 }
251 supportsMultipleAccounts(id) {
252 const authProvider = this.authenticationProviders.get(id);
253 if (authProvider) {
254 return authProvider.supportsMultipleAccounts;
255 }
256 else {
257 throw new Error(`No authentication provider '${id}' is currently registered.`);
258 }
259 }
260 async getSessions(id, scopes) {
261 const authProvider = this.authenticationProviders.get(id);
262 if (authProvider) {
263 return authProvider.getSessions(scopes);
264 }
265 else {
266 throw new Error(`No authentication provider '${id}' is currently registered.`);
267 }
268 }
269 async login(id, scopes) {
270 const authProvider = this.authenticationProviders.get(id);
271 if (authProvider) {
272 return authProvider.createSession(scopes);
273 }
274 else {
275 throw new Error(`No authentication provider '${id}' is currently registered.`);
276 }
277 }
278 async logout(id, sessionId) {
279 const authProvider = this.authenticationProviders.get(id);
280 if (authProvider) {
281 return authProvider.removeSession(sessionId);
282 }
283 else {
284 throw new Error(`No authentication provider '${id}' is currently registered.`);
285 }
286 }
287 async signOutOfAccount(id, accountName) {
288 const authProvider = this.authenticationProviders.get(id);
289 if (authProvider) {
290 return authProvider.signOut(accountName);
291 }
292 else {
293 throw new Error(`No authentication provider '${id}' is currently registered.`);
294 }
295 }
296};
297__decorate([
298 (0, inversify_1.inject)(menu_1.MenuModelRegistry),
299 __metadata("design:type", menu_1.MenuModelRegistry)
300], AuthenticationServiceImpl.prototype, "menus", void 0);
301__decorate([
302 (0, inversify_1.inject)(command_1.CommandRegistry),
303 __metadata("design:type", command_1.CommandRegistry)
304], AuthenticationServiceImpl.prototype, "commands", void 0);
305__decorate([
306 (0, inversify_1.inject)(storage_service_1.StorageService),
307 __metadata("design:type", Object)
308], AuthenticationServiceImpl.prototype, "storageService", void 0);
309__decorate([
310 (0, inversify_1.postConstruct)(),
311 __metadata("design:type", Function),
312 __metadata("design:paramtypes", []),
313 __metadata("design:returntype", void 0)
314], AuthenticationServiceImpl.prototype, "init", null);
315AuthenticationServiceImpl = __decorate([
316 (0, inversify_1.injectable)()
317], AuthenticationServiceImpl);
318exports.AuthenticationServiceImpl = AuthenticationServiceImpl;
319async function readAllowedExtensions(storageService, providerId, accountName) {
320 let trustedExtensions = [];
321 try {
322 const trustedExtensionSrc = await storageService.getData(`authentication-trusted-extensions-${providerId}-${accountName}`);
323 if (trustedExtensionSrc) {
324 trustedExtensions = JSON.parse(trustedExtensionSrc);
325 }
326 }
327 catch (err) {
328 console.error(err);
329 }
330 return trustedExtensions;
331}
332exports.readAllowedExtensions = readAllowedExtensions;
333//# sourceMappingURL=authentication-service.js.map
\No newline at end of file