-
Notifications
You must be signed in to change notification settings - Fork 47
Azure Resources API (v4) internal tests #1288
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: mwf/v4-client-tools
Are you sure you want to change the base?
Changes from 28 commits
0b31112
71f4f14
f188eca
e71785c
3f4cb3a
34d2f1a
c34d952
5b0d57a
4c49c09
0b1d03e
570ecf4
b446718
0a7b447
3cf4fa3
69051c0
e433035
1718244
06e2b99
ef7ad23
97b1239
eda6ce6
b5eb5f9
81ce320
5c11b97
ff9d912
1e9269d
8edafe9
0d63c1c
56385e1
1039782
0aa6f1c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| * Licensed under the MIT License. See License.txt in the project root for license information. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
|
|
||
| import { AzExtCredentialManager } from "../../../api/src/auth/credentialManager/AzExtCredentialManager"; | ||
| import { maskValue } from "../../../api/src/utils/maskValue"; | ||
|
|
||
| /** | ||
| * A mock credential manager with the same implementation as `AzExtUUIDCredentialManager`, | ||
| * but with a public getter to inspect the UUIDs during test. | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A less copy and paste mock would be possible if I extended from the |
||
| */ | ||
| export class MockUUIDCredentialManager implements AzExtCredentialManager { | ||
| #uuidMap: Map<string, string> = new Map(); | ||
|
|
||
| get uuidMap() { | ||
| return this.#uuidMap; | ||
| } | ||
|
|
||
| createCredential(extensionId: string): string { | ||
| const uuid: string = crypto.randomUUID(); | ||
| this.#uuidMap.set(extensionId, uuid); | ||
| return uuid; | ||
| } | ||
|
|
||
| verifyCredential(credential: string, extensionId: string): boolean { | ||
| if (!credential || !extensionId) { | ||
| return false; | ||
| } | ||
| return credential === this.#uuidMap.get(extensionId); | ||
| } | ||
|
|
||
| maskCredentials(data: string): string { | ||
| for (const uuid of this.#uuidMap.values()) { | ||
| data = maskValue(data, uuid); | ||
| } | ||
| return data; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| * Licensed under the MIT License. See License.txt in the project root for license information. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
|
|
||
| import { AzureExtensionApiFactory, createApiProvider } from "@microsoft/vscode-azext-utils"; | ||
| import { apiUtils, AzureExtensionApi, AzureResourcesExtensionAuthApi, GetApiOptions } from "../../../api/src"; | ||
| import { AuthApiFactoryDependencies, createAuthApiFactory } from "../../../src/api/auth/createAuthApiFactory"; | ||
|
|
||
| /** | ||
| * Creates a mock API provider with API factories matching the versions provided. | ||
| * Only the values required by the interface will be implemented. | ||
| */ | ||
| function createMockApiProvider(versions: string[]): apiUtils.AzureExtensionApiProvider { | ||
| const apiFactories: AzureExtensionApiFactory<AzureExtensionApi>[] = versions.map(version => { | ||
| return { | ||
| apiVersion: version, | ||
| createApi: (_options?: GetApiOptions) => { | ||
| return { | ||
| apiVersion: version, | ||
| }; | ||
| }, | ||
| }; | ||
| }); | ||
|
|
||
| return createApiProvider(apiFactories); | ||
| } | ||
|
|
||
| /** | ||
| * Creates a mock auth API protecting core API versions: ['0.0.1', '2.0.0', '3.0.0'] | ||
| */ | ||
| export function createMockAuthApi(customDependencies?: AuthApiFactoryDependencies): AzureResourcesExtensionAuthApi { | ||
| const coreApiVersions: string[] = ['0.0.1', '2.0.0', '3.0.0']; | ||
| const coreApiProvider = createMockApiProvider(coreApiVersions); | ||
| const authApiProvider = createAuthApiFactory(coreApiProvider, customDependencies); | ||
| return authApiProvider.createApi({ extensionId: 'ms-azuretools.vscode-azureresourcegroups-tests' }); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| * Licensed under the MIT License. See License.txt in the project root for license information. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
|
|
||
| import { nonNullValue, parseError } from "@microsoft/vscode-azext-utils"; | ||
| import * as assert from "assert"; | ||
| import { apiUtils, AzureExtensionApi, AzureResourcesExtensionAuthApi } from "../../../api/src"; | ||
| import { assertThrowsAsync } from "../../wrapFunctionsInTelemetry.test"; | ||
| import { MockUUIDCredentialManager } from "./MockUUIDCredentialManager"; | ||
| import { createMockAuthApi } from "./mockAuthApi"; | ||
|
|
||
| const clientExtensionId: string = 'ms-azuretools.vscode-azurecontainerapps'; | ||
| const clientExtensionVersion: string = '1.0.0'; | ||
|
|
||
| suite('v4 internal API auth tests', async () => { | ||
| test('v4 API should be defined', async () => { | ||
| const apiProvider = await apiUtils.getExtensionExports<apiUtils.AzureExtensionApiProvider>('ms-azuretools.vscode-azureresourcegroups'); | ||
| assert.ok(apiProvider, 'API provider is undefined'); | ||
|
|
||
| const v4Api = apiProvider.getApi('^4.0.0', { extensionId: 'ms-azuretools.vscode-azureresourcegroups-tests' }); | ||
| assert.ok(v4Api); | ||
| }); | ||
|
|
||
| test('createAzureResourcesApiSession should provide a valid credential but not return it directly', async () => { | ||
| let apiSession: unknown; | ||
| let receivedHostCredential: string = ''; | ||
| let receivedClientCredential: string = ''; | ||
|
|
||
| const credentialManager = new MockUUIDCredentialManager(); | ||
| const generatedClientCredential: string = crypto.randomUUID(); | ||
|
|
||
| await new Promise<void>((resolve) => { | ||
| const timeout = setTimeout(resolve, 5000); | ||
|
|
||
| const mockClientExtensionApi: AzureExtensionApi = { | ||
| apiVersion: clientExtensionVersion, | ||
| receiveAzureResourcesApiSession: (hostCredential: string, clientCredential: string) => { | ||
| clearTimeout(timeout); | ||
| receivedHostCredential = hostCredential; | ||
| receivedClientCredential = clientCredential; | ||
| resolve(); | ||
| }, | ||
| }; | ||
|
|
||
| const authApi: AzureResourcesExtensionAuthApi = createMockAuthApi({ credentialManager, clientApiProvider: { getApi: () => mockClientExtensionApi } }); | ||
| authApi.createAzureResourcesApiSession(clientExtensionId, clientExtensionVersion, generatedClientCredential) | ||
| .then(session => apiSession = session) | ||
| .catch(() => { clearTimeout(timeout); resolve(); }); | ||
| }); | ||
|
|
||
| assert.equal(apiSession, undefined); | ||
| assert.equal(receivedClientCredential, generatedClientCredential); | ||
|
|
||
| const generatedHostCredential: string = nonNullValue(credentialManager.uuidMap.get(clientExtensionId)); | ||
| assert.equal(receivedHostCredential, generatedHostCredential); | ||
| }); | ||
|
|
||
| test('createAzureResourcesApiSession should throw if an unallowed extension id is provided', async () => { | ||
| const authApi: AzureResourcesExtensionAuthApi = createMockAuthApi(); | ||
| await assertThrowsAsync(async () => await authApi.createAzureResourcesApiSession('extension1', clientExtensionVersion, crypto.randomUUID())); | ||
| }); | ||
|
|
||
| test('createAzureResourcesApiSession should not spill sensitive extension credentials in errors', async () => { | ||
| const credentialManager = new MockUUIDCredentialManager(); | ||
| credentialManager.createCredential('extension1'); | ||
| credentialManager.createCredential = () => { | ||
| throw new Error(credentialManager.uuidMap.get('extension1')); | ||
| }; | ||
|
|
||
| const authApi: AzureResourcesExtensionAuthApi = createMockAuthApi({ credentialManager }); | ||
|
|
||
| try { | ||
| await authApi.createAzureResourcesApiSession(clientExtensionId, clientExtensionVersion, crypto.randomUUID()); | ||
| assert.fail('We expect the credential manager to throw in this test.'); | ||
| } catch (err) { | ||
| const perr = parseError(err); | ||
| assert.doesNotMatch(perr.message, new RegExp(nonNullValue(credentialManager.uuidMap.get('extension1')), 'i')); | ||
| } | ||
| }); | ||
|
|
||
| test('getAzureResourcesApis should return matching APIs if provided a valid credential', async () => { | ||
| const credentialManager = new MockUUIDCredentialManager(); | ||
| const generatedHostCredential: string = credentialManager.createCredential(clientExtensionId); | ||
|
|
||
| const authApi: AzureResourcesExtensionAuthApi = createMockAuthApi({ credentialManager }); | ||
| const resourcesApis = await authApi.getAzureResourcesApis(clientExtensionId, generatedHostCredential, ['0.0.1', '^2.0.0']); | ||
|
|
||
| assert.equal(resourcesApis[0]?.apiVersion, '0.0.1'); | ||
| assert.match(resourcesApis[1]?.apiVersion ?? '', /^2\./); | ||
| }); | ||
|
|
||
| test('getAzureResourcesApis should throw if provided an invalid credential', async () => { | ||
| const credentialManager = new MockUUIDCredentialManager(); | ||
| const authApi: AzureResourcesExtensionAuthApi = createMockAuthApi({ credentialManager }); | ||
| await assertThrowsAsync(async () => await authApi.getAzureResourcesApis(clientExtensionId, crypto.randomUUID(), ['^2.0.0'])); | ||
|
|
||
| credentialManager.createCredential(clientExtensionId); | ||
| await assertThrowsAsync(async () => await authApi.getAzureResourcesApis(clientExtensionId, crypto.randomUUID(), ['^2.0.0'])); | ||
| }); | ||
|
|
||
| test('getAzureResourcesApis should not spill sensitive extension credentials in errors', async () => { | ||
| const credentialManager = new MockUUIDCredentialManager(); | ||
| const authApi: AzureResourcesExtensionAuthApi = createMockAuthApi({ credentialManager }); | ||
|
|
||
| credentialManager.createCredential('extension1'); | ||
| credentialManager.createCredential('extension2'); | ||
| credentialManager.createCredential('extension3'); | ||
|
|
||
| try { | ||
| await authApi.getAzureResourcesApis(clientExtensionId, crypto.randomUUID(), ['^2.0.0']); | ||
| assert.fail('Should throw if requesting Azure Resources APIs without a valid credential.'); | ||
| } catch (err) { | ||
| const perr = parseError(err); | ||
| for (const credential of credentialManager.uuidMap.values()) { | ||
| assert.doesNotMatch(perr.message, new RegExp(credential, 'i')); | ||
| } | ||
| } | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,8 +3,9 @@ | |
| * Licensed under the MIT License. See LICENSE.md in the project root for license information. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
|
|
||
| import { registerOnActionStartHandler, TestUserInput } from '@microsoft/vscode-azext-utils'; | ||
| import { registerOnActionStartHandler, registerUIExtensionVariables, TestUserInput } from '@microsoft/vscode-azext-utils'; | ||
| import * as vscode from 'vscode'; | ||
| import { TestApi } from '../src/testApi'; | ||
| import { settingUtils } from '../src/utils/settingUtils'; | ||
| import { getTestApi } from './utils/testApiAccess'; | ||
|
|
||
|
|
@@ -19,7 +20,8 @@ suiteSetup(async function (this: Mocha.Context): Promise<void> { | |
| this.timeout(1 * 60 * 1000); | ||
|
|
||
| // Initialize test API - this caches it for use throughout tests | ||
| await getTestApi(); | ||
| const testApi: TestApi = await getTestApi(); | ||
| registerUIExtensionVariables(testApi.extensionVariables.getUI()); | ||
|
||
|
|
||
| await vscode.commands.executeCommand('azureResourceGroups.refresh'); // activate the extension before tests begin | ||
|
|
||
|
|
||


Uh oh!
There was an error while loading. Please reload this page.