Path: blob/main/src/vs/workbench/services/authentication/test/browser/authenticationService.test.ts
5251 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { Emitter, Event } from '../../../../../base/common/event.js';7import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';8import { URI } from '../../../../../base/common/uri.js';9import { AuthenticationAccessService } from '../../browser/authenticationAccessService.js';10import { AuthenticationService } from '../../browser/authenticationService.js';11import { AuthenticationProviderInformation, AuthenticationSessionsChangeEvent, IAuthenticationProvider } from '../../common/authentication.js';12import { TestEnvironmentService } from '../../../../test/browser/workbenchTestServices.js';13import { TestExtensionService, TestProductService, TestStorageService } from '../../../../test/common/workbenchTestServices.js';14import { NullLogService } from '../../../../../platform/log/common/log.js';1516function createSession() {17return { id: 'session1', accessToken: 'token1', account: { id: 'account', label: 'Account' }, scopes: ['test'] };18}1920function createProvider(overrides: Partial<IAuthenticationProvider> = {}): IAuthenticationProvider {21return {22supportsMultipleAccounts: false,23onDidChangeSessions: new Emitter<AuthenticationSessionsChangeEvent>().event,24id: 'test',25label: 'Test',26getSessions: async () => [],27createSession: async () => createSession(),28removeSession: async () => { },29...overrides30};31}3233suite('AuthenticationService', () => {34const disposables = ensureNoDisposablesAreLeakedInTestSuite();3536let authenticationService: AuthenticationService;3738setup(() => {39const storageService = disposables.add(new TestStorageService());40const authenticationAccessService = disposables.add(new AuthenticationAccessService(storageService, TestProductService));41authenticationService = disposables.add(new AuthenticationService(new TestExtensionService(), authenticationAccessService, TestEnvironmentService, new NullLogService()));42});4344teardown(() => {45// Dispose the authentication service after each test46authenticationService.dispose();47});4849suite('declaredAuthenticationProviders', () => {50test('registerDeclaredAuthenticationProvider', async () => {51const changed = Event.toPromise(authenticationService.onDidChangeDeclaredProviders);52const provider: AuthenticationProviderInformation = {53id: 'github',54label: 'GitHub'55};56authenticationService.registerDeclaredAuthenticationProvider(provider);5758// Assert that the provider is added to the declaredProviders array and the event fires59assert.equal(authenticationService.declaredProviders.length, 1);60assert.deepEqual(authenticationService.declaredProviders[0], provider);61await changed;62});6364test('unregisterDeclaredAuthenticationProvider', async () => {65const provider: AuthenticationProviderInformation = {66id: 'github',67label: 'GitHub'68};69authenticationService.registerDeclaredAuthenticationProvider(provider);70const changed = Event.toPromise(authenticationService.onDidChangeDeclaredProviders);71authenticationService.unregisterDeclaredAuthenticationProvider(provider.id);7273// Assert that the provider is removed from the declaredProviders array and the event fires74assert.equal(authenticationService.declaredProviders.length, 0);75await changed;76});77});7879suite('authenticationProviders', () => {80test('isAuthenticationProviderRegistered', async () => {81const registered = Event.toPromise(authenticationService.onDidRegisterAuthenticationProvider);82const provider = createProvider();83assert.equal(authenticationService.isAuthenticationProviderRegistered(provider.id), false);84authenticationService.registerAuthenticationProvider(provider.id, provider);85assert.equal(authenticationService.isAuthenticationProviderRegistered(provider.id), true);86const result = await registered;87assert.deepEqual(result, { id: provider.id, label: provider.label });88});8990test('unregisterAuthenticationProvider', async () => {91const unregistered = Event.toPromise(authenticationService.onDidUnregisterAuthenticationProvider);92const provider = createProvider();93authenticationService.registerAuthenticationProvider(provider.id, provider);94assert.equal(authenticationService.isAuthenticationProviderRegistered(provider.id), true);95authenticationService.unregisterAuthenticationProvider(provider.id);96assert.equal(authenticationService.isAuthenticationProviderRegistered(provider.id), false);97const result = await unregistered;98assert.deepEqual(result, { id: provider.id, label: provider.label });99});100101test('getProviderIds', () => {102const provider1 = createProvider({103id: 'provider1',104label: 'Provider 1'105});106const provider2 = createProvider({107id: 'provider2',108label: 'Provider 2'109});110111authenticationService.registerAuthenticationProvider(provider1.id, provider1);112authenticationService.registerAuthenticationProvider(provider2.id, provider2);113114const providerIds = authenticationService.getProviderIds();115116// Assert that the providerIds array contains the registered provider ids117assert.deepEqual(providerIds, [provider1.id, provider2.id]);118});119120test('getProvider', () => {121const provider = createProvider();122123authenticationService.registerAuthenticationProvider(provider.id, provider);124125const retrievedProvider = authenticationService.getProvider(provider.id);126127// Assert that the retrieved provider is the same as the registered provider128assert.deepEqual(retrievedProvider, provider);129});130131test('getOrActivateProviderIdForServer - should return undefined when no provider matches the authorization server', async () => {132const authorizationServer = URI.parse('https://example.com');133const result = await authenticationService.getOrActivateProviderIdForServer(authorizationServer);134assert.strictEqual(result, undefined);135});136137test('getOrActivateProviderIdForServer - should return provider id if authorizationServerGlobs matches and authorizationServers match', async () => {138// Register a declared provider with an authorization server glob139const provider: AuthenticationProviderInformation = {140id: 'github',141label: 'GitHub',142authorizationServerGlobs: ['https://github.com/*']143};144authenticationService.registerDeclaredAuthenticationProvider(provider);145146// Register an authentication provider with matching authorization servers147const authProvider = createProvider({148id: 'github',149label: 'GitHub',150authorizationServers: [URI.parse('https://github.com/login')]151});152authenticationService.registerAuthenticationProvider('github', authProvider);153154// Test with a matching URI155const authorizationServer = URI.parse('https://github.com/login');156const result = await authenticationService.getOrActivateProviderIdForServer(authorizationServer);157158// Verify the result159assert.strictEqual(result, 'github');160});161162test('getOrActivateProviderIdForServer - should return undefined if authorizationServerGlobs match but authorizationServers do not match', async () => {163// Register a declared provider with an authorization server glob164const provider: AuthenticationProviderInformation = {165id: 'github',166label: 'GitHub',167authorizationServerGlobs: ['https://github.com/*']168};169authenticationService.registerDeclaredAuthenticationProvider(provider);170171// Register an authentication provider with non-matching authorization servers172const authProvider = createProvider({173id: 'github',174label: 'GitHub',175authorizationServers: [URI.parse('https://github.com/different')]176});177authenticationService.registerAuthenticationProvider('github', authProvider);178179// Test with a non-matching URI180const authorizationServer = URI.parse('https://github.com/login');181const result = await authenticationService.getOrActivateProviderIdForServer(authorizationServer);182183// Verify the result184assert.strictEqual(result, undefined);185});186187test('getOrActivateProviderIdForAuthorizationServer - should check multiple providers and return the first match', async () => {188// Register two declared providers with authorization server globs189const provider1: AuthenticationProviderInformation = {190id: 'github',191label: 'GitHub',192authorizationServerGlobs: ['https://github.com/*']193};194const provider2: AuthenticationProviderInformation = {195id: 'microsoft',196label: 'Microsoft',197authorizationServerGlobs: ['https://login.microsoftonline.com/*']198};199authenticationService.registerDeclaredAuthenticationProvider(provider1);200authenticationService.registerDeclaredAuthenticationProvider(provider2);201202// Register authentication providers203const githubProvider = createProvider({204id: 'github',205label: 'GitHub',206authorizationServers: [URI.parse('https://github.com/different')]207});208authenticationService.registerAuthenticationProvider('github', githubProvider);209210const microsoftProvider = createProvider({211id: 'microsoft',212label: 'Microsoft',213authorizationServers: [URI.parse('https://login.microsoftonline.com/common')]214});215authenticationService.registerAuthenticationProvider('microsoft', microsoftProvider);216217// Test with a URI that should match the second provider218const authorizationServer = URI.parse('https://login.microsoftonline.com/common');219const result = await authenticationService.getOrActivateProviderIdForServer(authorizationServer);220221// Verify the result222assert.strictEqual(result, 'microsoft');223});224225test('getOrActivateProviderIdForServer - should match when resourceServer matches provider resourceServer', async () => {226const authorizationServer = URI.parse('https://login.microsoftonline.com/common');227const resourceServer = URI.parse('https://graph.microsoft.com');228229// Register an authentication provider with a resourceServer230const authProvider = createProvider({231id: 'microsoft',232label: 'Microsoft',233authorizationServers: [authorizationServer],234resourceServer: resourceServer235});236authenticationService.registerAuthenticationProvider('microsoft', authProvider);237238// Test with matching authorization server and resource server239const result = await authenticationService.getOrActivateProviderIdForServer(authorizationServer, resourceServer);240241// Verify the result242assert.strictEqual(result, 'microsoft');243});244245test('getOrActivateProviderIdForServer - should not match when resourceServer does not match provider resourceServer', async () => {246const authorizationServer = URI.parse('https://login.microsoftonline.com/common');247const resourceServer = URI.parse('https://graph.microsoft.com');248const differentResourceServer = URI.parse('https://vault.azure.net');249250// Register an authentication provider with a resourceServer251const authProvider = createProvider({252id: 'microsoft',253label: 'Microsoft',254authorizationServers: [authorizationServer],255resourceServer: resourceServer256});257authenticationService.registerAuthenticationProvider('microsoft', authProvider);258259// Test with matching authorization server but different resource server260const result = await authenticationService.getOrActivateProviderIdForServer(authorizationServer, differentResourceServer);261262// Verify the result - should not match because resource servers don't match263assert.strictEqual(result, undefined);264});265266test('getOrActivateProviderIdForServer - should match when provider has no resourceServer and resourceServer is provided', async () => {267const authorizationServer = URI.parse('https://login.microsoftonline.com/common');268const resourceServer = URI.parse('https://graph.microsoft.com');269270// Register an authentication provider without a resourceServer271const authProvider = createProvider({272id: 'microsoft',273label: 'Microsoft',274authorizationServers: [authorizationServer]275});276authenticationService.registerAuthenticationProvider('microsoft', authProvider);277278// Test with matching authorization server and a resource server279// Should match because provider has no resourceServer defined280const result = await authenticationService.getOrActivateProviderIdForServer(authorizationServer, resourceServer);281282// Verify the result283assert.strictEqual(result, 'microsoft');284});285286test('getOrActivateProviderIdForServer - should match when provider has resourceServer but no resourceServer is provided', async () => {287const authorizationServer = URI.parse('https://login.microsoftonline.com/common');288const resourceServer = URI.parse('https://graph.microsoft.com');289290// Register an authentication provider with a resourceServer291const authProvider = createProvider({292id: 'microsoft',293label: 'Microsoft',294authorizationServers: [authorizationServer],295resourceServer: resourceServer296});297authenticationService.registerAuthenticationProvider('microsoft', authProvider);298299// Test with matching authorization server but no resource server provided300// Should match because no resourceServer is provided to check against301const result = await authenticationService.getOrActivateProviderIdForServer(authorizationServer);302303// Verify the result304assert.strictEqual(result, 'microsoft');305});306307test('getOrActivateProviderIdForServer - should distinguish between providers with same authorization server but different resource servers', async () => {308const authorizationServer = URI.parse('https://login.microsoftonline.com/common');309const graphResourceServer = URI.parse('https://graph.microsoft.com');310const vaultResourceServer = URI.parse('https://vault.azure.net');311312// Register first provider with Graph resource server313const graphProvider = createProvider({314id: 'microsoft-graph',315label: 'Microsoft Graph',316authorizationServers: [authorizationServer],317resourceServer: graphResourceServer318});319authenticationService.registerAuthenticationProvider('microsoft-graph', graphProvider);320321// Register second provider with Vault resource server322const vaultProvider = createProvider({323id: 'microsoft-vault',324label: 'Microsoft Vault',325authorizationServers: [authorizationServer],326resourceServer: vaultResourceServer327});328authenticationService.registerAuthenticationProvider('microsoft-vault', vaultProvider);329330// Test with Graph resource server - should match the first provider331const graphResult = await authenticationService.getOrActivateProviderIdForServer(authorizationServer, graphResourceServer);332assert.strictEqual(graphResult, 'microsoft-graph');333334// Test with Vault resource server - should match the second provider335const vaultResult = await authenticationService.getOrActivateProviderIdForServer(authorizationServer, vaultResourceServer);336assert.strictEqual(vaultResult, 'microsoft-vault');337338// Test with different resource server - should not match either339const otherResourceServer = URI.parse('https://storage.azure.com');340const noMatchResult = await authenticationService.getOrActivateProviderIdForServer(authorizationServer, otherResourceServer);341assert.strictEqual(noMatchResult, undefined);342});343});344345suite('authenticationSessions', () => {346test('getSessions - base case', async () => {347let isCalled = false;348const provider = createProvider({349getSessions: async () => {350isCalled = true;351return [createSession()];352},353});354authenticationService.registerAuthenticationProvider(provider.id, provider);355const sessions = await authenticationService.getSessions(provider.id);356357assert.equal(sessions.length, 1);358assert.ok(isCalled);359});360361test('getSessions - authorization server is not registered', async () => {362let isCalled = false;363const provider = createProvider({364getSessions: async () => {365isCalled = true;366return [createSession()];367},368});369authenticationService.registerAuthenticationProvider(provider.id, provider);370assert.rejects(() => authenticationService.getSessions(provider.id, [], { authorizationServer: URI.parse('https://example.com') }));371assert.ok(!isCalled);372});373374test('createSession', async () => {375const emitter = new Emitter<AuthenticationSessionsChangeEvent>();376const provider = createProvider({377onDidChangeSessions: emitter.event,378createSession: async () => {379const session = createSession();380emitter.fire({ added: [session], removed: [], changed: [] });381return session;382},383});384const changed = Event.toPromise(authenticationService.onDidChangeSessions);385authenticationService.registerAuthenticationProvider(provider.id, provider);386const session = await authenticationService.createSession(provider.id, ['repo']);387388// Assert that the created session matches the expected session and the event fires389assert.ok(session);390const result = await changed;391assert.deepEqual(result, {392providerId: provider.id,393label: provider.label,394event: { added: [session], removed: [], changed: [] }395});396});397398test('removeSession', async () => {399const emitter = new Emitter<AuthenticationSessionsChangeEvent>();400const session = createSession();401const provider = createProvider({402onDidChangeSessions: emitter.event,403removeSession: async () => emitter.fire({ added: [], removed: [session], changed: [] })404});405const changed = Event.toPromise(authenticationService.onDidChangeSessions);406authenticationService.registerAuthenticationProvider(provider.id, provider);407await authenticationService.removeSession(provider.id, session.id);408409const result = await changed;410assert.deepEqual(result, {411providerId: provider.id,412label: provider.label,413event: { added: [], removed: [session], changed: [] }414});415});416417test('onDidChangeSessions', async () => {418const emitter = new Emitter<AuthenticationSessionsChangeEvent>();419const provider = createProvider({420onDidChangeSessions: emitter.event,421getSessions: async () => []422});423authenticationService.registerAuthenticationProvider(provider.id, provider);424425const changed = Event.toPromise(authenticationService.onDidChangeSessions);426const session = createSession();427emitter.fire({ added: [], removed: [], changed: [session] });428429const result = await changed;430assert.deepEqual(result, {431providerId: provider.id,432label: provider.label,433event: { added: [], removed: [], changed: [session] }434});435});436});437});438439440