@microsoft/vscode-azext-azureauth 5.1.1 → 6.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +9 -79
  3. package/dist/cjs/src/contracts/AzureAccount.js +7 -0
  4. package/dist/cjs/src/contracts/AzureSubscriptionProviderRequestOptions.js +48 -0
  5. package/dist/cjs/src/index.js +13 -10
  6. package/dist/cjs/src/providers/AzureDevOpsSubscriptionProvider.js +178 -0
  7. package/dist/cjs/src/providers/AzureSubscriptionProviderBase.js +393 -0
  8. package/dist/cjs/src/providers/VSCodeAzureSubscriptionProvider.js +269 -0
  9. package/dist/cjs/src/utils/Limiter.js +41 -0
  10. package/dist/cjs/src/{NotSignedInError.js → utils/NotSignedInError.js} +3 -2
  11. package/dist/cjs/src/utils/configuredAzureEnv.js +14 -16
  12. package/dist/cjs/src/utils/dedupeSubscriptions.js +27 -0
  13. package/dist/cjs/src/utils/getMetricsForTelemetry.js +47 -0
  14. package/dist/cjs/src/{getSessionFromVSCode.js → utils/getSessionFromVSCode.js} +5 -2
  15. package/dist/cjs/src/utils/getSignalForToken.js +29 -0
  16. package/dist/cjs/src/utils/map/CaselessMap.js +71 -0
  17. package/dist/cjs/src/utils/map/TwoKeyCaselessMap.js +194 -0
  18. package/dist/cjs/src/utils/screen.js +62 -0
  19. package/dist/cjs/src/{signInToTenant.js → utils/signInToTenant.js} +15 -13
  20. package/dist/cjs/src/utils/tryGetTokenExpiration.js +25 -0
  21. package/dist/esm/src/contracts/AzureAccount.d.ts +5 -0
  22. package/dist/esm/src/contracts/AzureAccount.js +6 -0
  23. package/dist/esm/src/{AzureAuthentication.d.ts → contracts/AzureAuthentication.d.ts} +1 -1
  24. package/dist/esm/src/{AzureSubscription.d.ts → contracts/AzureSubscription.d.ts} +4 -4
  25. package/dist/esm/src/contracts/AzureSubscriptionProvider.d.ts +112 -0
  26. package/dist/esm/src/contracts/AzureSubscriptionProviderRequestOptions.d.ts +103 -0
  27. package/dist/esm/src/contracts/AzureSubscriptionProviderRequestOptions.js +44 -0
  28. package/dist/esm/src/contracts/AzureTenant.d.ts +15 -0
  29. package/dist/esm/src/index.d.ts +13 -10
  30. package/dist/esm/src/index.js +13 -10
  31. package/dist/esm/src/providers/AzureDevOpsSubscriptionProvider.d.ts +68 -0
  32. package/dist/esm/src/providers/AzureDevOpsSubscriptionProvider.js +140 -0
  33. package/dist/esm/src/providers/AzureSubscriptionProviderBase.d.ts +74 -0
  34. package/dist/esm/src/providers/AzureSubscriptionProviderBase.js +356 -0
  35. package/dist/esm/src/providers/VSCodeAzureSubscriptionProvider.d.ts +70 -0
  36. package/dist/esm/src/providers/VSCodeAzureSubscriptionProvider.js +232 -0
  37. package/dist/esm/src/utils/Limiter.d.ts +9 -0
  38. package/dist/esm/src/utils/Limiter.js +37 -0
  39. package/dist/esm/src/{NotSignedInError.d.ts → utils/NotSignedInError.d.ts} +2 -2
  40. package/dist/esm/src/{NotSignedInError.js → utils/NotSignedInError.js} +3 -2
  41. package/dist/esm/src/utils/configuredAzureEnv.d.ts +7 -4
  42. package/dist/esm/src/utils/configuredAzureEnv.js +14 -16
  43. package/dist/esm/src/utils/dedupeSubscriptions.d.ts +14 -0
  44. package/dist/esm/src/utils/dedupeSubscriptions.js +24 -0
  45. package/dist/esm/src/utils/getMetricsForTelemetry.d.ts +32 -0
  46. package/dist/esm/src/utils/getMetricsForTelemetry.js +44 -0
  47. package/dist/esm/src/{getSessionFromVSCode.js → utils/getSessionFromVSCode.js} +5 -2
  48. package/dist/esm/src/utils/getSignalForToken.d.ts +7 -0
  49. package/dist/esm/src/utils/getSignalForToken.js +26 -0
  50. package/dist/esm/src/utils/map/CaselessMap.d.ts +28 -0
  51. package/dist/esm/src/utils/map/CaselessMap.js +67 -0
  52. package/dist/esm/src/utils/map/TwoKeyCaselessMap.d.ts +49 -0
  53. package/dist/esm/src/utils/map/TwoKeyCaselessMap.js +190 -0
  54. package/dist/esm/src/utils/screen.d.ts +9 -0
  55. package/dist/esm/src/utils/screen.js +59 -0
  56. package/dist/esm/src/utils/signInToTenant.d.ts +7 -0
  57. package/dist/esm/src/{signInToTenant.js → utils/signInToTenant.js} +16 -14
  58. package/dist/esm/src/utils/tryGetTokenExpiration.d.ts +2 -0
  59. package/dist/esm/src/utils/tryGetTokenExpiration.js +22 -0
  60. package/package.json +33 -23
  61. package/AzureFederatedCredentialsGuide.md +0 -174
  62. package/dist/cjs/src/AzureDevOpsSubscriptionProvider.js +0 -215
  63. package/dist/cjs/src/VSCodeAzureSubscriptionProvider.js +0 -395
  64. package/dist/cjs/src/utils/getUnauthenticatedTenants.js +0 -23
  65. package/dist/cjs/src/utils/isGetSubscriptionsFilter.js +0 -27
  66. package/dist/esm/src/AzureDevOpsSubscriptionProvider.d.ts +0 -68
  67. package/dist/esm/src/AzureDevOpsSubscriptionProvider.js +0 -210
  68. package/dist/esm/src/AzureSubscriptionProvider.d.ts +0 -82
  69. package/dist/esm/src/AzureTenant.d.ts +0 -5
  70. package/dist/esm/src/VSCodeAzureSubscriptionProvider.d.ts +0 -116
  71. package/dist/esm/src/VSCodeAzureSubscriptionProvider.js +0 -358
  72. package/dist/esm/src/signInToTenant.d.ts +0 -6
  73. package/dist/esm/src/utils/getUnauthenticatedTenants.d.ts +0 -9
  74. package/dist/esm/src/utils/getUnauthenticatedTenants.js +0 -20
  75. package/dist/esm/src/utils/isGetSubscriptionsFilter.d.ts +0 -14
  76. package/dist/esm/src/utils/isGetSubscriptionsFilter.js +0 -23
  77. /package/dist/cjs/src/{AzureAuthentication.js → contracts/AzureAuthentication.js} +0 -0
  78. /package/dist/cjs/src/{AzureSubscription.js → contracts/AzureSubscription.js} +0 -0
  79. /package/dist/cjs/src/{AzureSubscriptionProvider.js → contracts/AzureSubscriptionProvider.js} +0 -0
  80. /package/dist/cjs/src/{AzureTenant.js → contracts/AzureTenant.js} +0 -0
  81. /package/dist/esm/src/{AzureAuthentication.js → contracts/AzureAuthentication.js} +0 -0
  82. /package/dist/esm/src/{AzureSubscription.js → contracts/AzureSubscription.js} +0 -0
  83. /package/dist/esm/src/{AzureSubscriptionProvider.js → contracts/AzureSubscriptionProvider.js} +0 -0
  84. /package/dist/esm/src/{AzureTenant.js → contracts/AzureTenant.js} +0 -0
  85. /package/dist/esm/src/{getSessionFromVSCode.d.ts → utils/getSessionFromVSCode.d.ts} +0 -0
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) Microsoft Corporation. All rights reserved.
4
+ * Licensed under the MIT License. See License.txt in the project root for license information.
5
+ *--------------------------------------------------------------------------------------------*/
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.Limiter = void 0;
8
+ class Limiter {
9
+ runningPromises;
10
+ maxDegreeOfParalellism;
11
+ outstandingPromises;
12
+ constructor(maxDegreeOfParalellism) {
13
+ this.maxDegreeOfParalellism = maxDegreeOfParalellism;
14
+ this.outstandingPromises = [];
15
+ this.runningPromises = 0;
16
+ }
17
+ queue(factory) {
18
+ return new Promise((c, e) => {
19
+ this.outstandingPromises.push({ factory, c, e });
20
+ this.consume();
21
+ });
22
+ }
23
+ consume() {
24
+ while (this.outstandingPromises.length && this.runningPromises < this.maxDegreeOfParalellism) {
25
+ const iLimitedTask = this.outstandingPromises.shift();
26
+ this.runningPromises++;
27
+ const promise = iLimitedTask.factory();
28
+ promise.then(iLimitedTask.c, iLimitedTask.e);
29
+ promise.then(() => this.consumed(), () => this.consumed());
30
+ }
31
+ }
32
+ consumed() {
33
+ this.runningPromises--;
34
+ if (this.outstandingPromises.length > 0) {
35
+ this.consume();
36
+ }
37
+ }
38
+ }
39
+ exports.Limiter = Limiter;
40
+ /* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
41
+ //# sourceMappingURL=Limiter.js.map
@@ -51,13 +51,14 @@ class NotSignedInError extends Error {
51
51
  }
52
52
  exports.NotSignedInError = NotSignedInError;
53
53
  /**
54
- * Tests if an object is a `NotSignedInError`. This should be used instead of `instanceof`.
54
+ * Tests if an object is a {@link NotSignedInError}. This should be used instead of `instanceof`.
55
55
  *
56
56
  * @param error The object to test
57
57
  *
58
- * @returns True if the object is a NotSignedInError, false otherwise
58
+ * @returns True if the object is a {@link NotSignedInError}, false otherwise
59
59
  */
