simplify drop object request handler (#1953)

* simplify drop object request handler

* fix test cases

* fix issues

* update strings

* fix error

* fix error
This commit is contained in:
Alan Ren
2023-03-20 21:54:34 -07:00
committed by GitHub
parent b4781cf267
commit 692f444ccb
14 changed files with 187 additions and 271 deletions

View File

@@ -10150,6 +10150,11 @@ namespace Microsoft.SqlTools.ServiceLayer
return Keys.GetString(Keys.UnsupportedModelType, type);
}
public static string ObjectNotRenamable(string urn)
{
return Keys.GetString(Keys.ObjectNotRenamable, urn);
}
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Keys
{
@@ -14043,6 +14048,9 @@ namespace Microsoft.SqlTools.ServiceLayer
public const string ErrorConnectionNotFound = "ErrorConnectionNotFound";
public const string ObjectNotRenamable = "ObjectNotRenamable";
public const string DefaultLanguagePlaceholder = "DefaultLanguagePlaceholder";

View File

@@ -5386,6 +5386,11 @@ The Query Processor estimates that implementing the following index could improv
<value>The connection could not be found</value>
<comment></comment>
</data>
<data name="ObjectNotRenamable" xml:space="preserve">
<value>The object could not be renamed. URN: &apos;{0}&apos;.</value>
<comment>.
Parameters: 0 - urn (string) </comment>
</data>
<data name="DefaultLanguagePlaceholder" xml:space="preserve">
<value>&lt;default&gt;</value>
<comment></comment>

View File

@@ -2443,6 +2443,7 @@ GetUserDefinedObjectsFromModelFailed = Failed to get user defined objects from m
#ObjectManagement Service
ErrorConnectionNotFound = The connection could not be found
ObjectNotRenamable(string urn) = The object could not be renamed. URN: '{0}'.
############################################################################
# Security Service

View File

@@ -6576,6 +6576,12 @@ The Query Processor estimates that implementing the following index could improv
<target state="new">Reset password for the login while unlocking.</target>
<note></note>
</trans-unit>
<trans-unit id="ObjectNotRenamable">
<source>The object could not be renamed. URN: '{0}'.</source>
<target state="new">The object could not be renamed. URN: '{0}'.</target>
<note>.
Parameters: 0 - urn (string) </note>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -0,0 +1,32 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts
{
public class DropRequestParams : GeneralRequestDetails
{
/// <summary>
/// SFC (SMO) URN identifying the object
/// </summary>
public string ObjectUrn { get; set; }
/// <summary>
/// Connection uri
/// </summary>
public string ConnectionUri { get; set; }
/// <summary>
/// Whether to throw an error if the object does not exist. The default value is false.
/// </summary>
public bool ThrowIfNotExist { get; set; } = false;
}
public class DropRequest
{
public static readonly RequestType<DropRequestParams, bool> Type = RequestType<DropRequestParams, bool>.Create("objectManagement/drop");
}
}

View File

@@ -11,7 +11,9 @@ using Microsoft.SqlServer.Management.Sdk.Sfc;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Management;
using Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts;
using Microsoft.SqlTools.ServiceLayer.Utility;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
@@ -48,6 +50,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
{
this.serviceHost = serviceHost;
this.serviceHost.SetRequestHandler(RenameRequest.Type, HandleRenameRequest, true);
this.serviceHost.SetRequestHandler(DropRequest.Type, HandleDropRequest, true);
}
/// <summary>
@@ -58,40 +61,74 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
/// <returns></returns>
internal async Task HandleRenameRequest(RenameRequestParams requestParams, RequestContext<bool> requestContext)
{
Logger.Verbose("Handle Request in HandleProcessRenameEditRequest()");
ConnectionInfo connInfo;
if (ConnectionServiceInstance.TryFindConnection(
requestParams.ConnectionUri,
out connInfo))
Logger.Verbose("Handle Request in HandleRenameRequest()");
ExecuteActionOnObject(requestParams.ConnectionUri, requestParams.ObjectUrn, (dbObject) =>
{
ServerConnection serverConnection = ConnectionService.OpenServerConnection(connInfo, ObjectManagementServiceApplicationName);
using (serverConnection.SqlConnectionObject)
var renamable = dbObject as IRenamable;
if (renamable != null)
{
IRenamable renameObject = this.GetRenamable(requestParams, serverConnection);
renameObject.Rename(requestParams.NewName);
renamable.Rename(requestParams.NewName);
}
}
else
{
Logger.Error($"The connection {requestParams.ConnectionUri} could not be found.");
throw new Exception(SR.ErrorConnectionNotFound);
}
else
{
throw new Exception(SR.ObjectNotRenamable(requestParams.ObjectUrn));
}
});
await requestContext.SendResult(true);
}
/// <summary>
/// Method to get the sql object, which should be renamed
/// Method to handle the delete object request
/// </summary>
/// <param name="requestParams">parameters which are required for the rename operation</param>
/// <param name="connection">the server connection on the server to search for the sqlobject</param>
/// <returns>the sql object if implements the interface IRenamable, so they can be renamed</returns>
private IRenamable GetRenamable(RenameRequestParams requestParams, ServerConnection connection)
/// <param name="requestParams">parameters which are needed to execute deletion operation</param>
/// <param name="requestContext">Request Context</param>
/// <returns></returns>
internal async Task HandleDropRequest(DropRequestParams requestParams, RequestContext<bool> requestContext)
{
Server server = new Server(connection);
SqlSmoObject dbObject = server.GetSmoObject(new Urn(requestParams.ObjectUrn));
return (IRenamable)dbObject;
Logger.Verbose("Handle Request in HandleDeleteRequest()");
ConnectionInfo connectionInfo = this.GetConnectionInfo(requestParams.ConnectionUri);
using (CDataContainer dataContainer = CDataContainer.CreateDataContainer(connectionInfo, databaseExists: true))
{
try
{
dataContainer.SqlDialogSubject = dataContainer.Server?.GetSmoObject(requestParams.ObjectUrn);
DatabaseUtils.DoDropObject(dataContainer);
}
catch (FailedOperationException ex)
{
if (ex.InnerException is MissingObjectException && requestParams.ThrowIfNotExist)
{
throw;
}
}
}
await requestContext.SendResult(true);
}
private ConnectionInfo GetConnectionInfo(string connectionUri)
{
ConnectionInfo connInfo;
if (ConnectionServiceInstance.TryFindConnection(connectionUri, out connInfo))
{
return connInfo;
}
else
{
Logger.Error($"The connection with URI '{connectionUri}' could not be found.");
throw new Exception(SR.ErrorConnectionNotFound);
}
}
private void ExecuteActionOnObject(string connectionUri, string objectUrn, Action<SqlSmoObject> action)
{
ConnectionInfo connInfo = this.GetConnectionInfo(connectionUri);
ServerConnection serverConnection = ConnectionService.OpenServerConnection(connInfo, ObjectManagementServiceApplicationName);
using (serverConnection.SqlConnectionObject)
{
Server server = new Server(serverConnection);
SqlSmoObject dbObject = server.GetSmoObject(new Urn(objectUrn));
action(dbObject);
}
}
}
}

