mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-18 01:25:41 -05:00
Simplify Object Management APIs (#2015)
* unify requests-wip * wip * unify api * fix test * add credential handler * fix credential handler issue. * generic type update * fix scripting for user
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// 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 DisposeViewRequestParams : GeneralRequestDetails
|
||||
{
|
||||
public string ContextId { get; set; }
|
||||
}
|
||||
|
||||
public class DisposeViewRequestResponse { }
|
||||
|
||||
public class DisposeViewRequest
|
||||
{
|
||||
public static readonly RequestType<DisposeViewRequestParams, DisposeViewRequestResponse> Type = RequestType<DisposeViewRequestParams, DisposeViewRequestResponse>.Create("objectManagement/disposeView");
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,10 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts
|
||||
{
|
||||
public class DropRequestParams : GeneralRequestDetails
|
||||
{
|
||||
/// <summary>
|
||||
/// The object type.
|
||||
/// </summary>
|
||||
public SqlObjectType ObjectType { get; set; }
|
||||
/// <summary>
|
||||
/// SFC (SMO) URN identifying the object
|
||||
/// </summary>
|
||||
@@ -25,8 +29,10 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts
|
||||
public bool ThrowIfNotExist { get; set; } = false;
|
||||
}
|
||||
|
||||
public class DropRequestResponse { }
|
||||
|
||||
public class DropRequest
|
||||
{
|
||||
public static readonly RequestType<DropRequestParams, bool> Type = RequestType<DropRequestParams, bool>.Create("objectManagement/drop");
|
||||
public static readonly RequestType<DropRequestParams, DropRequestResponse> Type = RequestType<DropRequestParams, DropRequestResponse>.Create("objectManagement/drop");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// 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 InitializeViewRequestParams : GeneralRequestDetails
|
||||
{
|
||||
/// <summary>
|
||||
/// The connection uri.
|
||||
/// </summary>
|
||||
public string ConnectionUri { get; set; }
|
||||
/// <summary>
|
||||
/// The target database name.
|
||||
/// </summary>
|
||||
public string Database { get; set; }
|
||||
/// <summary>
|
||||
/// The object type.
|
||||
/// </summary>
|
||||
public SqlObjectType ObjectType { get; set; }
|
||||
/// <summary>
|
||||
/// Whether the view is for a new object.
|
||||
/// </summary>
|
||||
public bool IsNewObject { get; set; }
|
||||
/// <summary>
|
||||
/// The object view context id.
|
||||
/// </summary>
|
||||
public string ContextId { get; set; }
|
||||
/// <summary>
|
||||
/// Urn of the parent object.
|
||||
/// </summary>
|
||||
public string ParentUrn { get; set; }
|
||||
/// <summary>
|
||||
/// Urn of the object. Only set when the view is for an existing object.
|
||||
/// </summary>
|
||||
public string ObjectUrn { get; set; }
|
||||
}
|
||||
|
||||
public class InitializeViewRequest
|
||||
{
|
||||
public static readonly RequestType<InitializeViewRequestParams, SqlObjectViewInfo> Type = RequestType<InitializeViewRequestParams, SqlObjectViewInfo>.Create("objectManagement/initializeView");
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,10 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts
|
||||
{
|
||||
public class RenameRequestParams : GeneralRequestDetails
|
||||
{
|
||||
/// <summary>
|
||||
/// The object type.
|
||||
/// </summary>
|
||||
public SqlObjectType ObjectType { get; set; }
|
||||
/// <summary>
|
||||
/// SFC (SMO) URN identifying the object
|
||||
/// </summary>
|
||||
@@ -24,8 +28,11 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts
|
||||
/// </summary>
|
||||
public string ConnectionUri { get; set; }
|
||||
}
|
||||
|
||||
public class RenameRequestResponse { }
|
||||
|
||||
public class RenameRequest
|
||||
{
|
||||
public static readonly RequestType<RenameRequestParams, bool> Type = RequestType<RenameRequestParams, bool>.Create("objectManagement/rename");
|
||||
public static readonly RequestType<RenameRequestParams, RenameRequestResponse> Type = RequestType<RenameRequestParams, RenameRequestResponse>.Create("objectManagement/rename");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// 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;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts
|
||||
{
|
||||
public class SaveObjectRequestParams : GeneralRequestDetails
|
||||
{
|
||||
/// <summary>
|
||||
/// The context id.
|
||||
/// </summary>
|
||||
public string ContextId { get; set; }
|
||||
/// <summary>
|
||||
/// The object information.
|
||||
/// </summary>
|
||||
public JToken Object { get; set; }
|
||||
}
|
||||
|
||||
public class SaveObjectRequestResponse { }
|
||||
|
||||
public class SaveObjectRequest
|
||||
{
|
||||
public static readonly RequestType<SaveObjectRequestParams, SaveObjectRequestResponse> Type = RequestType<SaveObjectRequestParams, SaveObjectRequestResponse>.Create("objectManagement/save");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// 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;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts
|
||||
{
|
||||
public class ScriptObjectRequestParams : GeneralRequestDetails
|
||||
{
|
||||
/// <summary>
|
||||
/// The context id.
|
||||
/// </summary>
|
||||
public string ContextId { get; set; }
|
||||
/// <summary>
|
||||
/// The object information.
|
||||
/// </summary>
|
||||
public JToken Object { get; set; }
|
||||
}
|
||||
|
||||
public class ScriptObjectRequest
|
||||
{
|
||||
public static readonly RequestType<ScriptObjectRequestParams, string> Type = RequestType<ScriptObjectRequestParams, string>.Create("objectManagement/script");
|
||||
}
|
||||
}
|
||||
@@ -6,15 +6,11 @@
|
||||
#nullable disable
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
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;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
@@ -23,12 +19,21 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
/// </summary>
|
||||
public class ObjectManagementService
|
||||
{
|
||||
private const string ObjectManagementServiceApplicationName = "azdata-object-management";
|
||||
public const string ApplicationName = "azdata-object-management";
|
||||
private static Lazy<ObjectManagementService> objectManagementServiceInstance = new Lazy<ObjectManagementService>(() => new ObjectManagementService());
|
||||
public static ObjectManagementService Instance => objectManagementServiceInstance.Value;
|
||||
public static ConnectionService connectionService;
|
||||
private IProtocolEndpoint serviceHost;
|
||||
public ObjectManagementService() { }
|
||||
private List<IObjectTypeHandler> objectTypeHandlers = new List<IObjectTypeHandler>();
|
||||
private ConcurrentDictionary<string, SqlObjectViewContext> contextMap = new ConcurrentDictionary<string, SqlObjectViewContext>();
|
||||
|
||||
public ObjectManagementService()
|
||||
{
|
||||
this.objectTypeHandlers.Add(new CommonObjectTypeHandler(ConnectionService.Instance));
|
||||
this.objectTypeHandlers.Add(new LoginHandler(ConnectionService.Instance));
|
||||
this.objectTypeHandlers.Add(new UserHandler(ConnectionService.Instance));
|
||||
this.objectTypeHandlers.Add(new CredentialHandler(ConnectionService.Instance));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal for testing purposes only
|
||||
@@ -51,84 +56,81 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
this.serviceHost = serviceHost;
|
||||
this.serviceHost.SetRequestHandler(RenameRequest.Type, HandleRenameRequest, true);
|
||||
this.serviceHost.SetRequestHandler(DropRequest.Type, HandleDropRequest, true);
|
||||
this.serviceHost.SetRequestHandler(InitializeViewRequest.Type, HandleInitializeViewRequest, true);
|
||||
this.serviceHost.SetRequestHandler(SaveObjectRequest.Type, HandleSaveObjectRequest, true);
|
||||
this.serviceHost.SetRequestHandler(ScriptObjectRequest.Type, HandleScriptObjectRequest, true);
|
||||
this.serviceHost.SetRequestHandler(DisposeViewRequest.Type, HandleDisposeViewRequest, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to handle the renaming operation
|
||||
/// </summary>
|
||||
/// <param name="requestParams">parameters which are needed to execute renaming operation</param>
|
||||
/// <param name="requestContext">Request Context</param>
|
||||
/// <returns></returns>
|
||||
internal async Task HandleRenameRequest(RenameRequestParams requestParams, RequestContext<bool> requestContext)
|
||||
internal async Task HandleRenameRequest(RenameRequestParams requestParams, RequestContext<RenameRequestResponse> requestContext)
|
||||
{
|
||||
Logger.Verbose("Handle Request in HandleRenameRequest()");
|
||||
ExecuteActionOnObject(requestParams.ConnectionUri, requestParams.ObjectUrn, (dbObject) =>
|
||||
{
|
||||
var renamable = dbObject as IRenamable;
|
||||
if (renamable != null)
|
||||
{
|
||||
renamable.Rename(requestParams.NewName);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(SR.ObjectNotRenamable(requestParams.ObjectUrn));
|
||||
}
|
||||
});
|
||||
await requestContext.SendResult(true);
|
||||
var handler = this.GetObjectTypeHandler(requestParams.ObjectType);
|
||||
await handler.Rename(requestParams.ConnectionUri, requestParams.ObjectUrn, requestParams.NewName);
|
||||
await requestContext.SendResult(new RenameRequestResponse());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to handle the delete object request
|
||||
/// </summary>
|
||||
/// <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)
|
||||
internal async Task HandleDropRequest(DropRequestParams requestParams, RequestContext<DropRequestResponse> requestContext)
|
||||
{
|
||||
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) || (ex.InnerException is MissingObjectException && requestParams.ThrowIfNotExist))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
await requestContext.SendResult(true);
|
||||
var handler = this.GetObjectTypeHandler(requestParams.ObjectType);
|
||||
await handler.Drop(requestParams.ConnectionUri, requestParams.ObjectUrn, requestParams.ThrowIfNotExist);
|
||||
await requestContext.SendResult(new DropRequestResponse());
|
||||
}
|
||||
|
||||
private ConnectionInfo GetConnectionInfo(string connectionUri)
|
||||
internal async Task HandleInitializeViewRequest(InitializeViewRequestParams requestParams, RequestContext<SqlObjectViewInfo> requestContext)
|
||||
{
|
||||
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);
|
||||
}
|
||||
var handler = this.GetObjectTypeHandler(requestParams.ObjectType);
|
||||
var result = await handler.InitializeObjectView(requestParams);
|
||||
contextMap[requestParams.ContextId] = result.Context;
|
||||
await requestContext.SendResult(result.ViewInfo);
|
||||
}
|
||||
|
||||
private void ExecuteActionOnObject(string connectionUri, string objectUrn, Action<SqlSmoObject> action)
|
||||
internal async Task HandleSaveObjectRequest(SaveObjectRequestParams requestParams, RequestContext<SaveObjectRequestResponse> requestContext)
|
||||
{
|
||||
ConnectionInfo connInfo = this.GetConnectionInfo(connectionUri);
|
||||
ServerConnection serverConnection = ConnectionService.OpenServerConnection(connInfo, ObjectManagementServiceApplicationName);
|
||||
using (serverConnection.SqlConnectionObject)
|
||||
var context = this.GetContext(requestParams.ContextId);
|
||||
var handler = this.GetObjectTypeHandler(context.Parameters.ObjectType);
|
||||
var obj = requestParams.Object.ToObject(handler.GetObjectType());
|
||||
await handler.Save(context, obj as SqlObject);
|
||||
await requestContext.SendResult(new SaveObjectRequestResponse());
|
||||
}
|
||||
|
||||
internal async Task HandleScriptObjectRequest(ScriptObjectRequestParams requestParams, RequestContext<string> requestContext)
|
||||
{
|
||||
var context = this.GetContext(requestParams.ContextId);
|
||||
var handler = this.GetObjectTypeHandler(context.Parameters.ObjectType);
|
||||
var obj = requestParams.Object.ToObject(handler.GetObjectType());
|
||||
var script = await handler.Script(context, obj as SqlObject);
|
||||
await requestContext.SendResult(script);
|
||||
}
|
||||
|
||||
internal async Task HandleDisposeViewRequest(DisposeViewRequestParams requestParams, RequestContext<DisposeViewRequestResponse> requestContext)
|
||||
{
|
||||
SqlObjectViewContext context;
|
||||
if (contextMap.Remove(requestParams.ContextId, out context))
|
||||
{
|
||||
Server server = new Server(serverConnection);
|
||||
SqlSmoObject dbObject = server.GetSmoObject(new Urn(objectUrn));
|
||||
action(dbObject);
|
||||
context.Dispose();
|
||||
}
|
||||
await requestContext.SendResult(new DisposeViewRequestResponse());
|
||||
}
|
||||
|
||||
private IObjectTypeHandler GetObjectTypeHandler(SqlObjectType objectType)
|
||||
{
|
||||
foreach (var handler in objectTypeHandlers)
|
||||
{
|
||||
if (handler.CanHandleType(objectType))
|
||||
{
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
throw new NotSupportedException(objectType.ToString());
|
||||
}
|
||||
|
||||
private SqlObjectViewContext GetContext(string contextId)
|
||||
{
|
||||
if (contextMap.TryGetValue(contextId, out SqlObjectViewContext context))
|
||||
{
|
||||
return context;
|
||||
}
|
||||
throw new ArgumentException($"Context '{contextId}' not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlServer.Management.Sdk.Sfc;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Management;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
public interface IObjectTypeHandler
|
||||
{
|
||||
bool CanHandleType(SqlObjectType objectType);
|
||||
Task<InitializeViewResult> InitializeObjectView(Contracts.InitializeViewRequestParams requestParams);
|
||||
Task Save(SqlObjectViewContext context, SqlObject obj);
|
||||
Task<string> Script(SqlObjectViewContext context, SqlObject obj);
|
||||
Type GetObjectType();
|
||||
Task Rename(string connectionUri, string objectUrn, string newName);
|
||||
Task Drop(string connectionUri, string objectUrn, bool throwIfNotExist);
|
||||
}
|
||||
|
||||
public abstract class ObjectTypeHandler<ObjectType, ContextType> : IObjectTypeHandler
|
||||
where ObjectType : SqlObject
|
||||
where ContextType : SqlObjectViewContext
|
||||
{
|
||||
protected ConnectionService ConnectionService { get; }
|
||||
|
||||
public ObjectTypeHandler(ConnectionService connectionService)
|
||||
{
|
||||
this.ConnectionService = connectionService;
|
||||
}
|
||||
|
||||
public abstract bool CanHandleType(SqlObjectType objectType);
|
||||
public abstract Task<InitializeViewResult> InitializeObjectView(Contracts.InitializeViewRequestParams requestParams);
|
||||
public abstract Task Save(ContextType context, ObjectType obj);
|
||||
public abstract Task<string> Script(ContextType context, ObjectType obj);
|
||||
|
||||
public Task Save(SqlObjectViewContext context, SqlObject obj)
|
||||
{
|
||||
return this.Save((ContextType)context, (ObjectType)obj);
|
||||
}
|
||||
|
||||
public Task<string> Script(SqlObjectViewContext context, SqlObject obj)
|
||||
{
|
||||
return this.Script((ContextType)context, (ObjectType)obj);
|
||||
}
|
||||
|
||||
public Type GetObjectType()
|
||||
{
|
||||
return typeof(ObjectType);
|
||||
}
|
||||
|
||||
public virtual Task Rename(string connectionUri, string objectUrn, string newName)
|
||||
{
|
||||
ConnectionInfo connInfo = this.GetConnectionInfo(connectionUri);
|
||||
ServerConnection serverConnection = ConnectionService.OpenServerConnection(connInfo, ObjectManagementService.ApplicationName);
|
||||
using (serverConnection.SqlConnectionObject)
|
||||
{
|
||||
Server server = new Server(serverConnection);
|
||||
SqlSmoObject dbObject = server.GetSmoObject(new Urn(objectUrn));
|
||||
var renamable = dbObject as IRenamable;
|
||||
if (renamable != null)
|
||||
{
|
||||
renamable.Rename(newName);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(SR.ObjectNotRenamable(objectUrn));
|
||||
}
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public virtual Task Drop(string connectionUri, string objectUrn, bool throwIfNotExist)
|
||||
{
|
||||
ConnectionInfo connectionInfo = this.GetConnectionInfo(connectionUri);
|
||||
using (CDataContainer dataContainer = CDataContainer.CreateDataContainer(connectionInfo, databaseExists: true))
|
||||
{
|
||||
try
|
||||
{
|
||||
dataContainer.SqlDialogSubject = dataContainer.Server?.GetSmoObject(objectUrn);
|
||||
DatabaseUtils.DoDropObject(dataContainer);
|
||||
}
|
||||
catch (FailedOperationException ex)
|
||||
{
|
||||
if (!(ex.InnerException is MissingObjectException) || (ex.InnerException is MissingObjectException && throwIfNotExist))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected ConnectionInfo GetConnectionInfo(string connectionUri)
|
||||
{
|
||||
ConnectionInfo connInfo;
|
||||
if (this.ConnectionService.TryFindConnection(connectionUri, out connInfo))
|
||||
{
|
||||
return connInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error($"The connection with URI '{connectionUri}' could not be found.");
|
||||
throw new Exception(SR.ErrorConnectionNotFound);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
public class CommonObjectType : SqlObject { }
|
||||
|
||||
public class CommonObjectTypeViewContext : SqlObjectViewContext
|
||||
{
|
||||
public CommonObjectTypeViewContext(InitializeViewRequestParams parameters) : base(parameters) { }
|
||||
|
||||
public override void Dispose() { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A handler for the object types that only has rename/drop support
|
||||
/// </summary>
|
||||
public class CommonObjectTypeHandler : ObjectTypeHandler<CommonObjectType, CommonObjectTypeViewContext>
|
||||
{
|
||||
// The message is only used in developing time, no need to be localized.
|
||||
private const string NotSupportedException = "This operation is not supported for this object type";
|
||||
|
||||
public CommonObjectTypeHandler(ConnectionService connectionService) : base(connectionService) { }
|
||||
|
||||
public override bool CanHandleType(SqlObjectType objectType)
|
||||
{
|
||||
return objectType == SqlObjectType.Column ||
|
||||
objectType == SqlObjectType.Table ||
|
||||
objectType == SqlObjectType.View;
|
||||
}
|
||||
|
||||
public override Task Save(CommonObjectTypeViewContext context, CommonObjectType obj)
|
||||
{
|
||||
throw new NotSupportedException(NotSupportedException);
|
||||
}
|
||||
|
||||
public override Task<InitializeViewResult> InitializeObjectView(Contracts.InitializeViewRequestParams requestParams)
|
||||
{
|
||||
throw new NotSupportedException(NotSupportedException);
|
||||
}
|
||||
|
||||
public override Task<string> Script(CommonObjectTypeViewContext context, CommonObjectType obj)
|
||||
{
|
||||
throw new NotSupportedException(NotSupportedException);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// 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 System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Management;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// Credential object type handler
|
||||
/// </summary>
|
||||
public class CredentialHandler : ObjectTypeHandler<CredentialInfo, CredentialViewContext>
|
||||
{
|
||||
public CredentialHandler(ConnectionService connectionService) : base(connectionService)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CanHandleType(SqlObjectType objectType)
|
||||
{
|
||||
return objectType == SqlObjectType.Credential;
|
||||
}
|
||||
|
||||
public override Task<InitializeViewResult> InitializeObjectView(Contracts.InitializeViewRequestParams parameters)
|
||||
{
|
||||
// TODO: this is partially implemented only.
|
||||
ConnectionInfo connInfo;
|
||||
this.ConnectionService.TryFindConnection(parameters.ConnectionUri, out connInfo);
|
||||
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
|
||||
|
||||
var credentialInfo = new CredentialInfo();
|
||||
if (!parameters.IsNewObject)
|
||||
{
|
||||
var credential = dataContainer.Server.GetSmoObject(parameters.ObjectUrn) as Credential;
|
||||
credentialInfo.Name = credential.Name;
|
||||
credentialInfo.Identity = credential.Identity;
|
||||
credentialInfo.Id = credential.ID;
|
||||
credentialInfo.DateLastModified = credential.DateLastModified;
|
||||
credentialInfo.CreateDate = credential.CreateDate;
|
||||
credentialInfo.ProviderName = credential.ProviderName;
|
||||
}
|
||||
var viewInfo = new CredentialViewInfo() { ObjectInfo = credentialInfo };
|
||||
var context = new CredentialViewContext(parameters);
|
||||
var result = new InitializeViewResult { ViewInfo = viewInfo, Context = context };
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
public override async Task Save(CredentialViewContext context, CredentialInfo obj)
|
||||
{
|
||||
await ConfigureCredential(context.Parameters.ConnectionUri, obj, ConfigAction.Update, RunType.RunNow);
|
||||
}
|
||||
|
||||
public override Task<string> Script(CredentialViewContext context, CredentialInfo obj)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private Task<Tuple<bool, string>> ConfigureCredential(string ownerUri, CredentialInfo credential, ConfigAction configAction, RunType runType)
|
||||
{
|
||||
return Task<Tuple<bool, string>>.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
ConnectionInfo connInfo;
|
||||
this.ConnectionService.TryFindConnection(ownerUri, out connInfo);
|
||||
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
|
||||
|
||||
using (CredentialActions actions = new CredentialActions(dataContainer, credential, configAction))
|
||||
{
|
||||
var executionHandler = new ExecutonHandler(actions);
|
||||
executionHandler.RunNow(runType, this);
|
||||
}
|
||||
|
||||
return new Tuple<bool, string>(true, string.Empty);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new Tuple<bool, string>(false, ex.ToString());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// 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 System;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// a class for storing various credential properties
|
||||
/// </summary>
|
||||
public class CredentialInfo : SqlObject
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Identity { get; set; }
|
||||
public DateTime DateLastModified { get; set; }
|
||||
public DateTime CreateDate { get; set; }
|
||||
public string ProviderName { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
public class CredentialViewContext : SqlObjectViewContext
|
||||
{
|
||||
public CredentialViewContext(InitializeViewRequestParams parameters) : base(parameters) { }
|
||||
|
||||
public override void Dispose() { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// 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.ObjectManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// The information required to render the credential view.
|
||||
/// </summary>
|
||||
public class CredentialViewInfo : SqlObjectViewInfo
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
//
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Management;
|
||||
using Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// Login object type handler
|
||||
/// </summary>
|
||||
public class LoginHandler : ObjectTypeHandler<LoginInfo, LoginViewContext>
|
||||
{
|
||||
public LoginHandler(ConnectionService connectionService) : base(connectionService) { }
|
||||
|
||||
public override bool CanHandleType(SqlObjectType objectType)
|
||||
{
|
||||
return objectType == SqlObjectType.ServerLevelLogin;
|
||||
}
|
||||
|
||||
public override Task<InitializeViewResult> InitializeObjectView(InitializeViewRequestParams parameters)
|
||||
{
|
||||
ConnectionInfo connInfo;
|
||||
this.ConnectionService.TryFindConnection(parameters.ConnectionUri, out connInfo);
|
||||
if (connInfo == null)
|
||||
{
|
||||
throw new ArgumentException("Invalid ConnectionUri");
|
||||
}
|
||||
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
|
||||
LoginViewInfo loginViewInfo = new LoginViewInfo();
|
||||
|
||||
// TODO cache databases and languages
|
||||
string[] databases = new string[dataContainer.Server.Databases.Count];
|
||||
for (int i = 0; i < dataContainer.Server.Databases.Count; i++)
|
||||
{
|
||||
databases[i] = dataContainer.Server.Databases[i].Name;
|
||||
}
|
||||
|
||||
var languageOptions = LanguageUtils.GetDefaultLanguageOptions(dataContainer);
|
||||
var languageOptionsList = languageOptions.Select(LanguageUtils.FormatLanguageDisplay).ToList();
|
||||
if (parameters.IsNewObject)
|
||||
{
|
||||
languageOptionsList.Insert(0, SR.DefaultLanguagePlaceholder);
|
||||
}
|
||||
string[] languages = languageOptionsList.ToArray();
|
||||
LoginPrototype prototype = parameters.IsNewObject
|
||||
? new LoginPrototype(dataContainer.Server)
|
||||
: new LoginPrototype(dataContainer.Server, dataContainer.Server.GetSmoObject(parameters.ObjectUrn) as Login);
|
||||
|
||||
List<string> loginServerRoles = new List<string>();
|
||||
foreach (string role in prototype.ServerRoles.ServerRoleNames)
|
||||
{
|
||||
if (prototype.ServerRoles.IsMember(role))
|
||||
{
|
||||
loginServerRoles.Add(role);
|
||||
}
|
||||
}
|
||||
|
||||
LoginInfo loginInfo = new LoginInfo()
|
||||
{
|
||||
Name = prototype.LoginName,
|
||||
Password = prototype.SqlPassword,
|
||||
OldPassword = prototype.OldPassword,
|
||||
AuthenticationType = LoginTypeToAuthenticationType(prototype.LoginType),
|
||||
EnforcePasswordExpiration = prototype.EnforceExpiration,
|
||||
EnforcePasswordPolicy = prototype.EnforcePolicy,
|
||||
MustChangePassword = prototype.MustChange,
|
||||
DefaultDatabase = prototype.DefaultDatabase,
|
||||
DefaultLanguage = parameters.IsNewObject ? SR.DefaultLanguagePlaceholder : LanguageUtils.FormatLanguageDisplay(languageOptions.FirstOrDefault(o => o?.Language.Name == prototype.DefaultLanguage || o?.Language.Alias == prototype.DefaultLanguage, null)),
|
||||
ServerRoles = loginServerRoles.ToArray(),
|
||||
ConnectPermission = prototype.WindowsGrantAccess,
|
||||
IsEnabled = !prototype.IsDisabled,
|
||||
IsLockedOut = prototype.IsLockedOut,
|
||||
UserMapping = new ServerLoginDatabaseUserMapping[0]
|
||||
};
|
||||
|
||||
var viewInfo = new LoginViewInfo()
|
||||
{
|
||||
ObjectInfo = loginInfo,
|
||||
SupportWindowsAuthentication = prototype.WindowsAuthSupported,
|
||||
SupportAADAuthentication = prototype.AADAuthSupported,
|
||||
SupportSQLAuthentication = true, // SQL Auth support for login, not necessarily mean SQL Auth support for CONNECT etc.
|
||||
CanEditLockedOutState = !parameters.IsNewObject && prototype.IsLockedOut,
|
||||
Databases = databases,
|
||||
Languages = languages,
|
||||
ServerRoles = prototype.ServerRoles.ServerRoleNames,
|
||||
SupportAdvancedPasswordOptions = dataContainer.Server.DatabaseEngineType == DatabaseEngineType.Standalone || dataContainer.Server.DatabaseEngineEdition == DatabaseEngineEdition.SqlDataWarehouse,
|
||||
SupportAdvancedOptions = dataContainer.Server.DatabaseEngineType == DatabaseEngineType.Standalone || dataContainer.Server.DatabaseEngineEdition == DatabaseEngineEdition.SqlManagedInstance
|
||||
};
|
||||
var context = new LoginViewContext(parameters);
|
||||
return Task.FromResult(new InitializeViewResult()
|
||||
{
|
||||
ViewInfo = viewInfo,
|
||||
Context = context
|
||||
});
|
||||
}
|
||||
|
||||
public override Task Save(LoginViewContext context, LoginInfo obj)
|
||||
{
|
||||
if (context.Parameters.IsNewObject)
|
||||
{
|
||||
this.DoHandleCreateLoginRequest(context, obj, RunType.RunNow);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.DoHandleUpdateLoginRequest(context, obj, RunType.RunNow);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override Task<string> Script(LoginViewContext context, LoginInfo obj)
|
||||
{
|
||||
string script;
|
||||
if (context.Parameters.IsNewObject)
|
||||
{
|
||||
script = this.DoHandleCreateLoginRequest(context, obj, RunType.ScriptToWindow);
|
||||
}
|
||||
else
|
||||
{
|
||||
script = this.DoHandleUpdateLoginRequest(context, obj, RunType.ScriptToWindow);
|
||||
}
|
||||
return Task.FromResult(script);
|
||||
}
|
||||
|
||||
private LoginAuthenticationType LoginTypeToAuthenticationType(LoginType loginType)
|
||||
{
|
||||
switch (loginType)
|
||||
{
|
||||
case LoginType.WindowsUser:
|
||||
case LoginType.WindowsGroup:
|
||||
return LoginAuthenticationType.Windows;
|
||||
case LoginType.SqlLogin:
|
||||
return LoginAuthenticationType.Sql;
|
||||
case LoginType.ExternalUser:
|
||||
case LoginType.ExternalGroup:
|
||||
return LoginAuthenticationType.AAD;
|
||||
default:
|
||||
return LoginAuthenticationType.Others;
|
||||
}
|
||||
}
|
||||
|
||||
private string ConfigureLogin(CDataContainer dataContainer, ConfigAction configAction, RunType runType, LoginPrototype prototype)
|
||||
{
|
||||
string sqlScript = string.Empty;
|
||||
using (var actions = new LoginActions(dataContainer, configAction, prototype))
|
||||
{
|
||||
var executionHandler = new ExecutonHandler(actions);
|
||||
executionHandler.RunNow(runType, this);
|
||||
if (executionHandler.ExecutionResult == ExecutionMode.Failure)
|
||||
{
|
||||
throw executionHandler.ExecutionFailureException;
|
||||
}
|
||||
|
||||
if (runType == RunType.ScriptToWindow)
|
||||
{
|
||||
sqlScript = executionHandler.ScriptTextFromLastRun;
|
||||
}
|
||||
}
|
||||
|
||||
return sqlScript;
|
||||
}
|
||||
|
||||
private string DoHandleUpdateLoginRequest(LoginViewContext context, LoginInfo login, RunType runType)
|
||||
{
|
||||
ConnectionInfo connInfo;
|
||||
this.ConnectionService.TryFindConnection(context.Parameters.ConnectionUri, out connInfo);
|
||||
if (connInfo == null)
|
||||
{
|
||||
throw new ArgumentException("Invalid ConnectionUri");
|
||||
}
|
||||
|
||||
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
|
||||
LoginPrototype prototype = new LoginPrototype(dataContainer.Server, dataContainer.Server.Logins[login.Name]);
|
||||
|
||||
prototype.SqlPassword = login.Password;
|
||||
if (0 != string.Compare(login.DefaultLanguage, SR.DefaultLanguagePlaceholder, StringComparison.Ordinal))
|
||||
{
|
||||
string[] arr = login.DefaultLanguage?.Split(" - ");
|
||||
if (arr != null && arr.Length > 1)
|
||||
{
|
||||
prototype.DefaultLanguage = arr[1];
|
||||
}
|
||||
}
|
||||
prototype.DefaultDatabase = login.DefaultDatabase;
|
||||
prototype.EnforcePolicy = login.EnforcePasswordPolicy;
|
||||
prototype.EnforceExpiration = login.EnforcePasswordPolicy ? login.EnforcePasswordExpiration : false;
|
||||
prototype.IsLockedOut = login.IsLockedOut;
|
||||
prototype.IsDisabled = !login.IsEnabled;
|
||||
prototype.MustChange = login.EnforcePasswordPolicy ? login.MustChangePassword : false;
|
||||
prototype.WindowsGrantAccess = login.ConnectPermission;
|
||||
|
||||
if (prototype.LoginType == SqlServer.Management.Smo.LoginType.SqlLogin)
|
||||
{
|
||||
// check that there is a password
|
||||
// this check is made if policy enforcement is off
|
||||
// with policy turned on we do not display this message, instead we let server
|
||||
// return the error associated with null password (coming from policy) - see bug 124377
|
||||
if (prototype.SqlPassword.Length == 0 && prototype.EnforcePolicy == false)
|
||||
{
|
||||
// raise error here
|
||||
}
|
||||
|
||||
// check that password and confirm password controls' text matches
|
||||
if (0 != string.Compare(prototype.SqlPassword, prototype.SqlPasswordConfirm, StringComparison.Ordinal))
|
||||
{
|
||||
// raise error here
|
||||
}
|
||||
}
|
||||
|
||||
var _ = prototype.ServerRoles.ServerRoleNames;
|
||||
|
||||
foreach (string role in prototype.ServerRoles.ServerRoleNames)
|
||||
{
|
||||
prototype.ServerRoles.SetMember(role, false);
|
||||
}
|
||||
|
||||
foreach (string role in login.ServerRoles)
|
||||
{
|
||||
prototype.ServerRoles.SetMember(role, true);
|
||||
}
|
||||
|
||||
return ConfigureLogin(
|
||||
dataContainer,
|
||||
ConfigAction.Update,
|
||||
runType,
|
||||
prototype);
|
||||
}
|
||||
|
||||
private string DoHandleCreateLoginRequest(LoginViewContext context, LoginInfo login, RunType runType)
|
||||
{
|
||||
ConnectionInfo connInfo;
|
||||
this.ConnectionService.TryFindConnection(context.Parameters.ConnectionUri, out connInfo);
|
||||
|
||||
if (connInfo == null)
|
||||
{
|
||||
throw new ArgumentException("Invalid ConnectionUri");
|
||||
}
|
||||
|
||||
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
|
||||
LoginPrototype prototype = new LoginPrototype(dataContainer.Server, login);
|
||||
|
||||
if (prototype.LoginType == SqlServer.Management.Smo.LoginType.SqlLogin)
|
||||
{
|
||||
// check that there is a password
|
||||
// this check is made if policy enforcement is off
|
||||
// with policy turned on we do not display this message, instead we let server
|
||||
// return the error associated with null password (coming from policy) - see bug 124377
|
||||
if (prototype.SqlPassword.Length == 0 && prototype.EnforcePolicy == false)
|
||||
{
|
||||
// raise error here
|
||||
}
|
||||
|
||||
// check that password and confirm password controls' text matches
|
||||
if (0 != string.Compare(prototype.SqlPassword, prototype.SqlPasswordConfirm, StringComparison.Ordinal))
|
||||
{
|
||||
// raise error here
|
||||
}
|
||||
}
|
||||
|
||||
// TODO move this to LoginData
|
||||
// TODO support role assignment for Azure
|
||||
foreach (string role in login.ServerRoles ?? Enumerable.Empty<string>())
|
||||
{
|
||||
prototype.ServerRoles.SetMember(role, true);
|
||||
}
|
||||
|
||||
return ConfigureLogin(dataContainer, ConfigAction.Create, runType, prototype);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// 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 System.Runtime.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum LoginAuthenticationType
|
||||
{
|
||||
[EnumMember(Value = "Windows")]
|
||||
Windows,
|
||||
[EnumMember(Value = "Sql")]
|
||||
Sql,
|
||||
[EnumMember(Value = "AAD")]
|
||||
AAD,
|
||||
[EnumMember(Value = "Others")]
|
||||
Others
|
||||
}
|
||||
|
||||
public class ServerLoginDatabaseUserMapping
|
||||
{
|
||||
public string Database { get; set; }
|
||||
public string User { get; set; }
|
||||
public string DefaultSchema { get; set; }
|
||||
public string[] DatabaseRoles { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// a class for storing various login properties
|
||||
/// </summary>
|
||||
public class LoginInfo : SqlObject
|
||||
{
|
||||
public LoginAuthenticationType AuthenticationType { get; set; }
|
||||
|
||||
public bool WindowsGrantAccess { get; set; }
|
||||
|
||||
public bool MustChangePassword { get; set; }
|
||||
|
||||
public bool IsEnabled { get; set; }
|
||||
public bool ConnectPermission { get; set; }
|
||||
|
||||
public bool IsLockedOut { get; set; }
|
||||
|
||||
public bool EnforcePasswordPolicy { get; set; }
|
||||
|
||||
public bool EnforcePasswordExpiration { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
|
||||
public string OldPassword { get; set; }
|
||||
|
||||
public string DefaultLanguage { get; set; }
|
||||
|
||||
public string DefaultDatabase { get; set; }
|
||||
|
||||
public string[] ServerRoles { get; set; }
|
||||
|
||||
public ServerLoginDatabaseUserMapping[] UserMapping;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// 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.ObjectManagement
|
||||
{
|
||||
public class LoginViewContext : SqlObjectViewContext
|
||||
{
|
||||
public LoginViewContext(Contracts.InitializeViewRequestParams parameters) : base(parameters)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
#nullable disable
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
public class LoginViewInfo : SqlObjectViewInfo
|
||||
{
|
||||
public bool SupportWindowsAuthentication { get; set; }
|
||||
public bool SupportAADAuthentication { get; set; }
|
||||
public bool SupportSQLAuthentication { get; set; }
|
||||
public bool CanEditLockedOutState { get; set; }
|
||||
public string[] Databases;
|
||||
public string[] Languages;
|
||||
public string[] ServerRoles;
|
||||
public bool SupportAdvancedPasswordOptions;
|
||||
public bool SupportAdvancedOptions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,653 @@
|
||||
//
|
||||
// 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.SqlServer.Management.Sdk.Sfc;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using System.Xml;
|
||||
using System.Data;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlTools.ServiceLayer.Management;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// AppRoleGeneral - main app role page
|
||||
/// </summary>
|
||||
internal class AppRoleGeneral
|
||||
{
|
||||
#region Members
|
||||
|
||||
/// <summary>
|
||||
/// data container member that contains data specific information like
|
||||
/// connection infor, SMO server object or an AMO server object as well
|
||||
/// as a hash table where one can manipulate custom data
|
||||
/// </summary>
|
||||
private CDataContainer dataContainer = null;
|
||||
|
||||
//SMO Server connection that MUST be used for all enumerator calls
|
||||
//We'll get this object out of CDataContainer, that must be initialized
|
||||
//property by the initialization code
|
||||
private ServerConnection serverConnection;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Trace support
|
||||
private const string componentName = "AppRoleGeneral";
|
||||
|
||||
public string ComponentName
|
||||
{
|
||||
get
|
||||
{
|
||||
return componentName;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constants - urn fields, etc...
|
||||
private const string ownerField = "Owner";
|
||||
private const string defaultSchemaField = "DefaultSchema";
|
||||
private const string schemaNameField = "Name";
|
||||
private const string schemaOwnerField = "Owner";
|
||||
private const string memberNameField = "Name";
|
||||
private const string memberUrnField = "Urn";
|
||||
#endregion
|
||||
|
||||
#region Constants - grid columns positions, etc...
|
||||
private const int colSchemasChecked = 0;
|
||||
private const int colSchemasOwnedSchemas = 1;
|
||||
|
||||
private const int colMembershipBitmap = 0;
|
||||
private const int colMembershipRoleMembers = 1;
|
||||
|
||||
private const int sizeCheckboxColumn = 20;
|
||||
private const int sizeBitmapColumn = 20;
|
||||
#endregion
|
||||
|
||||
#region Non-UI variables
|
||||
private System.Xml.XmlDocument document = null;
|
||||
|
||||
// info extracted from context
|
||||
private string serverName;
|
||||
private string databaseName;
|
||||
private string approleName;
|
||||
private bool passwordChanged = false;
|
||||
|
||||
|
||||
// initial values loaded from server
|
||||
private string initialDefaultSchema;
|
||||
|
||||
|
||||
private bool isYukonOrLater;
|
||||
#endregion
|
||||
|
||||
|
||||
#region Properties: CreateNew/Properties mode
|
||||
private bool IsPropertiesMode
|
||||
{
|
||||
get
|
||||
{
|
||||
return (approleName != null) && (approleName.Trim().Length != 0);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructors / Dispose
|
||||
public AppRoleGeneral()
|
||||
{
|
||||
// This call is required by the Windows.Forms Form Designer.
|
||||
// InitializeComponent();
|
||||
}
|
||||
|
||||
public AppRoleGeneral(CDataContainer context)
|
||||
{
|
||||
dataContainer = context;
|
||||
|
||||
if (dataContainer != null)
|
||||
{
|
||||
document = dataContainer.Document;
|
||||
}
|
||||
else
|
||||
{
|
||||
document = null;
|
||||
}
|
||||
|
||||
this.isYukonOrLater = (9 <= context.Server.ConnectionContext.ServerVersion.Major);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
// protected override void Dispose( bool disposing )
|
||||
// {
|
||||
// if ( disposing )
|
||||
// {
|
||||
// if (components != null)
|
||||
// {
|
||||
// components.Dispose();
|
||||
// }
|
||||
// }
|
||||
// base.Dispose( disposing );
|
||||
// }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation: LoadData(), InitProp(), SendDataToServer()
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// LoadData
|
||||
///
|
||||
/// loads connection parameters from an xml
|
||||
/// </summary>
|
||||
/// <param name="doc"></param>
|
||||
private void LoadData(XmlDocument doc)
|
||||
{
|
||||
// STrace.Params(ComponentName, "LoadData", "XmlDocument doc=\"{0}\"", doc.OuterXml);
|
||||
|
||||
STParameters param;
|
||||
bool bStatus;
|
||||
|
||||
param = new STParameters();
|
||||
|
||||
param.SetDocument(doc);
|
||||
|
||||
bStatus = param.GetParam("servername", ref this.serverName);
|
||||
bStatus = param.GetParam("database", ref this.databaseName);
|
||||
bStatus = param.GetParam("applicationrole", ref this.approleName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// InitProp
|
||||
///
|
||||
/// talks with enumerator an retrieves info
|
||||
/// </summary>
|
||||
private void InitProp()
|
||||
{
|
||||
// STrace.Params(ComponentName, "InitProp", "", null);
|
||||
|
||||
System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(this.serverName), "serverName is empty");
|
||||
System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(this.databaseName), "databaseName is empty");
|
||||
|
||||
passwordChanged = false;
|
||||
|
||||
// InitializeBitmapAndIcons(); // bitmapMember
|
||||
|
||||
if (this.IsPropertiesMode == true)
|
||||
{
|
||||
// initialize from enumerator in properties mode
|
||||
// STrace.Assert(!string.IsNullOrWhiteSpace(this.approleName), "approleName is empty");
|
||||
|
||||
// this.textBoxRoleName.Text = this.approleName;
|
||||
|
||||
if (this.isYukonOrLater)
|
||||
{
|
||||
// get the default schema
|
||||
|
||||
// STrace.Assert(this.DataContainer.ObjectUrn.Length != 0, "object urn is empty");
|
||||
|
||||
Enumerator enumerator = new Enumerator();
|
||||
Request request = new Request();
|
||||
request.Urn = this.dataContainer.ObjectUrn;
|
||||
request.Fields = new String[] { AppRoleGeneral.defaultSchemaField };
|
||||
|
||||
DataTable dataTable = enumerator.Process(serverConnection, request);
|
||||
// STrace.Assert(dataTable != null, "dataTable is null");
|
||||
// STrace.Assert(dataTable.Rows.Count == 1, "unexpected number of rows in dataTable");
|
||||
|
||||
if (dataTable.Rows.Count == 0)
|
||||
{
|
||||
throw new Exception("AppRoleSR.ErrorAppRoleNotFound");
|
||||
}
|
||||
|
||||
DataRow dataRow = dataTable.Rows[0];
|
||||
this.initialDefaultSchema = Convert.ToString(dataRow[AppRoleGeneral.defaultSchemaField], System.Globalization.CultureInfo.InvariantCulture);
|
||||
|
||||
// this.textBoxDefaultSchema.Text = this.initialDefaultSchema;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// initialize with empty values in create new mode
|
||||
// this.textBoxRoleName.Text = String.Empty;
|
||||
// this.textBoxDefaultSchema.Text = this.initialDefaultSchema;
|
||||
|
||||
// this.textBoxPasword.Text = String.Empty;
|
||||
// this.textBoxConfirmPassword.Text = String.Empty;
|
||||
}
|
||||
|
||||
LoadSchemas();
|
||||
// InitializeSchemasGridColumns();
|
||||
// FillSchemasGrid();
|
||||
|
||||
LoadMembership();
|
||||
// InitializeMembershipGridColumns();
|
||||
// FillMembershipGrid();
|
||||
|
||||
// dont display the membership controls - app roles dont support members
|
||||
// HideMembership();
|
||||
|
||||
// update UI enable/disable controls
|
||||
// EnableDisableControls();
|
||||
}
|
||||
|
||||
// private void HideMembership()
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// this.SuspendLayout();
|
||||
// this.panelSchema.SuspendLayout();
|
||||
|
||||
// this.panelSchema.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right | AnchorStyles.Left;
|
||||
// this.panelSchema.Size = new Size
|
||||
// (
|
||||
// this.panelSchema.Size.Width
|
||||
// ,
|
||||
// this.panelMembership.Location.Y + this.panelMembership.Size.Height -
|
||||
// this.panelSchema.Location.Y
|
||||
// );
|
||||
|
||||
// this.panelMembership.Visible = false;
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// this.panelSchema.ResumeLayout();
|
||||
// this.ResumeLayout();
|
||||
|
||||
// this.gridSchemasOwned.Refresh();
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
private string _selectedDefaultSchema;
|
||||
// private SqlSecureString _textBoxPaswordText;
|
||||
// private SqlSecureString _textBoxConfirmPasswordText;
|
||||
private string _textBoxRoleNameText;
|
||||
|
||||
/// <summary>
|
||||
/// Called to validate all date in controls and save them in
|
||||
/// temproary storage to be used when OnRunNow is called
|
||||
/// </summary>
|
||||
// public override void OnGatherUiInformation(RunType runType)
|
||||
// {
|
||||
// base.OnGatherUiInformation(runType);
|
||||
|
||||
// try
|
||||
// {
|
||||
// base.ExecutionMode = ExecutionMode.Success;
|
||||
|
||||
// _selectedDefaultSchema = this.textBoxDefaultSchema.Text;
|
||||
// _textBoxPaswordText = textBoxPasword.Text;
|
||||
// _textBoxConfirmPasswordText = textBoxConfirmPassword.Text;
|
||||
// _textBoxRoleNameText = textBoxRoleName.Text;
|
||||
// }
|
||||
// catch (Exception exception)
|
||||
// {
|
||||
// DisplayExceptionMessage(exception);
|
||||
// base.ExecutionMode = ExecutionMode.Failure;
|
||||
// }
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// SendDataToServer
|
||||
///
|
||||
/// here we talk with server via smo and do the actual data changing
|
||||
/// </summary>
|
||||
private void SendDataToServer()
|
||||
{
|
||||
// STrace.Params(ComponentName, "SendDataToServer", "", null);
|
||||
|
||||
System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(this.databaseName), "databaseName is empty");
|
||||
|
||||
Microsoft.SqlServer.Management.Smo.Server srv = this.dataContainer.Server;
|
||||
System.Diagnostics.Debug.Assert(srv != null, "server object is null");
|
||||
|
||||
Database db = srv.Databases[this.databaseName];
|
||||
System.Diagnostics.Debug.Assert(db != null, "database object is null");
|
||||
|
||||
if (this.IsPropertiesMode == true) // in properties mode -> alter role
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(this.approleName), "approleName is empty");
|
||||
|
||||
ApplicationRole approle = db.ApplicationRoles[this.approleName];
|
||||
System.Diagnostics.Debug.Assert(approle != null, "approle object is null");
|
||||
|
||||
bool alterRequired = false;
|
||||
|
||||
if (this.isYukonOrLater && _selectedDefaultSchema != this.initialDefaultSchema)
|
||||
{
|
||||
approle.DefaultSchema = _selectedDefaultSchema;
|
||||
alterRequired = true;
|
||||
}
|
||||
|
||||
if (passwordChanged == true)
|
||||
{
|
||||
approle.ChangePassword((string)"_textBoxPaswordText");
|
||||
}
|
||||
|
||||
if (alterRequired == true)
|
||||
{
|
||||
approle.Alter();
|
||||
}
|
||||
|
||||
SendToServerSchemaOwnershipChanges(db, approle);
|
||||
SendToServerMembershipChanges(db, approle);
|
||||
}
|
||||
else // not in properties mode -> create role
|
||||
{
|
||||
ApplicationRole approle = new ApplicationRole(db, _textBoxRoleNameText);
|
||||
if (this.isYukonOrLater && _selectedDefaultSchema.Length > 0)
|
||||
{
|
||||
approle.DefaultSchema = _selectedDefaultSchema;
|
||||
}
|
||||
|
||||
approle.Create((string)"_textBoxPaswordText");
|
||||
|
||||
SendToServerSchemaOwnershipChanges(db, approle);
|
||||
SendToServerMembershipChanges(db, approle);
|
||||
|
||||
this.dataContainer.SqlDialogSubject = approle; // needed by extended properties page
|
||||
}
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Schemas - general operations with ...
|
||||
HybridDictionary dictSchemas = null;
|
||||
StringCollection schemaNames = null;
|
||||
/// <summary>
|
||||
/// loads initial schemas from server together with information about the schema owner
|
||||
/// </summary>
|
||||
private void LoadSchemas()
|
||||
{
|
||||
if (this.isYukonOrLater)
|
||||
{
|
||||
this.dictSchemas = new HybridDictionary();
|
||||
this.schemaNames = new StringCollection();
|
||||
|
||||
Enumerator en = new Enumerator();
|
||||
Request req = new Request();
|
||||
req.Fields = new String[] { AppRoleGeneral.schemaNameField, AppRoleGeneral.schemaOwnerField };
|
||||
req.Urn = "Server/Database[@Name='" + Urn.EscapeString(this.databaseName) + "']/Schema";
|
||||
req.OrderByList = new OrderBy[] { new OrderBy("Name", OrderBy.Direction.Asc) };
|
||||
|
||||
DataTable dt = en.Process(serverConnection, req);
|
||||
// STrace.Assert((dt != null) && (dt.Rows.Count > 0), "No rows returned from schema enumerator");
|
||||
|
||||
foreach (DataRow dr in dt.Rows)
|
||||
{
|
||||
string name = Convert.ToString(dr[AppRoleGeneral.schemaNameField], System.Globalization.CultureInfo.InvariantCulture);
|
||||
string owner = Convert.ToString(dr[AppRoleGeneral.schemaOwnerField], System.Globalization.CultureInfo.InvariantCulture);
|
||||
|
||||
dictSchemas.Add(name, owner);
|
||||
schemaNames.Add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// sends to server changes related to schema ownership
|
||||
/// </summary>
|
||||
private void SendToServerSchemaOwnershipChanges(Database db, ApplicationRole approle)
|
||||
{
|
||||
if (this.isYukonOrLater)
|
||||
{
|
||||
// DlgGridControl grid = this.gridSchemasOwned;
|
||||
|
||||
for (int i = 0; i < 1; ++i)
|
||||
{
|
||||
string name = "grid.GetCellInfo(i, colSchemasOwnedSchemas).CellData.ToString()";
|
||||
object o = dictSchemas[name];
|
||||
|
||||
System.Diagnostics.Debug.Assert(o != null, "schema object is null");
|
||||
|
||||
// bool currentlyOwned = IsEmbeededCheckboxChecked(grid, i, colSchemasChecked);
|
||||
bool currentlyOwned = false;
|
||||
|
||||
if (IsPropertiesMode == true)
|
||||
{
|
||||
bool wasOwned = (o.ToString() == approleName);
|
||||
|
||||
if (currentlyOwned != wasOwned)
|
||||
{
|
||||
if (currentlyOwned == true)
|
||||
{
|
||||
Schema schema = db.Schemas[name];
|
||||
schema.Owner = approle.Name;
|
||||
schema.Alter();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* we cannot not renounce ownership
|
||||
Schema schema = db.Schemas[name];
|
||||
schema.Owner = null;
|
||||
schema.Alter();
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentlyOwned == true)
|
||||
{
|
||||
Schema schema = db.Schemas[name];
|
||||
schema.Owner = approle.Name;
|
||||
schema.Alter();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// private void gridSchemasOwned_MouseButtonClicked(object sender, Microsoft.SqlServer.Management.UI.Grid.MouseButtonClickedEventArgs args)
|
||||
// {
|
||||
// if (args.Button != MouseButtons.Left)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// int rowno = Convert.ToInt32(args.RowIndex);
|
||||
// int colno = Convert.ToInt32(args.ColumnIndex);
|
||||
|
||||
// switch (colno)
|
||||
// {
|
||||
// case colSchemasChecked:
|
||||
// FlipCheckbox(gridSchemasOwned, rowno, colno);
|
||||
// break;
|
||||
// default: // else do default action: e.g. edit - open combo - etc ...
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Membership - general operations with ...
|
||||
System.Collections.Specialized.HybridDictionary dictMembership = null;
|
||||
|
||||
/// <summary>
|
||||
/// loads from server initial membership information
|
||||
/// </summary>
|
||||
private void LoadMembership()
|
||||
{
|
||||
dictMembership = new System.Collections.Specialized.HybridDictionary();
|
||||
if (IsPropertiesMode == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Enumerator en = new Enumerator();
|
||||
Request req = new Request();
|
||||
req.Fields = new String[] { AppRoleGeneral.memberNameField, AppRoleGeneral.memberUrnField };
|
||||
req.Urn = "Server/Database[@Name='" + Urn.EscapeString(this.databaseName) + "']/ApplicationRole[@Name='" + Urn.EscapeString(this.approleName) + "']/Member";
|
||||
|
||||
try
|
||||
{
|
||||
DataTable dt = en.Process(serverConnection, req);
|
||||
System.Diagnostics.Debug.Assert(dt != null, "No results returned from membership query");
|
||||
|
||||
foreach (DataRow dr in dt.Rows)
|
||||
{
|
||||
string name = Convert.ToString(dr[AppRoleGeneral.memberNameField], System.Globalization.CultureInfo.InvariantCulture);
|
||||
string urn = Convert.ToString(dr[AppRoleGeneral.memberUrnField], System.Globalization.CultureInfo.InvariantCulture);
|
||||
|
||||
dictMembership.Add(name, urn);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// initialize grid column headers, but not the content
|
||||
/// </summary>
|
||||
// private void InitializeMembershipGridColumns()
|
||||
// {
|
||||
// Microsoft.SqlServer.Management.UI.Grid.DlgGridControl grid = this.gridRoleMembership;
|
||||
|
||||
// if (grid.RowsNumber != 0)
|
||||
// {
|
||||
// grid.DeleteAllRows();
|
||||
// }
|
||||
|
||||
// while (grid.ColumnsNumber != 0)
|
||||
// {
|
||||
// grid.DeleteColumn(0);
|
||||
// }
|
||||
|
||||
// GridColumnInfo colInfo = null;
|
||||
|
||||
// // bitmap member type
|
||||
// colInfo = new GridColumnInfo();
|
||||
// colInfo.ColumnWidth = sizeBitmapColumn;
|
||||
// colInfo.WidthType = GridColumnWidthType.InPixels;
|
||||
// colInfo.ColumnType = GridColumnType.Bitmap;
|
||||
// grid.AddColumn(colInfo);
|
||||
|
||||
// // member name
|
||||
// colInfo = new GridColumnInfo();
|
||||
// colInfo.ColumnWidth = grid.Width - sizeBitmapColumn - 2;
|
||||
// colInfo.WidthType = GridColumnWidthType.InPixels;
|
||||
// grid.AddColumn(colInfo);
|
||||
|
||||
// grid.SetHeaderInfo(colMembershipRoleMembers, AppRoleSR.HeaderRoleMembers, null);
|
||||
|
||||
// grid.SelectionType = GridSelectionType.SingleRow;
|
||||
// grid.UpdateGrid();
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// fills the membership grid with data (bitmaps, names, etc)
|
||||
/// </summary>
|
||||
// private void FillMembershipGrid()
|
||||
// {
|
||||
// Microsoft.SqlServer.Management.UI.Grid.DlgGridControl grid = this.gridRoleMembership;
|
||||
|
||||
// grid.DeleteAllRows();
|
||||
// foreach (DictionaryEntry de in dictMembership)
|
||||
// {
|
||||
// GridCellCollection row = new GridCellCollection();
|
||||
// GridCell cell = null;
|
||||
|
||||
// string name = de.Key.ToString();
|
||||
|
||||
// cell = new GridCell(bitmapMember); row.Add(cell); // compute type based on urn
|
||||
// cell = new GridCell(name); row.Add(cell);
|
||||
|
||||
// // row.Tag = urn == de.Value.ToString();
|
||||
|
||||
// grid.AddRow(row);
|
||||
// }
|
||||
|
||||
// if (grid.RowsNumber > 0)
|
||||
// {
|
||||
// grid.SelectedRow = 0;
|
||||
// }
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// sends to server user changes related to membership
|
||||
/// </summary>
|
||||
private void SendToServerMembershipChanges(Database db, ApplicationRole approle)
|
||||
{
|
||||
// DlgGridControl grid = this.gridRoleMembership;
|
||||
|
||||
if (IsPropertiesMode == true)
|
||||
{
|
||||
// members to add
|
||||
for (int i = 0; i < 1; ++i)
|
||||
{
|
||||
string name = "grid.GetCellInfo(i, colMembershipRoleMembers).CellData.ToString()";
|
||||
bool nameExistedInitially = dictMembership.Contains(name);
|
||||
|
||||
if (nameExistedInitially == false)
|
||||
{
|
||||
// need SMO for: role.Members.Add();
|
||||
}
|
||||
}
|
||||
// members to drop
|
||||
foreach (DictionaryEntry de in dictMembership)
|
||||
{
|
||||
if (FoundInMembershipGrid(de.Key.ToString(), de.Value.ToString()) == false)
|
||||
{
|
||||
// need SMO for: role.Members.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// add only
|
||||
for (int i = 0; i < 1; ++i)
|
||||
{
|
||||
string name = "grid.GetCellInfo(i, colMembershipRoleMembers).CellData.ToString()";
|
||||
// need SMO for: role.Members.Add();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// lookup in membership grid to see if user added a name
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="urn"></param>
|
||||
/// <returns></returns>
|
||||
private bool FoundInMembershipGrid(string name, string urn)
|
||||
{
|
||||
// DlgGridControl grid = this.gridRoleMembership;
|
||||
|
||||
// for (int i = 0; i < grid.RowsNumber; ++i)
|
||||
// {
|
||||
// string currentName = grid.GetCellInfo(i, colMembershipRoleMembers).CellData.ToString();
|
||||
// if (name == currentName)
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// 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.ServiceLayer.Management;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
internal class CredentialActions : ManagementActionBase
|
||||
{
|
||||
|
||||
#region Constants
|
||||
private const int MAX_SQL_SYS_NAME_LENGTH = 128; // max sql sys name length
|
||||
#endregion
|
||||
|
||||
#region Variables
|
||||
private CredentialData credentialData = null;
|
||||
private CredentialInfo credential;
|
||||
private ConfigAction configAction;
|
||||
#endregion
|
||||
|
||||
#region Constructors / Dispose
|
||||
/// <summary>
|
||||
/// required when loading from Object Explorer context
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
public CredentialActions(
|
||||
CDataContainer context,
|
||||
CredentialInfo credential,
|
||||
ConfigAction configAction)
|
||||
{
|
||||
this.DataContainer = context;
|
||||
this.credential = credential;
|
||||
this.configAction = configAction;
|
||||
|
||||
this.credentialData = new CredentialData(context, credential);
|
||||
this.credentialData.Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (disposing == true)
|
||||
{
|
||||
if (this.credentialData != null)
|
||||
{
|
||||
this.credentialData.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// called on background thread by the framework to execute the action
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
public override void OnRunNow(object sender)
|
||||
{
|
||||
if (this.configAction == ConfigAction.Drop)
|
||||
{
|
||||
if (this.credentialData.Credential != null)
|
||||
{
|
||||
this.credentialData.Credential.DropIfExists();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.credentialData.SendDataToServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
//
|
||||
// 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 System;
|
||||
using System.Security;
|
||||
using Microsoft.SqlServer.Management.Sdk.Sfc;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlTools.ServiceLayer.Management;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
internal class CredentialData : IDisposable
|
||||
{
|
||||
#region Properties
|
||||
private string credentialName = string.Empty;
|
||||
public string CredentialName
|
||||
{
|
||||
get { return credentialName; }
|
||||
set { credentialName = value; }
|
||||
}
|
||||
|
||||
private string credentialIdentity = string.Empty;
|
||||
public string CredentialIdentity
|
||||
{
|
||||
get { return credentialIdentity; }
|
||||
set { credentialIdentity = value; }
|
||||
}
|
||||
|
||||
private SecureString securePassword;
|
||||
public SecureString SecurePassword
|
||||
{
|
||||
get { return securePassword; }
|
||||
set
|
||||
{
|
||||
securePassword = value;
|
||||
PasswordWasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool isPropertiesMode = false;
|
||||
public bool IsPropertiesMode
|
||||
{
|
||||
get
|
||||
{
|
||||
return isPropertiesMode;
|
||||
}
|
||||
}
|
||||
|
||||
private bool passwordWasChanged = false;
|
||||
public bool PasswordWasChanged
|
||||
{
|
||||
get { return passwordWasChanged; }
|
||||
set { passwordWasChanged = value; }
|
||||
}
|
||||
|
||||
private bool isEncryptionByProvider = false;
|
||||
public bool IsEncryptionByProvider
|
||||
{
|
||||
get { return isEncryptionByProvider; }
|
||||
set { isEncryptionByProvider = value; }
|
||||
}
|
||||
|
||||
private string providerName = string.Empty;
|
||||
public string ProviderName
|
||||
{
|
||||
get { return providerName; }
|
||||
set { providerName = value; }
|
||||
}
|
||||
|
||||
public Microsoft.SqlServer.Management.Smo.Credential Credential
|
||||
{
|
||||
get
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(this.CredentialName)
|
||||
? this.Context.Server.Credentials[this.CredentialName]
|
||||
: null;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
private const string ENUMERATOR_FIELD_IDENTITY = "Identity";
|
||||
private const string ENUMERATOR_FIELD_PROVIDER_NAME = "ProviderName";
|
||||
|
||||
#region Constructor
|
||||
private CDataContainer context = null;
|
||||
private CDataContainer Context { get { return context; } set { context = value; } }
|
||||
private CredentialInfo credential;
|
||||
|
||||
public CredentialData(CDataContainer context, CredentialInfo credential)
|
||||
{
|
||||
this.Context = context;
|
||||
this.credential = credential;
|
||||
LoadDataFromXmlContext();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Implementation: LoadDataFromXmlContext(), LoadDataFromServer(), SendDataToServer()
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
LoadDataFromXmlContext();
|
||||
LoadDataFromServer();
|
||||
|
||||
// apply CredentialInfo properties
|
||||
this.CredentialIdentity = this.credential.Identity;
|
||||
this.CredentialName = this.credential.Name;
|
||||
this.SecurePassword = CDataContainer.BuildSecureStringFromPassword(string.Empty);
|
||||
|
||||
// need to update only during create time
|
||||
this.IsEncryptionByProvider = false;
|
||||
if (this.IsEncryptionByProvider)
|
||||
{
|
||||
this.ProviderName = string.Empty; // lookup provider here
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ProviderName = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LoadDataFromXmlContext
|
||||
///
|
||||
/// loads context information from xml - e.g. name of object
|
||||
/// </summary>
|
||||
private void LoadDataFromXmlContext()
|
||||
{
|
||||
this.CredentialName = this.Context.GetDocumentPropertyString("credential");
|
||||
this.isPropertiesMode = (this.CredentialName != null) && (this.CredentialName.Length > 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LoadDataFromServer
|
||||
///
|
||||
/// talks with enumerator an retrieves info that is not available in the xml context but on server
|
||||
/// </summary>
|
||||
private void LoadDataFromServer()
|
||||
{
|
||||
if (this.IsPropertiesMode == true)
|
||||
{
|
||||
bool isKatmaiAndNotMatrix = (this.context.Server.Version.Major >= 10);
|
||||
|
||||
Urn urn = new Urn("Server/Credential[@Name='" + Urn.EscapeString(this.CredentialName) + "']");
|
||||
string[] fields;
|
||||
if (isKatmaiAndNotMatrix)
|
||||
{
|
||||
fields = new string[] { ENUMERATOR_FIELD_IDENTITY, ENUMERATOR_FIELD_PROVIDER_NAME };
|
||||
}
|
||||
else
|
||||
{
|
||||
fields = new string[] { ENUMERATOR_FIELD_IDENTITY };
|
||||
}
|
||||
Request r = new Request(urn, fields);
|
||||
System.Data.DataTable dataTable = Enumerator.GetData(this.Context.ConnectionInfo, r);
|
||||
|
||||
if (dataTable.Rows.Count == 0)
|
||||
{
|
||||
throw new Exception(SR.CredentialNoLongerExists);
|
||||
}
|
||||
|
||||
System.Data.DataRow dataRow = dataTable.Rows[0];
|
||||
this.CredentialIdentity = Convert.ToString(dataRow[ENUMERATOR_FIELD_IDENTITY], System.Globalization.CultureInfo.InvariantCulture);
|
||||
|
||||
if (isKatmaiAndNotMatrix)
|
||||
{
|
||||
this.providerName = Convert.ToString(dataRow[ENUMERATOR_FIELD_PROVIDER_NAME], System.Globalization.CultureInfo.InvariantCulture);
|
||||
this.isEncryptionByProvider = !string.IsNullOrEmpty(providerName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.CredentialName = string.Empty;
|
||||
this.CredentialIdentity = string.Empty;
|
||||
this.providerName = string.Empty;
|
||||
this.isEncryptionByProvider = false;
|
||||
}
|
||||
|
||||
this.SecurePassword = new SecureString();
|
||||
this.PasswordWasChanged = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SendDataToServer
|
||||
///
|
||||
/// here we talk with server via smo and do the actual data changing
|
||||
/// </summary>
|
||||
public void SendDataToServer()
|
||||
{
|
||||
if (this.IsPropertiesMode == true)
|
||||
{
|
||||
SendToServerAlterCredential();
|
||||
}
|
||||
else
|
||||
{
|
||||
SendToServerCreateCredential();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// create credential - create mode
|
||||
/// </summary>
|
||||
private void SendToServerCreateCredential()
|
||||
{
|
||||
Microsoft.SqlServer.Management.Smo.Credential smoCredential = new Microsoft.SqlServer.Management.Smo.Credential(
|
||||
this.Context.Server,
|
||||
this.CredentialName);
|
||||
if (this.isEncryptionByProvider)
|
||||
{
|
||||
smoCredential.MappedClassType = MappedClassType.CryptographicProvider;
|
||||
smoCredential.ProviderName = this.providerName;
|
||||
}
|
||||
smoCredential.Create(this.CredentialIdentity, this.SecurePassword.ToString());
|
||||
GC.Collect(); // this.SecurePassword.ToString() just created an immutable string that lives in memory
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// alter credential - properties mode
|
||||
/// </summary>
|
||||
private void SendToServerAlterCredential()
|
||||
{
|
||||
Microsoft.SqlServer.Management.Smo.Credential smoCredential = this.Context.Server.Credentials[this.CredentialName];
|
||||
|
||||
if (smoCredential != null)
|
||||
{
|
||||
if (this.PasswordWasChanged == false)
|
||||
{
|
||||
if (smoCredential.Identity != this.CredentialIdentity)
|
||||
{
|
||||
smoCredential.Alter(this.CredentialIdentity);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
smoCredential.Alter(this.CredentialIdentity, this.SecurePassword.ToString());
|
||||
GC.Collect(); // this.SecurePassword.ToString() just created an immutable string that lives in memory
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(SR.CredentialNoLongerExists);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.SecurePassword.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,890 @@
|
||||
//
|
||||
// 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.SqlServer.Management.Sdk.Sfc;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using System.Xml;
|
||||
using System.Data;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlTools.ServiceLayer.Management;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// DatabaseRoleGeneral - main panel for database role
|
||||
/// </summary>
|
||||
internal class DatabaseRoleGeneral
|
||||
{
|
||||
#region Members
|
||||
/// <summary>
|
||||
/// data container member that contains data specific information like
|
||||
/// connection infor, SMO server object or an AMO server object as well
|
||||
/// as a hash table where one can manipulate custom data
|
||||
/// </summary>
|
||||
private CDataContainer dataContainer = null;
|
||||
|
||||
//SMO Server connection that MUST be used for all enumerator calls
|
||||
//We'll get this object out of CDataContainer, that must be initialized
|
||||
//property by the initialization code
|
||||
private ServerConnection serverConnection;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Trace support
|
||||
private const string componentName = "DatabaseRoleGeneral";
|
||||
|
||||
public string ComponentName
|
||||
{
|
||||
get
|
||||
{
|
||||
return componentName;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
private class SchemaOwnership
|
||||
{
|
||||
public bool initiallyOwned;
|
||||
public bool currentlyOwned;
|
||||
|
||||
public SchemaOwnership(bool initiallyOwned)
|
||||
{
|
||||
this.initiallyOwned = initiallyOwned;
|
||||
this.currentlyOwned = initiallyOwned;
|
||||
}
|
||||
}
|
||||
|
||||
private class RoleMembership
|
||||
{
|
||||
public bool initiallyAMember;
|
||||
public bool currentlyAMember;
|
||||
|
||||
public RoleMembership(bool initiallyAMember)
|
||||
{
|
||||
this.initiallyAMember = initiallyAMember;
|
||||
this.currentlyAMember = initiallyAMember;
|
||||
}
|
||||
|
||||
public RoleMembership(bool initiallyAMember, bool currentlyAMember)
|
||||
{
|
||||
this.initiallyAMember = initiallyAMember;
|
||||
this.currentlyAMember = currentlyAMember;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region Constants - urn fields, etc...
|
||||
private const string ownerField = "Owner";
|
||||
private const string schemaOwnerField = "Owner";
|
||||
private const string schemaNameField = "Name";
|
||||
private const string memberNameField = "Name";
|
||||
private const string memberUrnField = "Urn";
|
||||
#endregion
|
||||
|
||||
#region Constants - grid columns positions, etc...
|
||||
private const int colSchemasChecked = 0;
|
||||
private const int colSchemasOwnedSchemas = 1;
|
||||
|
||||
private const int colMembershipBitmap = 0;
|
||||
private const int colMembershipRoleMembers = 1;
|
||||
|
||||
private const int sizeCheckboxColumn = 20;
|
||||
private const int sizeBitmapColumn = 20;
|
||||
#endregion
|
||||
|
||||
#region Non-UI variables
|
||||
|
||||
private System.Xml.XmlDocument document = null;
|
||||
|
||||
// info extracted from context
|
||||
private string serverName;
|
||||
private string databaseName;
|
||||
private string dbroleName;
|
||||
private string dbroleUrn;
|
||||
|
||||
// initial values loaded from server
|
||||
private string initialOwner;
|
||||
|
||||
private string ownerName = String.Empty;
|
||||
private string roleName = String.Empty;
|
||||
private HybridDictionary schemaOwnership = null;
|
||||
private HybridDictionary roleMembers = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties: CreateNew/Properties mode
|
||||
private bool IsPropertiesMode
|
||||
{
|
||||
get
|
||||
{
|
||||
return (dbroleName != null) && (dbroleName.Trim().Length != 0);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructors / Dispose
|
||||
public DatabaseRoleGeneral()
|
||||
{
|
||||
// This call is required by the Windows.Forms Form Designer.
|
||||
// InitializeComponent();
|
||||
}
|
||||
|
||||
public DatabaseRoleGeneral(CDataContainer context)
|
||||
{
|
||||
// InitializeComponent();
|
||||
dataContainer = context;
|
||||
|
||||
if (dataContainer != null)
|
||||
{
|
||||
document = dataContainer.Document;
|
||||
}
|
||||
else
|
||||
{
|
||||
document = null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation: LoadData(), InitProp(), SendDataToServer()
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// LoadData
|
||||
///
|
||||
/// loads connection parameters from an xml
|
||||
/// </summary>
|
||||
/// <param name="doc"></param>
|
||||
private void LoadData(XmlDocument doc)
|
||||
{
|
||||
// STrace.Params(ComponentName, "LoadData", "XmlDocument doc=\"{0}\"", doc.OuterXml);
|
||||
|
||||
STParameters param;
|
||||
bool bStatus;
|
||||
|
||||
param = new STParameters();
|
||||
|
||||
param.SetDocument(doc);
|
||||
|
||||
bStatus = param.GetParam("servername", ref this.serverName);
|
||||
bStatus = param.GetParam("database", ref this.databaseName);
|
||||
|
||||
bStatus = param.GetParam("role", ref this.dbroleName);
|
||||
bStatus = param.GetParam("urn", ref this.dbroleUrn);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// InitProp
|
||||
///
|
||||
/// talks with enumerator an retrievives info
|
||||
/// </summary>
|
||||
private void InitProp()
|
||||
{
|
||||
// STrace.Params(ComponentName, "InitProp", "", null);
|
||||
|
||||
System.Diagnostics.Debug.Assert(this.serverName != null);
|
||||
System.Diagnostics.Debug.Assert((this.databaseName != null) && (this.databaseName.Trim().Length != 0));
|
||||
|
||||
|
||||
// InitializeSchemasGridColumns();
|
||||
if (this.dataContainer.Server.Information.Version.Major >= 9)
|
||||
{
|
||||
LoadSchemas();
|
||||
// FillSchemasGrid();
|
||||
}
|
||||
else
|
||||
{
|
||||
// panelSchema.Enabled = false;
|
||||
}
|
||||
|
||||
LoadMembership();
|
||||
// InitializeMembershipGridColumns();
|
||||
// FillMembershipGrid();
|
||||
|
||||
if (this.IsPropertiesMode == true)
|
||||
{
|
||||
// initialize from enumerator in properties mode
|
||||
System.Diagnostics.Debug.Assert(this.dbroleName != null);
|
||||
System.Diagnostics.Debug.Assert(this.dbroleName.Trim().Length != 0);
|
||||
System.Diagnostics.Debug.Assert(this.dbroleUrn != null);
|
||||
System.Diagnostics.Debug.Assert(this.dbroleUrn.Trim().Length != 0);
|
||||
|
||||
// this.textBoxDbRoleName.Text = this.dbroleName;
|
||||
|
||||
Enumerator en = new Enumerator();
|
||||
Request req = new Request();
|
||||
req.Fields = new String[] { DatabaseRoleGeneral.ownerField };
|
||||
|
||||
if ((this.dbroleUrn != null) && (this.dbroleUrn.Trim().Length != 0))
|
||||
{
|
||||
req.Urn = this.dbroleUrn;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.Urn = "Server/Database[@Name='" + Urn.EscapeString(this.databaseName) + "']/Role[@Name='" + Urn.EscapeString(this.dbroleName) + "]";
|
||||
}
|
||||
|
||||
DataTable dt = en.Process(serverConnection, req);
|
||||
System.Diagnostics.Debug.Assert(dt != null);
|
||||
System.Diagnostics.Debug.Assert(dt.Rows.Count == 1);
|
||||
|
||||
if (dt.Rows.Count == 0)
|
||||
{
|
||||
throw new Exception("DatabaseRoleSR.ErrorDbRoleNotFound");
|
||||
}
|
||||
|
||||
DataRow dr = dt.Rows[0];
|
||||
this.initialOwner = Convert.ToString(dr[DatabaseRoleGeneral.ownerField], System.Globalization.CultureInfo.InvariantCulture);
|
||||
// this.textBoxOwner.Text = this.initialOwner;
|
||||
}
|
||||
else
|
||||
{
|
||||
// initialize with empty values in create new mode
|
||||
// this.textBoxDbRoleName.Text = String.Empty;
|
||||
// this.textBoxOwner.Text = String.Empty;
|
||||
}
|
||||
|
||||
// update UI enable/disable controls
|
||||
// EnableDisableControls();
|
||||
}
|
||||
|
||||
|
||||
// public override void OnGatherUiInformation(RunType runType)
|
||||
// {
|
||||
// base.OnGatherUiInformation(runType);
|
||||
|
||||
// this.ownerName = this.textBoxOwner.Text;
|
||||
// this.roleName = this.textBoxDbRoleName.Text;
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// SendDataToServer
|
||||
///
|
||||
/// here we talk with server via smo and do the actual data changing
|
||||
/// </summary>
|
||||
private void SendDataToServer()
|
||||
{
|
||||
// STrace.Params(ComponentName, "SendDataToServer", "", null);
|
||||
|
||||
// STrace.Assert(this.databaseName != null && this.databaseName.Trim().Length != 0, "database name is empty");
|
||||
// STrace.Assert(this.DataContainer.Server != null, "server is null");
|
||||
|
||||
Database database = this.dataContainer.Server.Databases[this.databaseName];
|
||||
// STrace.Assert(database!= null, "database is null");
|
||||
|
||||
DatabaseRole role = null;
|
||||
|
||||
if (this.IsPropertiesMode == true) // in properties mode -> alter role
|
||||
{
|
||||
// STrace.Assert(this.dbroleName != null && this.dbroleName.Trim().Length != 0, "role name is empty");
|
||||
|
||||
role = database.Roles[this.dbroleName];
|
||||
// STrace.Assert(role != null, "role is null");
|
||||
|
||||
if (0 != String.Compare(this.ownerName, this.initialOwner, StringComparison.Ordinal))
|
||||
{
|
||||
role.Owner = this.ownerName;
|
||||
role.Alter();
|
||||
}
|
||||
}
|
||||
else // not in properties mode -> create role
|
||||
{
|
||||
role = new DatabaseRole(database, this.roleName);
|
||||
if (this.ownerName.Length != 0)
|
||||
{
|
||||
role.Owner = this.ownerName;
|
||||
}
|
||||
|
||||
role.Create();
|
||||
}
|
||||
|
||||
SendToServerSchemaOwnershipChanges(database, role);
|
||||
SendToServerMembershipChanges(database, role);
|
||||
|
||||
this.dataContainer.ObjectName = role.Name;
|
||||
this.dataContainer.SqlDialogSubject = role; // needed by extended properties page
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// #region Update UI enable/disable controls
|
||||
// private void EnableDisableControls()
|
||||
// {
|
||||
// if (this.DataContainer.Server.Information.Version.Major<9)
|
||||
// {
|
||||
// panelSchema.Enabled = false;
|
||||
// }
|
||||
|
||||
// if (this.IsPropertiesMode == true)
|
||||
// {
|
||||
// this.textBoxDbRoleName.Enabled = false;
|
||||
|
||||
// this.AllUIEnabled = true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// this.textBoxDbRoleName.Enabled = true;
|
||||
|
||||
// this.AllUIEnabled = (this.textBoxDbRoleName.Text.Trim().Length!=0);
|
||||
// }
|
||||
|
||||
// buttonRemove.Enabled = (gridRoleMembership.SelectedRow>=0);
|
||||
// }
|
||||
// #endregion
|
||||
|
||||
// #region ISupportValidation Members
|
||||
|
||||
// bool ISupportValidation.Validate()
|
||||
// {
|
||||
// if (IsPropertiesMode == false)
|
||||
// {
|
||||
// if (this.textBoxDbRoleName.Text.Trim().Length==0)
|
||||
// {
|
||||
// System.Exception e = new System.Exception(DatabaseRoleSR.Error_SpecifyAName);
|
||||
// this.DisplayExceptionMessage(e);
|
||||
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Component Designer generated code
|
||||
// /// <summary>
|
||||
// /// Required method for Designer support - do not modify
|
||||
// /// the contents of this method with the code editor.
|
||||
// /// </summary>
|
||||
// private void InitializeComponent()
|
||||
// {
|
||||
// System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DatabaseRoleGeneral));
|
||||
// this.panelEntireUserControl = new System.Windows.Forms.Panel();
|
||||
// this.panelSchema = new System.Windows.Forms.Panel();
|
||||
// this.gridSchemasOwned = new Microsoft.SqlServer.Management.SqlManagerUI.SqlManagerUIDlgGrid();
|
||||
// this.labelSchemasOwnedByDbRole = new System.Windows.Forms.Label();
|
||||
// this.panelMembership = new System.Windows.Forms.Panel();
|
||||
// this.buttonRemove = new System.Windows.Forms.Button();
|
||||
// this.buttonAdd = new System.Windows.Forms.Button();
|
||||
// this.gridRoleMembership = new Microsoft.SqlServer.Management.SqlManagerUI.SqlManagerUIDlgGrid();
|
||||
// this.labelMembersOfDbRole = new System.Windows.Forms.Label();
|
||||
// this.panelDbRoleGeneralInfo = new System.Windows.Forms.Panel();
|
||||
// this.buttonSearchOwner = new System.Windows.Forms.Button();
|
||||
// this.textBoxOwner = new System.Windows.Forms.TextBox();
|
||||
// this.labelDbRoleOwner = new System.Windows.Forms.Label();
|
||||
// this.textBoxDbRoleName = new System.Windows.Forms.TextBox();
|
||||
// this.labelDbRoleName = new System.Windows.Forms.Label();
|
||||
// this.panelEntireUserControl.SuspendLayout();
|
||||
// this.panelSchema.SuspendLayout();
|
||||
// ((System.ComponentModel.ISupportInitialize)(this.gridSchemasOwned)).BeginInit();
|
||||
// this.panelMembership.SuspendLayout();
|
||||
// ((System.ComponentModel.ISupportInitialize)(this.gridRoleMembership)).BeginInit();
|
||||
// this.panelDbRoleGeneralInfo.SuspendLayout();
|
||||
// this.SuspendLayout();
|
||||
// this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
// this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
// //
|
||||
// // panelEntireUserControl
|
||||
// //
|
||||
// this.panelEntireUserControl.Controls.Add(this.panelSchema);
|
||||
// this.panelEntireUserControl.Controls.Add(this.panelMembership);
|
||||
// this.panelEntireUserControl.Controls.Add(this.panelDbRoleGeneralInfo);
|
||||
// resources.ApplyResources(this.panelEntireUserControl, "panelEntireUserControl");
|
||||
// this.panelEntireUserControl.Name = "panelEntireUserControl";
|
||||
// //
|
||||
// // panelSchema
|
||||
// //
|
||||
// resources.ApplyResources(this.panelSchema, "panelSchema");
|
||||
// this.panelSchema.Controls.Add(this.gridSchemasOwned);
|
||||
// this.panelSchema.Controls.Add(this.labelSchemasOwnedByDbRole);
|
||||
// this.panelSchema.Name = "panelSchema";
|
||||
// //
|
||||
// // gridSchemasOwned
|
||||
// //
|
||||
// resources.ApplyResources(this.gridSchemasOwned, "gridSchemasOwned");
|
||||
// this.gridSchemasOwned.BackColor = System.Drawing.SystemColors.Window;
|
||||
// this.gridSchemasOwned.ForceEnabled = false;
|
||||
// this.gridSchemasOwned.Name = "gridSchemasOwned";
|
||||
// this.gridSchemasOwned.MouseButtonClicked += new Microsoft.SqlServer.Management.UI.Grid.MouseButtonClickedEventHandler(this.gridSchemasOwned_MouseButtonClicked);
|
||||
// //
|
||||
// // labelSchemasOwnedByDbRole
|
||||
// //
|
||||
// resources.ApplyResources(this.labelSchemasOwnedByDbRole, "labelSchemasOwnedByDbRole");
|
||||
// this.labelSchemasOwnedByDbRole.Name = "labelSchemasOwnedByDbRole";
|
||||
// //
|
||||
// // panelMembership
|
||||
// //
|
||||
// resources.ApplyResources(this.panelMembership, "panelMembership");
|
||||
// this.panelMembership.Controls.Add(this.buttonRemove);
|
||||
// this.panelMembership.Controls.Add(this.buttonAdd);
|
||||
// this.panelMembership.Controls.Add(this.gridRoleMembership);
|
||||
// this.panelMembership.Controls.Add(this.labelMembersOfDbRole);
|
||||
// this.panelMembership.Name = "panelMembership";
|
||||
// //
|
||||
// // buttonRemove
|
||||
// //
|
||||
// resources.ApplyResources(this.buttonRemove, "buttonRemove");
|
||||
// this.buttonRemove.Name = "buttonRemove";
|
||||
// this.buttonRemove.Click += new System.EventHandler(this.buttonRemove_Click);
|
||||
// //
|
||||
// // buttonAdd
|
||||
// //
|
||||
// resources.ApplyResources(this.buttonAdd, "buttonAdd");
|
||||
// this.buttonAdd.Name = "buttonAdd";
|
||||
// this.buttonAdd.Click += new System.EventHandler(this.buttonAdd_Click);
|
||||
// //
|
||||
// // gridRoleMembership
|
||||
// //
|
||||
// resources.ApplyResources(this.gridRoleMembership, "gridRoleMembership");
|
||||
// this.gridRoleMembership.BackColor = System.Drawing.SystemColors.Window;
|
||||
// this.gridRoleMembership.ForceEnabled = false;
|
||||
// this.gridRoleMembership.Name = "gridRoleMembership";
|
||||
// this.gridRoleMembership.SelectionChanged += new Microsoft.SqlServer.Management.UI.Grid.SelectionChangedEventHandler(this.gridRoleMembership_SelectionChanged);
|
||||
// //
|
||||
// // labelMembersOfDbRole
|
||||
// //
|
||||
// resources.ApplyResources(this.labelMembersOfDbRole, "labelMembersOfDbRole");
|
||||
// this.labelMembersOfDbRole.Name = "labelMembersOfDbRole";
|
||||
// //
|
||||
// // panelDbRoleGeneralInfo
|
||||
// //
|
||||
// resources.ApplyResources(this.panelDbRoleGeneralInfo, "panelDbRoleGeneralInfo");
|
||||
// this.panelDbRoleGeneralInfo.Controls.Add(this.buttonSearchOwner);
|
||||
// this.panelDbRoleGeneralInfo.Controls.Add(this.textBoxOwner);
|
||||
// this.panelDbRoleGeneralInfo.Controls.Add(this.labelDbRoleOwner);
|
||||
// this.panelDbRoleGeneralInfo.Controls.Add(this.textBoxDbRoleName);
|
||||
// this.panelDbRoleGeneralInfo.Controls.Add(this.labelDbRoleName);
|
||||
// this.panelDbRoleGeneralInfo.Name = "panelDbRoleGeneralInfo";
|
||||
// //
|
||||
// // buttonSearchOwner
|
||||
// //
|
||||
// resources.ApplyResources(this.buttonSearchOwner, "buttonSearchOwner");
|
||||
// this.buttonSearchOwner.Name = "buttonSearchOwner";
|
||||
// this.buttonSearchOwner.Click += new System.EventHandler(this.buttonSearchOwner_Click);
|
||||
// //
|
||||
// // textBoxOwner
|
||||
// //
|
||||
// resources.ApplyResources(this.textBoxOwner, "textBoxOwner");
|
||||
// this.textBoxOwner.Name = "textBoxOwner";
|
||||
// //
|
||||
// // labelDbRoleOwner
|
||||
// //
|
||||
// resources.ApplyResources(this.labelDbRoleOwner, "labelDbRoleOwner");
|
||||
// this.labelDbRoleOwner.Name = "labelDbRoleOwner";
|
||||
// //
|
||||
// // textBoxDbRoleName
|
||||
// //
|
||||
// resources.ApplyResources(this.textBoxDbRoleName, "textBoxDbRoleName");
|
||||
// this.textBoxDbRoleName.Name = "textBoxDbRoleName";
|
||||
// //
|
||||
// // labelDbRoleName
|
||||
// //
|
||||
// resources.ApplyResources(this.labelDbRoleName, "labelDbRoleName");
|
||||
// this.labelDbRoleName.Name = "labelDbRoleName";
|
||||
// //
|
||||
// // DatabaseRoleGeneral
|
||||
// //
|
||||
// this.Controls.Add(this.panelEntireUserControl);
|
||||
// this.Name = "DatabaseRoleGeneral";
|
||||
// resources.ApplyResources(this, "$this");
|
||||
// this.panelEntireUserControl.ResumeLayout(false);
|
||||
// this.panelSchema.ResumeLayout(false);
|
||||
// ((System.ComponentModel.ISupportInitialize)(this.gridSchemasOwned)).EndInit();
|
||||
// this.panelMembership.ResumeLayout(false);
|
||||
// ((System.ComponentModel.ISupportInitialize)(this.gridRoleMembership)).EndInit();
|
||||
// this.panelDbRoleGeneralInfo.ResumeLayout(false);
|
||||
// this.panelDbRoleGeneralInfo.PerformLayout();
|
||||
// this.ResumeLayout(false);
|
||||
|
||||
// }
|
||||
// #endregion
|
||||
|
||||
#region Schemas - general operations with ...
|
||||
/// <summary>
|
||||
/// loads initial schemas from server together with information about the schema owner
|
||||
/// </summary>
|
||||
private void LoadSchemas()
|
||||
{
|
||||
this.schemaOwnership = new HybridDictionary();
|
||||
|
||||
Enumerator en = new Enumerator();
|
||||
Request req = new Request();
|
||||
req.Fields = new String[] { DatabaseRoleGeneral.schemaNameField, DatabaseRoleGeneral.schemaOwnerField };
|
||||
req.Urn = "Server/Database[@Name='" + Urn.EscapeString(this.databaseName) + "']/Schema";
|
||||
|
||||
DataTable dt = en.Process(serverConnection, req);
|
||||
// STrace.Assert((dt != null) && (0 < dt.Rows.Count), "enumerator did not return schemas");
|
||||
// STrace.Assert(!this.IsPropertiesMode || (this.dbroleName.Length != 0), "role name is not known");
|
||||
|
||||
foreach (DataRow dr in dt.Rows)
|
||||
{
|
||||
string schemaName = Convert.ToString(dr[DatabaseRoleGeneral.schemaNameField], System.Globalization.CultureInfo.InvariantCulture);
|
||||
string schemaOwner = Convert.ToString(dr[DatabaseRoleGeneral.schemaOwnerField], System.Globalization.CultureInfo.InvariantCulture);
|
||||
bool roleOwnsSchema =
|
||||
this.IsPropertiesMode &&
|
||||
(0 == String.Compare(this.dbroleName, schemaOwner, StringComparison.Ordinal));
|
||||
|
||||
this.schemaOwnership[schemaName] = new SchemaOwnership(roleOwnsSchema);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// initializes the columns and headers of schema grid - but doesnt populate grid with any data
|
||||
/// </summary>
|
||||
// private void InitializeSchemasGridColumns()
|
||||
// {
|
||||
// Microsoft.SqlServer.Management.UI.Grid.DlgGridControl grid = this.gridSchemasOwned;
|
||||
|
||||
// if (grid.RowsNumber != 0)
|
||||
// {
|
||||
// grid.DeleteAllRows();
|
||||
// }
|
||||
|
||||
// while (grid.ColumnsNumber != 0)
|
||||
// {
|
||||
// grid.DeleteColumn(0);
|
||||
// }
|
||||
|
||||
// GridColumnInfo colInfo = null;
|
||||
|
||||
// // checkbox owned/not-owned
|
||||
// colInfo = new GridColumnInfo();
|
||||
// colInfo.ColumnWidth = sizeCheckboxColumn;
|
||||
// colInfo.WidthType = GridColumnWidthType.InPixels;
|
||||
// colInfo.ColumnType = GridColumnType.Checkbox;
|
||||
// grid.AddColumn(colInfo);
|
||||
|
||||
// // schema name
|
||||
// colInfo = new GridColumnInfo();
|
||||
// colInfo.ColumnWidth = grid.Width - sizeCheckboxColumn - 2;
|
||||
// colInfo.WidthType = GridColumnWidthType.InPixels;
|
||||
// grid.AddColumn(colInfo);
|
||||
|
||||
// grid.SetHeaderInfo(colSchemasOwnedSchemas, DatabaseRoleSR.HeaderOwnedSchemas, null);
|
||||
|
||||
// grid.SelectionType = GridSelectionType.SingleRow;
|
||||
// grid.UpdateGrid();
|
||||
// }
|
||||
|
||||
// private void FillSchemasGrid()
|
||||
// {
|
||||
// Microsoft.SqlServer.Management.UI.Grid.DlgGridControl grid = this.gridSchemasOwned;
|
||||
|
||||
// grid.BeginInit();
|
||||
// grid.DeleteAllRows();
|
||||
|
||||
// IDictionaryEnumerator enumerator = this.schemaOwnership.GetEnumerator();
|
||||
// enumerator.Reset();
|
||||
// while (enumerator.MoveNext())
|
||||
// {
|
||||
// DictionaryEntry entry = enumerator.Entry;
|
||||
// GridCellCollection row = new GridCellCollection();
|
||||
// GridCell cell = null;
|
||||
|
||||
// string schemaName = entry.Key.ToString();
|
||||
// bool roleCurrentlyOwnsSchema = ((SchemaOwnership)entry.Value).currentlyOwned;
|
||||
|
||||
// // grid is filled either
|
||||
// // a) disabled-checked checkboxes: Indeterminate - if already owning schema - we cannot renounce ownership
|
||||
// // b) enabled-unchecked checkboxes: Unchecked - user can check / uncheck them and we read final state
|
||||
// cell = new GridCell(roleCurrentlyOwnsSchema ? GridCheckBoxState.Indeterminate : GridCheckBoxState.Unchecked);
|
||||
// row.Add(cell);
|
||||
|
||||
// cell = new GridCell(schemaName);
|
||||
// row.Add(cell);
|
||||
|
||||
// grid.AddRow(row);
|
||||
// }
|
||||
|
||||
// grid.EndInit();
|
||||
|
||||
// if (grid.RowsNumber > 0)
|
||||
// {
|
||||
// grid.SelectedRow = 0;
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// sends to server changes related to schema ownership
|
||||
/// </summary>
|
||||
private void SendToServerSchemaOwnershipChanges(Database db, DatabaseRole dbrole)
|
||||
{
|
||||
if (9 <= this.dataContainer.Server.Information.Version.Major)
|
||||
{
|
||||
IDictionaryEnumerator enumerator = this.schemaOwnership.GetEnumerator();
|
||||
enumerator.Reset();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
DictionaryEntry de = enumerator.Entry;
|
||||
string schemaName = de.Key.ToString();
|
||||
SchemaOwnership ownership = (SchemaOwnership)de.Value;
|
||||
|
||||
// If we are creating a new role, then no schema will have been initially owned by this role.
|
||||
// If we are modifying an existing role, we can only take ownership of roles. (Ownership can't
|
||||
// be renounced, it can only be positively assigned to a principal.)
|
||||
if (ownership.currentlyOwned && !ownership.initiallyOwned)
|
||||
{
|
||||
Schema schema = db.Schemas[schemaName];
|
||||
schema.Owner = dbrole.Name;
|
||||
schema.Alter();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// private void gridSchemasOwned_MouseButtonClicked(object sender, Microsoft.SqlServer.Management.UI.Grid.MouseButtonClickedEventArgs args)
|
||||
// {
|
||||
// if ((args.Button == MouseButtons.Left) &&
|
||||
// (colSchemasChecked == args.ColumnIndex))
|
||||
// {
|
||||
// int row = (int) args.RowIndex;
|
||||
// string schemaName = this.gridSchemasOwned.GetCellInfo(row, colSchemasOwnedSchemas).CellData.ToString();
|
||||
// GridCheckBoxState newState = this.FlipCheckbox(this.gridSchemasOwned, row, colSchemasChecked);
|
||||
// bool nowOwned = ((GridCheckBoxState.Checked == newState) || (GridCheckBoxState.Indeterminate == newState));
|
||||
|
||||
// ((SchemaOwnership) this.schemaOwnership[schemaName]).currentlyOwned = nowOwned;
|
||||
// }
|
||||
// }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Membership - general operations with ...
|
||||
|
||||
/// <summary>
|
||||
/// loads from server initial membership information
|
||||
/// </summary>
|
||||
private void LoadMembership()
|
||||
{
|
||||
this.roleMembers = new HybridDictionary();
|
||||
|
||||
if (this.IsPropertiesMode)
|
||||
{
|
||||
Enumerator enumerator = new Enumerator();
|
||||
Urn urn = String.Format(System.Globalization.CultureInfo.InvariantCulture,
|
||||
"Server/Database[@Name='{0}']/Role[@Name='{1}']/Member",
|
||||
Urn.EscapeString(this.databaseName),
|
||||
Urn.EscapeString(this.dbroleName));
|
||||
string[] fields = new string[] { DatabaseRoleGeneral.memberNameField };
|
||||
OrderBy[] orderBy = new OrderBy[] { new OrderBy(DatabaseRoleGeneral.memberNameField, OrderBy.Direction.Asc) };
|
||||
Request request = new Request(urn, fields, orderBy);
|
||||
DataTable dt = enumerator.Process(this.serverConnection, request);
|
||||
|
||||
foreach (DataRow dr in dt.Rows)
|
||||
{
|
||||
string memberName = dr[DatabaseRoleGeneral.memberNameField].ToString();
|
||||
this.roleMembers[memberName] = new RoleMembership(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// initialize grid column headers, but not the content
|
||||
/// </summary>
|
||||
// private void InitializeMembershipGridColumns()
|
||||
// {
|
||||
// Microsoft.SqlServer.Management.UI.Grid.DlgGridControl grid = this.gridRoleMembership;
|
||||
|
||||
// if (grid.RowsNumber != 0)
|
||||
// {
|
||||
// grid.DeleteAllRows();
|
||||
// }
|
||||
|
||||
// while (grid.ColumnsNumber != 0)
|
||||
// {
|
||||
// grid.DeleteColumn(0);
|
||||
// }
|
||||
|
||||
// GridColumnInfo colInfo = null;
|
||||
|
||||
// // bitmap member type
|
||||
// colInfo = new GridColumnInfo();
|
||||
// colInfo.ColumnWidth = sizeBitmapColumn;
|
||||
// colInfo.WidthType = GridColumnWidthType.InPixels;
|
||||
// colInfo.ColumnType = GridColumnType.Bitmap;
|
||||
// grid.AddColumn(colInfo);
|
||||
|
||||
// // member name
|
||||
// colInfo = new GridColumnInfo();
|
||||
// colInfo.ColumnWidth = grid.Width - sizeBitmapColumn - 2;
|
||||
// colInfo.WidthType = GridColumnWidthType.InPixels;
|
||||
// grid.AddColumn(colInfo);
|
||||
|
||||
// grid.SetHeaderInfo(colMembershipRoleMembers, DatabaseRoleSR.HeaderRoleMembers, null);
|
||||
|
||||
// grid.SelectionType = GridSelectionType.SingleRow;
|
||||
// grid.UpdateGrid();
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// fills the membership grid with data (bitmaps, names, etc)
|
||||
/// </summary>
|
||||
// private void FillMembershipGrid()
|
||||
// {
|
||||
// Microsoft.SqlServer.Management.UI.Grid.DlgGridControl grid = this.gridRoleMembership;
|
||||
|
||||
// grid.BeginInit();
|
||||
// grid.DeleteAllRows();
|
||||
|
||||
// IDictionaryEnumerator enumerator = this.roleMembers.GetEnumerator();
|
||||
// enumerator.Reset();
|
||||
// while (enumerator.MoveNext())
|
||||
// {
|
||||
// DictionaryEntry entry = enumerator.Entry;
|
||||
// string memberName = entry.Key.ToString();
|
||||
// RoleMembership membership = (RoleMembership) entry.Value;
|
||||
|
||||
// if (membership.currentlyAMember)
|
||||
// {
|
||||
// GridCellCollection row = new GridCellCollection();
|
||||
// GridCell cell = null;
|
||||
|
||||
// cell = new GridCell(bitmapMember);
|
||||
// row.Add(cell);
|
||||
|
||||
// cell = new GridCell(memberName);
|
||||
// row.Add(cell);
|
||||
|
||||
// grid.AddRow(row);
|
||||
// }
|
||||
// }
|
||||
|
||||
// grid.EndInit();
|
||||
|
||||
// if (grid.RowsNumber > 0)
|
||||
// {
|
||||
// grid.SelectedRow = 0;
|
||||
// }
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// sends to server user changes related to membership
|
||||
/// </summary>
|
||||
private void SendToServerMembershipChanges(Database db, DatabaseRole dbrole)
|
||||
{
|
||||
IDictionaryEnumerator enumerator = this.roleMembers.GetEnumerator();
|
||||
enumerator.Reset();
|
||||
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
DictionaryEntry entry = enumerator.Entry;
|
||||
string memberName = entry.Key.ToString();
|
||||
RoleMembership membership = (RoleMembership)entry.Value;
|
||||
|
||||
if (!membership.initiallyAMember && membership.currentlyAMember)
|
||||
{
|
||||
dbrole.AddMember(memberName);
|
||||
}
|
||||
else if (membership.initiallyAMember && !membership.currentlyAMember)
|
||||
{
|
||||
dbrole.DropMember(memberName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// private void gridRoleMembership_SelectionChanged(object sender, Microsoft.SqlServer.Management.UI.Grid.SelectionChangedEventArgs args)
|
||||
// {
|
||||
// EnableDisableControls();
|
||||
// }
|
||||
|
||||
// private void buttonAdd_Click(object sender, System.EventArgs e)
|
||||
// {
|
||||
|
||||
// using (SqlObjectSearch dlg = new SqlObjectSearch(
|
||||
// this.Font,
|
||||
// iconSearchRolesAndUsers,
|
||||
// this.HelpProvider,
|
||||
// DatabaseRoleSR.Add_DialogTitle,
|
||||
// this.DataContainer.ConnectionInfo,
|
||||
// this.databaseName,
|
||||
// new SearchableObjectTypeCollection(SearchableObjectType.User, SearchableObjectType.DatabaseRole),
|
||||
// new SearchableObjectTypeCollection(SearchableObjectType.User, SearchableObjectType.DatabaseRole),
|
||||
// false))
|
||||
// {
|
||||
// if (DialogResult.OK == dlg.ShowDialog(this.FindForm()))
|
||||
// {
|
||||
// bool memberAdded = false;
|
||||
|
||||
// this.gridRoleMembership.BeginInit();
|
||||
|
||||
// foreach (SearchableObject principal in dlg.SearchResults)
|
||||
// {
|
||||
// if (!this.roleMembers.Contains(principal.Name))
|
||||
// {
|
||||
// this.roleMembers[principal.Name] = new RoleMembership(false, true);
|
||||
// memberAdded = true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// RoleMembership membership = (RoleMembership) this.roleMembers[principal.Name];
|
||||
|
||||
// if (!membership.currentlyAMember)
|
||||
// {
|
||||
// membership.currentlyAMember = true;
|
||||
// memberAdded = true;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (memberAdded)
|
||||
// {
|
||||
// GridCellCollection row = new GridCellCollection();
|
||||
// GridCell cell = null;
|
||||
|
||||
// cell = new GridCell(bitmapMember);
|
||||
// row.Add(cell);
|
||||
|
||||
// cell = new GridCell(principal.Name);
|
||||
// row.Add(cell);
|
||||
|
||||
// this.gridRoleMembership.AddRow(row);
|
||||
// }
|
||||
// }
|
||||
|
||||
// this.gridRoleMembership.EndInit();
|
||||
|
||||
// if (memberAdded)
|
||||
// {
|
||||
// this.gridRoleMembership.SelectedRow = this.gridRoleMembership.RowsNumber - 1;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// private void buttonRemove_Click(object sender, System.EventArgs e)
|
||||
// {
|
||||
// DlgGridControl grid = this.gridRoleMembership;
|
||||
|
||||
// int row = this.gridRoleMembership.SelectedRow;
|
||||
// STrace.Assert(0 <= row, "unexpected row number");
|
||||
|
||||
// if (0 <= row)
|
||||
// {
|
||||
// string memberName = this.gridRoleMembership.GetCellInfo(row, colMembershipRoleMembers).CellData.ToString();
|
||||
// RoleMembership membership = (RoleMembership) this.roleMembers[memberName];
|
||||
|
||||
// if (membership.initiallyAMember)
|
||||
// {
|
||||
// membership.currentlyAMember = false;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// this.roleMembers.Remove(memberName);
|
||||
// }
|
||||
|
||||
// this.gridRoleMembership.DeleteRow(row);
|
||||
// }
|
||||
// }
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.SqlTools.ServiceLayer.Management;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
internal class LoginActions : ManagementActionBase
|
||||
{
|
||||
private ConfigAction configAction;
|
||||
|
||||
private LoginPrototype prototype;
|
||||
|
||||
/// <summary>
|
||||
/// Handle login create and update actions
|
||||
/// </summary>
|
||||
public LoginActions(CDataContainer dataContainer, ConfigAction configAction, LoginPrototype prototype)
|
||||
{
|
||||
this.DataContainer = dataContainer;
|
||||
this.configAction = configAction;
|
||||
this.prototype = prototype;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// called by the management actions framework to execute the action
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
public override void OnRunNow(object sender)
|
||||
{
|
||||
if (this.configAction != ConfigAction.Drop)
|
||||
{
|
||||
prototype.ApplyGeneralChanges(this.DataContainer.Server);
|
||||
prototype.ApplyServerRoleChanges(this.DataContainer.Server);
|
||||
prototype.ApplyDatabaseRoleChanges(this.DataContainer.Server);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,212 @@
|
||||
//
|
||||
// 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 System;
|
||||
using System.Globalization;
|
||||
using SMO = Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// NetStandard compatible helpers
|
||||
/// </summary>
|
||||
#if NETCOREAPP2_0
|
||||
public class Utils
|
||||
#else
|
||||
internal partial class Utils
|
||||
#endif
|
||||
{
|
||||
private Utils() { }
|
||||
|
||||
public static bool IsKatmaiOrLater(int version)
|
||||
{
|
||||
return (10 <= version);
|
||||
}
|
||||
|
||||
public static bool IsKjOrLater(ServerVersion version)
|
||||
{
|
||||
return (version.Major > 10
|
||||
|| (version.Major == 10 && version.Minor >= 50));
|
||||
}
|
||||
|
||||
public static bool IsSql11OrLater(ServerVersion version)
|
||||
{
|
||||
return IsSql11OrLater(version.Major);
|
||||
}
|
||||
|
||||
public static bool IsSql11OrLater(int versionMajor)
|
||||
{
|
||||
return (versionMajor >= 11);
|
||||
}
|
||||
|
||||
public static bool IsSql12OrLater(ServerVersion version)
|
||||
{
|
||||
return IsSql12OrLater(version.Major);
|
||||
}
|
||||
|
||||
public static bool IsSql12OrLater(int versionMajor)
|
||||
{
|
||||
return (versionMajor >= 12);
|
||||
}
|
||||
|
||||
public static bool IsSql13OrLater(ServerVersion version)
|
||||
{
|
||||
return IsSql13OrLater(version.Major);
|
||||
}
|
||||
|
||||
public static bool IsSql13OrLater(int versionMajor)
|
||||
{
|
||||
return (versionMajor >= 13);
|
||||
}
|
||||
|
||||
public static bool IsSql14OrLater(ServerVersion version)
|
||||
{
|
||||
return IsSql14OrLater(version.Major);
|
||||
}
|
||||
|
||||
public static bool IsSql14OrLater(int versionMajor)
|
||||
{
|
||||
return (versionMajor >= 14);
|
||||
}
|
||||
|
||||
public static bool IsSql15OrLater(ServerVersion version)
|
||||
{
|
||||
return IsSql15OrLater(version.Major);
|
||||
}
|
||||
|
||||
public static bool IsSql15OrLater(int versionMajor)
|
||||
{
|
||||
return (versionMajor >= 15);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Check if the version is SQL 2019 CU4 or later.
|
||||
/// </summary>
|
||||
/// <param name="version"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// SQL2019 CU3 is going to be 4023; CU4 is going to be 4033
|
||||
/// SQL2019 CU4 (before the snap to the release branch) is 4028.
|
||||
/// </remarks>
|
||||
public static bool IsSql15OCU4OrLater(Version version)
|
||||
{
|
||||
return(version >= new Version(15, 0, 4028));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the version is SQL 2016 SP1 or later.
|
||||
/// </summary>
|
||||
/// <param name="version"></param>
|
||||
/// <returns>true if the version is SQL 2016 SP1 or later, false otherwise</returns>
|
||||
public static bool IsSql13SP1OrLater(Version version)
|
||||
{
|
||||
return (version >= new Version(13, 0, 3510));
|
||||
}
|
||||
|
||||
public static bool IsXTPSupportedOnServer(SMO.Server server)
|
||||
{
|
||||
if(server.DatabaseEngineEdition == DatabaseEngineEdition.SqlOnDemand)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool isXTPSupported = false;
|
||||
|
||||
if (server.ConnectionContext.ExecuteScalar("SELECT SERVERPROPERTY('IsXTPSupported')") != DBNull.Value)
|
||||
{
|
||||
isXTPSupported = server.IsXTPSupported;
|
||||
}
|
||||
|
||||
return isXTPSupported;
|
||||
}
|
||||
|
||||
public static bool IsPolybasedInstalledOnServer(SMO.Server server)
|
||||
{
|
||||
bool isPolybaseInstalled = false;
|
||||
|
||||
if (server.IsSupportedProperty("IsPolyBaseInstalled"))
|
||||
{
|
||||
isPolybaseInstalled = server.IsPolyBaseInstalled;
|
||||
}
|
||||
|
||||
return isPolybaseInstalled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if current user has given permission on given server.
|
||||
/// </summary>
|
||||
/// <param name="server"></param>
|
||||
/// <param name="permissionName"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasPermissionOnServer(SMO.Server server, string permissionName)
|
||||
{
|
||||
return Convert.ToBoolean(server.ConnectionContext.ExecuteScalar(
|
||||
string.Format(CultureInfo.InvariantCulture,
|
||||
"SELECT HAS_PERMS_BY_NAME(null, null, '{0}');",
|
||||
permissionName)));
|
||||
}
|
||||
|
||||
public static bool FilestreamEnabled(SMO.Server svr)
|
||||
{
|
||||
bool result = false;
|
||||
if (svr != null)
|
||||
{
|
||||
if (IsKatmaiOrLater(svr.Information.Version.Major)
|
||||
&& svr.ServerType != DatabaseEngineType.SqlAzureDatabase) //Azure doesn't support filestream
|
||||
{
|
||||
if (svr.Configuration.FilestreamAccessLevel.RunValue != 0)
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool IsYukonOrAbove(SMO.Server server)
|
||||
{
|
||||
return server.Version.Major >= 9;
|
||||
}
|
||||
|
||||
public static bool IsBelowYukon(SMO.Server server)
|
||||
{
|
||||
return server.Version.Major < 9;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Some calendars, such as the UmAlQuraCalendar, support an upper date range that is earlier than MaxValue.
|
||||
/// In these cases, trying to access MaxValue in variable assignments or formatting and parsing operations can throw
|
||||
/// an ArgumentOutOfRangeException. Rather than retrieving the value of DateTime.MaxValue, you can retrieve the value
|
||||
/// of the specified culture's latest valid date value from the
|
||||
/// System.Globalization.CultureInfo.DateTimeFormat.Calendar.MaxSupportedDateTime property.
|
||||
/// http://msdn.microsoft.com/en-us/library/system.datetime.maxvalue(v=VS.90).aspx
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static DateTime GetMaxCultureDateTime()
|
||||
{
|
||||
CultureInfo currentCulture = System.Threading.Thread.CurrentThread.CurrentCulture;
|
||||
return currentCulture.DateTimeFormat.Calendar.MaxSupportedDateTime;
|
||||
}
|
||||
|
||||
public static string MakeSqlBracket(string s)
|
||||
{
|
||||
return "[" + s.Replace("]", "]]") + "]";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the server is in AS Azure
|
||||
/// </summary>
|
||||
/// <param name="serverName"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsASAzure(string serverName)
|
||||
{
|
||||
return !string.IsNullOrEmpty(serverName) && serverName.StartsWith("asazure://", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// 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 System;
|
||||
using System.Linq;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
internal static class PermissionsDataExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether this SecurableType is a valid Schema-Scoped Securable for the given server version, engine edition and engine type
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="serverVersion"></param>
|
||||
/// <param name="databaseEngineEdition"></param>
|
||||
/// <param name="databaseEngineType"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsValidSchemaBoundSecurable(this SecurableType type, ServerVersion serverVersion, DatabaseEngineEdition databaseEngineEdition, DatabaseEngineType databaseEngineType)
|
||||
{
|
||||
return
|
||||
type.GetType().GetField(type.ToString())
|
||||
.GetCustomAttributes(typeof (SchemaScopedSecurableAttribute), true)
|
||||
.Cast<SchemaScopedSecurableAttribute>()
|
||||
.Any(attr => attr.IsValid(serverVersion, databaseEngineType, databaseEngineEdition));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Schema-Scoped URN for this SecurableType
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="schema"></param>
|
||||
/// <param name="databaseName"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetSchemaScopedUrn(this SecurableType type, string schema, string databaseName)
|
||||
{
|
||||
SchemaScopedSecurableAttribute attr =
|
||||
type.GetType().GetField(type.ToString())
|
||||
.GetCustomAttributes(typeof (SchemaScopedSecurableAttribute), true)
|
||||
.Cast<SchemaScopedSecurableAttribute>()
|
||||
.FirstOrDefault() ?? throw new InvalidOperationException("Type {0} did not define a SchemaScopedSecurableUrn attribute");
|
||||
return attr.GetUrn(schema, databaseName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// 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 System;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlServer.Management.Sdk.Sfc;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// An attribute for sqlmgmt\src\permissionsdata.cs!SecurableType that maps it to the corresponding SMO
|
||||
/// type. This allows us to use that type to decide whether that securable is valid for a given server
|
||||
/// version/engine edition/engine type combo and to get the URN suffix value for that type using SMO
|
||||
/// instead of duplicating it in SqlMgmt.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
internal class SchemaScopedSecurableAttribute : Attribute
|
||||
{
|
||||
private readonly Type _smoType;
|
||||
private readonly string _urnSuffix;
|
||||
private readonly string _additionalParam;
|
||||
|
||||
/// <summary>
|
||||
/// Basic public constructor
|
||||
/// </summary>
|
||||
/// <param name="smoType">The SMO Type this Securable is mapped to</param>
|
||||
/// <param name="additionalParamName">(Optional) The name of an additional param</param>
|
||||
/// <param name="additionalParamValue">(Optional) The value of an additional param</param>
|
||||
public SchemaScopedSecurableAttribute(Type smoType, string additionalParamName = "", object additionalParamValue = null )
|
||||
{
|
||||
_smoType = smoType;
|
||||
//The additional param is optional - just ignore if we don't have a valid name
|
||||
_additionalParam = string.IsNullOrEmpty(additionalParamName)
|
||||
? String.Empty
|
||||
: string.Format("@{0}='{1}'", additionalParamName, Urn.EscapeString(additionalParamValue.ToString()));
|
||||
PropertyInfo urnSuffixProperty = _smoType.GetProperty("UrnSuffix", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public) ?? throw new InvalidArgumentException(string.Format("Type {0} did not have expected UrnSuffix property defined", smoType.Name));
|
||||
_urnSuffix = urnSuffixProperty.GetValue(null, null).ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The SMO Type that this securable is mapped to
|
||||
/// </summary>
|
||||
public Type SmoType
|
||||
{
|
||||
get { return _smoType; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this Securable is valid for the given server version/engine type/engine edition combo.
|
||||
/// </summary>
|
||||
/// <param name="serverVersion"></param>
|
||||
/// <param name="databaseEngineType"></param>
|
||||
/// <param name="databaseEngineEdition"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsValid(ServerVersion serverVersion, DatabaseEngineType databaseEngineType, DatabaseEngineEdition databaseEngineEdition)
|
||||
{
|
||||
return SmoUtility.IsSupportedObject(_smoType, serverVersion, databaseEngineType, databaseEngineEdition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the URN for this Securable using the specified schema name (with optional database name for db-scoped securables)
|
||||
/// </summary>
|
||||
/// <param name="schemaName"></param>
|
||||
/// <param name="databaseName"></param>
|
||||
/// <returns></returns>
|
||||
public string GetUrn(string schemaName, string databaseName = "")
|
||||
{
|
||||
StringBuilder urn = new StringBuilder("Server");
|
||||
if (!string.IsNullOrEmpty(databaseName))
|
||||
{
|
||||
urn.AppendFormat("/Database[@Name='{0}']", Urn.EscapeString(databaseName));
|
||||
}
|
||||
urn.AppendFormat("/{0}[{1}{2}@Schema='{3}']",
|
||||
_urnSuffix,
|
||||
_additionalParam,
|
||||
string.IsNullOrEmpty(_additionalParam) ? string.Empty : " and ",
|
||||
Urn.EscapeString(schemaName));
|
||||
return urn.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
//
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.SqlServer.Management.Sdk.Sfc;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using System.ComponentModel;
|
||||
using System.Collections.Specialized;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlTools.ServiceLayer.Management;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
public class ServerRoleManageTaskFormComponent
|
||||
{
|
||||
protected ServerRole serverRole;
|
||||
ServerRoleExtender extender;
|
||||
|
||||
ServerRole instance;
|
||||
Server server;
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
components = new System.ComponentModel.Container();
|
||||
}
|
||||
|
||||
public ServerRoleManageTaskFormComponent()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public ServerRoleManageTaskFormComponent(IContainer container)
|
||||
{
|
||||
// container.Add(this);
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected string MethodName
|
||||
{
|
||||
get
|
||||
{
|
||||
return "Alter";
|
||||
}
|
||||
}
|
||||
|
||||
protected ServerRole Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
#pragma warning disable IDE0074 // Use compound assignment
|
||||
if (this.instance == null)
|
||||
{
|
||||
this.instance = CreateSmoObjectInstance();
|
||||
}
|
||||
#pragma warning restore IDE0074 // Use compound assignment
|
||||
return this.instance;
|
||||
}
|
||||
}
|
||||
|
||||
protected Server Server
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.server;
|
||||
}
|
||||
}
|
||||
|
||||
protected Microsoft.SqlServer.Management.Sdk.Sfc.ISfcPropertyProvider CreatePropertyProvider()
|
||||
{
|
||||
#pragma warning disable IDE0074 // Use compound assignment
|
||||
if (extender == null)
|
||||
{
|
||||
extender = new ServerRoleExtender(this.Instance);
|
||||
}
|
||||
#pragma warning restore IDE0074 // Use compound assignment
|
||||
return extender;
|
||||
}
|
||||
|
||||
protected ServerRole CreateSmoObjectInstance()
|
||||
{
|
||||
if (this.serverRole == null)
|
||||
{
|
||||
Urn urn;
|
||||
// if (!SmoTaskHelper.TryGetUrn(this.TaskManager.Context, out urn))
|
||||
if (true) // TODO new server role
|
||||
{
|
||||
this.serverRole = new ServerRole(this.Server, GetServerRoleName());
|
||||
}
|
||||
else
|
||||
{
|
||||
this.serverRole = this.Server.GetSmoObject(urn) as ServerRole;
|
||||
}
|
||||
}
|
||||
return this.serverRole;
|
||||
}
|
||||
|
||||
protected void PerformTask()
|
||||
{
|
||||
this.CreateOrManage();
|
||||
|
||||
//General Page Permissions Related actions
|
||||
EventHandler tempEventHandler = (EventHandler)this.extender.GeneralPageOnRunNow;
|
||||
if (tempEventHandler != null)
|
||||
{
|
||||
tempEventHandler(this, new EventArgs());
|
||||
}
|
||||
|
||||
//Disposing DataContainer object as it has a dedicated server connection.
|
||||
if (this.extender.GeneralPageDataContainer != null)
|
||||
{
|
||||
((CDataContainer)this.extender.GeneralPageDataContainer).Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateOrManage()
|
||||
{
|
||||
//General Page Actions
|
||||
if (Utils.IsSql11OrLater(this.Instance.ServerVersion.Major))
|
||||
{
|
||||
if (this.Instance.State == SqlSmoState.Creating)
|
||||
{
|
||||
if (this.extender.OwnerForUI == string.Empty) //In order to avoid scripting Authorization part of ddl.
|
||||
{
|
||||
this.extender.OwnerForUI = null;
|
||||
}
|
||||
this.Instance.Create();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Instance.Alter();
|
||||
}
|
||||
}
|
||||
|
||||
//Members Page Related actions
|
||||
this.extender.RefreshRoleMembersHash();
|
||||
Dictionary<string, bool> memberNameIsMemberHash = this.extender.MemberNameIsMemberHash;
|
||||
|
||||
StringCollection membersToBeDropped = new StringCollection();
|
||||
StringCollection membersToBeAdded = new StringCollection();
|
||||
|
||||
StringCollection membershipsToBeDropped = new StringCollection();
|
||||
StringCollection membershipsToBeAdded = new StringCollection();
|
||||
|
||||
foreach (string memberName in memberNameIsMemberHash.Keys)
|
||||
{
|
||||
if (memberNameIsMemberHash[memberName]) //if added as member
|
||||
{
|
||||
membersToBeAdded.Add(memberName);
|
||||
}
|
||||
else //if dropped from members
|
||||
{
|
||||
membersToBeDropped.Add(memberName);
|
||||
}
|
||||
}
|
||||
|
||||
//Membership page Related actions
|
||||
this.extender.RefreshServerRoleNameHasMembershipHash();
|
||||
Dictionary<string, bool> membershipInfoHash = this.extender.ServerRoleNameHasMembershipHash;
|
||||
|
||||
foreach (string serverRoleName in membershipInfoHash.Keys)
|
||||
{
|
||||
if (membershipInfoHash[serverRoleName]) //If new membership added
|
||||
{
|
||||
membershipsToBeAdded.Add(serverRoleName);
|
||||
}
|
||||
else //If now not a member of
|
||||
{
|
||||
membershipsToBeDropped.Add(serverRoleName);
|
||||
}
|
||||
}
|
||||
|
||||
//First dropping members and memberships
|
||||
foreach (string member in membersToBeDropped)
|
||||
{
|
||||
this.serverRole.DropMember(member);
|
||||
}
|
||||
|
||||
foreach (string containingRole in membershipsToBeDropped)
|
||||
{
|
||||
this.serverRole.DropMembershipFromRole(containingRole);
|
||||
}
|
||||
|
||||
//Now adding members and memberships.
|
||||
foreach (string member in membersToBeAdded)
|
||||
{
|
||||
this.serverRole.AddMember(member);
|
||||
}
|
||||
|
||||
foreach (string containingRole in membershipsToBeAdded)
|
||||
{
|
||||
this.serverRole.AddMembershipToRole(containingRole);
|
||||
}
|
||||
}
|
||||
|
||||
// protected System.Collections.Specialized.StringCollection GetScriptStrings(ITaskExecutionContext context)
|
||||
// {
|
||||
// StringCollection script = this.GetScriptForCreateOrManage();
|
||||
|
||||
// StringCollection permissionScript = this.GetPermissionRelatedScripts();
|
||||
|
||||
// foreach (string str in permissionScript)
|
||||
// {
|
||||
// script.Add(str);
|
||||
// }
|
||||
|
||||
// if (script.Count == 0)
|
||||
// {
|
||||
// //When the user tries to script and no changes have been made.
|
||||
// // throw new SsmsException(SR.NoActionToBeScripted);
|
||||
// }
|
||||
// return script;
|
||||
// }
|
||||
|
||||
private StringCollection GetPermissionRelatedScripts()
|
||||
{
|
||||
StringCollection script = new StringCollection();
|
||||
|
||||
if (this.extender.GeneralPageDataContainer != null) //Permission controls have been initialized.
|
||||
{
|
||||
//For General Page permissions
|
||||
ServerConnection permControlConn = ((CDataContainer)this.extender.GeneralPageDataContainer).ServerConnection;
|
||||
SqlExecutionModes em = permControlConn.SqlExecutionModes;
|
||||
|
||||
//PermissionUI has a cloned connection.
|
||||
permControlConn.CapturedSql.Clear();
|
||||
permControlConn.SqlExecutionModes = SqlExecutionModes.CaptureSql;
|
||||
|
||||
//This will run General page's permission related actions.
|
||||
EventHandler tempEventHandler = (EventHandler)this.extender.GeneralPageOnRunNow;
|
||||
if (tempEventHandler != null)
|
||||
{
|
||||
tempEventHandler(this, new EventArgs());
|
||||
}
|
||||
|
||||
script = permControlConn.CapturedSql.Text;
|
||||
permControlConn.SqlExecutionModes = em;
|
||||
}
|
||||
|
||||
return script;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates script for Create and Properties.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private StringCollection GetScriptForCreateOrManage()
|
||||
{
|
||||
StringCollection script = new StringCollection();
|
||||
|
||||
Server svr = this.Instance.Parent;
|
||||
svr.ConnectionContext.CapturedSql.Clear();
|
||||
SqlExecutionModes em = svr.ConnectionContext.SqlExecutionModes;
|
||||
|
||||
svr.ConnectionContext.SqlExecutionModes = SqlExecutionModes.CaptureSql;
|
||||
|
||||
//T-SQL Capturing starts.
|
||||
|
||||
this.CreateOrManage();
|
||||
|
||||
//T-SQL capturing ends
|
||||
script = svr.ConnectionContext.CapturedSql.Text;
|
||||
svr.ConnectionContext.SqlExecutionModes = em;
|
||||
|
||||
return script;
|
||||
}
|
||||
|
||||
protected string GetServerRoleName()
|
||||
{
|
||||
return "ServerRole-" + DateTime.Now.ToString("yyyyMMdd-HHmmss",SmoApplication.DefaultCulture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected void Dispose(bool disposing)
|
||||
{
|
||||
// if (disposing && (components != null))
|
||||
// {
|
||||
// components.Dispose();
|
||||
// }
|
||||
// base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
public class ServerRoleCreateTaskFormComponent : ServerRoleManageTaskFormComponent
|
||||
{
|
||||
public ServerRoleCreateTaskFormComponent()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public ServerRoleCreateTaskFormComponent(IContainer container)
|
||||
: base(container)
|
||||
{
|
||||
}
|
||||
|
||||
protected ServerRole CreateSmoObjectInstance()
|
||||
{
|
||||
#pragma warning disable IDE0074 // Use compound assignment
|
||||
if (this.serverRole == null)
|
||||
{
|
||||
this.serverRole = new ServerRole(this.Server, GetServerRoleName());
|
||||
}
|
||||
#pragma warning restore IDE0074 // Use compound assignment
|
||||
return this.serverRole;
|
||||
}
|
||||
|
||||
protected string MethodName
|
||||
{
|
||||
get
|
||||
{
|
||||
return "Create";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
#nullable disable
|
||||
#region Using directives
|
||||
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// String comparer that uses the case sensitivity and other settings
|
||||
/// from a particular SQL collation
|
||||
/// </summary>
|
||||
#if DEBUG || EXPOSE_MANAGED_INTERNALS
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
class SqlCollationSensitiveStringComparer : IComparer
|
||||
{
|
||||
private CompareOptions compareOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="sqlCollation">The name of the SQL collation, like ALGERIAN_CI_AI</param>
|
||||
public SqlCollationSensitiveStringComparer(string sqlCollation)
|
||||
{
|
||||
if (sqlCollation != null && sqlCollation.Length != 0)
|
||||
{
|
||||
this.compareOptions = SqlSupport.GetCompareOptionsFromCollation(sqlCollation);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.compareOptions = CompareOptions.Ordinal;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="compareOptions">The CompareOptions for the SQL collation</param>
|
||||
public SqlCollationSensitiveStringComparer(CompareOptions compareOptions)
|
||||
{
|
||||
this.compareOptions = compareOptions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two strings
|
||||
/// </summary>
|
||||
/// <param name="x">The first string to compare</param>
|
||||
/// <param name="y">The second string to compare</param>
|
||||
/// <returns>Less than zero if x is less than y, 0 if x equals y, greater than zero if x is greater than y</returns>
|
||||
public int Compare(object x, object y)
|
||||
{
|
||||
if (null == x && null == y)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (null != x && null == y)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (null == x && null != y)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CultureInfo.InvariantCulture.CompareInfo.Compare((string) x, (string) y, compareOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// 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.Sdk.Sfc;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlTools.ServiceLayer.Management;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
internal class UserActions : ManagementActionBase
|
||||
{
|
||||
#region Variables
|
||||
private UserPrototype userPrototype;
|
||||
private ConfigAction configAction;
|
||||
#endregion
|
||||
|
||||
#region Constructors / Dispose
|
||||
/// <summary>
|
||||
/// Handle user create and update actions
|
||||
/// </summary>
|
||||
public UserActions(
|
||||
CDataContainer dataContainer,
|
||||
ConfigAction configAction,
|
||||
UserInfo user,
|
||||
UserPrototypeData? originalData)
|
||||
{
|
||||
this.DataContainer = dataContainer;
|
||||
this.IsDatabaseOperation = true;
|
||||
this.configAction = configAction;
|
||||
|
||||
ExhaustiveUserTypes currentUserType;
|
||||
if (dataContainer.IsNewObject)
|
||||
{
|
||||
currentUserType = UserActions.GetUserTypeForUserInfo(user);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentUserType = UserActions.GetCurrentUserTypeForExistingUser(dataContainer.Server.GetSmoObject(dataContainer.ObjectUrn) as User);
|
||||
}
|
||||
|
||||
this.userPrototype = UserPrototypeFactory.GetUserPrototype(dataContainer, user, originalData, currentUserType);
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// Clean up any resources being used.
|
||||
// /// </summary>
|
||||
// protected override void Dispose(bool disposing)
|
||||
// {
|
||||
// base.Dispose(disposing);
|
||||
// }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// called by the management actions framework to execute the action
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
public override void OnRunNow(object sender)
|
||||
{
|
||||
if (this.configAction != ConfigAction.Drop)
|
||||
{
|
||||
this.userPrototype.ApplyChanges(this.ParentDb);
|
||||
}
|
||||
}
|
||||
|
||||
internal static ExhaustiveUserTypes GetUserTypeForUserInfo(UserInfo user)
|
||||
{
|
||||
ExhaustiveUserTypes userType = ExhaustiveUserTypes.LoginMappedUser;
|
||||
switch (user.Type)
|
||||
{
|
||||
case DatabaseUserType.WithLogin:
|
||||
userType = ExhaustiveUserTypes.LoginMappedUser;
|
||||
break;
|
||||
case DatabaseUserType.WithWindowsGroupLogin:
|
||||
userType = ExhaustiveUserTypes.WindowsUser;
|
||||
break;
|
||||
case DatabaseUserType.Contained:
|
||||
if (user.AuthenticationType == ServerAuthenticationType.AzureActiveDirectory)
|
||||
{
|
||||
userType = ExhaustiveUserTypes.ExternalUser;
|
||||
}
|
||||
else
|
||||
{
|
||||
userType = ExhaustiveUserTypes.SqlUserWithPassword;
|
||||
}
|
||||
break;
|
||||
case DatabaseUserType.NoConnectAccess:
|
||||
userType = ExhaustiveUserTypes.SqlUserWithoutLogin;
|
||||
break;
|
||||
}
|
||||
return userType;
|
||||
}
|
||||
|
||||
internal static DatabaseUserType GetDatabaseUserTypeForUserType(ExhaustiveUserTypes userType)
|
||||
{
|
||||
DatabaseUserType databaseUserType = DatabaseUserType.WithLogin;
|
||||
switch (userType)
|
||||
{
|
||||
case ExhaustiveUserTypes.LoginMappedUser:
|
||||
databaseUserType = DatabaseUserType.WithLogin;
|
||||
break;
|
||||
case ExhaustiveUserTypes.WindowsUser:
|
||||
databaseUserType = DatabaseUserType.WithWindowsGroupLogin;
|
||||
break;
|
||||
case ExhaustiveUserTypes.SqlUserWithPassword:
|
||||
databaseUserType = DatabaseUserType.Contained;
|
||||
break;
|
||||
case ExhaustiveUserTypes.SqlUserWithoutLogin:
|
||||
databaseUserType = DatabaseUserType.NoConnectAccess;
|
||||
break;
|
||||
case ExhaustiveUserTypes.ExternalUser:
|
||||
databaseUserType = DatabaseUserType.Contained;
|
||||
break;
|
||||
}
|
||||
return databaseUserType;
|
||||
}
|
||||
|
||||
internal static ExhaustiveUserTypes GetCurrentUserTypeForExistingUser(User? user)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
return ExhaustiveUserTypes.Unknown;
|
||||
}
|
||||
|
||||
switch (user.UserType)
|
||||
{
|
||||
case UserType.SqlUser:
|
||||
if (user.IsSupportedProperty("AuthenticationType"))
|
||||
{
|
||||
if (user.AuthenticationType == AuthenticationType.Windows)
|
||||
{
|
||||
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;
|
||||
case UserType.External:
|
||||
return ExhaustiveUserTypes.ExternalUser;
|
||||
default:
|
||||
return ExhaustiveUserTypes.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsParentDatabaseContained(Urn parentDbUrn, Server server)
|
||||
{
|
||||
string parentDbName = parentDbUrn.GetNameForType("Database");
|
||||
return IsParentDatabaseContained(server.Databases[parentDbName]);
|
||||
}
|
||||
|
||||
internal static bool IsParentDatabaseContained(Database parentDatabase)
|
||||
{
|
||||
return parentDatabase.IsSupportedProperty("ContainmentType")
|
||||
&& parentDatabase.ContainmentType == ContainmentType.Partial;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,283 @@
|
||||
//
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlServer.Management.Sdk.Sfc;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Management;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// User object type handler
|
||||
/// </summary>
|
||||
public class UserHandler : ObjectTypeHandler<UserInfo, UserViewContext>
|
||||
{
|
||||
public UserHandler(ConnectionService connectionService) : base(connectionService)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CanHandleType(SqlObjectType objectType)
|
||||
{
|
||||
return objectType == SqlObjectType.User;
|
||||
}
|
||||
|
||||
public override async Task<InitializeViewResult> InitializeObjectView(Contracts.InitializeViewRequestParams parameters)
|
||||
{
|
||||
// check input parameters
|
||||
if (string.IsNullOrWhiteSpace(parameters.Database))
|
||||
{
|
||||
throw new ArgumentNullException("parameters.Database");
|
||||
}
|
||||
|
||||
// open a connection for running the user dialog and associated task
|
||||
ConnectionInfo originalConnInfo;
|
||||
this.ConnectionService.TryFindConnection(parameters.ConnectionUri, out originalConnInfo);
|
||||
if (originalConnInfo == null)
|
||||
{
|
||||
throw new ArgumentException("Invalid connection URI '{0}'", parameters.ConnectionUri);
|
||||
}
|
||||
string originalDatabaseName = originalConnInfo.ConnectionDetails.DatabaseName;
|
||||
try
|
||||
{
|
||||
originalConnInfo.ConnectionDetails.DatabaseName = parameters.Database;
|
||||
ConnectParams connectParams = new ConnectParams
|
||||
{
|
||||
OwnerUri = parameters.ContextId,
|
||||
Connection = originalConnInfo.ConnectionDetails,
|
||||
Type = Connection.ConnectionType.Default
|
||||
};
|
||||
await this.ConnectionService.Connect(connectParams);
|
||||
}
|
||||
finally
|
||||
{
|
||||
originalConnInfo.ConnectionDetails.DatabaseName = originalDatabaseName;
|
||||
}
|
||||
ConnectionInfo connInfo;
|
||||
this.ConnectionService.TryFindConnection(parameters.ContextId, out connInfo);
|
||||
|
||||
// create a default user data context and database object
|
||||
CDataContainer dataContainer = CreateUserDataContainer(connInfo, null, ConfigAction.Create, parameters.Database);
|
||||
string databaseUrn = string.Format(System.Globalization.CultureInfo.InvariantCulture,
|
||||
"Server/Database[@Name='{0}']", Urn.EscapeString(parameters.Database));
|
||||
Database parentDb = dataContainer.Server.GetSmoObject(databaseUrn) as Database;
|
||||
|
||||
var languageOptions = LanguageUtils.GetDefaultLanguageOptions(dataContainer);
|
||||
var languageOptionsList = languageOptions.Select(LanguageUtils.FormatLanguageDisplay).ToList();
|
||||
languageOptionsList.Insert(0, SR.DefaultLanguagePlaceholder);
|
||||
|
||||
// if viewing an exisitng user then populate some properties
|
||||
UserInfo userInfo = null;
|
||||
string defaultLanguageAlias = null;
|
||||
ExhaustiveUserTypes userType = ExhaustiveUserTypes.LoginMappedUser;
|
||||
if (!parameters.IsNewObject)
|
||||
{
|
||||
User existingUser = dataContainer.Server.GetSmoObject(parameters.ObjectUrn) as User;
|
||||
userType = UserActions.GetCurrentUserTypeForExistingUser(existingUser);
|
||||
DatabaseUserType databaseUserType = UserActions.GetDatabaseUserTypeForUserType(userType);
|
||||
|
||||
// if contained user determine if SQL or AAD auth type
|
||||
ServerAuthenticationType authenticationType =
|
||||
(databaseUserType == DatabaseUserType.Contained && userType == ExhaustiveUserTypes.ExternalUser)
|
||||
? ServerAuthenticationType.AzureActiveDirectory : ServerAuthenticationType.Sql;
|
||||
|
||||
userInfo = new UserInfo()
|
||||
{
|
||||
Type = databaseUserType,
|
||||
AuthenticationType = authenticationType,
|
||||
Name = existingUser.Name,
|
||||
LoginName = existingUser.Login,
|
||||
DefaultSchema = existingUser.DefaultSchema,
|
||||
};
|
||||
|
||||
// Default language is only applicable for users inside a contained database.
|
||||
if (LanguageUtils.IsDefaultLanguageSupported(dataContainer.Server)
|
||||
&& parentDb.ContainmentType != ContainmentType.None)
|
||||
{
|
||||
defaultLanguageAlias = LanguageUtils.GetLanguageAliasFromName(
|
||||
existingUser.Parent.Parent,
|
||||
existingUser.DefaultLanguage.Name);
|
||||
}
|
||||
}
|
||||
|
||||
// generate a user prototype
|
||||
UserPrototype currentUserPrototype = UserPrototypeFactory.GetUserPrototype(dataContainer, userInfo, originalData: null, userType);
|
||||
|
||||
// get the default schema if available
|
||||
string defaultSchema = null;
|
||||
IUserPrototypeWithDefaultSchema defaultSchemaPrototype = currentUserPrototype as IUserPrototypeWithDefaultSchema;
|
||||
if (defaultSchemaPrototype != null && defaultSchemaPrototype.IsDefaultSchemaSupported)
|
||||
{
|
||||
defaultSchema = defaultSchemaPrototype.DefaultSchema;
|
||||
}
|
||||
|
||||
ServerConnection serverConnection = dataContainer.ServerConnection;
|
||||
bool isSqlAzure = serverConnection.DatabaseEngineType == DatabaseEngineType.SqlAzureDatabase;
|
||||
bool supportsContainedUser = isSqlAzure || UserActions.IsParentDatabaseContained(parentDb);
|
||||
|
||||
// set default alias to <default> if needed
|
||||
if (string.IsNullOrEmpty(defaultLanguageAlias)
|
||||
&& supportsContainedUser
|
||||
&& LanguageUtils.IsDefaultLanguageSupported(dataContainer.Server))
|
||||
{
|
||||
defaultLanguageAlias = SR.DefaultLanguagePlaceholder;
|
||||
}
|
||||
|
||||
// set the fake password placeholder when editing an existing user
|
||||
string password = null;
|
||||
IUserPrototypeWithPassword userWithPwdPrototype = currentUserPrototype as IUserPrototypeWithPassword;
|
||||
if (userWithPwdPrototype != null && !parameters.IsNewObject)
|
||||
{
|
||||
userWithPwdPrototype.Password = DatabaseUtils.GetReadOnlySecureString(LoginPrototype.fakePassword);
|
||||
userWithPwdPrototype.PasswordConfirm = DatabaseUtils.GetReadOnlySecureString(LoginPrototype.fakePassword);
|
||||
password = LoginPrototype.fakePassword;
|
||||
}
|
||||
|
||||
// get the login name if it exists
|
||||
string loginName = null;
|
||||
IUserPrototypeWithMappedLogin mappedLoginPrototype = currentUserPrototype as IUserPrototypeWithMappedLogin;
|
||||
if (mappedLoginPrototype != null)
|
||||
{
|
||||
loginName = mappedLoginPrototype.LoginName;
|
||||
}
|
||||
|
||||
// populate user's role assignments
|
||||
List<string> databaseRoles = new List<string>();
|
||||
foreach (string role in currentUserPrototype.DatabaseRoleNames)
|
||||
{
|
||||
if (currentUserPrototype.IsRoleMember(role))
|
||||
{
|
||||
databaseRoles.Add(role);
|
||||
}
|
||||
}
|
||||
|
||||
// populate user's schema ownerships
|
||||
List<string> schemaNames = new List<string>();
|
||||
foreach (string schema in currentUserPrototype.SchemaNames)
|
||||
{
|
||||
if (currentUserPrototype.IsSchemaOwner(schema))
|
||||
{
|
||||
schemaNames.Add(schema);
|
||||
}
|
||||
}
|
||||
|
||||
UserViewInfo userViewInfo = new UserViewInfo()
|
||||
{
|
||||
ObjectInfo = new UserInfo()
|
||||
{
|
||||
Type = userInfo?.Type ?? DatabaseUserType.WithLogin,
|
||||
AuthenticationType = userInfo?.AuthenticationType ?? ServerAuthenticationType.Sql,
|
||||
Name = currentUserPrototype.Name,
|
||||
LoginName = loginName,
|
||||
Password = password,
|
||||
DefaultSchema = defaultSchema,
|
||||
OwnedSchemas = schemaNames.ToArray(),
|
||||
DatabaseRoles = databaseRoles.ToArray(),
|
||||
DefaultLanguage = LanguageUtils.FormatLanguageDisplay(
|
||||
languageOptions.FirstOrDefault(o => o?.Language.Name == defaultLanguageAlias || o?.Language.Alias == defaultLanguageAlias, null)),
|
||||
},
|
||||
SupportContainedUser = supportsContainedUser,
|
||||
SupportWindowsAuthentication = false,
|
||||
SupportAADAuthentication = currentUserPrototype.AADAuthSupported,
|
||||
SupportSQLAuthentication = true,
|
||||
Languages = languageOptionsList.ToArray(),
|
||||
Schemas = currentUserPrototype.SchemaNames.ToArray(),
|
||||
Logins = DatabaseUtils.LoadSqlLogins(serverConnection),
|
||||
DatabaseRoles = currentUserPrototype.DatabaseRoleNames.ToArray()
|
||||
};
|
||||
var context = new UserViewContext(parameters, serverConnection, currentUserPrototype.CurrentState);
|
||||
return new InitializeViewResult { ViewInfo = userViewInfo, Context = context };
|
||||
}
|
||||
|
||||
public override Task Save(UserViewContext context, UserInfo obj)
|
||||
{
|
||||
ConfigureUser(
|
||||
context.Parameters.ContextId,
|
||||
obj,
|
||||
context.Parameters.IsNewObject ? ConfigAction.Create : ConfigAction.Update,
|
||||
RunType.RunNow,
|
||||
context.Parameters.Database,
|
||||
context.OriginalUserData);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override Task<string> Script(UserViewContext context, UserInfo obj)
|
||||
{
|
||||
var script = ConfigureUser(
|
||||
context.Parameters.ContextId,
|
||||
obj,
|
||||
context.Parameters.IsNewObject ? ConfigAction.Create : ConfigAction.Update,
|
||||
RunType.ScriptToWindow,
|
||||
context.Parameters.Database,
|
||||
context.OriginalUserData);
|
||||
return Task.FromResult(script);
|
||||
}
|
||||
|
||||
internal CDataContainer CreateUserDataContainer(ConnectionInfo connInfo, UserInfo user, ConfigAction configAction, string databaseName)
|
||||
{
|
||||
var serverConnection = ConnectionService.OpenServerConnection(connInfo, "DataContainer");
|
||||
var connectionInfoWithConnection = new SqlConnectionInfoWithConnection();
|
||||
connectionInfoWithConnection.ServerConnection = serverConnection;
|
||||
|
||||
string urn = (configAction == ConfigAction.Update && user != null)
|
||||
? string.Format(System.Globalization.CultureInfo.InvariantCulture,
|
||||
"Server/Database[@Name='{0}']/User[@Name='{1}']",
|
||||
Urn.EscapeString(databaseName),
|
||||
Urn.EscapeString(user.Name))
|
||||
: string.Format(System.Globalization.CultureInfo.InvariantCulture,
|
||||
"Server/Database[@Name='{0}']",
|
||||
Urn.EscapeString(databaseName));
|
||||
|
||||
ActionContext context = new ActionContext(serverConnection, "User", urn);
|
||||
DataContainerXmlGenerator containerXml = new DataContainerXmlGenerator(context);
|
||||
|
||||
if (configAction == ConfigAction.Create)
|
||||
{
|
||||
containerXml.AddProperty("itemtype", "User");
|
||||
}
|
||||
|
||||
XmlDocument xmlDoc = containerXml.GenerateXmlDocument();
|
||||
return CDataContainer.CreateDataContainer(connectionInfoWithConnection, xmlDoc);
|
||||
}
|
||||
|
||||
internal string ConfigureUser(string ownerUri, UserInfo user, ConfigAction configAction, RunType runType, string databaseName, UserPrototypeData originalData)
|
||||
{
|
||||
ConnectionInfo connInfo;
|
||||
this.ConnectionService.TryFindConnection(ownerUri, out connInfo);
|
||||
if (connInfo == null)
|
||||
{
|
||||
throw new ArgumentException("Invalid connection URI '{0}'", ownerUri);
|
||||
}
|
||||
|
||||
string sqlScript = string.Empty;
|
||||
CDataContainer dataContainer = CreateUserDataContainer(connInfo, user, configAction, databaseName);
|
||||
using (var actions = new UserActions(dataContainer, configAction, user, originalData))
|
||||
{
|
||||
var executionHandler = new ExecutonHandler(actions);
|
||||
executionHandler.RunNow(runType, this);
|
||||
if (executionHandler.ExecutionResult == ExecutionMode.Failure)
|
||||
{
|
||||
throw executionHandler.ExecutionFailureException;
|
||||
}
|
||||
|
||||
if (runType == RunType.ScriptToWindow)
|
||||
{
|
||||
sqlScript = executionHandler.ScriptTextFromLastRun;
|
||||
}
|
||||
}
|
||||
|
||||
return sqlScript;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Runtime.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum ServerAuthenticationType
|
||||
{
|
||||
[EnumMember(Value = "Windows")]
|
||||
Windows,
|
||||
[EnumMember(Value = "Sql")]
|
||||
Sql,
|
||||
[EnumMember(Value = "AAD")]
|
||||
AzureActiveDirectory
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum DatabaseUserType
|
||||
{
|
||||
// User with a server level login.
|
||||
[EnumMember(Value = "WithLogin")]
|
||||
WithLogin,
|
||||
// User based on a Windows user/group that has no login, but can connect to the Database Engine through membership in a Windows group.
|
||||
[EnumMember(Value = "WithWindowsGroupLogin")]
|
||||
WithWindowsGroupLogin,
|
||||
// Contained user, authentication is done within the database.
|
||||
[EnumMember(Value = "Contained")]
|
||||
Contained,
|
||||
// User that cannot authenticate.
|
||||
[EnumMember(Value = "NoConnectAccess")]
|
||||
NoConnectAccess
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// a class for storing various user properties
|
||||
/// </summary>
|
||||
public class UserInfo : SqlObject
|
||||
{
|
||||
public DatabaseUserType? Type { get; set; }
|
||||
|
||||
public string? LoginName { get; set; }
|
||||
|
||||
public string? Password { get; set; }
|
||||
|
||||
public string? DefaultSchema { get; set; }
|
||||
|
||||
public string[]? OwnedSchemas { get; set; }
|
||||
|
||||
public string[]? DatabaseRoles { get; set; }
|
||||
|
||||
public ServerAuthenticationType AuthenticationType { get; set; }
|
||||
|
||||
public string? DefaultLanguage { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// 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.Common;
|
||||
using Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
public class UserViewContext : SqlObjectViewContext
|
||||
{
|
||||
public UserViewContext(InitializeViewRequestParams parameters, ServerConnection connection, UserPrototypeData originalUserData) : base(parameters)
|
||||
{
|
||||
this.OriginalUserData = originalUserData;
|
||||
this.Connection = connection;
|
||||
}
|
||||
|
||||
public UserPrototypeData OriginalUserData { get; }
|
||||
|
||||
public ServerConnection Connection { get; }
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.Connection.Disconnect();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// 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.ObjectManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// The information required to render the user view.
|
||||
/// </summary>
|
||||
public class UserViewInfo : SqlObjectViewInfo
|
||||
{
|
||||
public bool SupportContainedUser { get; set; }
|
||||
|
||||
public bool SupportWindowsAuthentication { get; set; }
|
||||
|
||||
public bool SupportAADAuthentication { get; set; }
|
||||
|
||||
public bool SupportSQLAuthentication { get; set; }
|
||||
|
||||
public string[]? Languages { get; set; }
|
||||
|
||||
public string[]? Schemas { get; set; }
|
||||
|
||||
public string[]? Logins { get; set; }
|
||||
|
||||
public string[]? DatabaseRoles { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
//
|
||||
// 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.ObjectManagement
|
||||
{
|
||||
public abstract class SqlObject
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Runtime.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum SqlObjectType
|
||||
{
|
||||
[EnumMember(Value = "Column")]
|
||||
Column,
|
||||
[EnumMember(Value = "Credential")]
|
||||
Credential,
|
||||
[EnumMember(Value = "ServerLevelLogin")]
|
||||
ServerLevelLogin,
|
||||
[EnumMember(Value = "Table")]
|
||||
Table,
|
||||
[EnumMember(Value = "User")]
|
||||
User,
|
||||
[EnumMember(Value = "View")]
|
||||
View
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// 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 System;
|
||||
using Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
public abstract class SqlObjectViewContext : IDisposable
|
||||
{
|
||||
public SqlObjectViewContext(InitializeViewRequestParams parameters)
|
||||
{
|
||||
this.Parameters = parameters;
|
||||
}
|
||||
|
||||
public InitializeViewRequestParams Parameters { get; }
|
||||
|
||||
public abstract void Dispose();
|
||||
}
|
||||
|
||||
public class InitializeViewResult
|
||||
{
|
||||
public SqlObjectViewContext Context { get; set; }
|
||||
public SqlObjectViewInfo ViewInfo { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
#nullable disable
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
public abstract class SqlObjectViewInfo
|
||||
{
|
||||
public SqlObject ObjectInfo { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user