UNPKG

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