Skip to content

Commit

Permalink
[auth] Add filtering getSubscriptions by a specific account and/or te…
Browse files Browse the repository at this point in the history
…nant. (#1880)
  • Loading branch information
MRayermannMSFT authored Jan 31, 2025
1 parent 012787d commit 241fee7
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 23 deletions.
4 changes: 4 additions & 0 deletions auth/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log

## 4.1.0 - 2025-01-31

* Add filtering `getSubscriptions` by a specific account and/or tenant.

## 4.0.3 - 2024-12-20
* [#1862](https://github.com/microsoft/vscode-azuretools/pull/1862) Display account name on duplicate tenant picks

Expand Down
8 changes: 6 additions & 2 deletions auth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@ export interface AzureSubscriptionProvider {
/**
* Gets a list of Azure subscriptions available to the user.
*
* @param filter - Whether to filter the list returned, according to the list returned
* by `getTenantFilters()` and `getSubscriptionFilters()`. Optional, default true.
* @param filter - Whether to filter the list returned. When:
* - `true`: according to the list returned by `getTenantFilters()` and `getSubscriptionFilters()`.
* - `false`: return all subscriptions.
* - `GetSubscriptionsFilter`: according to the values in the filter.
*
* Optional, default true.
*
* @returns A list of Azure subscriptions.
*
Expand Down
4 changes: 2 additions & 2 deletions auth/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion auth/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@microsoft/vscode-azext-azureauth",
"author": "Microsoft Corporation",
"version": "4.0.3",
"version": "4.1.0",
"description": "Azure authentication helpers for Visual Studio Code",
"tags": [
"azure",
Expand Down
6 changes: 3 additions & 3 deletions auth/src/AzureDevOpsSubscriptionProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import type { PipelineRequest } from '@azure/core-rest-pipeline';
import { Disposable, Event } from 'vscode';
import { AzureAuthentication } from './AzureAuthentication';
import { AzureSubscription } from './AzureSubscription';
import { AzureSubscriptionProvider } from './AzureSubscriptionProvider';
import { getConfiguredAzureEnv } from './utils/configuredAzureEnv';
import { AzureSubscriptionProvider, GetSubscriptionsFilter } from './AzureSubscriptionProvider';
import { AzureTenant } from './AzureTenant';
import { getConfiguredAzureEnv } from './utils/configuredAzureEnv';

export interface AzureDevOpsSubscriptionProviderInitializer {
/**
Expand Down Expand Up @@ -76,7 +76,7 @@ export class AzureDevOpsSubscriptionProvider implements AzureSubscriptionProvide
this._CLIENT_ID = clientId;
}

async getSubscriptions(_filter: boolean): Promise<AzureSubscription[]> {
async getSubscriptions(_filter: boolean | GetSubscriptionsFilter): Promise<AzureSubscription[]> {
// ignore the filter setting because not every consumer of this provider will use the Resources extension
const results: AzureSubscription[] = [];
for (const tenant of await this.getTenants()) {
Expand Down
26 changes: 23 additions & 3 deletions auth/src/AzureSubscriptionProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ import type * as vscode from 'vscode';
import type { AzureSubscription } from './AzureSubscription';
import type { AzureTenant } from './AzureTenant';

/**
* A filter for {@link AzureSubscriptionProvider.getSubscriptions}
*/
export type GetSubscriptionsFilter = {
/**
* The account to get subscriptions for. If not provided, all accounts the extension
* currently has access to are used.
*/
account?: vscode.AuthenticationSessionAccountInformation;
/**
* The tenant to get subscriptions for. If not provided, all tenants for each account
* are used.
*/
tenantId?: string;
};

/**
* An interface for obtaining Azure subscription information
*/
Expand All @@ -24,16 +40,20 @@ export interface AzureSubscriptionProvider {
/**
* Gets a list of Azure subscriptions available to the user.
*
* @param filter - Whether to filter the list returned, according to the list returned
* by `getTenantFilters()` and `getSubscriptionFilters()`. Optional, default true.
* @param filter - Whether to filter the list returned. When:
* - `true`: according to the list returned by `getTenantFilters()` and `getSubscriptionFilters()`.
* - `false`: return all subscriptions.
* - `GetSubscriptionsFilter`: according to the values in the filter.
*
* Optional, default true.
*
* @returns A list of Azure subscriptions.
*
* @throws A {@link NotSignedInError} If the user is not signed in to Azure.
* Use {@link isSignedIn} and/or {@link signIn} before this method to ensure
* the user is signed in.
*/
getSubscriptions(filter: boolean): Promise<AzureSubscription[]>;
getSubscriptions(filter: boolean | GetSubscriptionsFilter): Promise<AzureSubscription[]>;

/**
* Checks to see if a user is signed in.
Expand Down
33 changes: 21 additions & 12 deletions auth/src/VSCodeAzureSubscriptionProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import type { TokenCredential } from '@azure/core-auth'; // Keep this as `import
import * as vscode from 'vscode';
import { AzureAuthentication } from './AzureAuthentication';
import { AzureSubscription, SubscriptionId, TenantId } from './AzureSubscription';
import { AzureSubscriptionProvider } from './AzureSubscriptionProvider';
import { AzureSubscriptionProvider, GetSubscriptionsFilter } from './AzureSubscriptionProvider';
import { AzureTenant } from './AzureTenant';
import { getSessionFromVSCode } from './getSessionFromVSCode';
import { NotSignedInError } from './NotSignedInError';
import { getConfiguredAuthProviderId, getConfiguredAzureEnv } from './utils/configuredAzureEnv';
import { AzureTenant } from './AzureTenant';

const EventDebounce = 5 * 1000; // 5 seconds

Expand Down Expand Up @@ -84,8 +84,12 @@ export class VSCodeAzureSubscriptionProvider extends vscode.Disposable implement
/**
* Gets a list of Azure subscriptions available to the user.
*
* @param filter - Whether to filter the list returned, according to the list returned
* by `getTenantFilters()` and `getSubscriptionFilters()`. Optional, default true.
* @param filter - Whether to filter the list returned. When:
* - `true`: according to the list returned by `getTenantFilters()` and `getSubscriptionFilters()`.
* - `false`: return all subscriptions.
* - `GetSubscriptionsFilter`: according to the values in the filter.
*
* Optional, default true.
*
* @returns A list of Azure subscriptions. The list is sorted by subscription name.
* The list can contain duplicate subscriptions if they come from different accounts.
Expand All @@ -94,27 +98,32 @@ export class VSCodeAzureSubscriptionProvider extends vscode.Disposable implement
* Use {@link isSignedIn} and/or {@link signIn} before this method to ensure
* the user is signed in.
*/
public async getSubscriptions(filter: boolean = true): Promise<AzureSubscription[]> {
public async getSubscriptions(filter: boolean | GetSubscriptionsFilter = true): Promise<AzureSubscription[]> {
this.logger?.debug('auth: Loading subscriptions...');
const startTime = Date.now();
const tenantIds = await this.getTenantFilters();
const shouldFilterTenants = filter && !!tenantIds.length; // If the list is empty it is treated as "no filter"

const configuredTenantFilter = await this.getTenantFilters();
const tenantIdsToFilterBy =
// Only filter by the tenant ID option if it is provided
(typeof filter === 'object' && filter.tenantId ? [filter.tenantId] :
// Only filter by the configured filter if `filter` is true AND there are tenants in the configured filter
filter === true && configuredTenantFilter.length > 0 ? configuredTenantFilter :
undefined);


const allSubscriptions: AzureSubscription[] = [];
let accountCount: number; // only used for logging
try {
this.suppressSignInEvents = true;

// Get the list of tenants from each account
const accounts = await vscode.authentication.getAccounts(getConfiguredAuthProviderId());
// Get the list of tenants from each account (filtered or all)
const accounts = typeof filter === 'object' && filter.account ? [filter.account] : await vscode.authentication.getAccounts(getConfiguredAuthProviderId());
accountCount = accounts.length;
for (const account of accounts) {
for (const tenant of await this.getTenants(account)) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const tenantId = tenant.tenantId!;

// If filtering is enabled, and the current tenant is not in that list, then skip it
if (shouldFilterTenants && !tenantIds.includes(tenantId)) {
if (tenantIdsToFilterBy?.includes(tenantId) === false) {
continue;
}

Expand Down
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 241fee7

Please sign in to comment.