diff --git a/src/lib/azure/azurecredentials.ts b/src/lib/azure/azurecredentials.ts index ebfe926e9..4972016e3 100644 --- a/src/lib/azure/azurecredentials.ts +++ b/src/lib/azure/azurecredentials.ts @@ -3,6 +3,8 @@ import * as msRestNodeAuth from "@azure/ms-rest-nodeauth"; import { Config } from "../../config"; import { logger } from "../../logger"; import { AzureAccessOpts } from "../../types"; +import { build as buildError } from "../../lib/errorBuilder"; +import { errorStatusCode } from "../errorStatusCode"; const verifyConfigDefined = ( servicePrincipalId?: string, @@ -85,13 +87,21 @@ export const getManagementCredentials = async ( return undefined; } - // verifyConfigDefined has confirmed that these values are defined. - return msRestNodeAuth.loginWithServicePrincipalSecret( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - servicePrincipalId!, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - servicePrincipalPassword!, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - tenantId! - ); + try { + // verifyConfigDefined has confirmed that these values are defined. + return await msRestNodeAuth.loginWithServicePrincipalSecret( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + servicePrincipalId!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + servicePrincipalPassword!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + tenantId! + ); + } catch (err) { + throw buildError( + errorStatusCode.AZURE_CLIENT, + "azure-client-auth-sp-err", + err + ); + } }; diff --git a/src/lib/azure/storage.test.ts b/src/lib/azure/storage.test.ts index 09b4a3104..fbbb489f8 100644 --- a/src/lib/azure/storage.test.ts +++ b/src/lib/azure/storage.test.ts @@ -2,10 +2,16 @@ jest.mock("@azure/arm-storage"); jest.mock("azure-storage"); jest.mock("../../config"); +import * as msRestNodeAuth from "@azure/ms-rest-nodeauth"; import uuid from "uuid/v4"; import { disableVerboseLogging, enableVerboseLogging } from "../../logger"; import { Config } from "../../config"; -import { getStorageAccount, validateStorageAccount } from "./storage"; +import * as config from "../../config"; +import { + getStorageAccount, + getStorageManagementClient, + validateStorageAccount, +} from "./storage"; import * as storage from "./storage"; import * as azureStorage from "azure-storage"; import { getErrorMessage } from "../../lib/errorBuilder"; @@ -14,6 +20,17 @@ const resourceGroupName = uuid(); const storageAccountName = uuid(); const location = uuid(); +jest.mock("@azure/arm-storage", () => { + class MockClient { + constructor() { + return {}; + } + } + return { + StorageManagementClient: MockClient, + }; +}); + (Config as jest.Mock).mockReturnValue({ introspection: { azure: { @@ -373,3 +390,71 @@ describe("test validateStorageAccount function", () => { expect(res).toBe(true); }); }); + +describe("test getStorageManagementClient function", () => { + it("negative test: missing credential", async () => { + jest.spyOn(config, "Config").mockReturnValueOnce({}); + await expect(getStorageManagementClient({})).rejects.toThrow( + getErrorMessage("storage-client-err-missing-creds") + ); + }); + it("negative test: incorrect credential", async () => { + jest.spyOn(config, "Config").mockReturnValueOnce({}); + await expect( + getStorageManagementClient({ + servicePrincipalId: "servicePrincipalId", + servicePrincipalPassword: "servicePrincipalPassword", + tenantId: "tenantId", + }) + ).rejects.toThrow(getErrorMessage("azure-client-auth-sp-err")); + }); + it("negative test: authentication to management client failed", async () => { + jest.spyOn(config, "Config").mockReturnValueOnce({}); + jest + .spyOn(msRestNodeAuth, "loginWithServicePrincipalSecret") + .mockResolvedValueOnce(null as never); + await expect( + getStorageManagementClient({ + servicePrincipalId: "servicePrincipalId", + servicePrincipalPassword: "servicePrincipalPassword", + tenantId: "tenantId", + }) + ).rejects.toThrow(getErrorMessage("storage-client-err-missing-creds")); + }); + it("negative test: missing storage cred.", async () => { + jest.spyOn(config, "Config").mockReturnValueOnce({}); + jest.spyOn(config, "Config").mockReturnValueOnce({}); + jest + .spyOn(msRestNodeAuth, "loginWithServicePrincipalSecret") + .mockResolvedValueOnce({} as never); + await expect( + getStorageManagementClient({ + servicePrincipalId: "servicePrincipalId", + servicePrincipalPassword: "servicePrincipalPassword", + tenantId: "tenantId", + }) + ).rejects.toThrow(getErrorMessage("storage-client-err-missing-sub-id")); + }); + it("positive test: missing storage cred.", async () => { + jest.spyOn(config, "Config").mockReturnValueOnce({}); + jest.spyOn(config, "Config").mockReturnValueOnce({ + introspection: { + azure: { + subscription_id: "something", + }, + }, + }); + jest + .spyOn(msRestNodeAuth, "loginWithServicePrincipalSecret") + .mockResolvedValueOnce({} as never); + await getStorageManagementClient({ + servicePrincipalId: "servicePrincipalId", + servicePrincipalPassword: "servicePrincipalPassword", + tenantId: "tenantId", + }); + }); + it("positive test: client should be cached.", async () => { + const client = await getStorageManagementClient(); // cached copy will be returned + expect(client).toBeDefined(); + }); +}); diff --git a/src/lib/i18n.json b/src/lib/i18n.json index 8f5436211..c112feae4 100644 --- a/src/lib/i18n.json +++ b/src/lib/i18n.json @@ -310,6 +310,7 @@ "service-endpoint-err-create-missing-name": "Could not create service endpoint because name was missing.", "service-endpoint-err-create": "Could not create service endpoint.", + "azure-client-auth-sp-err": "Could not authenticate with service principal credential.", "azure-client-get-web-api-err-missing-access-token": "Could not get azure web API because personal access token was missing. Provide it.", "azure-client-get-web-api-err-missing-org": "Could not get azure web API because organization name was missing. Provide it.", "azure-client-get-rest-client-err": "Could not get REST client. Check the Azure credential",