Skip to content

Commit

Permalink
Migrate to new Azure SDK packages, fixes #140 (#197)
Browse files Browse the repository at this point in the history
  • Loading branch information
Rachel Macfarlane authored Jun 26, 2020
1 parent 1ca63c5 commit 0fa0989
Show file tree
Hide file tree
Showing 11 changed files with 1,144 additions and 462 deletions.
563 changes: 279 additions & 284 deletions ThirdPartyNotice.txt

Large diffs are not rendered by default.

877 changes: 773 additions & 104 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -222,15 +222,21 @@
"webpack-cli": "3.1.2"
},
"dependencies": {
"@azure/arm-appservice": "^6.0.0",
"@azure/arm-resources": "^3.0.0",
"@azure/arm-subscriptions": "^2.0.0",
"@azure/ms-rest-azure-env": "^2.0.0",
"@azure/ms-rest-nodeauth": "3.0.5",
"adal-node": "0.1.28",
"azure-arm-resource": "7.0.1",
"form-data": "2.3.3",
"http-proxy-agent": "2.1.0",
"https-proxy-agent": "2.2.3",
"ms-rest-azure": "2.5.9",
"ms-rest-js": "^1.0.1",
"request": "2.88.0",
"request-promise": "4.2.2",
"semver": "5.6.0",
"terser-webpack-plugin": "^3.0.6",
"vscode-extension-telemetry": "0.1.5",
"vscode-nls": "4.0.0",
"ws": "6.1.0"
Expand Down
11 changes: 6 additions & 5 deletions sample/src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { window, ExtensionContext, commands, QuickPickItem, extensions } from 'vscode';
import { AzureAccount, AzureSession } from '../../src/azure-account.api'; // Other extensions need to copy this .d.ts to their repository.
import { SubscriptionClient, ResourceManagementClient, SubscriptionModels } from 'azure-arm-resource';
import WebSiteManagementClient = require('azure-arm-website');
import { SubscriptionClient, SubscriptionModels } from '@azure/arm-subscriptions';
import { ResourceManagementClient } from '@azure/arm-resources';
import { WebSiteManagementClient } from '@azure/arm-appservice';

export function activate(context: ExtensionContext) {
const azureAccount = extensions.getExtension<AzureAccount>('ms-vscode.azure-account')!.exports;
Expand Down Expand Up @@ -35,7 +36,7 @@ async function loadSubscriptionItems(api: AzureAccount) {
await api.waitForFilters();
const subscriptionItems: SubscriptionItem[] = [];
for (const session of api.sessions) {
const credentials = session.credentials;
const credentials = session.credentials2;
const subscriptionClient = new SubscriptionClient(credentials);
const subscriptions = await listAll(subscriptionClient.subscriptions, subscriptionClient.subscriptions.list());
subscriptionItems.push(...subscriptions.map(subscription => ({
Expand All @@ -51,7 +52,7 @@ async function loadSubscriptionItems(api: AzureAccount) {

async function loadResourceGroupItems(subscriptionItem: SubscriptionItem) {
const { session, subscription } = subscriptionItem;
const resources = new ResourceManagementClient(session.credentials, subscription.subscriptionId!);
const resources = new ResourceManagementClient(session.credentials2, subscription.subscriptionId!);
const resourceGroups = await listAll(resources.resourceGroups, resources.resourceGroups.list());
resourceGroups.sort((a, b) => (a.name || '').localeCompare(b.name || ''));
return resourceGroups.map(resourceGroup => ({
Expand All @@ -75,7 +76,7 @@ async function loadWebAppItems(api: AzureAccount) {
await api.waitForFilters();
const webAppsPromises: Promise<QuickPickItem[]>[] = [];
for (const filter of api.filters) {
const client = new WebSiteManagementClient(filter.session.credentials, filter.subscription.subscriptionId!);
const client = new WebSiteManagementClient(filter.session.credentials2, filter.subscription.subscriptionId!);
webAppsPromises.push(listAll(client.webApps, client.webApps.list())
.then(webApps => webApps.map(webApp => {
return {
Expand Down
16 changes: 13 additions & 3 deletions src/azure-account.api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@

import { Event, Terminal, Progress, CancellationToken } from 'vscode';
import { ServiceClientCredentials } from 'ms-rest';
import { AzureEnvironment } from 'ms-rest-azure';
import { SubscriptionModels } from 'azure-arm-resource';
import { ReadStream } from 'fs';
import { TokenCredentialsBase } from '@azure/ms-rest-nodeauth';
import { Environment } from '@azure/ms-rest-azure-env';
import { SubscriptionModels } from '@azure/arm-subscriptions';

export type AzureLoginStatus = 'Initializing' | 'LoggingIn' | 'LoggedIn' | 'LoggedOut';

Expand All @@ -27,10 +28,19 @@ export interface AzureAccount {
}

export interface AzureSession {
readonly environment: AzureEnvironment;
readonly environment: Environment;
readonly userId: string;
readonly tenantId: string;

/**
* The credentials object for azure-sdk-for-node modules https://github.com/azure/azure-sdk-for-node
*/
readonly credentials: ServiceClientCredentials;

/**
* The credentials object for azure-sdk-for-js modules https://github.com/azure/azure-sdk-for-js
*/
readonly credentials2: TokenCredentialsBase;
}

export interface AzureSubscription {
Expand Down
62 changes: 33 additions & 29 deletions src/azure-account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ const CacheDriver = require('adal-node/lib/cache-driver');
const createLogContext = require('adal-node/lib/log').createLogContext;

import { MemoryCache, AuthenticationContext, Logging, UserCodeInfo } from 'adal-node';
import { DeviceTokenCredentials, AzureEnvironment } from 'ms-rest-azure';
import { SubscriptionClient, SubscriptionModels } from 'azure-arm-resource';
import { DeviceTokenCredentials } from 'ms-rest-azure';
import { SubscriptionClient, SubscriptionModels } from '@azure/arm-subscriptions';
import * as nls from 'vscode-nls';
import * as keytarType from 'keytar';
import * as http from 'http';
Expand All @@ -20,6 +20,8 @@ import { createCloudConsole } from './cloudConsole';
import * as codeFlowLogin from './codeFlowLogin';
import { TelemetryReporter } from './telemetry';
import { TokenResponse } from 'adal-node';
import { DeviceTokenCredentials as DeviceTokenCredentials2 } from '@azure/ms-rest-nodeauth';
import { Environment } from '@azure/ms-rest-azure-env';

const localize = nls.loadMessageBundle();

Expand All @@ -44,7 +46,7 @@ function getNodeModule<T>(moduleName: string): T | undefined {

const credentialsSection = 'VS Code Azure';

async function getStoredCredentials(environment: AzureEnvironment, migrateToken?: boolean) {
async function getStoredCredentials(environment: Environment, migrateToken?: boolean) {
if (!keytar) {
return;
}
Expand All @@ -68,7 +70,7 @@ async function getStoredCredentials(environment: AzureEnvironment, migrateToken?
}
}

async function storeRefreshToken(environment: AzureEnvironment, token: string) {
async function storeRefreshToken(environment: Environment, token: string) {
if (keytar) {
try {
await keytar.setPassword(credentialsSection, environment.name, token);
Expand All @@ -88,11 +90,11 @@ async function deleteRefreshToken(environmentName: string) {
}
}

const staticEnvironments: AzureEnvironment[] = [
AzureEnvironment.Azure,
AzureEnvironment.AzureChina,
AzureEnvironment.AzureGermanCloud,
AzureEnvironment.AzureUSGovernment
const staticEnvironments: Environment[] = [
Environment.AzureCloud,
Environment.ChinaCloud,
Environment.GermanCloud,
Environment.USGovernment
];

const azurePPE = 'AzurePPE';
Expand Down Expand Up @@ -378,7 +380,7 @@ export class AzureLoginHelper {
throw new AzureLoginError(localize('azure-account.malformedCredentials', "Stored credentials are invalid"));
}

tokenResponse = await codeFlowLogin.tokenWithAuthorizationCode(clientId, AzureEnvironment.Azure, redirectionUrl, tenantId, code);
tokenResponse = await codeFlowLogin.tokenWithAuthorizationCode(clientId, Environment.AzureCloud, redirectionUrl, tenantId, code);
}

if (!tokenResponse) {
Expand Down Expand Up @@ -460,18 +462,19 @@ export class AzureLoginHelper {
const key = `${environment} ${userId} ${tenantId}`;
if (!sessions[key]) {
sessions[key] = {
environment: (<any>AzureEnvironment)[environment],
environment: (<any>Environment)[environment],
userId,
tenantId,
credentials: new DeviceTokenCredentials({ environment: (<any>AzureEnvironment)[environment], username: userId, clientId, tokenCache: this.delayedCache, domain: tenantId })
credentials: new DeviceTokenCredentials({ environment: (<any>Environment)[environment], username: userId, clientId, tokenCache: this.delayedCache, domain: tenantId }),
credentials2: new DeviceTokenCredentials2(clientId, tenantId, userId, undefined, (<any>Environment)[environment], this.delayedCache)
};
this.api.sessions.push(sessions[key]);
}
}
return sessions;
}

private async updateSessions(environment: AzureEnvironment, tokenResponses: TokenResponse[]) {
private async updateSessions(environment: Environment, tokenResponses: TokenResponse[]) {
await clearTokenCache(this.tokenCache);
for (const tokenResponse of tokenResponses) {
await addTokenToCache(environment, this.tokenCache, tokenResponse);
Expand All @@ -482,7 +485,8 @@ export class AzureLoginHelper {
environment,
userId: tokenResponse.userId!,
tenantId: tokenResponse.tenantId!,
credentials: new DeviceTokenCredentials({ environment: environment, username: tokenResponse.userId, clientId, tokenCache: this.delayedCache, domain: tokenResponse.tenantId })
credentials: new DeviceTokenCredentials({ environment: (<any>environment), username: tokenResponse.userId, clientId, tokenCache: this.delayedCache, domain: tokenResponse.tenantId }),
credentials2: new DeviceTokenCredentials2(clientId, tokenResponse.tenantId, tokenResponse.userId, undefined, environment, this.delayedCache)
})));
this.onSessionsChanged.fire();
}
Expand Down Expand Up @@ -602,8 +606,8 @@ export class AzureLoginHelper {

private async loadSubscriptions() {
const lists = await Promise.all(this.api.sessions.map(session => {
const credentials = session.credentials;
const client = new SubscriptionClient.SubscriptionClient(credentials, session.environment.resourceManagerEndpointUrl);
const credentials = session.credentials2;
const client = new SubscriptionClient(credentials, { baseUri: session.environment.resourceManagerEndpointUrl });
return listAll(client.subscriptions, client.subscriptions.list())
.then(list => list.map(subscription => ({
session,
Expand Down Expand Up @@ -703,15 +707,15 @@ export class AzureLoginHelper {
}
}

function getSelectedEnvironment(): AzureEnvironment {
function getSelectedEnvironment(): Environment {
const envConfig = workspace.getConfiguration('azure');
const envSetting = envConfig.get<string>('cloud');
return getEnvironments().find(environment => environment.name === envSetting) || AzureEnvironment.Azure;
return getEnvironments().find(environment => environment.name === envSetting) || Environment.AzureCloud;
}

function getEnvironments() {
const config = workspace.getConfiguration('azure');
const ppe = config.get<AzureEnvironment>('ppe');
const ppe = config.get<Environment>('ppe');
if (ppe) {
return [
...staticEnvironments,
Expand All @@ -730,7 +734,7 @@ function getTenantId() {
return envConfig.get<string>('tenant') || commonTenantId;
}

async function deviceLogin(environment: AzureEnvironment, tenantId: string) {
async function deviceLogin(environment: Environment, tenantId: string) {
const deviceLogin = await deviceLogin1(environment, tenantId);
const message = showDeviceCodeMessage(deviceLogin);
const login2 = deviceLogin2(environment, tenantId, deviceLogin);
Expand All @@ -748,7 +752,7 @@ async function showDeviceCodeMessage(deviceLogin: UserCodeInfo): Promise<void> {
}
}

async function deviceLogin1(environment: AzureEnvironment, tenantId: string): Promise<UserCodeInfo> {
async function deviceLogin1(environment: Environment, tenantId: string): Promise<UserCodeInfo> {
return new Promise<UserCodeInfo>((resolve, reject) => {
const cache = new MemoryCache();
const context = new AuthenticationContext(`${environment.activeDirectoryEndpointUrl}${tenantId}`, validateAuthority, cache);
Expand All @@ -762,7 +766,7 @@ async function deviceLogin1(environment: AzureEnvironment, tenantId: string): Pr
});
}

async function deviceLogin2(environment: AzureEnvironment, tenantId: string, deviceLogin: UserCodeInfo) {
async function deviceLogin2(environment: Environment, tenantId: string, deviceLogin: UserCodeInfo) {
return new Promise<TokenResponse>((resolve, reject) => {
const tokenCache = new MemoryCache();
const context = new AuthenticationContext(`${environment.activeDirectoryEndpointUrl}${tenantId}`, validateAuthority, tokenCache);
Expand All @@ -785,7 +789,7 @@ async function redirectTimeout() {
}
}

export async function tokenFromRefreshToken(environment: AzureEnvironment, refreshToken: string, tenantId: string, resource?: string) {
export async function tokenFromRefreshToken(environment: Environment, refreshToken: string, tenantId: string, resource?: string) {
return new Promise<TokenResponse>((resolve, reject) => {
const tokenCache = new MemoryCache();
const context = new AuthenticationContext(`${environment.activeDirectoryEndpointUrl}${tenantId}`, validateAuthority, tokenCache);
Expand All @@ -801,11 +805,11 @@ export async function tokenFromRefreshToken(environment: AzureEnvironment, refre
});
}

async function tokensFromToken(environment: AzureEnvironment, firstTokenResponse: TokenResponse) {
async function tokensFromToken(environment: Environment, firstTokenResponse: TokenResponse) {
const tokenCache = new MemoryCache();
await addTokenToCache(environment, tokenCache, firstTokenResponse);
const credentials = new DeviceTokenCredentials({ username: firstTokenResponse.userId, clientId, tokenCache, environment });
const client = new SubscriptionClient.SubscriptionClient(credentials, environment.resourceManagerEndpointUrl);
const credentials = new DeviceTokenCredentials2(clientId, undefined, firstTokenResponse.userId, undefined, environment, tokenCache);
const client = new SubscriptionClient(credentials, { baseUri: environment.resourceManagerEndpointUrl });
const tenants = await listAll(client.tenants, client.tenants.list());
const responses = <TokenResponse[]>(await Promise.all<TokenResponse | null>(tenants.map((tenant, i) => {
if (tenant.tenantId === firstTokenResponse.tenantId) {
Expand All @@ -823,7 +827,7 @@ async function tokensFromToken(environment: AzureEnvironment, firstTokenResponse
return responses;
}

async function addTokenToCache(environment: AzureEnvironment, tokenCache: any, tokenResponse: TokenResponse) {
async function addTokenToCache(environment: Environment, tokenCache: any, tokenResponse: TokenResponse) {
return new Promise<any>((resolve, reject) => {
const driver = new CacheDriver(
{ _logContext: createLogContext('') },
Expand Down Expand Up @@ -921,7 +925,7 @@ function getErrorMessage(err: any): string | undefined {
return str;
}

async function becomeOnline(environment: AzureEnvironment, interval: number, token = new CancellationTokenSource().token) {
async function becomeOnline(environment: Environment, interval: number, token = new CancellationTokenSource().token) {
let o = isOnline(environment);
let d = delay(interval, false);
while (!token.isCancellationRequested && !await Promise.race([o, d])) {
Expand All @@ -931,7 +935,7 @@ async function becomeOnline(environment: AzureEnvironment, interval: number, tok
}
}

async function isOnline(environment: AzureEnvironment) {
async function isOnline(environment: Environment) {
try {
await new Promise<http.IncomingMessage | https.IncomingMessage>((resolve, reject) => {
const url = environment.activeDirectoryEndpointUrl;
Expand Down
6 changes: 4 additions & 2 deletions src/cloudConsole.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,12 +380,14 @@ export function createCloudConsole(api: AzureAccount, reporter: TelemetryReporte
// Additional tokens
const [graphToken, keyVaultToken] = await Promise.all([
tokenFromRefreshToken(session.environment, result.token.refreshToken, session.tenantId, session.environment.activeDirectoryGraphResourceId),
tokenFromRefreshToken(session.environment, result.token.refreshToken, session.tenantId, `https://${session.environment.keyVaultDnsSuffix.substr(1)}`)
session.environment.keyVaultDnsSuffix
? tokenFromRefreshToken(session.environment, result.token.refreshToken, session.tenantId, `https://${session.environment.keyVaultDnsSuffix!.substr(1)}`)
: Promise.resolve(undefined)
]);
const accessTokens: AccessTokens = {
resource: accessToken,
graph: graphToken.accessToken,
keyVault: keyVaultToken.accessToken
keyVault: keyVaultToken && keyVaultToken.accessToken
};
deferredTokens!.resolve(accessTokens);

Expand Down
4 changes: 2 additions & 2 deletions src/cloudConsoleLauncher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export interface UserSettings {
export interface AccessTokens {
resource: string;
graph: string;
keyVault: string;
keyVault?: string;
}

export interface ConsoleUris {
Expand Down Expand Up @@ -180,7 +180,7 @@ async function initializeTerminal(accessTokens: AccessTokens, consoleUri: string
resolveWithFullResponse: true,
json: true,
body: {
tokens: [accessTokens.graph, accessTokens.keyVault]
tokens: accessTokens.keyVault ? [accessTokens.graph, accessTokens.keyVault] : [accessTokens.graph]
}
});
}
Expand Down
Loading

0 comments on commit 0fa0989

Please sign in to comment.