Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate to new Azure SDK packages #197

Merged
merged 9 commits into from
Jun 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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