View File

@@ -32,29 +32,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Security.Contracts
RequestType<CreateLoginParams, object>.Create("objectManagement/createLogin");
}
/// <summary>
/// Delete Login params
/// </summary>
public class DeleteLoginParams
{
public string ConnectionUri { get; set; }
public string Name { get; set; }
}
/// <summary>
/// Delete Login request type
/// </summary>
public class DeleteLoginRequest
{
/// <summary>
/// Request definition
/// </summary>
public static readonly
RequestType<DeleteLoginParams, object> Type =
RequestType<DeleteLoginParams, object>.Create("objectManagement/deleteLogin");
}
/// <summary>
/// Update Login params
/// </summary>

View File

@@ -89,31 +89,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Security.Contracts
RequestType<UpdateUserParams, ResultStatus>.Create("objectManagement/updateUser");
}
/// <summary>
/// Delete User params
/// </summary>
public class DeleteUserParams
{
public string? ConnectionUri { get; set; }
public string? Database { get; set; }
public string? Name { get; set; }
}
/// <summary>
/// Delete User request type
/// </summary>
public class DeleteUserRequest
{
/// <summary>
/// Request definition
/// </summary>
public static readonly
RequestType<DeleteUserParams, ResultStatus> Type =
RequestType<DeleteUserParams, ResultStatus>.Create("objectManagement/deleteUser");
}
/// <summary>
/// Update User params
/// </summary>

View File

