mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
New azure authentication experience (#8483)
* Changes * Work in progress * Authenticate with azure * enbable national clouds and initialization * Add support for tenants * Finish up account work * Finish up azure auth * Don't allow prompt if we're not initialized * Shut down server * Remove trailing comma * encode uri component * ignore errors * Address comments on github * Fix issues and disable feature without env var * Don't encode the nonce * Only use env variables to disable the new sign in * Show more user friendly messages to users
This commit is contained in:
@@ -432,6 +432,7 @@ export class AzureAccountProvider implements azdata.AccountProvider {
|
||||
displayName: displayName
|
||||
},
|
||||
properties: {
|
||||
providerSettings: this._metadata,
|
||||
isMsAccount: msa,
|
||||
tenants: tenants
|
||||
},
|
||||
|
||||
@@ -0,0 +1,448 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import * as http from 'http';
|
||||
import * as url from 'url';
|
||||
import * as crypto from 'crypto';
|
||||
import * as nls from 'vscode-nls';
|
||||
import * as request from 'request';
|
||||
import {
|
||||
AzureAccount,
|
||||
AzureAccountProviderMetadata,
|
||||
AzureAccountSecurityTokenCollection,
|
||||
AzureAccountSecurityToken,
|
||||
Tenant,
|
||||
} from './interfaces';
|
||||
|
||||
import TokenCache from './tokenCache';
|
||||
import { AddressInfo } from 'net';
|
||||
import { AuthenticationContext, TokenResponse, ErrorResponse } from 'adal-node';
|
||||
import { promisify } from 'util';
|
||||
import * as events from 'events';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
const notInitalizedMessage = localize('accountProviderNotInitialized', "Account provider not initialized, cannot perform action");
|
||||
|
||||
export class AzureAccountProvider implements azdata.AccountProvider {
|
||||
private static AzureAccountAuthenticatedEvent: string = 'AzureAccountAuthenticated';
|
||||
private static WorkSchoolAccountType: string = 'work_school';
|
||||
private static MicrosoftAccountType: string = 'microsoft';
|
||||
private static AadCommonTenant: string = 'common';
|
||||
|
||||
private static eventEmitter = new events.EventEmitter();
|
||||
private static redirectUrlAAD = 'https://vscode-redirect.azurewebsites.net/';
|
||||
private commonAuthorityUrl: string;
|
||||
private isInitialized: boolean = false;
|
||||
|
||||
|
||||
constructor(private metadata: AzureAccountProviderMetadata, private _tokenCache: TokenCache) {
|
||||
this.commonAuthorityUrl = url.resolve(this.metadata.settings.host, AzureAccountProvider.AadCommonTenant);
|
||||
// Temporary override
|
||||
this.metadata.settings.clientId = 'aebc6443-996d-45c2-90f0-388ff96faa56';
|
||||
}
|
||||
|
||||
// interface method
|
||||
initialize(storedAccounts: azdata.Account[]): Thenable<azdata.Account[]> {
|
||||
return this._initialize(storedAccounts);
|
||||
}
|
||||
|
||||
private async _initialize(storedAccounts: azdata.Account[]): Promise<azdata.Account[]> {
|
||||
for (let account of storedAccounts) {
|
||||
try {
|
||||
await this.getAccessTokens(account, azdata.AzureResource.ResourceManagement);
|
||||
} catch (e) {
|
||||
console.error(`Refreshing account ${account.displayInfo} failed - ${e}`);
|
||||
account.isStale = true;
|
||||
azdata.accounts.accountUpdated(account);
|
||||
}
|
||||
}
|
||||
this.isInitialized = true;
|
||||
return storedAccounts;
|
||||
}
|
||||
|
||||
private async getToken(userId: string, tenantId: string, resourceId: string): Promise<TokenResponse> {
|
||||
let authorityUrl = url.resolve(this.metadata.settings.host, tenantId);
|
||||
const context = new AuthenticationContext(authorityUrl, null, this._tokenCache);
|
||||
|
||||
const acquireToken = promisify(context.acquireToken).bind(context);
|
||||
|
||||
let response: (TokenResponse | ErrorResponse) = await acquireToken(resourceId, userId, this.metadata.settings.clientId);
|
||||
if (response.error) {
|
||||
throw new Error(`Response contained error ${response}`);
|
||||
}
|
||||
|
||||
response = response as TokenResponse;
|
||||
|
||||
context.cache.add([response], (err, result) => {
|
||||
console.log(err, result);
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private async getAccessTokens(account: azdata.Account, resource: azdata.AzureResource): Promise<AzureAccountSecurityTokenCollection> {
|
||||
const resourceIdMap = new Map<azdata.AzureResource, string>([
|
||||
[azdata.AzureResource.ResourceManagement, this.metadata.settings.armResource.id],
|
||||
[azdata.AzureResource.Sql, this.metadata.settings.sqlResource.id]
|
||||
]);
|
||||
const tenantRefreshPromises: Promise<{ tenantId: any, securityToken: AzureAccountSecurityToken }>[] = [];
|
||||
const tokenCollection: AzureAccountSecurityTokenCollection = {};
|
||||
|
||||
for (let tenant of account.properties.tenants) {
|
||||
const promise = new Promise<{ tenantId: any, securityToken: AzureAccountSecurityToken }>(async (resolve, reject) => {
|
||||
try {
|
||||
let response = await this.getToken(tenant.userId, tenant.id, resourceIdMap.get(resource));
|
||||
|
||||
resolve({
|
||||
tenantId: tenant.id,
|
||||
securityToken: {
|
||||
expiresOn: response.expiresOn,
|
||||
resource: response.resource,
|
||||
token: response.accessToken,
|
||||
tokenType: response.tokenType
|
||||
} as AzureAccountSecurityToken,
|
||||
});
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
}
|
||||
|
||||
});
|
||||
tenantRefreshPromises.push(promise);
|
||||
}
|
||||
|
||||
const refreshedTenants = await Promise.all(tenantRefreshPromises);
|
||||
refreshedTenants.forEach((refreshed) => {
|
||||
tokenCollection[refreshed.tenantId] = refreshed.securityToken;
|
||||
});
|
||||
|
||||
return tokenCollection;
|
||||
}
|
||||
|
||||
// interface method
|
||||
getSecurityToken(account: azdata.Account, resource: azdata.AzureResource): Thenable<{}> {
|
||||
return this._getSecurityToken(account, resource);
|
||||
}
|
||||
|
||||
private async _getSecurityToken(account: azdata.Account, resource: azdata.AzureResource): Promise<{}> {
|
||||
return this.getAccessTokens(account, resource);
|
||||
}
|
||||
|
||||
// interface method
|
||||
prompt(): Thenable<azdata.Account | azdata.PromptFailedResult> {
|
||||
return this._prompt();
|
||||
}
|
||||
|
||||
|
||||
private async _prompt(): Promise<azdata.Account | azdata.PromptFailedResult> {
|
||||
if (this.isInitialized === false) {
|
||||
vscode.window.showInformationMessage(notInitalizedMessage);
|
||||
return { canceled: false };
|
||||
}
|
||||
const pathMappings = new Map<string, (req: http.IncomingMessage, res: http.ServerResponse, reqUrl: url.UrlWithParsedQuery) => void>();
|
||||
|
||||
const nonce = crypto.randomBytes(16).toString('base64');
|
||||
|
||||
const server = this.createAuthServer(pathMappings);
|
||||
|
||||
const port = await this.listenToServer(server);
|
||||
try {
|
||||
const authUrl = this.createAuthUrl(
|
||||
this.metadata.settings.host,
|
||||
AzureAccountProvider.redirectUrlAAD,
|
||||
this.metadata.settings.clientId,
|
||||
this.metadata.settings.signInResourceId,
|
||||
AzureAccountProvider.AadCommonTenant,
|
||||
`${port},${encodeURIComponent(nonce)}`
|
||||
);
|
||||
|
||||
this.addServerPaths(pathMappings, nonce, authUrl);
|
||||
|
||||
const accountAuthenticatedPromise = new Promise<AzureAccount>((resolve, reject) => {
|
||||
AzureAccountProvider.eventEmitter.on(AzureAccountProvider.AzureAccountAuthenticatedEvent, ({ account, error }) => {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
return resolve(account);
|
||||
});
|
||||
});
|
||||
|
||||
const urlToOpen = `http://localhost:${port}/signin?nonce=${encodeURIComponent(nonce)}`;
|
||||
|
||||
vscode.env.openExternal(vscode.Uri.parse(urlToOpen));
|
||||
|
||||
const account = await accountAuthenticatedPromise;
|
||||
|
||||
return account;
|
||||
} finally {
|
||||
server.close();
|
||||
}
|
||||
}
|
||||
|
||||
private addServerPaths(
|
||||
pathMappings: Map<string, (req: http.IncomingMessage, res: http.ServerResponse, reqUrl: url.UrlWithParsedQuery) => void>,
|
||||
nonce: string,
|
||||
authUrl: string) {
|
||||
|
||||
const initialSignIn = ((req: http.IncomingMessage, res: http.ServerResponse, reqUrl: url.UrlWithParsedQuery) => {
|
||||
const receivedNonce = (reqUrl.query.nonce as string || '').replace(/ /g, '+');
|
||||
if (receivedNonce !== nonce) {
|
||||
res.writeHead(400, { 'content-type': 'text/html' });
|
||||
res.write(localize('azureAuth.nonceError', "Authentication failed due to a nonce mismatch, please close ADS and try again."));
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
res.writeHead(302, { Location: authUrl });
|
||||
res.end();
|
||||
});
|
||||
|
||||
const callback = ((req: http.IncomingMessage, res: http.ServerResponse, reqUrl: url.UrlWithParsedQuery) => {
|
||||
const state = reqUrl.query.state as string ?? '';
|
||||
const code = reqUrl.query.code as string ?? '';
|
||||
|
||||
const stateSplit = state.split(',');
|
||||
if (stateSplit.length !== 2) {
|
||||
res.writeHead(400, { 'content-type': 'text/html' });
|
||||
res.write(localize('azureAuth.stateError', "Authentication failed due to a state mismatch, please close ADS and try again."));
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (stateSplit[1] !== nonce) {
|
||||
res.writeHead(400, { 'content-type': 'text/html' });
|
||||
res.write(localize('azureAuth.nonceError', "Authentication failed due to a nonce mismatch, please close ADS and try again."));
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
res.writeHead(200, { 'content-type': 'text/html' });
|
||||
res.write(localize('azureAuth.authSuccessful', "Authentication was successful, you can now close this page."));
|
||||
res.end();
|
||||
|
||||
this.handleAuthentication(code).catch(console.error);
|
||||
});
|
||||
|
||||
pathMappings.set('/signin', initialSignIn);
|
||||
pathMappings.set('/callback', callback);
|
||||
}
|
||||
private async makeWebRequest(accessToken: TokenResponse, uri: string): Promise<any> {
|
||||
const params = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${accessToken.accessToken}`
|
||||
},
|
||||
json: true
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
request.get(uri, params, (error: any, response: request.Response, body: any) => {
|
||||
const err = error ?? body.error;
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
return resolve(body.value);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async getTenants(userId: string, homeTenant: string): Promise<Tenant[]> {
|
||||
const armToken = await this.getToken(userId, AzureAccountProvider.AadCommonTenant, this.metadata.settings.armResource.id);
|
||||
const tenantUri = url.resolve(this.metadata.settings.armResource.endpoint, 'tenants?api-version=2015-01-01');
|
||||
const armWebResponse: any[] = await this.makeWebRequest(armToken, tenantUri);
|
||||
|
||||
const promises = armWebResponse.map(async (value: { tenantId: string }) => {
|
||||
const graphToken = await this.getToken(userId, value.tenantId, this.metadata.settings.graphResource.id);
|
||||
let tenantDetailsUri = url.resolve(this.metadata.settings.graphResource.endpoint, value.tenantId + '/');
|
||||
tenantDetailsUri = url.resolve(tenantDetailsUri, 'tenantDetails?api-version=2013-04-05');
|
||||
const tenantDetails: any[] = await this.makeWebRequest(graphToken, tenantDetailsUri);
|
||||
|
||||
return {
|
||||
id: value.tenantId,
|
||||
userId: userId,
|
||||
displayName: tenantDetailsUri.length && tenantDetails[0].displayName
|
||||
? tenantDetails[0].displayName
|
||||
: localize('azureWorkAccountDisplayName', "Work or school account")
|
||||
} as Tenant;
|
||||
});
|
||||
|
||||
const tenants = await Promise.all(promises);
|
||||
const homeTenantIndex = tenants.findIndex(tenant => tenant.id === homeTenant);
|
||||
if (homeTenantIndex >= 0) {
|
||||
const homeTenant = tenants.splice(homeTenantIndex, 1);
|
||||
tenants.unshift(homeTenant[0]);
|
||||
}
|
||||
return tenants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticates an azure account and then emits an event
|
||||
* @param code Code from authenticating
|
||||
*/
|
||||
private async handleAuthentication(code: string): Promise<void> {
|
||||
const token = await this.getTokenWithAuthCode(code, AzureAccountProvider.redirectUrlAAD);
|
||||
|
||||
const tenants = await this.getTenants(token.userId, token.userId);
|
||||
let identityProvider = token.identityProvider;
|
||||
if (identityProvider) {
|
||||
identityProvider = identityProvider.toLowerCase();
|
||||
}
|
||||
|
||||
// Determine if this is a microsoft account
|
||||
let msa = identityProvider && (
|
||||
identityProvider.indexOf('live.com') !== -1 || // lgtm [js/incomplete-url-substring-sanitization]
|
||||
identityProvider.indexOf('live-int.com') !== -1 || // lgtm [js/incomplete-url-substring-sanitization]
|
||||
identityProvider.indexOf('f8cdef31-a31e-4b4a-93e4-5f571e91255a') !== -1 ||
|
||||
identityProvider.indexOf('ea8a4392-515e-481f-879e-6571ff2a8a36') !== -1);
|
||||
|
||||
// Calculate the display name for the user
|
||||
let displayName = (token.givenName && token.familyName)
|
||||
? `${token.givenName} ${token.familyName}`
|
||||
: token.userId;
|
||||
|
||||
// Calculate the home tenant display name to use for the contextual display name
|
||||
let contextualDisplayName = msa
|
||||
? localize('microsoftAccountDisplayName', "Microsoft Account")
|
||||
: tenants[0].displayName;
|
||||
|
||||
let accountType = msa
|
||||
? AzureAccountProvider.MicrosoftAccountType
|
||||
: AzureAccountProvider.WorkSchoolAccountType;
|
||||
|
||||
const account = {
|
||||
key: {
|
||||
providerId: this.metadata.id,
|
||||
accountId: token.userId
|
||||
},
|
||||
name: token.userId,
|
||||
displayInfo: {
|
||||
accountType: accountType,
|
||||
userId: token.userId,
|
||||
contextualDisplayName: contextualDisplayName,
|
||||
displayName: displayName
|
||||
},
|
||||
properties: {
|
||||
providerSettings: this.metadata,
|
||||
isMsAccount: msa,
|
||||
tenants,
|
||||
},
|
||||
isStale: false
|
||||
} as AzureAccount;
|
||||
|
||||
AzureAccountProvider.eventEmitter.emit(AzureAccountProvider.AzureAccountAuthenticatedEvent, { account });
|
||||
}
|
||||
|
||||
private async getTokenWithAuthCode(code: string, redirectUrl: string): Promise<TokenResponse> {
|
||||
const context = new AuthenticationContext(this.commonAuthorityUrl, null, this._tokenCache);
|
||||
const acquireToken = promisify(context.acquireTokenWithAuthorizationCode).bind(context);
|
||||
|
||||
let token = await acquireToken(code, redirectUrl, this.metadata.settings.signInResourceId, this.metadata.settings.clientId, undefined);
|
||||
if (token.error) {
|
||||
throw new Error(`${token.error} - ${token.errorDescription}`);
|
||||
}
|
||||
token = token as TokenResponse;
|
||||
token._clientId = this.metadata.settings.clientId;
|
||||
token._authority = this.commonAuthorityUrl;
|
||||
token.isMRRT = true;
|
||||
|
||||
context.cache.add([token], (err, result) => {
|
||||
console.log(err, result);
|
||||
});
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
private createAuthUrl(baseHost: string, redirectUri: string, clientId: string, resource: string, tenant: string, nonce: string): string {
|
||||
return `${baseHost}${encodeURIComponent(tenant)}/oauth2/authorize?response_type=code&client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUri)}&state=${nonce}&resource=${encodeURIComponent(resource)}&prompt=select_account`;
|
||||
}
|
||||
|
||||
private createAuthServer(pathMappings: Map<string, (req: http.IncomingMessage, res: http.ServerResponse, reqUrl: url.UrlWithParsedQuery) => void>) {
|
||||
const server = http.createServer((req, res) => {
|
||||
// Parse URL and the query string
|
||||
const reqUrl = url.parse(req.url, true);
|
||||
|
||||
const method = pathMappings.get(reqUrl.pathname);
|
||||
if (method) {
|
||||
method(req, res, reqUrl);
|
||||
} else {
|
||||
console.error('undefined request ', reqUrl, req);
|
||||
}
|
||||
});
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually starts listening for the server - returns the port the server is listening on
|
||||
* @param server http.Server
|
||||
*/
|
||||
private async listenToServer(server: http.Server): Promise<number> {
|
||||
let portTimer: NodeJS.Timer;
|
||||
const cancelPortTimer = (() => {
|
||||
clearTimeout(portTimer);
|
||||
});
|
||||
|
||||
const port = new Promise<number>((resolve, reject) => {
|
||||
// If no port for 5 seconds, reject it.
|
||||
portTimer = setTimeout(() => {
|
||||
reject(new Error('Timeout waiting for port'));
|
||||
}, 5000);
|
||||
|
||||
server.on('listening', () => {
|
||||
const address = server.address() as AddressInfo;
|
||||
if (address!.port === undefined) {
|
||||
reject(new Error('Port was not defined'));
|
||||
}
|
||||
resolve(address.port);
|
||||
});
|
||||
server.on('error', err => {
|
||||
reject(err);
|
||||
});
|
||||
server.on('close', () => {
|
||||
reject(new Error('Closed'));
|
||||
});
|
||||
server.listen(0, '127.0.0.1');
|
||||
});
|
||||
|
||||
const portValue = await port;
|
||||
cancelPortTimer();
|
||||
|
||||
return portValue;
|
||||
}
|
||||
|
||||
// interface method
|
||||
refresh(account: azdata.Account): Thenable<azdata.Account | azdata.PromptFailedResult> {
|
||||
return this._refresh(account);
|
||||
}
|
||||
|
||||
private async _refresh(account: azdata.Account): Promise<azdata.Account | azdata.PromptFailedResult> {
|
||||
return this.prompt();
|
||||
}
|
||||
|
||||
// interface method
|
||||
clear(accountKey: azdata.AccountKey): Thenable<void> {
|
||||
return this._clear(accountKey);
|
||||
}
|
||||
|
||||
private async _clear(accountKey: azdata.AccountKey): Promise<void> {
|
||||
// Put together a query to look up any tokens associated with the account key
|
||||
let query = { userId: accountKey.accountId } as TokenResponse;
|
||||
|
||||
// 1) Look up the tokens associated with the query
|
||||
// 2) Remove them
|
||||
let results = await this._tokenCache.findThenable(query);
|
||||
this._tokenCache.removeThenable(results);
|
||||
}
|
||||
|
||||
// interface method
|
||||
autoOAuthCancelled(): Thenable<void> {
|
||||
return this._autoOAuthCancelled();
|
||||
}
|
||||
|
||||
private async _autoOAuthCancelled(): Promise<void> {
|
||||
// I don't think we need this?
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as constants from '../constants';
|
||||
import * as azdata from 'azdata';
|
||||
import * as events from 'events';
|
||||
@@ -13,7 +11,8 @@ import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import CredentialServiceTokenCache from './tokenCache';
|
||||
import providerSettings from './providerSettings';
|
||||
import { AzureAccountProvider } from './azureAccountProvider';
|
||||
import { AzureAccountProvider as AzureAccountProviderDeprecated } from './azureAccountProvider';
|
||||
import { AzureAccountProvider as AzureAccountProvider } from './azureAccountProvider2';
|
||||
import { AzureAccountProviderMetadata, ProviderSettings } from './interfaces';
|
||||
|
||||
let localize = nls.loadMessageBundle();
|
||||
@@ -26,7 +25,7 @@ export class AzureAccountProviderService implements vscode.Disposable {
|
||||
|
||||
// MEMBER VARIABLES ////////////////////////////////////////////////////////
|
||||
private _accountDisposals: { [accountProviderId: string]: vscode.Disposable };
|
||||
private _accountProviders: { [accountProviderId: string]: AzureAccountProvider };
|
||||
private _accountProviders: { [accountProviderId: string]: azdata.AccountProvider };
|
||||
private _credentialProvider: azdata.CredentialProvider;
|
||||
private _configChangePromiseChain: Thenable<void>;
|
||||
private _currentConfig: vscode.WorkspaceConfiguration;
|
||||
@@ -69,10 +68,11 @@ export class AzureAccountProviderService implements vscode.Disposable {
|
||||
|
||||
// PRIVATE HELPERS /////////////////////////////////////////////////////
|
||||
private onClearTokenCache(): Thenable<void> {
|
||||
let self = this;
|
||||
// let self = this;
|
||||
|
||||
let promises: Thenable<void>[] = providerSettings.map(provider => {
|
||||
return self._accountProviders[provider.metadata.id].clearTokenCache();
|
||||
// return self._accountProviders[provider.metadata.id].clearTokenCache();
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
return Promise.all(promises)
|
||||
@@ -129,14 +129,23 @@ export class AzureAccountProviderService implements vscode.Disposable {
|
||||
}
|
||||
|
||||
private registerAccountProvider(provider: ProviderSettings): Thenable<void> {
|
||||
|
||||
let self = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
//let config = vscode.workspace.getConfiguration(AzureAccountProviderService.ConfigurationSection);
|
||||
|
||||
let tokenCacheKey = `azureTokenCache-${provider.metadata.id}`;
|
||||
let tokenCachePath = path.join(this._userStoragePath, tokenCacheKey);
|
||||
let tokenCache = new CredentialServiceTokenCache(self._credentialProvider, tokenCacheKey, tokenCachePath);
|
||||
let accountProvider = new AzureAccountProvider(<AzureAccountProviderMetadata>provider.metadata, tokenCache);
|
||||
let accountProvider: azdata.AccountProvider;
|
||||
|
||||
if (/*config.get('useNewSignInExperience') === true && */ Boolean(process.env['NEW_SIGN_IN_EXPERIENCE']) === true) {
|
||||
accountProvider = new AzureAccountProvider(provider.metadata as AzureAccountProviderMetadata, tokenCache);
|
||||
} else {
|
||||
accountProvider = new AzureAccountProviderDeprecated(provider.metadata as AzureAccountProviderMetadata, tokenCache);
|
||||
}
|
||||
self._accountProviders[provider.metadata.id] = accountProvider;
|
||||
self._accountDisposals[provider.metadata.id] = azdata.accounts.registerAccountProvider(provider.metadata, accountProvider);
|
||||
resolve();
|
||||
|
||||
@@ -127,6 +127,7 @@ export interface AzureAccountProviderMetadata extends azdata.AccountProviderMeta
|
||||
* Properties specific to an Azure account
|
||||
*/
|
||||
interface AzureAccountProperties {
|
||||
providerSettings: AzureAccountProviderMetadata;
|
||||
/**
|
||||
* Whether or not the account is a Microsoft account
|
||||
*/
|
||||
@@ -151,7 +152,7 @@ export interface AzureAccount extends azdata.Account {
|
||||
/**
|
||||
* Token returned from a request for an access token
|
||||
*/
|
||||
interface AzureAccountSecurityToken {
|
||||
export interface AzureAccountSecurityToken {
|
||||
/**
|
||||
* Access token, itself
|
||||
*/
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
import { ProviderSettings } from './interfaces';
|
||||
|
||||
@@ -40,11 +38,11 @@ const publicAzureSettings: ProviderSettings = {
|
||||
}
|
||||
};
|
||||
|
||||
/* Leaving for reference
|
||||
|
||||
const usGovAzureSettings: ProviderSettings = {
|
||||
configKey: 'enableUsGovCloud',
|
||||
metadata: {
|
||||
displayName: localize('usGovCloudDisplayName', 'Azure (US Government)'),
|
||||
displayName: localize('usGovCloudDisplayName', "Azure (US Government)"),
|
||||
id: 'usGovAzureCloud',
|
||||
settings: {
|
||||
host: 'https://login.microsoftonline.com/',
|
||||
@@ -63,32 +61,11 @@ const usGovAzureSettings: ProviderSettings = {
|
||||
}
|
||||
};
|
||||
|
||||
const chinaAzureSettings: ProviderSettings = {
|
||||
configKey: 'enableChinaCloud',
|
||||
metadata: {
|
||||
displayName: localize('chinaCloudDisplayName', 'Azure (China)'),
|
||||
id: 'chinaAzureCloud',
|
||||
settings: {
|
||||
host: 'https://login.chinacloudapi.cn/',
|
||||
clientId: 'TBD',
|
||||
signInResourceId: 'https://management.core.chinacloudapi.cn/',
|
||||
graphResource: {
|
||||
id: 'https://graph.chinacloudapi.cn/',
|
||||
endpoint: 'https://graph.chinacloudapi.cn'
|
||||
},
|
||||
armResource: {
|
||||
id: 'https://management.core.chinacloudapi.cn/',
|
||||
endpoint: 'https://managemement.chinacloudapi.net'
|
||||
},
|
||||
redirectUri: 'http://localhost/redirect'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const germanyAzureSettings: ProviderSettings = {
|
||||
configKey: 'enableGermanyCloud',
|
||||
metadata: {
|
||||
displayName: localize('germanyCloud', 'Azure (Germany)'),
|
||||
displayName: localize('germanyCloud', "Azure (Germany)"),
|
||||
id: 'germanyAzureCloud',
|
||||
settings: {
|
||||
host: 'https://login.microsoftazure.de/',
|
||||
@@ -106,7 +83,27 @@ const germanyAzureSettings: ProviderSettings = {
|
||||
}
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
// TODO: Enable China, Germany, and US Gov clouds: (#3031)
|
||||
export default <ProviderSettings[]>[publicAzureSettings, /*chinaAzureSettings, germanyAzureSettings, usGovAzureSettings*/];
|
||||
const chinaAzureSettings: ProviderSettings = {
|
||||
configKey: 'enableChinaCloud',
|
||||
metadata: {
|
||||
displayName: localize('chinaCloudDisplayName', "Azure (China)"),
|
||||
id: 'chinaAzureCloud',
|
||||
settings: {
|
||||
host: 'https://login.chinacloudapi.cn/',
|
||||
clientId: 'TBD',
|
||||
signInResourceId: 'https://management.core.chinacloudapi.cn/',
|
||||
graphResource: {
|
||||
id: 'https://graph.chinacloudapi.cn/',
|
||||
endpoint: 'https://graph.chinacloudapi.cn'
|
||||
},
|
||||
armResource: {
|
||||
id: 'https://management.core.chinacloudapi.cn/',
|
||||
endpoint: 'https://managemement.chinacloudapi.net'
|
||||
},
|
||||
redirectUri: 'http://localhost/redirect'
|
||||
}
|
||||
}
|
||||
};
|
||||
const allSettings = [publicAzureSettings, usGovAzureSettings, germanyAzureSettings, chinaAzureSettings];
|
||||
export default allSettings;
|
||||
|
||||
@@ -76,20 +76,22 @@ export default class TokenCache implements adal.TokenCache {
|
||||
public find(query: any, callback: (error: Error, results: any[]) => void): void {
|
||||
let self = this;
|
||||
|
||||
this.doOperation(() => {
|
||||
return self.readCache()
|
||||
.then(cache => {
|
||||
return cache.filter(
|
||||
entry => TokenCache.findByPartial(entry, query)
|
||||
);
|
||||
})
|
||||
.then(
|
||||
results => callback(null, results),
|
||||
(err) => callback(err, null)
|
||||
);
|
||||
this.doOperation(async () => {
|
||||
try {
|
||||
const cache = await self.readCache();
|
||||
const filtered = cache.filter(entry => {
|
||||
return TokenCache.findByPartial(entry, query);
|
||||
});
|
||||
|
||||
callback(null, filtered);
|
||||
} catch (ex) {
|
||||
console.log(ex);
|
||||
callback(ex, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper to make callback-based find method into a thenable method
|
||||
* @param query Partial object to use to look up tokens. Ideally should be partial of adal.TokenResponse
|
||||
|
||||
1
extensions/azurecore/src/typings/ref.d.ts
vendored
1
extensions/azurecore/src/typings/ref.d.ts
vendored
@@ -4,6 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
||||
/// <reference path='../../../../src/vs/vscode.proposed.d.ts'/>
|
||||
/// <reference path='../../../../src/sql/azdata.d.ts'/>
|
||||
/// <reference path='../../../../src/sql/azdata.proposed.d.ts'/>
|
||||
/// <reference types='@types/node'/>
|
||||
|
||||
Reference in New Issue
Block a user