60
60
  function isNotSignedInError(error) {
61
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-boolean-literal-compare, @typescript-eslint/no-unnecessary-condition
61
62
  return !!error && typeof error === 'object' && error.isNotSignedInError === true;
62
63
  }
63
64
  //# sourceMappingURL=NotSignedInError.js.map
@@ -52,6 +52,16 @@ var CloudEnvironmentSettingValue;
52
52
  CloudEnvironmentSettingValue["USGovernment"] = "USGovernment";
53
53
  CloudEnvironmentSettingValue["Custom"] = "custom";
54
54
  })(CloudEnvironmentSettingValue || (CloudEnvironmentSettingValue = {}));
55
+ class ExtendedEnvironment extends azureEnv.Environment {
56
+ isCustomCloud;
57
+ constructor(parameters, isCustomCloud) {
58
+ super(parameters);
59
+ this.isCustomCloud = isCustomCloud;
60
+ // The Environment constructor only copies required properties. Copy all remaining
61
+ // optional properties (e.g. storageEndpointSuffix, keyVaultDnsSuffix) from the source.
62
+ Object.assign(this, parameters);
63
+ }
64
+ }
55
65
  /**
56
66
  * Gets the configured Azure environment.
57
67
  *
@@ -61,31 +71,19 @@ function getConfiguredAzureEnv() {
61
71
  const authProviderConfig = vscode.workspace.getConfiguration(CustomCloudConfigurationSection);
62
72
  const environmentSettingValue = authProviderConfig.get(CloudEnvironmentSettingName);
63
73
  if (environmentSettingValue === CloudEnvironmentSettingValue.ChinaCloud) {
64
- return {
65
- ...azureEnv.Environment.ChinaCloud,
66
- isCustomCloud: false,
67
- };
74
+ return new ExtendedEnvironment(azureEnv.Environment.ChinaCloud, false);
68
75
  }
69
76
  else if (environmentSettingValue === CloudEnvironmentSettingValue.USGovernment) {
70
- return {
71
- ...azureEnv.Environment.USGovernment,
72
- isCustomCloud: false,
73
- };
77
+ return new ExtendedEnvironment(azureEnv.Environment.USGovernment, false);
74
78
  }
75
79
  else if (environmentSettingValue === CloudEnvironmentSettingValue.Custom) {
76
80
  const customCloud = authProviderConfig.get(CustomEnvironmentSettingName);
77
81
  if (customCloud) {
78
- return {
79
- ...new azureEnv.Environment(customCloud),
80
- isCustomCloud: true,
81
- };
82
+ return new ExtendedEnvironment(customCloud, true);
82
83
  }
83
84
  throw new Error(vscode.l10n.t('The custom cloud choice is not configured. Please configure the setting `{0}.{1}`.', CustomCloudConfigurationSection, CustomEnvironmentSettingName));
84
85
  }
85
- return {
86
- ...azureEnv.Environment.get(azureEnv.Environment.AzureCloud.name),
87
- isCustomCloud: false,
88
- };
86
+ return new ExtendedEnvironment(azureEnv.Environment.AzureCloud, false);
89
87
  }
90
88
  /**
91
89
  * Sets the configured Azure cloud.
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) Microsoft Corporation. All rights reserved.
4
+ * Licensed under the MIT License. See License.txt in the project root for license information.
5
+ *--------------------------------------------------------------------------------------------*/
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.dedupeSubscriptions = dedupeSubscriptions;
8
+ /**
9
+ * Deduplicates (and sorts) a list of Azure subscriptions.
10
+ * In short, the behavior is that the combination of account + tenant + subscriptionId will be unique.
11
+ * The last appearance of any duplicates will be kept. The resulting list is sorted by subscription name.
12
+ *
13
+ * Please note:
14
+ * - The same tenant may appear under different accounts.
15
+ * - The same subscriptionId may appear under different accounts that share the same tenant.
16
+ * - Rarely, the same subscriptionId may also appear under different accounts + different tenants.
17
+ *
18
+ * @param subscriptions The list of subscriptions to deduplicate
19
+ */
20
+ function dedupeSubscriptions(subscriptions) {
21
+ const deduped = new Map();
22
+ for (const sub of subscriptions) {
23
+ deduped.set(`${sub.account.id}/${sub.tenantId}/${sub.subscriptionId}`, sub);
24
+ }
25
+ return Array.from(deduped.values()).sort((a, b) => a.name.localeCompare(b.name));
26
+ }
27
+ //# sourceMappingURL=dedupeSubscriptions.js.map
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) Microsoft Corporation. All rights reserved.
4
+ * Licensed under the MIT License. See License.txt in the project root for license information.
5
+ *--------------------------------------------------------------------------------------------*/
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.getMetricsForTelemetry = getMetricsForTelemetry;
8
+ /**
9
+ * Gets telemetry metrics about accounts, tenants, and subscriptions.
10
+ * @param subscriptionProvider The subscription provider to use to get the metrics.
11
+ * @throws A NotSignedInError if no accounts are signed in.
12
+ */
13
+ async function getMetricsForTelemetry(subscriptionProvider) {
14
+ // Get the total number of accounts (requires no web requests)
15
+ // Errors are deliberately not caught here, so if no accounts are signed in, this will throw
16
+ const accounts = await subscriptionProvider.getAccounts();
17
+ const numAccounts = accounts.length;
18
+ // Get the total number of tenants across all accounts (requires one web request per account, which is reasonable)
19
+ let numTenants = 0;
20
+ for (const account of accounts) {
21
+ try {
22
+ const tenants = await subscriptionProvider.getTenantsForAccount(account);
23
+ numTenants += tenants.length;
24
+ }
25
+ catch {
26
+ continue; // If we can't get tenants for an account, skip it instead of throwing
27
+ }
28
+ }
29
+ // Get as many subscriptions as we can (requires one web request per account+tenant)
30
+ // This is limited to the first 10 account+tenants, thus a max of 10 web requests
31
+ // Errors are deliberately not caught here; none are expected since at least one account is signed in
32
+ const subscriptions = await subscriptionProvider.getAvailableSubscriptions();
33
+ const numSubscriptions = subscriptions.length;
34
+ // Get subscription IDs (up to 25)
35
+ const subscriptionSet = new Set();
36
+ subscriptions.slice(0, 25).forEach(sub => {
37
+ subscriptionSet.add(sub.subscriptionId);
38
+ });
39
+ return {
40
+ totalAccounts: numAccounts,
41
+ visibleTenants: numTenants,
42
+ visibleSubscriptions: numSubscriptions,
43
+ subscriptionIdList: JSON.stringify(Array.from(subscriptionSet)),
44
+ subscriptionIdListIsIncomplete: subscriptions.length > 25,
45
+ };
46
+ }
47
+ //# sourceMappingURL=getMetricsForTelemetry.js.map
@@ -39,13 +39,16 @@ var __importStar = (this && this.__importStar) || (function () {
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
40
  exports.getSessionFromVSCode = getSessionFromVSCode;
41
41
  const vscode = __importStar(require("vscode"));
42
- const configuredAzureEnv_1 = require("./utils/configuredAzureEnv");
43
- const isAuthenticationWwwAuthenticateRequest_1 = require("./utils/isAuthenticationWwwAuthenticateRequest");
42
+ const configuredAzureEnv_1 = require("./configuredAzureEnv");
43
+ const isAuthenticationWwwAuthenticateRequest_1 = require("./isAuthenticationWwwAuthenticateRequest");
44
44
  function ensureEndingSlash(value) {
45
45
  return value.endsWith('/') ? value : `${value}/`;
46
46
  }
47
47
  function getResourceScopes(scopes) {
48
48
  if (scopes === undefined || scopes === "" || scopes.length === 0) {
49
+ // For https://github.com/microsoft/vscode-azurefunctions/issues/3913, the default scope was changed from
50
+ // ~'https://management.azure.com/.default' (`AzureEnv.resourceManagerEndpointUrl`) to
51
+ // ~'https://management.core.windows.net/.default' (`AzureEnv.managementEndpointUrl`)
49
52
  scopes = ensureEndingSlash((0, configuredAzureEnv_1.getConfiguredAzureEnv)().managementEndpointUrl);
50
53
  }
51
54
  const arrScopes = (Array.isArray(scopes) ? scopes : [scopes])
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) Microsoft Corporation. All rights reserved.
4
+ * Licensed under the MIT License. See License.txt in the project root for license information.
5
+ *--------------------------------------------------------------------------------------------*/
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.getSignalForToken = getSignalForToken;
8
+ /**
9
+ * Gets an AbortSignal that is tied to the given {@link vscode.CancellationToken}.
10
+ * @param token The token to convert
11
+ * @returns The {@link AbortSignal} or undefined if no token is provided
12
+ */
13
+ function getSignalForToken(token) {
14
+ if (!token) {
15
+ return undefined;
16
+ }
17
+ const controller = new AbortController();
18
+ if (token.isCancellationRequested) {
19
+ controller.abort();
20
+ }
21
+ else {
22
+ const disposable = token.onCancellationRequested(() => {
23
+ disposable.dispose();
24
+ controller.abort();
25
+ });
26
+ }
27
+ return controller.signal;
28
+ }
29
+ //# sourceMappingURL=getSignalForToken.js.map
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) Microsoft Corporation. All rights reserved.
4
+ * Licensed under the MIT License. See License.txt in the project root for license information.
5
+ *--------------------------------------------------------------------------------------------*/
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.CaselessMap = void 0;
8
+ class CaselessMap {
9
+ #map = new Map();
10
+ clear() {
11
+ this.#map.clear();
12
+ }
13
+ delete(key) {
14
+ return this.#map.delete(this.normalizeKey(key));
15
+ }
16
+ /**
17
+ * @important Keys returned **match original case**!
18
+ */
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Must exactly match interface
20
+ forEach(callbackfn, thisArg) {
21
+ this.#map.forEach(entry => {
22
+ callbackfn.call(thisArg, entry.value, entry.originalKey, this);
23
+ });
24
+ }
25
+ get(key) {
26
+ return this.#map.get(this.normalizeKey(key))?.value;
27
+ }
28
+ has(key) {
29
+ return this.#map.has(this.normalizeKey(key));
30
+ }
31
+ set(key, value) {
32
+ this.#map.set(this.normalizeKey(key), { originalKey: key, value });
33
+ return this;
34
+ }
35
+ get size() {
36
+ return this.#map.size;
37
+ }
38
+ /**
39
+ * @important Keys returned **match original case**!
40
+ */
41
+ *entries() {
42
+ for (const [, entry] of this.#map) {
43
+ yield [entry.originalKey, entry.value];
44
+ }
45
+ }
46
+ /**
47
+ * @important Keys returned **match original case**!
48
+ */
49
+ *keys() {
50
+ for (const [, entry] of this.#map) {
51
+ yield entry.originalKey;
52
+ }
53
+ }
54
+ *values() {
55
+ for (const [, entry] of this.#map) {
56
+ yield entry.value;
57
+ }
58
+ }
59
+ /**
60
+ * @important Keys returned **match original case**!
61
+ */
62
+ [Symbol.iterator]() {
63
+ return this.entries();
64
+ }
65
+ [Symbol.toStringTag] = 'CaselessMap';
66
+ normalizeKey(key) {
67
+ return key.toLowerCase();
68
+ }
69
+ }
70
+ exports.CaselessMap = CaselessMap;
71
+ //# sourceMappingURL=CaselessMap.js.map
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) Microsoft Corporation. All rights reserved.
4
+ * Licensed under the MIT License. See License.txt in the project root for license information.
5
+ *--------------------------------------------------------------------------------------------*/
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.TwoKeyCaselessMap = void 0;
8
+ const CaselessMap_1 = require("./CaselessMap");
9
+ class TwoKeyCaselessMap {
10
+ testMode;
11
+ #map1 = new CaselessMap_1.CaselessMap(); // First map has key1 as the outer key and key2 as the inner key
12
+ #map2 = new CaselessMap_1.CaselessMap(); // Second key map has key2 as the outer key and key1 as the inner key
13
+ #size = 0;
14
+ constructor(testMode = false) {
15
+ this.testMode = testMode;
16
+ }
17
+ clear() {
18
+ this.#map1.clear();
19
+ this.#map2.clear();
20
+ this.#size = 0;
21
+ }
22
+ clearByFirstKey(key1) {
23
+ const innerMap1 = this.#map1.get(key1);
24
+ if (!innerMap1) {
25
+ return;
26
+ }
27
+ // Decrement size before any deletions
28
+ this.#size -= innerMap1.size;
29
+ // Remove all entries from #map2 that reference this key1
30
+ innerMap1.forEach((_, key2) => {
31
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- The existence of innerMap1 ensures this is non-null
32
+ const innerMap2 = this.#map2.get(key2);
33
+ innerMap2.delete(key1);
34
+ if (innerMap2.size === 0) {
35
+ this.#map2.delete(key2);
36
+ }
37
+ });
38
+ // Remove the entire inner map from #map1
39
+ this.#map1.delete(key1);
40
+ }
41
+ clearBySecondKey(key2) {
42
+ const innerMap2 = this.#map2.get(key2);
43
+ if (!innerMap2) {
44
+ return;
45
+ }
46
+ // Decrement size before any deletions
47
+ this.#size -= innerMap2.size;
48
+ // Remove all entries from #map1 that reference this key2
49
+ innerMap2.forEach((_, key1) => {
50
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- The existence of innerMap2 ensures this is non-null
51
+ const innerMap1 = this.#map1.get(key1);
52
+ innerMap1.delete(key2);
53
+ if (innerMap1.size === 0) {
54
+ this.#map1.delete(key1);
55
+ }
56
+ });
57
+ // Remove the entire inner map from #map2
58
+ this.#map2.delete(key2);
59
+ }
60
+ delete(key1, key2) {
61
+ if (!this.has(key1, key2)) {
62
+ return false;
63
+ }
64
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- The has() check above ensures non-null
65
+ const innerMap1 = this.#map1.get(key1);
66
+ innerMap1.delete(key2);
67
+ if (innerMap1.size === 0) {
68
+ this.#map1.delete(key1);
69
+ }
70
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- The has() check above ensures non-null
71
+ const innerMap2 = this.#map2.get(key2);
72
+ innerMap2.delete(key1);
73
+ if (innerMap2.size === 0) {
74
+ this.#map2.delete(key2);
75
+ }
76
+ this.#size--;
77
+ return true;
78
+ }
79
+ /**
80
+ * @important Keys returned **match original case**!
81
+ */
82
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Must exactly match interface
83
+ forEach(callbackfn, thisArg) {
84
+ this.#map1.forEach((innerMap1, key1) => {
85
+ innerMap1.forEach((value, key2) => {
86
+ callbackfn.call(thisArg, value, key1, key2);
87
+ });
88
+ });
89
+ }
90
+ get(key1, key2) {
91
+ return this.#map1.get(key1)?.get(key2);
92
+ }
93
+ has(key1, key2) {
94
+ const result1 = this.#map1.get(key1)?.has(key2);
95
+ if (this.testMode) {
96
+ const result2 = this.#map2.get(key2)?.has(key1);
97
+ if (!!result1 !== !!result2) {
98
+ throw new Error('Inconsistent state in TwoKeyCaselessMap');
99
+ }
100
+ }
101
+ return result1 ?? false;
102
+ }
103
+ set(key1, key2, value) {
104
+ const alreadyHadEntry = this.has(key1, key2);
105
+ let innerMap1 = this.#map1.get(key1);
106
+ if (!innerMap1) {
107
+ innerMap1 = new CaselessMap_1.CaselessMap();
108
+ this.#map1.set(key1, innerMap1);
109
+ }
110
+ innerMap1.set(key2, value);
111
+ let innerMap2 = this.#map2.get(key2);
112
+ if (!innerMap2) {
113
+ innerMap2 = new CaselessMap_1.CaselessMap();
114
+ this.#map2.set(key2, innerMap2);
115
+ }
116
+ innerMap2.set(key1, value);
117
+ if (!alreadyHadEntry) {
118
+ this.#size++;
119
+ }
120
+ return this;
121
+ }
122
+ get size() {
123
+ return this.#size;
124
+ }
125
+ /**
126
+ * @important Keys returned **match original case**!
127
+ */
128
+ entriesByFirstKey(key1) {
129
+ return this.#map1.get(key1)?.entries() ?? emptyIterator();
130
+ }
131
+ /**
132
+ * @important Keys returned **match original case**!
133
+ */
134
+ entriesBySecondKey(key2) {
135
+ return this.#map2.get(key2)?.entries() ?? emptyIterator();
136
+ }
137
+ /**
138
+ * @important Keys returned **match original case**!
139
+ */
140
+ *entries() {
141
+ for (const [key1, innerMap1] of this.#map1) {
142
+ for (const [key2, value] of innerMap1) {
143
+ yield [key1, key2, value];
144
+ }
145
+ }
146
+ }
147
+ /**
148
+ * @important Keys returned **match original case**!
149
+ */
150
+ keysByFirstKey(key1) {
151
+ return this.#map1.get(key1)?.keys() ?? emptyIterator();
152
+ }
153
+ /**
154
+ * @important Keys returned **match original case**!
155
+ */
156
+ keysBySecondKey(key2) {
157
+ return this.#map2.get(key2)?.keys() ?? emptyIterator();
158
+ }
159
+ /**
160
+ * @important Keys returned **match original case**!
161
+ */
162
+ *keys() {
163
+ for (const [key1, innerMap1] of this.#map1) {
164
+ for (const key2 of innerMap1.keys()) {
165
+ yield [key1, key2];
166
+ }
167
+ }
168
+ }
169
+ valuesByFirstKey(key1) {
170
+ return this.#map1.get(key1)?.values() ?? emptyIterator();
171
+ }
172
+ valuesBySecondKey(key2) {
173
+ return this.#map2.get(key2)?.values() ?? emptyIterator();
174
+ }
175
+ *values() {
176
+ for (const innerMap1 of this.#map1.values()) {
177
+ for (const value of innerMap1.values()) {
178
+ yield value;
179
+ }
180
+ }
181
+ }
182
+ /**
183
+ * @important Keys returned **match original case**!
184
+ */
185
+ [Symbol.iterator]() {
186
+ return this.entries();
187
+ }
188
+ [Symbol.toStringTag] = 'TwoKeyCaselessMap';
189
+ }
190
+ exports.TwoKeyCaselessMap = TwoKeyCaselessMap;
191
+ function emptyIterator() {
192
+ return [][Symbol.iterator]();
193
+ }
194
+ //# sourceMappingURL=TwoKeyCaselessMap.js.map
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) Microsoft Corporation. All rights reserved.
4
+ * Licensed under the MIT License. See License.txt in the project root for license information.
5
+ *--------------------------------------------------------------------------------------------*/
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.screen = screen;
8
+ // These regexes are not perfect, but should work for common cases
9
+ // If they don't work, we'll just return the account ID, which does not need to be screened
10
+ const accountLabelRegex = /^(?<email>[^@]+)@(?<domain>[\w]+(\.[\w]+)+)$/i;
11
+ const domainRegex = /^(?<domain>[^.]+)(?<safeTld>\.com(\..*)?|\.net|\.org|\.co\..*|\.gov)$/i;
12
+ /**
13
+ * Screens the label or display name of an Azure account or tenant so that it can be logged without exposing PII.
14
+ * This should *NOT* be considered fool-proof nor safe for telemetry, but is acceptable for local logging.
15
+ * @param accountOrTenant The account or tenant to screen the label / display name of
16
+ * @returns The screened label / display name
17
+ */
18
+ function screen(accountOrTenant) {
19
+ if ('label' in accountOrTenant && !!accountOrTenant.label) {
20
+ const match = accountLabelRegex.exec(accountOrTenant.label);
21
+ if (match?.groups?.email && match.groups.domain) {
22
+ let screenedEmail = match.groups.email;
23
+ if (screenedEmail.length <= 2) {
24
+ screenedEmail = '***';
25
+ }
26
+ else {
27
+ screenedEmail = `${screenedEmail.at(0)}***${screenedEmail.at(-1)}`;
28
+ }
29
+ let screenedDomain = match.groups.domain;
30
+ const domainMatch = domainRegex.exec(match.groups.domain);
31
+ if (domainMatch?.groups?.domain && domainMatch.groups.safeTld) {
32
+ if (domainMatch.groups.domain.length <= 2) {
33
+ screenedDomain = `***${domainMatch.groups.safeTld}`;
34
+ }
35
+ else {
36
+ screenedDomain = `${domainMatch.groups.domain.at(0)}***${domainMatch.groups.safeTld}`;
37
+ }
38
+ }
39
+ else if (screenedDomain.length <= 2) {
40
+ screenedDomain = '***';
41
+ }
42
+ else {
43
+ screenedDomain = `${screenedDomain.at(0)}***${screenedDomain.at(-1)}`;
44
+ }
45
+ return `${screenedEmail}@${screenedDomain}`;
46
+ }
47
+ // If we can't match it with our simple regex, just return the account ID instead
48
+ return accountOrTenant.id;
49
+ }
50
+ else if ('displayName' in accountOrTenant && !!accountOrTenant.displayName) {
51
+ if (accountOrTenant.displayName.length <= 2) {
52
+ // For too-short names, just return the ID
53
+ return accountOrTenant.tenantId;
54
+ }
55
+ else {
56
+ // Return the first character, three stars, and the last character
57
+ return `${accountOrTenant.displayName.at(0)}***${accountOrTenant.displayName.at(-1)}`;
58
+ }
59
+ }
60
+ return 'unknown';
61
+ }
62
+ //# sourceMappingURL=screen.js.map
@@ -39,27 +39,30 @@ var __importStar = (this && this.__importStar) || (function () {
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
40
  exports.signInToTenant = signInToTenant;
41
41
  const vscode = __importStar(require("vscode"));
42
- const getUnauthenticatedTenants_1 = require("./utils/getUnauthenticatedTenants");
43
42
  /**
44
43
  * Prompts user to select from a list of unauthenticated tenants.
45
- * Once selected, requests a new session from VS Code specifially for this tenant.
44
+ * Once selected, requests a new session from VS Code specifically for this tenant.
46
45
  */
47
- async function signInToTenant(subscriptionProvider) {
48
- const tenantId = await pickTenant(subscriptionProvider);
49
- if (tenantId) {
50
- await subscriptionProvider.signIn(tenantId);
46
+ async function signInToTenant(subscriptionProvider, account) {
47
+ const tenant = await pickTenant(subscriptionProvider, account);
48
+ if (tenant) {
49
+ await subscriptionProvider.signIn(tenant);
51
50
  }
52
51
  }
53
- async function pickTenant(subscriptionProvider) {
54
- const pick = await vscode.window.showQuickPick(getPicks(subscriptionProvider), {
55
- placeHolder: 'Select a Tenant (Directory) to Sign In To', // TODO: localize
52
+ async function pickTenant(subscriptionProvider, account) {
53
+ const pick = await vscode.window.showQuickPick(getPicks(subscriptionProvider, account), {
54
+ placeHolder: vscode.l10n.t('Select a Tenant (Directory) to Sign In To'),
56
55
  matchOnDescription: true, // allow searching by tenantId
57
56
  ignoreFocusOut: true,
58
57
  });
59
- return pick?.tenant.tenantId;
58
+ return pick?.tenant;
60
59
  }
61
- async function getPicks(subscriptionProvider) {
62
- const unauthenticatedTenants = await (0, getUnauthenticatedTenants_1.getUnauthenticatedTenants)(subscriptionProvider);
60
+ async function getPicks(subscriptionProvider, account) {
61
+ const unauthenticatedTenants = [];
62
+ const accounts = account ? [account] : await subscriptionProvider.getAccounts();
63
+ for (const account of accounts) {
64
+ unauthenticatedTenants.push(...await subscriptionProvider.getUnauthenticatedTenantsForAccount(account));
65
+ }
63
66
  const duplicateTenants = new Set(unauthenticatedTenants
64
67
  .filter((tenant, index, self) => index !== self.findIndex(t => t.tenantId === tenant.tenantId))
65
68
  .map(tenant => tenant.tenantId));
@@ -69,7 +72,6 @@ async function getPicks(subscriptionProvider) {
69
72
  .sort((a, b) => (a.displayName).localeCompare(b.displayName))
70
73
  .map(tenant => ({
71
74
  label: tenant.displayName ?? '',
72
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
73
75
  description: `${tenant.tenantId}${isDuplicate(tenant.tenantId) ? ` (${tenant.account.label})` : ''}`,
74
76
  detail: tenant.defaultDomain ?? '',
75
77
  tenant,
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) Microsoft Corporation. All rights reserved.
4
+ * Licensed under the MIT License. See License.txt in the project root for license information.
5
+ *--------------------------------------------------------------------------------------------*/
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.tryGetTokenExpiration = tryGetTokenExpiration;
8
+ function tryGetTokenExpiration(session) {
9
+ try {
10
+ if (!!session?.idToken) {
11
+ const idTokenParts = session.idToken.split('.');
12
+ if (idTokenParts.length === 3) {
13
+ const payload = JSON.parse(Buffer.from(idTokenParts[1], 'base64').toString());
14
+ if (payload.exp !== undefined && Number.isInteger(payload.exp)) {
15
+ return payload.exp * 1000; // Convert to milliseconds
16
+ }
17
+ }
18
+ }
19
+ }
20
+ catch {
21
+ // Best effort only
22
+ }
23
+ return 0;
24
+ }
25
+ //# sourceMappingURL=tryGetTokenExpiration.js.map
@@ -0,0 +1,5 @@
1
+ import type * as vscode from 'vscode';
2
+ /**
3
+ * A shortcut type for {@link vscode.AuthenticationSessionAccountInformation}
4
+ */
5
+ export type AzureAccount = Readonly<vscode.AuthenticationSessionAccountInformation>;
@@ -0,0 +1,6 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License. See License.txt in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ export {};
6
+ //# sourceMappingURL=AzureAccount.js.map
@@ -5,7 +5,7 @@ import type * as vscode from 'vscode';
5
5
  export interface AzureAuthentication {
6
6
  /**
7
7
  * Gets a VS Code authentication session for an Azure subscription.
8
- * Always uses the default scope, `https://management.azure.com/.default/` and respects `microsoft-sovereign-cloud.environment` setting.
8
+ * Always uses the default scope, ~`https://management.core.windows.net/.default` and respects `microsoft-sovereign-cloud.environment` setting.
9
9
  *
10
10
  * @returns A VS Code authentication session or undefined, if none could be obtained.
11
11
  */