@@ -88,13 +88,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
// Credential request handlers
this.ServiceHost.SetRequestHandler(CreateCredentialRequest.Type, HandleCreateCredentialRequest, true);
this.ServiceHost.SetRequestHandler(UpdateCredentialRequest.Type, HandleUpdateCredentialRequest, true);
this.ServiceHost.SetRequestHandler(DeleteCredentialRequest.Type, HandleDeleteCredentialRequest, true);
this.ServiceHost.SetRequestHandler(GetCredentialsRequest.Type, HandleGetCredentialsRequest, true);
// Login request handlers
this.ServiceHost.SetRequestHandler(CreateLoginRequest.Type, HandleCreateLoginRequest, true);
this.ServiceHost.SetRequestHandler(UpdateLoginRequest.Type, HandleUpdateLoginRequest, true);
this.ServiceHost.SetRequestHandler(DeleteLoginRequest.Type, HandleDeleteLoginRequest, true);
this.ServiceHost.SetRequestHandler(InitializeLoginViewRequest.Type, HandleInitializeLoginViewRequest, true);
this.ServiceHost.SetRequestHandler(DisposeLoginViewRequest.Type, HandleDisposeLoginViewRequest, true);
@@ -102,12 +100,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
this.ServiceHost.SetRequestHandler(InitializeUserViewRequest.Type, this.userServiceHandler.HandleInitializeUserViewRequest, true);
this.ServiceHost.SetRequestHandler(CreateUserRequest.Type, this.userServiceHandler.HandleCreateUserRequest, true);
this.ServiceHost.SetRequestHandler(UpdateUserRequest.Type, this.userServiceHandler.HandleUpdateUserRequest, true);
this.ServiceHost.SetRequestHandler(DeleteUserRequest.Type, this.userServiceHandler.HandleDeleteUserRequest, true);
this.ServiceHost.SetRequestHandler(DisposeUserViewRequest.Type, this.userServiceHandler.HandleDisposeUserViewRequest, true);
}
#region "Login Handlers"
#region "Login Handlers"
/// <summary>
/// Handle request to create a login
@@ -161,31 +158,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
await requestContext.SendResult(new object());
}
/// <summary>
/// Handle request to delete a credential
/// </summary>
internal async Task HandleDeleteLoginRequest(DeleteLoginParams parameters, RequestContext<object> requestContext)
{
ConnectionInfo connInfo;
ConnectionServiceInstance.TryFindConnection(parameters.ConnectionUri, out connInfo);
if (connInfo == null)
{
throw new ArgumentException("Invalid ConnectionUri");
}
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
Login login = dataContainer.Server?.Logins[parameters.Name];
dataContainer.SqlDialogSubject = login;
DatabaseUtils.DoDropObject(dataContainer);
await requestContext.SendResult(new ResultStatus()
{
Success = true,
ErrorMessage = string.Empty
});
}
internal async Task HandleUpdateLoginRequest(UpdateLoginParams parameters, RequestContext<object> requestContext)
{
ConnectionInfo connInfo;
@@ -418,24 +390,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
});
}
/// <summary>
/// Handle request to delete a credential
/// </summary>
internal async Task HandleDeleteCredentialRequest(DeleteCredentialParams parameters, RequestContext<ResultStatus> requestContext)
{
var result = await ConfigureCredential(parameters.OwnerUri,
parameters.Credential,
ConfigAction.Drop,
RunType.RunNow);
await requestContext.SendResult(new ResultStatus()
{
Success = result.Item1,
ErrorMessage = result.Item2
});
}
/// <summary>
/// Handle request to get all credentials
/// </summary>

View File

