mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Fix tenant Id design for Azure accounts (#21086)
This commit is contained in:
@@ -133,12 +133,6 @@ export abstract class AzureAuth implements vscode.Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getHomeTenant(account: AzureAccount): Tenant {
|
|
||||||
// Home is defined by the API
|
|
||||||
// Lets pick the home tenant - and fall back to commonTenant if they don't exist
|
|
||||||
return account.properties.tenants.find(t => t.tenantCategory === 'Home') ?? account.properties.tenants[0] ?? this.commonTenant;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async refreshAccess(account: AzureAccount): Promise<AzureAccount> {
|
public async refreshAccess(account: AzureAccount): Promise<AzureAccount> {
|
||||||
// Deprecated account - delete it.
|
// Deprecated account - delete it.
|
||||||
if (account.key.accountVersion !== AzureAuth.ACCOUNT_VERSION) {
|
if (account.key.accountVersion !== AzureAuth.ACCOUNT_VERSION) {
|
||||||
@@ -146,7 +140,10 @@ export abstract class AzureAuth implements vscode.Disposable {
|
|||||||
return account;
|
return account;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const tenant = this.getHomeTenant(account);
|
// There can be multiple home tenants
|
||||||
|
// We want to return the one that owns the Azure account.
|
||||||
|
// Not doing so can result in token being issued for the wrong tenant
|
||||||
|
const tenant = account.properties.owningTenant;
|
||||||
const tokenResult = await this.getAccountSecurityToken(account, tenant.id, azdata.AzureResource.MicrosoftResourceManagement);
|
const tokenResult = await this.getAccountSecurityToken(account, tenant.id, azdata.AzureResource.MicrosoftResourceManagement);
|
||||||
if (!tokenResult) {
|
if (!tokenResult) {
|
||||||
account.isStale = true;
|
account.isStale = true;
|
||||||
@@ -186,7 +183,14 @@ export abstract class AzureAuth implements vscode.Disposable {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tenant = account.properties.tenants.find(t => t.id === tenantId);
|
if (!account.properties.owningTenant) {
|
||||||
|
// Should never happen
|
||||||
|
throw new AzureAuthError(localize('azure.owningTenantNotFound', "Owning Tenant information not found for account."), 'Owning tenant not found.', undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tenant = account.properties.owningTenant?.id === tenantId
|
||||||
|
? account.properties.owningTenant
|
||||||
|
: account.properties.tenants.find(t => t.id === tenantId);
|
||||||
|
|
||||||
if (!tenant) {
|
if (!tenant) {
|
||||||
throw new AzureAuthError(localize('azure.tenantNotFound', "Specified tenant with ID '{0}' not found.", tenantId), `Tenant ${tenantId} not found.`, undefined);
|
throw new AzureAuthError(localize('azure.tenantNotFound', "Specified tenant with ID '{0}' not found.", tenantId), `Tenant ${tenantId} not found.`, undefined);
|
||||||
@@ -557,6 +561,10 @@ export abstract class AzureAuth implements vscode.Disposable {
|
|||||||
const name = tokenClaims.name ?? tokenClaims.email ?? tokenClaims.unique_name;
|
const name = tokenClaims.name ?? tokenClaims.email ?? tokenClaims.unique_name;
|
||||||
const email = tokenClaims.email ?? tokenClaims.unique_name;
|
const email = tokenClaims.email ?? tokenClaims.unique_name;
|
||||||
|
|
||||||
|
// Read more about tid > https://learn.microsoft.com/azure/active-directory/develop/id-tokens
|
||||||
|
const owningTenant = tenants.find(t => t.id === tokenClaims.tid)
|
||||||
|
?? { 'id': tokenClaims.tid, 'displayName': 'Microsoft Account' };
|
||||||
|
|
||||||
let displayName = name;
|
let displayName = name;
|
||||||
if (email) {
|
if (email) {
|
||||||
displayName = `${displayName} - ${email}`;
|
displayName = `${displayName} - ${email}`;
|
||||||
@@ -596,6 +604,7 @@ export abstract class AzureAuth implements vscode.Disposable {
|
|||||||
properties: {
|
properties: {
|
||||||
providerSettings: this.metadata,
|
providerSettings: this.metadata,
|
||||||
isMsAccount: accountIssuer === 'msft',
|
isMsAccount: accountIssuer === 'msft',
|
||||||
|
owningTenant: owningTenant,
|
||||||
tenants,
|
tenants,
|
||||||
azureAuthType: this.authType
|
azureAuthType: this.authType
|
||||||
},
|
},
|
||||||
|
|||||||
6
extensions/azurecore/src/azurecore.d.ts
vendored
6
extensions/azurecore/src/azurecore.d.ts
vendored
@@ -38,11 +38,17 @@ declare module 'azurecore' {
|
|||||||
azureAuthType?: AzureAuthType
|
azureAuthType?: AzureAuthType
|
||||||
|
|
||||||
providerSettings: AzureAccountProviderMetadata;
|
providerSettings: AzureAccountProviderMetadata;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not the account is a Microsoft account
|
* Whether or not the account is a Microsoft account
|
||||||
*/
|
*/
|
||||||
isMsAccount: boolean;
|
isMsAccount: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the tenant that the user would be signing in to. For work and school accounts, the GUID is the immutable tenant ID of the organization that the user is signing in to.
|
||||||
|
* For sign-ins to the personal Microsoft account tenant (services like Xbox, Teams for Life, or Outlook), the value is 9188040d-6c67-4c5b-b112-36a304b66dad.
|
||||||
|
*/
|
||||||
|
owningTenant: Tenant;
|
||||||
/**
|
/**
|
||||||
* A list of tenants (aka directories) that the account belongs to
|
* A list of tenants (aka directories) that the account belongs to
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ describe('Azure Authentication', function () {
|
|||||||
mockAccount = {
|
mockAccount = {
|
||||||
isStale: false,
|
isStale: false,
|
||||||
properties: {
|
properties: {
|
||||||
|
owningTenant: mockTenant,
|
||||||
tenants: [mockTenant]
|
tenants: [mockTenant]
|
||||||
}
|
}
|
||||||
} as AzureAccount;
|
} as AzureAccount;
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ const mockAccount: AzureAccount = {
|
|||||||
properties: {
|
properties: {
|
||||||
providerSettings: settings[0].metadata,
|
providerSettings: settings[0].metadata,
|
||||||
isMsAccount: true,
|
isMsAccount: true,
|
||||||
|
owningTenant: {
|
||||||
|
id: 'tenantId',
|
||||||
|
displayName: 'tenantDisplayName',
|
||||||
|
},
|
||||||
tenants: []
|
tenants: []
|
||||||
},
|
},
|
||||||
isStale: false
|
isStale: false
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ const mockAccount: AzureAccount = {
|
|||||||
properties: {
|
properties: {
|
||||||
providerSettings: settings[0].metadata,
|
providerSettings: settings[0].metadata,
|
||||||
isMsAccount: true,
|
isMsAccount: true,
|
||||||
|
owningTenant: {
|
||||||
|
id: 'tenantId',
|
||||||
|
displayName: 'tenantDisplayName',
|
||||||
|
},
|
||||||
tenants: []
|
tenants: []
|
||||||
},
|
},
|
||||||
isStale: false
|
isStale: false
|
||||||
|
|||||||
@@ -28,13 +28,17 @@ const mockAccount: AzureAccount = {
|
|||||||
properties: {
|
properties: {
|
||||||
providerSettings: settings[0].metadata,
|
providerSettings: settings[0].metadata,
|
||||||
isMsAccount: true,
|
isMsAccount: true,
|
||||||
|
owningTenant: {
|
||||||
|
id: 'tenantId',
|
||||||
|
displayName: 'tenantDisplayName',
|
||||||
|
},
|
||||||
tenants: []
|
tenants: []
|
||||||
},
|
},
|
||||||
isStale: false
|
isStale: false
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockTenantId: string = 'mock_tenant';
|
const mockTenantId: string = 'mock_tenant';
|
||||||
const mockSubscriptionId ='mock_subscription';
|
const mockSubscriptionId = 'mock_subscription';
|
||||||
|
|
||||||
const mockSubscription: azureResource.AzureResourceSubscription = {
|
const mockSubscription: azureResource.AzureResourceSubscription = {
|
||||||
id: mockSubscriptionId,
|
id: mockSubscriptionId,
|
||||||
@@ -50,7 +54,7 @@ let mockResourceProvider2: TypeMoq.IMock<azureResource.IAzureResourceProvider>;
|
|||||||
|
|
||||||
let resourceService: AzureResourceService;
|
let resourceService: AzureResourceService;
|
||||||
|
|
||||||
describe('AzureResourceService.listResourceProviderIds', function(): void {
|
describe('AzureResourceService.listResourceProviderIds', function (): void {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
|
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
|
||||||
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object]));
|
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object]));
|
||||||
@@ -71,7 +75,7 @@ describe('AzureResourceService.listResourceProviderIds', function(): void {
|
|||||||
resourceService.areResourceProvidersLoaded = true;
|
resourceService.areResourceProvidersLoaded = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should be correct when registering providers.', async function(): Promise<void> {
|
it('Should be correct when registering providers.', async function (): Promise<void> {
|
||||||
resourceService.registerResourceProvider(mockResourceProvider1.object);
|
resourceService.registerResourceProvider(mockResourceProvider1.object);
|
||||||
let providerIds = await resourceService.listResourceProviderIds();
|
let providerIds = await resourceService.listResourceProviderIds();
|
||||||
should(providerIds).Array();
|
should(providerIds).Array();
|
||||||
@@ -87,7 +91,7 @@ describe('AzureResourceService.listResourceProviderIds', function(): void {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('AzureResourceService.getRootChildren', function(): void {
|
describe('AzureResourceService.getRootChildren', function (): void {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
|
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
|
||||||
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object]));
|
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object]));
|
||||||
@@ -101,13 +105,13 @@ describe('AzureResourceService.getRootChildren', function(): void {
|
|||||||
resourceService.areResourceProvidersLoaded = true;
|
resourceService.areResourceProvidersLoaded = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should be correct when provider id is correct.', async function(): Promise<void> {
|
it('Should be correct when provider id is correct.', async function (): Promise<void> {
|
||||||
const children = await resourceService.getRootChildren(mockResourceProvider1.object.providerId, mockAccount, mockSubscription, mockTenantId);
|
const children = await resourceService.getRootChildren(mockResourceProvider1.object.providerId, mockAccount, mockSubscription, mockTenantId);
|
||||||
|
|
||||||
should(children).Array();
|
should(children).Array();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should throw exceptions when provider id is incorrect.', async function(): Promise<void> {
|
it('Should throw exceptions when provider id is incorrect.', async function (): Promise<void> {
|
||||||
const providerId = 'non_existent_provider_id';
|
const providerId = 'non_existent_provider_id';
|
||||||
try {
|
try {
|
||||||
await resourceService.getRootChildren(providerId, mockAccount, mockSubscription, mockTenantId);
|
await resourceService.getRootChildren(providerId, mockAccount, mockSubscription, mockTenantId);
|
||||||
@@ -120,7 +124,7 @@ describe('AzureResourceService.getRootChildren', function(): void {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('AzureResourceService.getChildren', function(): void {
|
describe('AzureResourceService.getChildren', function (): void {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
|
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
|
||||||
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object]));
|
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object]));
|
||||||
@@ -135,12 +139,12 @@ describe('AzureResourceService.getChildren', function(): void {
|
|||||||
resourceService.areResourceProvidersLoaded = true;
|
resourceService.areResourceProvidersLoaded = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should be correct when provider id is correct.', async function(): Promise<void> {
|
it('Should be correct when provider id is correct.', async function (): Promise<void> {
|
||||||
const children = await resourceService.getChildren(mockResourceProvider1.object.providerId, TypeMoq.It.isAny());
|
const children = await resourceService.getChildren(mockResourceProvider1.object.providerId, TypeMoq.It.isAny());
|
||||||
should(children).Array();
|
should(children).Array();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should throw exceptions when provider id is incorrect.', async function(): Promise<void> {
|
it('Should throw exceptions when provider id is incorrect.', async function (): Promise<void> {
|
||||||
const providerId = 'non_existent_provider_id';
|
const providerId = 'non_existent_provider_id';
|
||||||
try {
|
try {
|
||||||
await resourceService.getRootChildren(providerId, mockAccount, mockSubscription, mockTenantId);
|
await resourceService.getRootChildren(providerId, mockAccount, mockSubscription, mockTenantId);
|
||||||
@@ -153,7 +157,7 @@ describe('AzureResourceService.getChildren', function(): void {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('AzureResourceService.getTreeItem', function(): void {
|
describe('AzureResourceService.getTreeItem', function (): void {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
|
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
|
||||||
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object]));
|
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object]));
|
||||||
@@ -168,12 +172,12 @@ describe('AzureResourceService.getTreeItem', function(): void {
|
|||||||
resourceService.areResourceProvidersLoaded = true;
|
resourceService.areResourceProvidersLoaded = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should be correct when provider id is correct.', async function(): Promise<void> {
|
it('Should be correct when provider id is correct.', async function (): Promise<void> {
|
||||||
const treeItem = await resourceService.getTreeItem(mockResourceProvider1.object.providerId, TypeMoq.It.isAny());
|
const treeItem = await resourceService.getTreeItem(mockResourceProvider1.object.providerId, TypeMoq.It.isAny());
|
||||||
should(treeItem).Object();
|
should(treeItem).Object();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should throw exceptions when provider id is incorrect.', async function(): Promise<void> {
|
it('Should throw exceptions when provider id is incorrect.', async function (): Promise<void> {
|
||||||
const providerId = 'non_existent_provider_id';
|
const providerId = 'non_existent_provider_id';
|
||||||
try {
|
try {
|
||||||
await resourceService.getRootChildren(providerId, mockAccount, mockSubscription, mockTenantId);
|
await resourceService.getRootChildren(providerId, mockAccount, mockSubscription, mockTenantId);
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ const mockAccount: AzureAccount = {
|
|||||||
properties: {
|
properties: {
|
||||||
providerSettings: settings[0].metadata,
|
providerSettings: settings[0].metadata,
|
||||||
isMsAccount: true,
|
isMsAccount: true,
|
||||||
|
owningTenant: {
|
||||||
|
id: 'tenantId',
|
||||||
|
displayName: 'tenantDisplayName',
|
||||||
|
},
|
||||||
tenants: []
|
tenants: []
|
||||||
},
|
},
|
||||||
isStale: false
|
isStale: false
|
||||||
|
|||||||
@@ -35,7 +35,10 @@ let mockTreeChangeHandler: TypeMoq.IMock<IAzureResourceTreeChangeHandler>;
|
|||||||
|
|
||||||
// Mock test data
|
// Mock test data
|
||||||
const mockTenantId = 'mock_tenant_id';
|
const mockTenantId = 'mock_tenant_id';
|
||||||
|
const mockTenant = {
|
||||||
|
id: mockTenantId,
|
||||||
|
displayName: 'Mock Tenant'
|
||||||
|
};
|
||||||
const mockAccount: AzureAccount = {
|
const mockAccount: AzureAccount = {
|
||||||
key: {
|
key: {
|
||||||
accountId: '97915f6d-84fa-4926-b60c-38db64327ad7',
|
accountId: '97915f6d-84fa-4926-b60c-38db64327ad7',
|
||||||
@@ -50,11 +53,9 @@ const mockAccount: AzureAccount = {
|
|||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
tenants: [
|
tenants: [
|
||||||
{
|
mockTenant
|
||||||
id: mockTenantId,
|
|
||||||
displayName: 'Mock Tenant'
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
|
owningTenant: mockTenant,
|
||||||
providerSettings: {
|
providerSettings: {
|
||||||
settings: allSettings[0].metadata.settings,
|
settings: allSettings[0].metadata.settings,
|
||||||
id: 'azure',
|
id: 'azure',
|
||||||
@@ -360,8 +361,7 @@ describe('AzureResourceAccountTreeNode.clearCache', function (): void {
|
|||||||
sinon.stub(azdata.accounts, 'getAccountSecurityToken').returns(Promise.resolve(mockToken));
|
sinon.stub(azdata.accounts, 'getAccountSecurityToken').returns(Promise.resolve(mockToken));
|
||||||
mockCacheService.setup((o) => o.generateKey(TypeMoq.It.isAnyString())).returns(() => generateGuid());
|
mockCacheService.setup((o) => o.generateKey(TypeMoq.It.isAnyString())).returns(() => generateGuid());
|
||||||
mockCacheService.setup((o) => o.get(TypeMoq.It.isAnyString())).returns(() => mockSubscriptionCache);
|
mockCacheService.setup((o) => o.get(TypeMoq.It.isAnyString())).returns(() => mockSubscriptionCache);
|
||||||
mockCacheService.setup((o) => o.update(TypeMoq.It.isAnyString(), TypeMoq.It.isAny())).returns(() =>
|
mockCacheService.setup((o) => o.update(TypeMoq.It.isAnyString(), TypeMoq.It.isAny())).returns(() => {
|
||||||
{
|
|
||||||
mockSubscriptionCache = mockSubscriptions;
|
mockSubscriptionCache = mockSubscriptions;
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./out",
|
"outDir": "./out",
|
||||||
"noUnusedParameters": false,
|
"noUnusedParameters": false,
|
||||||
|
"sourceMap": true,
|
||||||
"typeRoots": [
|
"typeRoots": [
|
||||||
"./node_modules/@types"
|
"./node_modules/@types"
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user