mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Fix a few problems with new Azure auth (#9760)
* Fix a few problems * Fix bug * One resource per line * Dispose before cleaning * Dispose the event handler * Dispose webserver
This commit is contained in:
@@ -91,7 +91,7 @@ export interface TokenClaims { // https://docs.microsoft.com/en-us/azure/active-
|
|||||||
|
|
||||||
export type TokenRefreshResponse = { accessToken: AccessToken, refreshToken: RefreshToken, tokenClaims: TokenClaims };
|
export type TokenRefreshResponse = { accessToken: AccessToken, refreshToken: RefreshToken, tokenClaims: TokenClaims };
|
||||||
|
|
||||||
export abstract class AzureAuth {
|
export abstract class AzureAuth implements vscode.Disposable {
|
||||||
protected readonly memdb = new MemoryDatabase();
|
protected readonly memdb = new MemoryDatabase();
|
||||||
|
|
||||||
protected readonly WorkSchoolAccountType: string = 'work_school';
|
protected readonly WorkSchoolAccountType: string = 'work_school';
|
||||||
@@ -110,6 +110,7 @@ export abstract class AzureAuth {
|
|||||||
protected readonly metadata: AzureAccountProviderMetadata,
|
protected readonly metadata: AzureAccountProviderMetadata,
|
||||||
protected readonly tokenCache: SimpleTokenCache,
|
protected readonly tokenCache: SimpleTokenCache,
|
||||||
protected readonly context: vscode.ExtensionContext,
|
protected readonly context: vscode.ExtensionContext,
|
||||||
|
protected readonly uriEventEmitter: vscode.EventEmitter<vscode.Uri>,
|
||||||
protected readonly authType: AzureAuthType,
|
protected readonly authType: AzureAuthType,
|
||||||
public readonly userFriendlyName: string
|
public readonly userFriendlyName: string
|
||||||
) {
|
) {
|
||||||
@@ -119,8 +120,11 @@ export abstract class AzureAuth {
|
|||||||
this.clientId = this.metadata.settings.clientId;
|
this.clientId = this.metadata.settings.clientId;
|
||||||
|
|
||||||
this.resources = [
|
this.resources = [
|
||||||
this.metadata.settings.armResource, this.metadata.settings.sqlResource,
|
this.metadata.settings.armResource,
|
||||||
this.metadata.settings.graphResource, this.metadata.settings.ossRdbmsResource
|
this.metadata.settings.sqlResource,
|
||||||
|
this.metadata.settings.graphResource,
|
||||||
|
this.metadata.settings.ossRdbmsResource,
|
||||||
|
this.metadata.settings.azureKeyVaultResource
|
||||||
];
|
];
|
||||||
|
|
||||||
this.scopes = [...this.metadata.settings.scopes];
|
this.scopes = [...this.metadata.settings.scopes];
|
||||||
@@ -131,6 +135,7 @@ export abstract class AzureAuth {
|
|||||||
|
|
||||||
public abstract async autoOAuthCancelled(): Promise<void>;
|
public abstract async autoOAuthCancelled(): Promise<void>;
|
||||||
|
|
||||||
|
public dispose() { }
|
||||||
|
|
||||||
public async refreshAccess(account: azdata.Account): Promise<azdata.Account> {
|
public async refreshAccess(account: azdata.Account): Promise<azdata.Account> {
|
||||||
const response = await this.getCachedToken(account.key);
|
const response = await this.getCachedToken(account.key);
|
||||||
|
|||||||
@@ -30,12 +30,6 @@ import { SimpleWebServer } from '../utils/simpleWebServer';
|
|||||||
import { SimpleTokenCache } from '../simpleTokenCache';
|
import { SimpleTokenCache } from '../simpleTokenCache';
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
class UriEventHandler extends vscode.EventEmitter<vscode.Uri> implements vscode.UriHandler {
|
|
||||||
public handleUri(uri: vscode.Uri) {
|
|
||||||
this.fire(uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseQuery(uri: vscode.Uri) {
|
function parseQuery(uri: vscode.Uri) {
|
||||||
return uri.query.split('&').reduce((prev: any, current) => {
|
return uri.query.split('&').reduce((prev: any, current) => {
|
||||||
const queryString = current.split('=');
|
const queryString = current.split('=');
|
||||||
@@ -52,14 +46,14 @@ interface AuthCodeResponse {
|
|||||||
export class AzureAuthCodeGrant extends AzureAuth {
|
export class AzureAuthCodeGrant extends AzureAuth {
|
||||||
private static readonly USER_FRIENDLY_NAME: string = localize('azure.azureAuthCodeGrantName', "Azure Auth Code Grant");
|
private static readonly USER_FRIENDLY_NAME: string = localize('azure.azureAuthCodeGrantName', "Azure Auth Code Grant");
|
||||||
private server: SimpleWebServer;
|
private server: SimpleWebServer;
|
||||||
private readonly _uriHandler: UriEventHandler;
|
|
||||||
|
|
||||||
constructor(metadata: AzureAccountProviderMetadata,
|
constructor(
|
||||||
|
metadata: AzureAccountProviderMetadata,
|
||||||
tokenCache: SimpleTokenCache,
|
tokenCache: SimpleTokenCache,
|
||||||
context: vscode.ExtensionContext) {
|
context: vscode.ExtensionContext,
|
||||||
super(metadata, tokenCache, context, AzureAuthType.AuthCodeGrant, AzureAuthCodeGrant.USER_FRIENDLY_NAME);
|
uriEventEmitter: vscode.EventEmitter<vscode.Uri>,
|
||||||
this._uriHandler = new UriEventHandler();
|
) {
|
||||||
vscode.window.registerUriHandler(this._uriHandler);
|
super(metadata, tokenCache, context, uriEventEmitter, AzureAuthType.AuthCodeGrant, AzureAuthCodeGrant.USER_FRIENDLY_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async autoOAuthCancelled(): Promise<void> {
|
public async autoOAuthCancelled(): Promise<void> {
|
||||||
@@ -146,7 +140,7 @@ export class AzureAuthCodeGrant extends AzureAuth {
|
|||||||
public async handleCodeResponse(state: string): Promise<string> {
|
public async handleCodeResponse(state: string): Promise<string> {
|
||||||
let uriEventListener: vscode.Disposable;
|
let uriEventListener: vscode.Disposable;
|
||||||
return new Promise((resolve: (value: any) => void, reject) => {
|
return new Promise((resolve: (value: any) => void, reject) => {
|
||||||
uriEventListener = this._uriHandler.event(async (uri: vscode.Uri) => {
|
uriEventListener = this.uriEventEmitter.event(async (uri: vscode.Uri) => {
|
||||||
try {
|
try {
|
||||||
const query = parseQuery(uri);
|
const query = parseQuery(uri);
|
||||||
const code = query.code;
|
const code = query.code;
|
||||||
@@ -316,4 +310,8 @@ export class AzureAuthCodeGrant extends AzureAuth {
|
|||||||
|
|
||||||
return this.getToken(postData);
|
return this.getToken(postData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public dispose() {
|
||||||
|
this.server?.shutdown().catch(console.error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,10 +46,13 @@ export class AzureDeviceCode extends AzureAuth {
|
|||||||
|
|
||||||
private static readonly USER_FRIENDLY_NAME: string = localize('azure.azureDeviceCodeAuth', "Azure Device Code");
|
private static readonly USER_FRIENDLY_NAME: string = localize('azure.azureDeviceCodeAuth', "Azure Device Code");
|
||||||
private readonly pageTitle: string;
|
private readonly pageTitle: string;
|
||||||
constructor(metadata: AzureAccountProviderMetadata,
|
constructor(
|
||||||
|
metadata: AzureAccountProviderMetadata,
|
||||||
tokenCache: SimpleTokenCache,
|
tokenCache: SimpleTokenCache,
|
||||||
context: vscode.ExtensionContext) {
|
context: vscode.ExtensionContext,
|
||||||
super(metadata, tokenCache, context, AzureAuthType.AuthCodeGrant, AzureDeviceCode.USER_FRIENDLY_NAME);
|
uriEventEmitter: vscode.EventEmitter<vscode.Uri>,
|
||||||
|
) {
|
||||||
|
super(metadata, tokenCache, context, uriEventEmitter, AzureAuthType.AuthCodeGrant, AzureDeviceCode.USER_FRIENDLY_NAME);
|
||||||
this.pageTitle = localize('addAccount', "Add {0} account", this.metadata.displayName);
|
this.pageTitle = localize('addAccount', "Add {0} account", this.metadata.displayName);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import { AzureDeviceCode } from './auths/azureDeviceCode';
|
|||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
export class AzureAccountProvider implements azdata.AccountProvider {
|
export class AzureAccountProvider implements azdata.AccountProvider, vscode.Disposable {
|
||||||
private static readonly CONFIGURATION_SECTION = 'accounts.azure.auth';
|
private static readonly CONFIGURATION_SECTION = 'accounts.azure.auth';
|
||||||
private readonly authMappings = new Map<AzureAuthType, AzureAuth>();
|
private readonly authMappings = new Map<AzureAuthType, AzureAuth>();
|
||||||
private initComplete: Deferred<void>;
|
private initComplete: Deferred<void>;
|
||||||
@@ -30,23 +30,29 @@ export class AzureAccountProvider implements azdata.AccountProvider {
|
|||||||
metadata: AzureAccountProviderMetadata,
|
metadata: AzureAccountProviderMetadata,
|
||||||
tokenCache: SimpleTokenCache,
|
tokenCache: SimpleTokenCache,
|
||||||
context: vscode.ExtensionContext,
|
context: vscode.ExtensionContext,
|
||||||
|
uriEventHandler: vscode.EventEmitter<vscode.Uri>,
|
||||||
private readonly forceDeviceCode: boolean = false
|
private readonly forceDeviceCode: boolean = false
|
||||||
) {
|
) {
|
||||||
vscode.workspace.onDidChangeConfiguration((changeEvent) => {
|
vscode.workspace.onDidChangeConfiguration((changeEvent) => {
|
||||||
const impact = changeEvent.affectsConfiguration(AzureAccountProvider.CONFIGURATION_SECTION);
|
const impact = changeEvent.affectsConfiguration(AzureAccountProvider.CONFIGURATION_SECTION);
|
||||||
if (impact === true) {
|
if (impact === true) {
|
||||||
this.handleAuthMapping(metadata, tokenCache, context);
|
this.handleAuthMapping(metadata, tokenCache, context, uriEventHandler);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.handleAuthMapping(metadata, tokenCache, context);
|
|
||||||
|
|
||||||
|
this.handleAuthMapping(metadata, tokenCache, context, uriEventHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this.authMappings.forEach(x => x.dispose());
|
||||||
}
|
}
|
||||||
|
|
||||||
clearTokenCache(): Thenable<void> {
|
clearTokenCache(): Thenable<void> {
|
||||||
return this.getAuthMethod().deleteAllCache();
|
return this.getAuthMethod().deleteAllCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleAuthMapping(metadata: AzureAccountProviderMetadata, tokenCache: SimpleTokenCache, context: vscode.ExtensionContext) {
|
private handleAuthMapping(metadata: AzureAccountProviderMetadata, tokenCache: SimpleTokenCache, context: vscode.ExtensionContext, uriEventHandler: vscode.EventEmitter<vscode.Uri>) {
|
||||||
|
this.authMappings.forEach(m => m.dispose());
|
||||||
this.authMappings.clear();
|
this.authMappings.clear();
|
||||||
const configuration = vscode.workspace.getConfiguration(AzureAccountProvider.CONFIGURATION_SECTION);
|
const configuration = vscode.workspace.getConfiguration(AzureAccountProvider.CONFIGURATION_SECTION);
|
||||||
|
|
||||||
@@ -54,10 +60,10 @@ export class AzureAccountProvider implements azdata.AccountProvider {
|
|||||||
const deviceCodeMethod: boolean = configuration.get('deviceCode');
|
const deviceCodeMethod: boolean = configuration.get('deviceCode');
|
||||||
|
|
||||||
if (codeGrantMethod === true && !this.forceDeviceCode) {
|
if (codeGrantMethod === true && !this.forceDeviceCode) {
|
||||||
this.authMappings.set(AzureAuthType.AuthCodeGrant, new AzureAuthCodeGrant(metadata, tokenCache, context));
|
this.authMappings.set(AzureAuthType.AuthCodeGrant, new AzureAuthCodeGrant(metadata, tokenCache, context, uriEventHandler));
|
||||||
}
|
}
|
||||||
if (deviceCodeMethod === true || this.forceDeviceCode) {
|
if (deviceCodeMethod === true || this.forceDeviceCode) {
|
||||||
this.authMappings.set(AzureAuthType.DeviceCode, new AzureDeviceCode(metadata, tokenCache, context));
|
this.authMappings.set(AzureAuthType.DeviceCode, new AzureDeviceCode(metadata, tokenCache, context, uriEventHandler));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,12 @@ import * as loc from '../localizedConstants';
|
|||||||
|
|
||||||
let localize = nls.loadMessageBundle();
|
let localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
class UriEventHandler extends vscode.EventEmitter<vscode.Uri> implements vscode.UriHandler {
|
||||||
|
public handleUri(uri: vscode.Uri) {
|
||||||
|
this.fire(uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class AzureAccountProviderService implements vscode.Disposable {
|
export class AzureAccountProviderService implements vscode.Disposable {
|
||||||
// CONSTANTS ///////////////////////////////////////////////////////////////
|
// CONSTANTS ///////////////////////////////////////////////////////////////
|
||||||
private static CommandClearTokenCache = 'accounts.clearTokenCache';
|
private static CommandClearTokenCache = 'accounts.clearTokenCache';
|
||||||
@@ -22,12 +28,14 @@ export class AzureAccountProviderService implements vscode.Disposable {
|
|||||||
private static CredentialNamespace = 'azureAccountProviderCredentials';
|
private static CredentialNamespace = 'azureAccountProviderCredentials';
|
||||||
|
|
||||||
// MEMBER VARIABLES ////////////////////////////////////////////////////////
|
// MEMBER VARIABLES ////////////////////////////////////////////////////////
|
||||||
|
private _disposables: vscode.Disposable[] = [];
|
||||||
private _accountDisposals: { [accountProviderId: string]: vscode.Disposable };
|
private _accountDisposals: { [accountProviderId: string]: vscode.Disposable };
|
||||||
private _accountProviders: { [accountProviderId: string]: azdata.AccountProvider };
|
private _accountProviders: { [accountProviderId: string]: azdata.AccountProvider };
|
||||||
private _credentialProvider: azdata.CredentialProvider;
|
private _credentialProvider: azdata.CredentialProvider;
|
||||||
private _configChangePromiseChain: Thenable<void>;
|
private _configChangePromiseChain: Thenable<void>;
|
||||||
private _currentConfig: vscode.WorkspaceConfiguration;
|
private _currentConfig: vscode.WorkspaceConfiguration;
|
||||||
private _event: events.EventEmitter;
|
private _event: events.EventEmitter;
|
||||||
|
private readonly _uriEventHandler: UriEventHandler;
|
||||||
|
|
||||||
constructor(private _context: vscode.ExtensionContext, private _userStoragePath: string) {
|
constructor(private _context: vscode.ExtensionContext, private _userStoragePath: string) {
|
||||||
this._accountDisposals = {};
|
this._accountDisposals = {};
|
||||||
@@ -35,6 +43,9 @@ export class AzureAccountProviderService implements vscode.Disposable {
|
|||||||
this._configChangePromiseChain = Promise.resolve();
|
this._configChangePromiseChain = Promise.resolve();
|
||||||
this._currentConfig = null;
|
this._currentConfig = null;
|
||||||
this._event = new events.EventEmitter();
|
this._event = new events.EventEmitter();
|
||||||
|
|
||||||
|
this._uriEventHandler = new UriEventHandler();
|
||||||
|
this._disposables.push(vscode.window.registerUriHandler(this._uriEventHandler));
|
||||||
}
|
}
|
||||||
|
|
||||||
// PUBLIC METHODS //////////////////////////////////////////////////////
|
// PUBLIC METHODS //////////////////////////////////////////////////////
|
||||||
@@ -65,7 +76,14 @@ export class AzureAccountProviderService implements vscode.Disposable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public dispose() { }
|
public dispose() {
|
||||||
|
while (this._disposables.length) {
|
||||||
|
const item = this._disposables.pop();
|
||||||
|
if (item) {
|
||||||
|
item.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// PRIVATE HELPERS /////////////////////////////////////////////////////
|
// PRIVATE HELPERS /////////////////////////////////////////////////////
|
||||||
private onClearTokenCache(): Thenable<void> {
|
private onClearTokenCache(): Thenable<void> {
|
||||||
@@ -133,7 +151,7 @@ export class AzureAccountProviderService implements vscode.Disposable {
|
|||||||
await simpleTokenCache.init();
|
await simpleTokenCache.init();
|
||||||
|
|
||||||
const isSaw: boolean = vscode.env.appName.toLowerCase().indexOf('saw') > 0;
|
const isSaw: boolean = vscode.env.appName.toLowerCase().indexOf('saw') > 0;
|
||||||
let accountProvider = new AzureAccountProvider(provider.metadata as AzureAccountProviderMetadata, simpleTokenCache, this._context, isSaw);
|
let accountProvider = new AzureAccountProvider(provider.metadata as AzureAccountProviderMetadata, simpleTokenCache, this._context, this._uriEventHandler, isSaw);
|
||||||
|
|
||||||
this._accountProviders[provider.metadata.id] = accountProvider;
|
this._accountProviders[provider.metadata.id] = accountProvider;
|
||||||
this._accountDisposals[provider.metadata.id] = azdata.accounts.registerAccountProvider(provider.metadata, accountProvider);
|
this._accountDisposals[provider.metadata.id] = azdata.accounts.registerAccountProvider(provider.metadata, accountProvider);
|
||||||
|
|||||||
@@ -39,8 +39,9 @@ const publicAzureSettings: ProviderSettings = {
|
|||||||
azureResourceId: AzureResource.OssRdbms
|
azureResourceId: AzureResource.OssRdbms
|
||||||
},
|
},
|
||||||
azureKeyVaultResource: {
|
azureKeyVaultResource: {
|
||||||
id: 'https://vault.azure.net',
|
id: 'vault',
|
||||||
endpoint: 'https://vault.azure.net'
|
endpoint: 'https://vault.azure.net',
|
||||||
|
azureResourceId: AzureResource.AzureKeyVault
|
||||||
},
|
},
|
||||||
redirectUri: 'https://vscode-redirect.azurewebsites.net/',
|
redirectUri: 'https://vscode-redirect.azurewebsites.net/',
|
||||||
scopes: [
|
scopes: [
|
||||||
@@ -82,8 +83,9 @@ const usGovAzureSettings: ProviderSettings = {
|
|||||||
azureResourceId: AzureResource.OssRdbms
|
azureResourceId: AzureResource.OssRdbms
|
||||||
},
|
},
|
||||||
azureKeyVaultResource: {
|
azureKeyVaultResource: {
|
||||||
id: 'https://vault.usgovcloudapi.net',
|
id: 'vault',
|
||||||
endpoint: 'https://vault.usgovcloudapi.net'
|
endpoint: 'https://vault.usgovcloudapi.net',
|
||||||
|
azureResourceId: AzureResource.AzureKeyVault
|
||||||
},
|
},
|
||||||
redirectUri: 'https://vscode-redirect.azurewebsites.net/',
|
redirectUri: 'https://vscode-redirect.azurewebsites.net/',
|
||||||
scopes: [
|
scopes: [
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ describe('AccountProvider.AzureAuth', function (): void {
|
|||||||
beforeEach(async function (): Promise<void> {
|
beforeEach(async function (): Promise<void> {
|
||||||
const tokenCache = new SimpleTokenCache('testTokenService', os.tmpdir(), true, new CredentialsTestProvider());
|
const tokenCache = new SimpleTokenCache('testTokenService', os.tmpdir(), true, new CredentialsTestProvider());
|
||||||
await tokenCache.init();
|
await tokenCache.init();
|
||||||
baseAuth = new BasicAzureAuth(providerSettings[0].metadata, tokenCache, undefined, AzureAuthType.AuthCodeGrant, 'Auth Code Grant');
|
baseAuth = new BasicAzureAuth(providerSettings[0].metadata, tokenCache, undefined, undefined, AzureAuthType.AuthCodeGrant, 'Auth Code Grant');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Basic token set and get', async function (): Promise<void> {
|
it('Basic token set and get', async function (): Promise<void> {
|
||||||
|
|||||||
Reference in New Issue
Block a user