Skip to content

Commit

Permalink
Apply vscode Authentication plugin API
Browse files Browse the repository at this point in the history
Signed-off-by: Igor Vinokur <[email protected]>
  • Loading branch information
vinokurig committed Aug 25, 2020
1 parent 4390a7d commit be223fb
Show file tree
Hide file tree
Showing 9 changed files with 1,053 additions and 2 deletions.
203 changes: 203 additions & 0 deletions packages/core/src/browser/authentication-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
/********************************************************************************
* 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
********************************************************************************/

/*---------------------------------------------------------------------------------------------
* 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

import { injectable } from 'inversify';
import { Emitter, Event } from '../common/event';
import { StorageService } from '../browser/storage-service';

export interface AuthenticationSession {
id: string;
accessToken: string;
account: {
displayName: string;
id: string;
}
scopes: string[];
}

export interface AuthenticationSessionsChangeEvent {
added: string[];
removed: string[];
changed: string[];
}

export interface AuthenticationProvider {
id: string;

supportsMultipleAccounts: boolean;

displayName: string;

hasSessions(): boolean;

signOut(accountName: string): Promise<void>;

getSessions(): Promise<ReadonlyArray<AuthenticationSession>>;

updateSessionItems(event: AuthenticationSessionsChangeEvent): Promise<void>;

login(scopes: string[]): Promise<AuthenticationSession>;

logout(sessionId: string): Promise<void>;
}
export const AuthenticationService = Symbol('AuthenticationService');

export interface AuthenticationService {
isAuthenticationProviderRegistered(id: string): boolean;
getProviderIds(): string[];
registerAuthenticationProvider(id: string, provider: AuthenticationProvider): void;
unregisterAuthenticationProvider(id: string): void;
sessionsUpdate(providerId: string, event: AuthenticationSessionsChangeEvent): void;

readonly onDidRegisterAuthenticationProvider: Event<string>;
readonly onDidUnregisterAuthenticationProvider: Event<string>;

readonly onDidChangeSessions: Event<{ providerId: string, event: AuthenticationSessionsChangeEvent }>;
getSessions(providerId: string): Promise<ReadonlyArray<AuthenticationSession>>;
getDisplayName(providerId: string): string;
supportsMultipleAccounts(providerId: string): boolean;
login(providerId: string, scopes: string[]): Promise<AuthenticationSession>;
logout(providerId: string, sessionId: string): Promise<void>;

signOutOfAccount(providerId: string, accountName: string): Promise<void>;
}

@injectable()
export class AuthenticationServiceImpl implements AuthenticationService {
private authenticationProviders: Map<string, AuthenticationProvider> = new Map<string, AuthenticationProvider>();

private onDidRegisterAuthenticationProviderEmitter: Emitter<string> = new Emitter<string>();
readonly onDidRegisterAuthenticationProvider: Event<string> = this.onDidRegisterAuthenticationProviderEmitter.event;

private onDidUnregisterAuthenticationProviderEmitter: Emitter<string> = new Emitter<string>();
readonly onDidUnregisterAuthenticationProvider: Event<string> = this.onDidUnregisterAuthenticationProviderEmitter.event;

private onDidChangeSessionsEmitter: Emitter<{ providerId: string, event: AuthenticationSessionsChangeEvent }> =
new Emitter<{ providerId: string, event: AuthenticationSessionsChangeEvent }>();
readonly onDidChangeSessions: Event<{ providerId: string, event: AuthenticationSessionsChangeEvent }> = this.onDidChangeSessionsEmitter.event;

getProviderIds(): string[] {
const providerIds: string[] = [];
this.authenticationProviders.forEach(provider => {
providerIds.push(provider.id);
});
return providerIds;
}

isAuthenticationProviderRegistered(id: string): boolean {
return this.authenticationProviders.has(id);
}

registerAuthenticationProvider(id: string, authenticationProvider: AuthenticationProvider): void {
this.authenticationProviders.set(id, authenticationProvider);
this.onDidRegisterAuthenticationProviderEmitter.fire(id);
}

unregisterAuthenticationProvider(id: string): void {
const provider = this.authenticationProviders.get(id);
if (provider) {
this.authenticationProviders.delete(id);
this.onDidUnregisterAuthenticationProviderEmitter.fire(id);
}
}

async sessionsUpdate(id: string, event: AuthenticationSessionsChangeEvent): Promise<void> {
this.onDidChangeSessionsEmitter.fire({ providerId: id, event: event });
const provider = this.authenticationProviders.get(id);
if (provider) {
await provider.updateSessionItems(event);
}
}

getDisplayName(id: string): string {
const authProvider = this.authenticationProviders.get(id);
if (authProvider) {
return authProvider.displayName;
} else {
throw new Error(`No authentication provider '${id}' is currently registered.`);
}
}

supportsMultipleAccounts(id: string): boolean {
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: string): Promise<ReadonlyArray<AuthenticationSession>> {
const authProvider = this.authenticationProviders.get(id);
if (authProvider) {
return await authProvider.getSessions();
} else {
throw new Error(`No authentication provider '${id}' is currently registered.`);
}
}

async login(id: string, scopes: string[]): Promise<AuthenticationSession> {
const authProvider = this.authenticationProviders.get(id);
if (authProvider) {
return authProvider.login(scopes);
} else {
throw new Error(`No authentication provider '${id}' is currently registered.`);
}
}

async logout(id: string, sessionId: string): Promise<void> {
const authProvider = this.authenticationProviders.get(id);
if (authProvider) {
return authProvider.logout(sessionId);
} else {
throw new Error(`No authentication provider '${id}' is currently registered.`);
}
}

async signOutOfAccount(id: string, accountName: string): Promise<void> {
const authProvider = this.authenticationProviders.get(id);
if (authProvider) {
return authProvider.signOut(accountName);
} else {
throw new Error(`No authentication provider '${id}' is currently registered.`);
}
}
}

export interface AllowedExtension {
id: string;
name: string;
}

export async function readAllowedExtensions(storageService: StorageService, providerId: string, accountName: string): Promise<AllowedExtension[]> {
let trustedExtensions: AllowedExtension[] = [];
try {
const trustedExtensionSrc: string | undefined = await storageService.getData(`${providerId}-${accountName}`);
if (trustedExtensionSrc) {
trustedExtensions = JSON.parse(trustedExtensionSrc);
}
} catch (err) {
console.log(err);
}

return trustedExtensions;
}
3 changes: 3 additions & 0 deletions packages/core/src/browser/frontend-application-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ import { CommandOpenHandler } from './command-open-handler';
import { LanguageService } from './language-service';
import { EncodingRegistry } from './encoding-registry';
import { EncodingService } from '../common/encoding-service';
import { AuthenticationService, AuthenticationServiceImpl } from '../browser/authentication-service';

export { bindResourceProvider, bindMessageService, bindPreferenceService };

Expand Down Expand Up @@ -336,4 +337,6 @@ export const frontendApplicationModule = new ContainerModule((bind, unbind, isBo
});

bind(ContextMenuContext).toSelf().inSingletonScope();

bind(AuthenticationService).to(AuthenticationServiceImpl).inSingletonScope();
});
31 changes: 30 additions & 1 deletion packages/plugin-ext/src/common/plugin-api-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ import {
} from './plugin-api-rpc-model';
import { ExtPluginApi } from './plugin-ext-api-contribution';
import { KeysToAnyValues, KeysToKeysToAnyValue } from './types';
import { CancellationToken, Progress, ProgressOptions } from '@theia/plugin';
import { AuthenticationSession, CancellationToken, Progress, ProgressOptions } from '@theia/plugin';
import { DebuggerDescription } from '@theia/debug/lib/common/debug-service';
import { DebugProtocol } from 'vscode-debugprotocol';
import { SymbolInformation } from 'vscode-languageserver-types';
Expand All @@ -78,6 +78,7 @@ import { QuickTitleButton } from '@theia/core/lib/common/quick-open-model';
import * as files from '@theia/filesystem/lib/common/files';
import { BinaryBuffer } from '@theia/core/lib/common/buffer';
import { ResourceLabelFormatter } from '@theia/core/lib/common/label-protocol';
import { AuthenticationSessionsChangeEvent } from '@theia/core/lib/browser/authentication-service';

export interface PreferenceData {
[scope: number]: any;
Expand Down Expand Up @@ -1423,6 +1424,7 @@ export interface ClipboardMain {
}

export const PLUGIN_RPC_CONTEXT = {
AUTHENTICATION_MAIN: <ProxyIdentifier<AuthenticationMain>>createProxyIdentifier<AuthenticationMain>('AuthenticationMain'),
COMMAND_REGISTRY_MAIN: <ProxyIdentifier<CommandRegistryMain>>createProxyIdentifier<CommandRegistryMain>('CommandRegistryMain'),
QUICK_OPEN_MAIN: createProxyIdentifier<QuickOpenMain>('QuickOpenMain'),
DIALOGS_MAIN: createProxyIdentifier<DialogsMain>('DialogsMain'),
Expand Down Expand Up @@ -1452,6 +1454,7 @@ export const PLUGIN_RPC_CONTEXT = {
};

export const MAIN_RPC_CONTEXT = {
AUTHENTICATION_EXT: createProxyIdentifier<AuthenticationExt>('AuthenticationExt'),
HOSTED_PLUGIN_MANAGER_EXT: createProxyIdentifier<PluginManagerExt>('PluginManagerExt'),
COMMAND_REGISTRY_EXT: createProxyIdentifier<CommandRegistryExt>('CommandRegistryExt'),
QUICK_OPEN_EXT: createProxyIdentifier<QuickOpenExt>('QuickOpenExt'),
Expand Down Expand Up @@ -1496,6 +1499,32 @@ export interface TasksMain {
$terminateTask(id: number): void;
}

export interface AuthenticationExt {
$getSessions(id: string): Promise<ReadonlyArray<theia.AuthenticationSession>>;
$getSessionAccessToken(id: string, sessionId: string): Promise<string>;
$login(id: string, scopes: string[]): Promise<theia.AuthenticationSession>;
$logout(id: string, sessionId: string): Promise<void>;
$onDidChangeAuthenticationSessions(providerId: string, event: theia.AuthenticationSessionsChangeEvent): Promise<void>;
$onDidChangeAuthenticationProviders(added: string[], removed: string[]): Promise<void>;
}

export interface AuthenticationMain {
$registerAuthenticationProvider(id: string, displayName: string, supportsMultipleAccounts: boolean): void;
$unregisterAuthenticationProvider(id: string): void;
$getProviderIds(): Promise<string[]>;
$sendDidChangeSessions(providerId: string, event: AuthenticationSessionsChangeEvent): void;
$getSession(providerId: string, scopes: string[], extensionId: string, extensionName: string,
options: { createIfNone?: boolean, clearSessionPreference?: boolean }): Promise<AuthenticationSession | undefined>;
$selectSession(providerId: string, providerName: string, extensionId: string, extensionName: string,
potentialSessions: AuthenticationSession[], scopes: string[], clearSessionPreference: boolean): Promise<AuthenticationSession>;
$getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean>;
$loginPrompt(providerName: string, extensionName: string): Promise<boolean>;
$setTrustedExtension(providerId: string, accountName: string, extensionId: string, extensionName: string): Promise<void>;

$getSessions(providerId: string): Promise<ReadonlyArray<theia.AuthenticationSession>>;
$logout(providerId: string, sessionId: string): Promise<void>;
}

export interface RawColorInfo {
color: [number, number, number, number];
range: Range;
Expand Down
Loading

0 comments on commit be223fb

Please sign in to comment.