@@ -37,7 +37,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
private ConnectionService? connectionService;
private Dictionary<string, UserViewState> contextIdToViewState = new Dictionary<string, UserViewState>();
/// <summary>
/// Internal for testing purposes only
/// </summary>
@@ -122,8 +122,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
}
UserPrototypeFactory userPrototypeFactory = UserPrototypeFactory.GetInstance(dataContainer, userInfo, originalData: null);
UserPrototype currentUserPrototype = userPrototypeFactory.GetUserPrototype(ExhaustiveUserTypes.LoginMappedUser);
UserPrototype currentUserPrototype = userPrototypeFactory.GetUserPrototype(ExhaustiveUserTypes.LoginMappedUser);
IUserPrototypeWithDefaultLanguage defaultLanguagePrototype = currentUserPrototype as IUserPrototypeWithDefaultLanguage;
string? defaultLanguageAlias = null;
if (defaultLanguagePrototype != null && defaultLanguagePrototype.IsDefaultLanguageSupported)
@@ -209,7 +209,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
};
this.contextIdToViewState.Add(
parameters.ContextId,
parameters.ContextId,
new UserViewState(parameters.Database, currentUserPrototype.CurrentState));
await requestContext.SendResult(userViewInfo);
@@ -282,42 +282,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
});
}
/// <summary>
/// Handle request to delete a user
/// </summary>
internal async Task HandleDeleteUserRequest(DeleteUserParams parameters, RequestContext<ResultStatus> requestContext)
{
ConnectionInfo connInfo;
ConnectionServiceInstance.TryFindConnection(parameters.ConnectionUri, out connInfo);
if (connInfo == null)
{
throw new ArgumentException("Invalid ConnectionUri");
}
if (string.IsNullOrWhiteSpace(parameters.Name) || string.IsNullOrWhiteSpace(parameters.Database))
{
throw new ArgumentException("Invalid null parameter");
}
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
string dbUrn = "Server/Database[@Name='" + Urn.EscapeString(parameters.Database) + "']";
Database? parent = dataContainer.Server.GetSmoObject(new Urn(dbUrn)) as Database;
User user = parent.Users[parameters.Name];
dataContainer.SqlDialogSubject = user;
CheckForSchemaOwnerships(parent, user);
DatabaseUtils.DoDropObject(dataContainer);
await requestContext.SendResult(new ResultStatus()
{
Success = true,
ErrorMessage = string.Empty
});
}
internal async Task HandleDisposeUserViewRequest(DisposeUserViewRequestParams parameters, RequestContext<ResultStatus> requestContext)
{
this.ConnectionServiceInstance.Disconnect(new DisconnectParams(){
this.ConnectionServiceInstance.Disconnect(new DisconnectParams()
{
OwnerUri = parameters.ContextId,
Type = null
});
@@ -335,8 +303,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
}
internal CDataContainer CreateUserDataContainer(
ConnectionInfo connInfo,
UserInfo? user,
ConnectionInfo connInfo,
UserInfo? user,
ConfigAction configAction,
string databaseName)
{
@@ -344,7 +312,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
var connectionInfoWithConnection = new SqlConnectionInfoWithConnection();
connectionInfoWithConnection.ServerConnection = serverConnection;
string urn = (configAction == ConfigAction.Update && user != null)
string urn = (configAction == ConfigAction.Update && user != null)
? string.Format(System.Globalization.CultureInfo.InvariantCulture,
"Server/Database[@Name='{0}']/User[@Name='{1}']",
Urn.EscapeString(databaseName),
@@ -361,7 +329,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
containerXml.AddProperty("itemtype", "User");
}
XmlDocument xmlDoc = containerXml.GenerateXmlDocument();
XmlDocument xmlDoc = containerXml.GenerateXmlDocument();
return CDataContainer.CreateDataContainer(connectionInfoWithConnection, xmlDoc);
}
@@ -393,30 +361,18 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
return new Tuple<bool, string>(true, string.Empty);
}
private void CheckForSchemaOwnerships(Database parentDb, User existingUser)
{
foreach (Schema sch in parentDb.Schemas)
{
var comparer = parentDb.GetStringComparer();
if (comparer.Compare(sch.Owner, existingUser.Name) == 0)
{
throw new ApplicationException("Cannot drop user since it owns a schema");
}
}
}
}
internal class UserActions : ManagementActionBase
{
#region Variables
#region Variables
//private UserPrototypeData userData;
private UserPrototype userPrototype;
private UserInfo? user;
private ConfigAction configAction;
#endregion
#endregion
#region Constructors / Dispose
#region Constructors / Dispose
/// <summary>
/// required when loading from Object Explorer context
/// </summary>
@@ -442,7 +398,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
// base.Dispose(disposing);
// }
#endregion
#endregion
/// <summary>
/// called on background thread by the framework to execute the action
@@ -462,7 +418,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
this.userPrototype.ApplyChanges();
}
}
private UserPrototype InitUserPrototype(CDataContainer dataContainer, UserInfo user, UserPrototypeData? originalData)
{
ExhaustiveUserTypes currentUserType;
@@ -485,8 +441,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
dataContainer.Server.GetSmoObject(dataContainer.ObjectUrn) as User);
}
UserPrototype currentUserPrototype = userPrototypeFactory.GetUserPrototype(currentUserType);
return currentUserPrototype;
UserPrototype currentUserPrototype = userPrototypeFactory.GetUserPrototype(currentUserType);
return currentUserPrototype;
}
private ExhaustiveUserTypes GetCurrentUserTypeForExistingUser(User? user)
@@ -503,25 +459,20 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
{
if (user.AuthenticationType == AuthenticationType.Windows)
{
return ExhaustiveUserTypes.WindowsUser;
return ExhaustiveUserTypes.WindowsUser;
}
else if (user.AuthenticationType == AuthenticationType.Database)
{
return ExhaustiveUserTypes.SqlUserWithPassword;
}
}
return ExhaustiveUserTypes.LoginMappedUser;
case UserType.NoLogin:
return ExhaustiveUserTypes.SqlUserWithoutLogin;
case UserType.Certificate:
return ExhaustiveUserTypes.CertificateMappedUser;
case UserType.AsymmetricKey:
return ExhaustiveUserTypes.AsymmetricKeyMappedUser;
default:
return ExhaustiveUserTypes.Unknown;
}