azureAuth.ts strict nulls (#20583)

* azureAuth.ts strict nulls

* fix test compile
This commit is contained in:
Charles Gagnon
2022-09-12 14:44:31 -07:00
committed by GitHub
parent a4f023f5b9
commit 63aeb606bf
3 changed files with 42 additions and 31 deletions

View File

@@ -62,27 +62,31 @@ export abstract class AzureAuth implements vscode.Disposable {
this.resources = [ this.resources = [
this.metadata.settings.armResource, this.metadata.settings.armResource,
this.metadata.settings.sqlResource,
this.metadata.settings.graphResource, this.metadata.settings.graphResource,
this.metadata.settings.ossRdbmsResource,
this.metadata.settings.microsoftResource,
this.metadata.settings.azureKeyVaultResource this.metadata.settings.azureKeyVaultResource
]; ];
if (this.metadata.settings.sqlResource) {
this.resources.push(this.metadata.settings.sqlResource);
}
if (this.metadata.settings.ossRdbmsResource) {
this.resources.push(this.metadata.settings.ossRdbmsResource);
}
if (this.metadata.settings.microsoftResource) {
this.resources.push(this.metadata.settings.microsoftResource);
}
if (this.metadata.settings.azureDevOpsResource) { if (this.metadata.settings.azureDevOpsResource) {
this.resources = this.resources.concat(this.metadata.settings.azureDevOpsResource); this.resources.push(this.metadata.settings.azureDevOpsResource);
} }
if (this.metadata.settings.azureLogAnalyticsResource) { if (this.metadata.settings.azureLogAnalyticsResource) {
this.resources = this.resources.concat(this.metadata.settings.azureLogAnalyticsResource); this.resources.push(this.metadata.settings.azureLogAnalyticsResource);
} }
if (this.metadata.settings.azureKustoResource) { if (this.metadata.settings.azureKustoResource) {
this.resources = this.resources.concat(this.metadata.settings.azureKustoResource); this.resources.push(this.metadata.settings.azureKustoResource);
} }
if (this.metadata.settings.powerBiResource) { if (this.metadata.settings.powerBiResource) {
this.resources = this.resources.concat(this.metadata.settings.powerBiResource); this.resources.push(this.metadata.settings.powerBiResource);
} }
this.scopes = [...this.metadata.settings.scopes]; this.scopes = [...this.metadata.settings.scopes];
@@ -90,9 +94,12 @@ export abstract class AzureAuth implements vscode.Disposable {
} }
public async startLogin(): Promise<AzureAccount | azdata.PromptFailedResult> { public async startLogin(): Promise<AzureAccount | azdata.PromptFailedResult> {
let loginComplete: Deferred<void, Error>; let loginComplete: Deferred<void, Error> | undefined = undefined;
try { try {
Logger.verbose('Starting login'); Logger.verbose('Starting login');
if (!this.metadata.settings.microsoftResource) {
throw new Error(localize('noMicrosoftResource', "Provider '{0}' does not have a Microsoft resource endpoint defined.", this.metadata.displayName));
}
const result = await this.login(this.commonTenant, this.metadata.settings.microsoftResource); const result = await this.login(this.commonTenant, this.metadata.settings.microsoftResource);
loginComplete = result.authComplete; loginComplete = result.authComplete;
if (!result?.response) { if (!result?.response) {
@@ -220,6 +227,9 @@ export abstract class AzureAuth implements vscode.Disposable {
// User didn't have any cached tokens, or the cached tokens weren't useful. // User didn't have any cached tokens, or the cached tokens weren't useful.
// For most users we can use the refresh token from the general microsoft resource to an access token of basically any type of resource we want. // For most users we can use the refresh token from the general microsoft resource to an access token of basically any type of resource we want.
if (!this.metadata.settings.microsoftResource) {
throw new Error(localize('noMicrosoftResource', "Provider '{0}' does not have a Microsoft resource endpoint defined.", this.metadata.displayName));
}
const baseTokens = await this.getSavedToken(this.commonTenant, this.metadata.settings.microsoftResource, account.key); const baseTokens = await this.getSavedToken(this.commonTenant, this.metadata.settings.microsoftResource, account.key);
if (!baseTokens) { if (!baseTokens) {
Logger.error('User had no base tokens for the basic resource registered. This should not happen and indicates something went wrong with the authentication cycle'); Logger.error('User had no base tokens for the basic resource registered. This should not happen and indicates something went wrong with the authentication cycle');
@@ -251,7 +261,7 @@ export abstract class AzureAuth implements vscode.Disposable {
* @returns The oauth token response or undefined. Undefined is returned when the user wants to ignore a tenant or chooses not to start the * @returns The oauth token response or undefined. Undefined is returned when the user wants to ignore a tenant or chooses not to start the
* re-authentication process for their tenant. * re-authentication process for their tenant.
*/ */
public async refreshToken(tenant: Tenant, resource: Resource, refreshToken: RefreshToken | undefined): Promise<OAuthTokenResponse> | undefined { public async refreshToken(tenant: Tenant, resource: Resource, refreshToken: RefreshToken | undefined): Promise<OAuthTokenResponse | undefined> {
Logger.pii('Refreshing token', [{ name: 'token', objOrArray: refreshToken }], []); Logger.pii('Refreshing token', [{ name: 'token', objOrArray: refreshToken }], []);
if (refreshToken) { if (refreshToken) {
const postData: RefreshTokenPostData = { const postData: RefreshTokenPostData = {
@@ -268,7 +278,7 @@ export abstract class AzureAuth implements vscode.Disposable {
return this.handleInteractionRequired(tenant, resource); return this.handleInteractionRequired(tenant, resource);
} }
public async getToken(tenant: Tenant, resource: Resource, postData: AuthorizationCodePostData | TokenPostData | RefreshTokenPostData): Promise<OAuthTokenResponse> { public async getToken(tenant: Tenant, resource: Resource, postData: AuthorizationCodePostData | TokenPostData | RefreshTokenPostData): Promise<OAuthTokenResponse | undefined> {
Logger.verbose('Fetching token'); Logger.verbose('Fetching token');
const tokenUrl = `${this.loginEndpointUrl}${tenant.id}/oauth2/token`; const tokenUrl = `${this.loginEndpointUrl}${tenant.id}/oauth2/token`;
const response = await this.makePostRequest(tokenUrl, postData); const response = await this.makePostRequest(tokenUrl, postData);
@@ -317,7 +327,7 @@ export abstract class AzureAuth implements vscode.Disposable {
token: accessTokenString, token: accessTokenString,
key: userKey key: userKey
}; };
let refreshToken: RefreshToken; let refreshToken: RefreshToken | undefined = undefined;
if (refreshTokenString) { if (refreshTokenString) {
refreshToken = { refreshToken = {
@@ -402,7 +412,7 @@ export abstract class AzureAuth implements vscode.Disposable {
} }
} }
public async getSavedToken(tenant: Tenant, resource: Resource, accountKey: azdata.AccountKey): Promise<{ accessToken: AccessToken, refreshToken: RefreshToken, expiresOn: string }> { public async getSavedToken(tenant: Tenant, resource: Resource, accountKey: azdata.AccountKey): Promise<{ accessToken: AccessToken, refreshToken: RefreshToken | undefined, expiresOn: string } | undefined> {
const getMsg = localize('azure.cacheErrorGet', "Error when getting your account from the cache"); const getMsg = localize('azure.cacheErrorGet', "Error when getting your account from the cache");
const parseMsg = localize('azure.cacheErrorParse', "Error when parsing your account from the cache"); const parseMsg = localize('azure.cacheErrorParse', "Error when parsing your account from the cache");
@@ -411,8 +421,8 @@ export abstract class AzureAuth implements vscode.Disposable {
throw new AzureAuthError(getMsg, 'Getting account from cache failed', undefined); throw new AzureAuthError(getMsg, 'Getting account from cache failed', undefined);
} }
let accessTokenString: string; let accessTokenString: string | undefined = undefined;
let refreshTokenString: string; let refreshTokenString: string | undefined = undefined;
let expiresOn: string; let expiresOn: string;
try { try {
Logger.info('Fetching saved token'); Logger.info('Fetching saved token');
@@ -430,7 +440,7 @@ export abstract class AzureAuth implements vscode.Disposable {
return undefined; return undefined;
} }
const accessToken: AccessToken = JSON.parse(accessTokenString); const accessToken: AccessToken = JSON.parse(accessTokenString);
let refreshToken: RefreshToken; let refreshToken: RefreshToken | undefined = undefined;
if (refreshTokenString) { if (refreshTokenString) {
refreshToken = JSON.parse(refreshTokenString); refreshToken = JSON.parse(refreshTokenString);
} }
@@ -512,11 +522,11 @@ export abstract class AzureAuth implements vscode.Disposable {
const messageBody = localize('azurecore.consentDialog.body', "Your tenant '{0} ({1})' requires you to re-authenticate again to access {2} resources. Press Open to start the authentication process.", tenant.displayName, tenant.id, resource.id); const messageBody = localize('azurecore.consentDialog.body', "Your tenant '{0} ({1})' requires you to re-authenticate again to access {2} resources. Press Open to start the authentication process.", tenant.displayName, tenant.id, resource.id);
const result = await vscode.window.showInformationMessage(messageBody, { modal: true }, openItem, closeItem, dontAskAgainItem); const result = await vscode.window.showInformationMessage(messageBody, { modal: true }, openItem, closeItem, dontAskAgainItem);
if (result.action) { if (result?.action) {
await result.action(tenant.id); await result.action(tenant.id);
} }
return result.booleanResult; return result?.booleanResult || false;
} }
//#endregion //#endregion
@@ -624,7 +634,7 @@ export abstract class AzureAuth implements vscode.Disposable {
//#endregion //#endregion
//#region inconsequential //#region inconsequential
protected getTokenClaims(accessToken: string): TokenClaims | undefined { protected getTokenClaims(accessToken: string): TokenClaims {
try { try {
const split = accessToken.split('.'); const split = accessToken.split('.');
return JSON.parse(Buffer.from(split[1], 'base64').toString('binary')); return JSON.parse(Buffer.from(split[1], 'base64').toString('binary'));
@@ -744,7 +754,7 @@ export interface TokenClaims { // https://docs.microsoft.com/en-us/azure/active-
ver: string; ver: string;
} }
export type OAuthTokenResponse = { accessToken: AccessToken, refreshToken: RefreshToken, tokenClaims: TokenClaims, expiresOn: string }; export type OAuthTokenResponse = { accessToken: AccessToken, refreshToken: RefreshToken | undefined, tokenClaims: TokenClaims, expiresOn: string };
export interface TokenPostData { export interface TokenPostData {
grant_type: 'refresh_token' | 'authorization_code' | 'urn:ietf:params:oauth:grant-type:device_code'; grant_type: 'refresh_token' | 'authorization_code' | 'urn:ietf:params:oauth:grant-type:device_code';

View File

@@ -72,12 +72,12 @@ declare module 'azurecore' {
/** /**
* Host of the authority * Host of the authority
*/ */
host?: string; host: string;
/** /**
* Identifier of the client application * Identifier of the client application
*/ */
clientId?: string; clientId: string;
/** /**
* Information that describes the Microsoft resource management resource * Information that describes the Microsoft resource management resource
@@ -87,7 +87,7 @@ declare module 'azurecore' {
/** /**
* Information that describes the AAD graph resource * Information that describes the AAD graph resource
*/ */
graphResource?: Resource; graphResource: Resource;
/** /**
* Information that describes the MS graph resource * Information that describes the MS graph resource
@@ -97,7 +97,7 @@ declare module 'azurecore' {
/** /**
* Information that describes the Azure resource management resource * Information that describes the Azure resource management resource
*/ */
armResource?: Resource; armResource: Resource;
/** /**
* Information that describes the SQL Azure resource * Information that describes the SQL Azure resource
@@ -112,7 +112,7 @@ declare module 'azurecore' {
/** /**
* Information that describes the Azure Key Vault resource * Information that describes the Azure Key Vault resource
*/ */
azureKeyVaultResource?: Resource; azureKeyVaultResource: Resource;
/** /**
* Information that describes the Azure Dev Ops resource * Information that describes the Azure Dev Ops resource
@@ -132,7 +132,7 @@ declare module 'azurecore' {
/** /**
* Information that describes the Azure Storage resource * Information that describes the Azure Storage resource
*/ */
azureStorageResource?: Resource; azureStorageResource: Resource;
/** /**
* Information that describes the Power BI resource * Information that describes the Power BI resource
@@ -153,11 +153,11 @@ declare module 'azurecore' {
siteId?: string; siteId?: string;
/** /**
* Redirect URI that is used to signify the end of the interactive aspect of sign it * Redirect URI that is used to signify the end of the interactive aspect of sign in
*/ */
redirectUri?: string; redirectUri: string;
scopes?: string[] scopes: string[]
portalEndpoint?: string portalEndpoint?: string
} }

View File

@@ -23,6 +23,7 @@ import { AzureResourceItemType, AzureResourceServiceNames } from '../../../azure
import { AzureResourceMessageTreeNode } from '../../../azureResource/messageTreeNode'; import { AzureResourceMessageTreeNode } from '../../../azureResource/messageTreeNode';
import { generateGuid } from '../../../azureResource/utils'; import { generateGuid } from '../../../azureResource/utils';
import { AzureAccount, azureResource } from 'azurecore'; import { AzureAccount, azureResource } from 'azurecore';
import allSettings from '../../../account-provider/providerSettings';
// Mock services // Mock services
let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>; let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>;
@@ -55,7 +56,7 @@ const mockAccount: AzureAccount = {
} }
], ],
providerSettings: { providerSettings: {
settings: { }, settings: allSettings[0].metadata.settings,
id: 'azure', id: 'azure',
displayName: 'Azure' displayName: 'Azure'
}, },