mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Refresh token with SqlToolService session update (#21308)
This commit is contained in:
@@ -146,10 +146,6 @@ export class AzureAccountProviderService implements vscode.Disposable {
|
|||||||
const noSystemKeychain = vscode.workspace.getConfiguration(Constants.AzureSection).get<boolean>(Constants.NoSystemKeyChainSection);
|
const noSystemKeychain = vscode.workspace.getConfiguration(Constants.AzureSection).get<boolean>(Constants.NoSystemKeyChainSection);
|
||||||
const platform = os.platform();
|
const platform = os.platform();
|
||||||
const tokenCacheKey = `azureTokenCache-${provider.metadata.id}`;
|
const tokenCacheKey = `azureTokenCache-${provider.metadata.id}`;
|
||||||
const lockOptions = {
|
|
||||||
retryNumber: 100,
|
|
||||||
retryDelay: 50
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!this._credentialProvider) {
|
if (!this._credentialProvider) {
|
||||||
@@ -176,7 +172,12 @@ export class AzureAccountProviderService implements vscode.Disposable {
|
|||||||
throw new Error('Unable to intialize persistence for access token cache. Tokens will not persist in system memory for future use.');
|
throw new Error('Unable to intialize persistence for access token cache. Tokens will not persist in system memory for future use.');
|
||||||
}
|
}
|
||||||
|
|
||||||
let persistenceCachePlugin: PersistenceCachePlugin = new PersistenceCachePlugin(this.persistence, lockOptions); // or any of the other ones.
|
let persistenceCachePlugin: PersistenceCachePlugin = new PersistenceCachePlugin(
|
||||||
|
this.persistence, {
|
||||||
|
retryNumber: 500,
|
||||||
|
retryDelay: 150
|
||||||
|
});
|
||||||
|
|
||||||
const MSAL_CONFIG = {
|
const MSAL_CONFIG = {
|
||||||
auth: {
|
auth: {
|
||||||
clientId: provider.metadata.settings.clientId,
|
clientId: provider.metadata.settings.clientId,
|
||||||
|
|||||||
16
src/sql/azdata.proposed.d.ts
vendored
16
src/sql/azdata.proposed.d.ts
vendored
@@ -446,6 +446,22 @@ declare module 'azdata' {
|
|||||||
showOnConnectionDialog?: boolean;
|
showOnConnectionDialog?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Object Explorer interfaces --------------------------------
|
||||||
|
export interface ObjectExplorerSession {
|
||||||
|
/**
|
||||||
|
* Authentication token for the current session.
|
||||||
|
*/
|
||||||
|
token?: accounts.AccountSecurityToken | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExpandNodeInfo {
|
||||||
|
/**
|
||||||
|
* Authentication token for the current session.
|
||||||
|
*/
|
||||||
|
token?: accounts.AccountSecurityToken | undefined;
|
||||||
|
}
|
||||||
|
// End Object Explorer interfaces ----------------------------
|
||||||
|
|
||||||
export interface TaskInfo {
|
export interface TaskInfo {
|
||||||
targetLocation?: string;
|
targetLocation?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -403,7 +403,6 @@ suite('SQL Connection Tree Action tests', () => {
|
|||||||
resolve(connection);
|
resolve(connection);
|
||||||
}));
|
}));
|
||||||
connectionManagementService.setup(x => x.isConnected(undefined, TypeMoq.It.isAny())).returns(() => isConnectedReturnValue);
|
connectionManagementService.setup(x => x.isConnected(undefined, TypeMoq.It.isAny())).returns(() => isConnectedReturnValue);
|
||||||
connectionManagementService.setup(x => x.refreshAzureAccountTokenIfNecessary(TypeMoq.It.isAny())).returns(async () => true);
|
|
||||||
|
|
||||||
let objectExplorerSession = {
|
let objectExplorerSession = {
|
||||||
success: true,
|
success: true,
|
||||||
@@ -450,7 +449,6 @@ suite('SQL Connection Tree Action tests', () => {
|
|||||||
|
|
||||||
return refreshAction.run().then((value) => {
|
return refreshAction.run().then((value) => {
|
||||||
connectionManagementService.verify(x => x.isConnected(undefined, TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
|
connectionManagementService.verify(x => x.isConnected(undefined, TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
|
||||||
connectionManagementService.verify(x => x.refreshAzureAccountTokenIfNecessary(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
|
|
||||||
objectExplorerService.verify(x => x.getObjectExplorerNode(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
|
objectExplorerService.verify(x => x.getObjectExplorerNode(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
|
||||||
objectExplorerService.verify(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.exactly(0));
|
objectExplorerService.verify(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.exactly(0));
|
||||||
tree.verify(x => x.refresh(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
|
tree.verify(x => x.refresh(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
|
||||||
|
|||||||
@@ -926,7 +926,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
|||||||
/**
|
/**
|
||||||
* Refresh Azure access token if it's expired.
|
* Refresh Azure access token if it's expired.
|
||||||
* @param uriOrConnectionProfile connection uri or connection profile
|
* @param uriOrConnectionProfile connection uri or connection profile
|
||||||
* @returns true if no need to refresh or successfully refreshed token
|
* @returns true if no need to refresh or successfully refreshed token, false if refresh fails or auth mode is not AzureMFA
|
||||||
*/
|
*/
|
||||||
public async refreshAzureAccountTokenIfNecessary(uriOrConnectionProfile: string | ConnectionProfile): Promise<boolean> {
|
public async refreshAzureAccountTokenIfNecessary(uriOrConnectionProfile: string | ConnectionProfile): Promise<boolean> {
|
||||||
if (!uriOrConnectionProfile) {
|
if (!uriOrConnectionProfile) {
|
||||||
@@ -997,9 +997,11 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
|||||||
} else {
|
} else {
|
||||||
this._logService.warn(`Invalid expiry time ${expiry} for connection ${connectionProfile.id} with uri ${uri}`);
|
this._logService.warn(`Invalid expiry time ${expiry} for connection ${connectionProfile.id} with uri ${uri}`);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Request Senders
|
// Request Senders
|
||||||
private async sendConnectRequest(connection: interfaces.IConnectionProfile, uri: string): Promise<boolean> {
|
private async sendConnectRequest(connection: interfaces.IConnectionProfile, uri: string): Promise<boolean> {
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ export class RefreshAction extends Action {
|
|||||||
if (this.element instanceof ConnectionProfile) {
|
if (this.element instanceof ConnectionProfile) {
|
||||||
let connection: ConnectionProfile = this.element;
|
let connection: ConnectionProfile = this.element;
|
||||||
if (this._connectionManagementService.isConnected(undefined, connection)) {
|
if (this._connectionManagementService.isConnected(undefined, connection)) {
|
||||||
await this._connectionManagementService.refreshAzureAccountTokenIfNecessary(connection);
|
|
||||||
treeNode = this._objectExplorerService.getObjectExplorerNode(connection);
|
treeNode = this._objectExplorerService.getObjectExplorerNode(connection);
|
||||||
if (treeNode === undefined) {
|
if (treeNode === undefined) {
|
||||||
await this._objectExplorerService.updateObjectExplorerNodes(connection.toIConnectionProfile());
|
await this._objectExplorerService.updateObjectExplorerNodes(connection.toIConnectionProfile());
|
||||||
@@ -56,8 +55,6 @@ export class RefreshAction extends Action {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (this.element instanceof TreeNode) {
|
} else if (this.element instanceof TreeNode) {
|
||||||
let connection: ConnectionProfile = this.element.getConnectionProfile();
|
|
||||||
this._connectionManagementService.refreshAzureAccountTokenIfNecessary(connection);
|
|
||||||
treeNode = this.element;
|
treeNode = this.element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -440,7 +440,8 @@ export class ObjectExplorerService implements IObjectExplorerService {
|
|||||||
allProviders.forEach(provider => {
|
allProviders.forEach(provider => {
|
||||||
self.callExpandOrRefreshFromProvider(provider, {
|
self.callExpandOrRefreshFromProvider(provider, {
|
||||||
sessionId: session.sessionId!,
|
sessionId: session.sessionId!,
|
||||||
nodePath: node.nodePath
|
nodePath: node.nodePath,
|
||||||
|
token: session.token
|
||||||
}, refresh).then(isExpanding => {
|
}, refresh).then(isExpanding => {
|
||||||
if (!isExpanding) {
|
if (!isExpanding) {
|
||||||
// The provider stated it's not going to expand the node, therefore do not need to track when merging results
|
// The provider stated it's not going to expand the node, therefore do not need to track when merging results
|
||||||
@@ -595,7 +596,18 @@ export class ObjectExplorerService implements IObjectExplorerService {
|
|||||||
session: azdata.ObjectExplorerSession,
|
session: azdata.ObjectExplorerSession,
|
||||||
parentTree: TreeNode,
|
parentTree: TreeNode,
|
||||||
refresh: boolean = false): Promise<TreeNode[]> {
|
refresh: boolean = false): Promise<TreeNode[]> {
|
||||||
const providerName = parentTree.getConnectionProfile()?.providerName;
|
let connection = parentTree.getConnectionProfile();
|
||||||
|
if (connection) {
|
||||||
|
// Refresh access token on connection if needed.
|
||||||
|
let refreshResult = await this._connectionManagementService.refreshAzureAccountTokenIfNecessary(connection);
|
||||||
|
if (refreshResult) {
|
||||||
|
session.token = {
|
||||||
|
token: connection.options['azureAccountToken'],
|
||||||
|
expiresOn: connection.options['expiresOn']
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const providerName = connection?.providerName;
|
||||||
if (!providerName) {
|
if (!providerName) {
|
||||||
throw new Error('Failed to expand node - no provider name');
|
throw new Error('Failed to expand node - no provider name');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -262,6 +262,7 @@ suite('SQL Object Explorer Service tests', () => {
|
|||||||
connectionManagementService = TypeMoq.Mock.ofType(TestConnectionManagementService, TypeMoq.MockBehavior.Strict);
|
connectionManagementService = TypeMoq.Mock.ofType(TestConnectionManagementService, TypeMoq.MockBehavior.Strict);
|
||||||
connectionManagementService.setup(x => x.getConnectionGroups()).returns(() => [conProfGroup]);
|
connectionManagementService.setup(x => x.getConnectionGroups()).returns(() => [conProfGroup]);
|
||||||
connectionManagementService.setup(x => x.getActiveConnections()).returns(() => [connection]);
|
connectionManagementService.setup(x => x.getActiveConnections()).returns(() => [connection]);
|
||||||
|
connectionManagementService.setup(x => x.refreshAzureAccountTokenIfNecessary(TypeMoq.It.isAny())).returns(async () => true);
|
||||||
connectionManagementService.setup(x => x.addSavedPassword(TypeMoq.It.isAny())).returns(() => new Promise<ConnectionProfile>((resolve) => {
|
connectionManagementService.setup(x => x.addSavedPassword(TypeMoq.It.isAny())).returns(() => new Promise<ConnectionProfile>((resolve) => {
|
||||||
resolve(connection);
|
resolve(connection);
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user