mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 18:47:57 -05:00
Support updating access token when found expired for OE (#1772)
This commit is contained in:
@@ -18,6 +18,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ConnectionInfo
|
public class ConnectionInfo
|
||||||
{
|
{
|
||||||
|
private static readonly DateTime UnixEpochUtc = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -27,7 +29,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
|||||||
OwnerUri = ownerUri;
|
OwnerUri = ownerUri;
|
||||||
ConnectionDetails = details;
|
ConnectionDetails = details;
|
||||||
ConnectionId = Guid.NewGuid();
|
ConnectionId = Guid.NewGuid();
|
||||||
IntellisenseMetrics = new InteractionMetrics<double>(new int[] {50, 100, 200, 500, 1000, 2000});
|
IntellisenseMetrics = new InteractionMetrics<double>(new int[] { 50, 100, 200, 500, 1000, 2000 });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -73,10 +75,16 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsSqlDb { get; set; }
|
public bool IsSqlDb { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
/// Returns true if the sql connection is to a DW instance
|
/// Returns true if the sql connection is to a DW instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsSqlDW { get; set; }
|
public bool IsSqlDW { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if Authentication mode is AzureMFA, determines if Access token is in use.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsAzureAuth { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the connection Engine Edition
|
/// Returns the connection Engine Edition
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -158,8 +166,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
|||||||
public void RemoveConnection(string connectionType)
|
public void RemoveConnection(string connectionType)
|
||||||
{
|
{
|
||||||
Validate.IsNotNullOrEmptyString("Connection Type", connectionType);
|
Validate.IsNotNullOrEmptyString("Connection Type", connectionType);
|
||||||
DbConnection connection;
|
ConnectionTypeToConnectionMap.TryRemove(connectionType, out _);
|
||||||
ConnectionTypeToConnectionMap.TryRemove(connectionType, out connection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -169,18 +176,44 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
|||||||
{
|
{
|
||||||
foreach (var type in AllConnectionTypes)
|
foreach (var type in AllConnectionTypes)
|
||||||
{
|
{
|
||||||
DbConnection connection;
|
ConnectionTypeToConnectionMap.TryRemove(type, out _);
|
||||||
ConnectionTypeToConnectionMap.TryRemove(type, out connection);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the Auth Token and Expires On fields
|
/// Updates the Auth Token and Expires On fields
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void UpdateAuthToken(string token, int expiresOn)
|
public bool TryUpdateAccessToken(SecurityToken? securityToken)
|
||||||
{
|
{
|
||||||
ConnectionDetails.AzureAccountToken = token;
|
if (securityToken != null && !string.IsNullOrEmpty(securityToken.Token) && IsAzureAuth && IsAccessTokenExpired)
|
||||||
ConnectionDetails.ExpiresOn = expiresOn;
|
{
|
||||||
|
ConnectionDetails.AzureAccountToken = securityToken.Token;
|
||||||
|
ConnectionDetails.ExpiresOn = securityToken.ExpiresOn;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if Access token saved in connection details is expired or about to expire in 2 minutes.
|
||||||
|
/// </summary>
|
||||||
|
private bool IsAccessTokenExpired
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (IsAzureAuth && ConnectionDetails.ExpiresOn != null && double.TryParse(ConnectionDetails.ExpiresOn.ToString(), out var expiresOn))
|
||||||
|
{
|
||||||
|
DateTime dateTime = UnixEpochUtc.AddSeconds(expiresOn);
|
||||||
|
|
||||||
|
// Check if access token is already expired or shall expire in 2 minutes.
|
||||||
|
if (dateTime <= DateTime.UtcNow.AddMinutes(2))
|
||||||
|
{
|
||||||
|
Logger.Verbose("Access token found expired.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.TokenUpdateUris.Remove(tokenRefreshedParams.Uri, out var result);
|
this.TokenUpdateUris.Remove(tokenRefreshedParams.Uri, out var result);
|
||||||
connection.UpdateAuthToken(tokenRefreshedParams.Token, tokenRefreshedParams.ExpiresOn);
|
connection.TryUpdateAccessToken(new SecurityToken() { Token = tokenRefreshedParams.Token, ExpiresOn = tokenRefreshedParams.ExpiresOn });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -585,6 +585,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
|||||||
connectionInfo.MajorVersion = serverInfo.ServerMajorVersion;
|
connectionInfo.MajorVersion = serverInfo.ServerMajorVersion;
|
||||||
connectionInfo.IsSqlDb = serverInfo.EngineEditionId == (int)DatabaseEngineEdition.SqlDatabase;
|
connectionInfo.IsSqlDb = serverInfo.EngineEditionId == (int)DatabaseEngineEdition.SqlDatabase;
|
||||||
connectionInfo.IsSqlDW = (serverInfo.EngineEditionId == (int)DatabaseEngineEdition.SqlDataWarehouse);
|
connectionInfo.IsSqlDW = (serverInfo.EngineEditionId == (int)DatabaseEngineEdition.SqlDataWarehouse);
|
||||||
|
// Determines that access token is used for creating connection.
|
||||||
|
connectionInfo.IsAzureAuth = connectionInfo.ConnectionDetails.AuthenticationType == "AzureMFA";
|
||||||
connectionInfo.EngineEdition = (DatabaseEngineEdition)serverInfo.EngineEditionId;
|
connectionInfo.EngineEdition = (DatabaseEngineEdition)serverInfo.EngineEditionId;
|
||||||
// Azure Data Studio supports SQL Server 2014 and later releases.
|
// Azure Data Studio supports SQL Server 2014 and later releases.
|
||||||
response.IsSupportedVersion = serverInfo.IsCloud || serverInfo.ServerMajorVersion >= 12;
|
response.IsSupportedVersion = serverInfo.IsCloud || serverInfo.ServerMajorVersion >= 12;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
|||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
|
namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
|
||||||
{
|
{
|
||||||
class RefreshTokenParams
|
public class RefreshTokenParams
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ID of the tenant
|
/// ID of the tenant
|
||||||
@@ -39,14 +39,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Refresh token request mapping entry
|
/// Refresh token request mapping entry
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class RefreshTokenNotification
|
public class RefreshTokenNotification
|
||||||
{
|
{
|
||||||
public static readonly
|
public static readonly
|
||||||
EventType<RefreshTokenParams> Type =
|
EventType<RefreshTokenParams> Type =
|
||||||
EventType<RefreshTokenParams>.Create("account/refreshToken");
|
EventType<RefreshTokenParams>.Create("account/refreshToken");
|
||||||
}
|
}
|
||||||
|
|
||||||
class TokenRefreshedParams
|
public class TokenRefreshedParams
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the refresh token.
|
/// Gets or sets the refresh token.
|
||||||
@@ -64,7 +64,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
|
|||||||
public string Uri { get; set; }
|
public string Uri { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
class TokenRefreshedNotification
|
public class TokenRefreshedNotification
|
||||||
{
|
{
|
||||||
public static readonly
|
public static readonly
|
||||||
EventType<TokenRefreshedParams> Type =
|
EventType<TokenRefreshedParams> Type =
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
|
||||||
|
{
|
||||||
|
public class SecurityToken
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the refresh token.
|
||||||
|
/// </summary>
|
||||||
|
public string Token { get; set; }
|
||||||
|
|
||||||
|
/// <summmary>
|
||||||
|
/// Gets or sets the token expiration, a Unix epoch
|
||||||
|
/// </summary>
|
||||||
|
public int? ExpiresOn { get; set; }
|
||||||
|
|
||||||
|
/// <summmary>
|
||||||
|
/// Gets or sets the token type, e.g. 'Bearer'
|
||||||
|
/// </summary>
|
||||||
|
public string? TokenType { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts
|
namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts
|
||||||
{
|
{
|
||||||
@@ -49,6 +50,11 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts
|
|||||||
/// Path identifying the node to expand. See <see cref="NodeInfo.NodePath"/> for details
|
/// Path identifying the node to expand. See <see cref="NodeInfo.NodePath"/> for details
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string NodePath { get; set; }
|
public string NodePath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Security token for AzureMFA authentication for refresing access token on connection.
|
||||||
|
/// </summary>
|
||||||
|
public SecurityToken? SecurityToken { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parameters to the <see cref="ExpandRequest"/>.
|
/// Parameters to the <see cref="ExpandRequest"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RefreshParams: ExpandParams
|
public class RefreshParams : ExpandParams
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -240,14 +240,14 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes
|
|||||||
/// Expands this node and returns its children
|
/// Expands this node and returns its children
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Children as an IList. This is the raw children collection, not a copy</returns>
|
/// <returns>Children as an IList. This is the raw children collection, not a copy</returns>
|
||||||
public IList<TreeNode> Expand(string name, CancellationToken cancellationToken)
|
public IList<TreeNode> Expand(string name, CancellationToken cancellationToken, string? accessToken = null)
|
||||||
{
|
{
|
||||||
// TODO consider why solution explorer has separate Children and Items options
|
// TODO consider why solution explorer has separate Children and Items options
|
||||||
if (children.IsInitialized)
|
if (children.IsInitialized)
|
||||||
{
|
{
|
||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
PopulateChildren(false, name, cancellationToken);
|
PopulateChildren(false, name, cancellationToken, accessToken);
|
||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,19 +255,19 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes
|
|||||||
/// Expands this node and returns its children
|
/// Expands this node and returns its children
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Children as an IList. This is the raw children collection, not a copy</returns>
|
/// <returns>Children as an IList. This is the raw children collection, not a copy</returns>
|
||||||
public IList<TreeNode> Expand(CancellationToken cancellationToken)
|
public IList<TreeNode> Expand(CancellationToken cancellationToken, string? accessToken = null)
|
||||||
{
|
{
|
||||||
return Expand(null, cancellationToken);
|
return Expand(null, cancellationToken, accessToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Refresh this node and returns its children
|
/// Refresh this node and returns its children
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Children as an IList. This is the raw children collection, not a copy</returns>
|
/// <returns>Children as an IList. This is the raw children collection, not a copy</returns>
|
||||||
public virtual IList<TreeNode> Refresh(CancellationToken cancellationToken)
|
public virtual IList<TreeNode> Refresh(CancellationToken cancellationToken, string? accessToken = null)
|
||||||
{
|
{
|
||||||
// TODO consider why solution explorer has separate Children and Items options
|
// TODO consider why solution explorer has separate Children and Items options
|
||||||
PopulateChildren(true, null, cancellationToken);
|
PopulateChildren(true, null, cancellationToken, accessToken);
|
||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,7 +319,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes
|
|||||||
return Parent as T;
|
return Parent as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void PopulateChildren(bool refresh, string name, CancellationToken cancellationToken)
|
protected virtual void PopulateChildren(bool refresh, string name, CancellationToken cancellationToken, string? accessToken = null)
|
||||||
{
|
{
|
||||||
Logger.Write(TraceEventType.Verbose, string.Format(CultureInfo.InvariantCulture, "Populating oe node :{0}", this.GetNodePath()));
|
Logger.Write(TraceEventType.Verbose, string.Format(CultureInfo.InvariantCulture, "Populating oe node :{0}", this.GetNodePath()));
|
||||||
Debug.Assert(IsAlwaysLeaf == false);
|
Debug.Assert(IsAlwaysLeaf == false);
|
||||||
@@ -335,6 +335,9 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes
|
|||||||
children.Clear();
|
children.Clear();
|
||||||
BeginChildrenInit();
|
BeginChildrenInit();
|
||||||
|
|
||||||
|
// Update access token for future queries
|
||||||
|
context.UpdateAccessToken(accessToken);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ErrorMessage = null;
|
ErrorMessage = null;
|
||||||
|
|||||||
@@ -212,7 +212,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
RunExpandTask(session, expandParams);
|
bool refreshNeeded = session.ConnectionInfo.TryUpdateAccessToken(expandParams.SecurityToken);
|
||||||
|
RunExpandTask(session, expandParams, refreshNeeded);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -239,6 +240,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
session.ConnectionInfo.TryUpdateAccessToken(refreshParams.SecurityToken);
|
||||||
RunExpandTask(session, refreshParams, true);
|
RunExpandTask(session, refreshParams, true);
|
||||||
}
|
}
|
||||||
await context.SendResult(true);
|
await context.SendResult(true);
|
||||||
@@ -373,12 +375,12 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Task<ExpandResponse> ExpandNode(ObjectExplorerSession session, string nodePath, bool forceRefresh = false)
|
internal Task<ExpandResponse> ExpandNode(ObjectExplorerSession session, string nodePath, bool forceRefresh = false, SecurityToken? securityToken = null)
|
||||||
{
|
{
|
||||||
return Task.Run(() => QueueExpandNodeRequest(session, nodePath, forceRefresh));
|
return Task.Run(() => QueueExpandNodeRequest(session, nodePath, forceRefresh, securityToken));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal ExpandResponse QueueExpandNodeRequest(ObjectExplorerSession session, string nodePath, bool forceRefresh = false)
|
internal ExpandResponse QueueExpandNodeRequest(ObjectExplorerSession session, string nodePath, bool forceRefresh = false, SecurityToken? securityToken = null)
|
||||||
{
|
{
|
||||||
NodeInfo[] nodes = null;
|
NodeInfo[] nodes = null;
|
||||||
TreeNode node = session.Root.FindNodeByPath(nodePath);
|
TreeNode node = session.Root.FindNodeByPath(nodePath);
|
||||||
@@ -432,15 +434,21 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
|
|||||||
waitForLockTimeout: timeout,
|
waitForLockTimeout: timeout,
|
||||||
bindOperation: (bindingContext, cancelToken) =>
|
bindOperation: (bindingContext, cancelToken) =>
|
||||||
{
|
{
|
||||||
|
if (!session.ConnectionInfo.IsAzureAuth)
|
||||||
|
{
|
||||||
|
// explicitly set null here to prevent setting access token for non-Azure auth modes.
|
||||||
|
securityToken = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (forceRefresh)
|
if (forceRefresh)
|
||||||
{
|
{
|
||||||
Logger.Verbose($"Forcing refresh for {nodePath}");
|
Logger.Verbose($"Forcing refresh for {nodePath}");
|
||||||
nodes = node.Refresh(cancelToken).Select(x => x.ToNodeInfo()).ToArray();
|
nodes = node.Refresh(cancelToken, securityToken?.Token).Select(x => x.ToNodeInfo()).ToArray();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Verbose($"Expanding {nodePath}");
|
Logger.Verbose($"Expanding {nodePath}");
|
||||||
nodes = node.Expand(cancelToken).Select(x => x.ToNodeInfo()).ToArray();
|
nodes = node.Expand(cancelToken, securityToken?.Token).Select(x => x.ToNodeInfo()).ToArray();
|
||||||
}
|
}
|
||||||
response.Nodes = nodes;
|
response.Nodes = nodes;
|
||||||
response.ErrorMessage = node.ErrorMessage;
|
response.ErrorMessage = node.ErrorMessage;
|
||||||
@@ -635,7 +643,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
|
|||||||
private async Task ExpandNodeAsync(ObjectExplorerSession session, ExpandParams expandParams, CancellationToken cancellationToken, bool forceRefresh = false)
|
private async Task ExpandNodeAsync(ObjectExplorerSession session, ExpandParams expandParams, CancellationToken cancellationToken, bool forceRefresh = false)
|
||||||
{
|
{
|
||||||
ExpandResponse response = null;
|
ExpandResponse response = null;
|
||||||
response = await ExpandNode(session, expandParams.NodePath, forceRefresh);
|
response = await ExpandNode(session, expandParams.NodePath, forceRefresh, expandParams.SecurityToken);
|
||||||
if (cancellationToken.IsCancellationRequested)
|
if (cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
Logger.Write(TraceEventType.Verbose, "OE expand canceled");
|
Logger.Write(TraceEventType.Verbose, "OE expand canceled");
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
|
|||||||
{
|
{
|
||||||
internal partial class DatabaseTreeNode
|
internal partial class DatabaseTreeNode
|
||||||
{
|
{
|
||||||
public DatabaseTreeNode(ServerNode serverNode, string databaseName): this()
|
public DatabaseTreeNode(ServerNode serverNode, string databaseName) : this()
|
||||||
{
|
{
|
||||||
Parent = serverNode;
|
Parent = serverNode;
|
||||||
NodeValue = databaseName;
|
NodeValue = databaseName;
|
||||||
@@ -52,12 +52,12 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PopulateChildren(bool refresh, string name, CancellationToken cancellationToken)
|
protected override void PopulateChildren(bool refresh, string name, CancellationToken cancellationToken, string? accessToken = null)
|
||||||
{
|
{
|
||||||
var smoQueryContext = this.GetContextAs<SmoQueryContext>();
|
var smoQueryContext = this.GetContextAs<SmoQueryContext>();
|
||||||
if (IsAccessible(smoQueryContext))
|
if (IsAccessible(smoQueryContext))
|
||||||
{
|
{
|
||||||
base.PopulateChildren(refresh, name, cancellationToken);
|
base.PopulateChildren(refresh, name, cancellationToken, accessToken);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
using Microsoft.SqlServer.Management.Smo;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
|
||||||
|
{
|
||||||
|
internal static class SmoExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Updates access token on the connection context of <paramref name="sqlObj"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sqlObj">(this) SMO SQL Object containing connection context.</param>
|
||||||
|
/// <param name="accessToken">Access token</param>
|
||||||
|
public static void UpdateAccessToken(this SqlSmoObject sqlObj, string accessToken)
|
||||||
|
{
|
||||||
|
if (sqlObj != null && !string.IsNullOrEmpty(accessToken)
|
||||||
|
&& sqlObj.ExecutionManager != null
|
||||||
|
&& sqlObj.ExecutionManager.ConnectionContext != null)
|
||||||
|
{
|
||||||
|
sqlObj.ExecutionManager.ConnectionContext.AccessToken = new AzureAccessToken(accessToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,7 +46,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The server SMO will query against
|
/// The server SMO will query against
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Server Server {
|
public Server Server
|
||||||
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return GetObjectWithOpenedConnection(server);
|
return GetObjectWithOpenedConnection(server);
|
||||||
@@ -56,7 +57,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Optional Database context object to query against
|
/// Optional Database context object to query against
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Database Database {
|
public Database Database
|
||||||
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return GetObjectWithOpenedConnection(database);
|
return GetObjectWithOpenedConnection(database);
|
||||||
@@ -70,7 +72,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parent of a give node to use for queries
|
/// Parent of a give node to use for queries
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SmoObjectBase Parent {
|
public SmoObjectBase Parent
|
||||||
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return GetObjectWithOpenedConnection(parent);
|
return GetObjectWithOpenedConnection(parent);
|
||||||
@@ -147,7 +150,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if(validFor == 0)
|
if (validFor == 0)
|
||||||
{
|
{
|
||||||
validFor = ServerVersionHelper.GetValidForFlag(SqlServerType, Database);
|
validFor = ServerVersionHelper.GetValidForFlag(SqlServerType, Database);
|
||||||
}
|
}
|
||||||
@@ -169,6 +172,31 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
|
|||||||
return smoObj;
|
return smoObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates access token on parent connection context.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="accessToken">Acquired access token</param>
|
||||||
|
public void UpdateAccessToken(string? accessToken)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(accessToken))
|
||||||
|
{
|
||||||
|
// Update all applicable nodes that could contain access token
|
||||||
|
// to prevent stale token from being re-used.
|
||||||
|
if (server != null)
|
||||||
|
{
|
||||||
|
(server as SqlSmoObject).UpdateAccessToken(accessToken);
|
||||||
|
}
|
||||||
|
if (database != null)
|
||||||
|
{
|
||||||
|
(database as SqlSmoObject).UpdateAccessToken(accessToken);
|
||||||
|
}
|
||||||
|
if (parent != null)
|
||||||
|
{
|
||||||
|
(parent as SqlSmoObject).UpdateAccessToken(accessToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ensures the server objects connection context is open. This is used by all child objects, and
|
/// Ensures the server objects connection context is open. This is used by all child objects, and
|
||||||
/// the only way to easily access is via the server object. This should be called during access of
|
/// the only way to easily access is via the server object. This should be called during access of
|
||||||
|
|||||||
@@ -14,11 +14,21 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class SmoWrapper
|
internal class SmoWrapper
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates instance of <see cref="Server"/> from provided <paramref name="serverConn"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serverConn">Server connection instance.</param>
|
||||||
|
/// <returns>Server instance.</returns>
|
||||||
public virtual Server CreateServer(ServerConnection serverConn)
|
public virtual Server CreateServer(ServerConnection serverConn)
|
||||||
{
|
{
|
||||||
return serverConn == null ? null : new Server(serverConn);
|
return serverConn == null ? null : new Server(serverConn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if connection is open on the <paramref name="smoObj"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="smoObj">SMO Object containing connection context.</param>
|
||||||
|
/// <returns>True if connection is open, otherwise false.</returns>
|
||||||
public virtual bool IsConnectionOpen(SmoObjectBase smoObj)
|
public virtual bool IsConnectionOpen(SmoObjectBase smoObj)
|
||||||
{
|
{
|
||||||
SqlSmoObject sqlObj = smoObj as SqlSmoObject;
|
SqlSmoObject sqlObj = smoObj as SqlSmoObject;
|
||||||
@@ -28,6 +38,10 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
|
|||||||
&& sqlObj.ExecutionManager.ConnectionContext.IsOpen;
|
&& sqlObj.ExecutionManager.ConnectionContext.IsOpen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens connection on the connection context of <paramref name="smoObj"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="smoObj">SMO Object containing connection context.</param>
|
||||||
public virtual void OpenConnection(SmoObjectBase smoObj)
|
public virtual void OpenConnection(SmoObjectBase smoObj)
|
||||||
{
|
{
|
||||||
SqlSmoObject sqlObj = smoObj as SqlSmoObject;
|
SqlSmoObject sqlObj = smoObj as SqlSmoObject;
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ using System.Threading;
|
|||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility
|
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility
|
||||||
{
|
{
|
||||||
public class LiveConnectionException : Exception {
|
public class LiveConnectionException : Exception
|
||||||
|
{
|
||||||
public LiveConnectionException(string message)
|
public LiveConnectionException(string message)
|
||||||
: base(message) { }
|
: base(message) { }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
|||||||
public void FindNodeCanExpandParentNodes()
|
public void FindNodeCanExpandParentNodes()
|
||||||
{
|
{
|
||||||
var mockTreeNode = new Mock<TreeNode>();
|
var mockTreeNode = new Mock<TreeNode>();
|
||||||
object[] populateChildrenArguments = { ItExpr.Is<bool>(x => x == false), ItExpr.IsNull<string>(), new CancellationToken() };
|
object[] populateChildrenArguments = { ItExpr.Is<bool>(x => x == false), ItExpr.IsNull<string>(), new CancellationToken(), ItExpr.IsNull<string>() };
|
||||||
mockTreeNode.Protected().Setup("PopulateChildren", populateChildrenArguments);
|
mockTreeNode.Protected().Setup("PopulateChildren", populateChildrenArguments);
|
||||||
mockTreeNode.Object.IsAlwaysLeaf = false;
|
mockTreeNode.Object.IsAlwaysLeaf = false;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user