mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-14 01:25:40 -05:00
1209 lines
51 KiB
C#
1209 lines
51 KiB
C#
//
|
|
// 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.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using System.Xml;
|
|
using Microsoft.SqlServer.Management.Common;
|
|
using Microsoft.SqlServer.Management.Dmf;
|
|
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.Hosting;
|
|
using Microsoft.SqlTools.ServiceLayer.Management;
|
|
using Microsoft.SqlTools.ServiceLayer.Security.Contracts;
|
|
using Microsoft.SqlTools.ServiceLayer.Utility;
|
|
|
|
namespace Microsoft.SqlTools.ServiceLayer.Security
|
|
{
|
|
/// <summary>
|
|
/// Main class for Security Service functionality
|
|
/// </summary>
|
|
public sealed class SecurityService : IDisposable
|
|
{
|
|
private bool disposed;
|
|
|
|
private ConnectionService? connectionService;
|
|
|
|
private static readonly Lazy<SecurityService> instance = new Lazy<SecurityService>(() => new SecurityService());
|
|
|
|
private Dictionary<string, string> contextIdToConnectionUriMap = new Dictionary<string, string>();
|
|
|
|
/// <summary>
|
|
/// Construct a new SecurityService instance with default parameters
|
|
/// </summary>
|
|
public SecurityService()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the singleton instance object
|
|
/// </summary>
|
|
public static SecurityService Instance
|
|
{
|
|
get { return instance.Value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Internal for testing purposes only
|
|
/// </summary>
|
|
internal ConnectionService ConnectionServiceInstance
|
|
{
|
|
get
|
|
{
|
|
connectionService ??= ConnectionService.Instance;
|
|
return connectionService;
|
|
}
|
|
|
|
set
|
|
{
|
|
connectionService = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Service host object for sending/receiving requests/events.
|
|
/// Internal for testing purposes.
|
|
/// </summary>
|
|
internal IProtocolEndpoint? ServiceHost
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the Security Service instance
|
|
/// </summary>
|
|
public void InitializeService(ServiceHost serviceHost)
|
|
{
|
|
this.ServiceHost = serviceHost;
|
|
|
|
// Credential request handlers
|
|
this.ServiceHost.SetRequestHandler(CreateCredentialRequest.Type, HandleCreateCredentialRequest, true);
|
|
this.ServiceHost.SetRequestHandler(UpdateCredentialRequest.Type, HandleUpdateCredentialRequest, true);
|
|
this.ServiceHost.SetRequestHandler(DeleteCredentialRequest.Type, HandleDeleteCredentialRequest, true);
|
|
this.ServiceHost.SetRequestHandler(GetCredentialsRequest.Type, HandleGetCredentialsRequest, true);
|
|
|
|
// Login request handlers
|
|
this.ServiceHost.SetRequestHandler(CreateLoginRequest.Type, HandleCreateLoginRequest, true);
|
|
this.ServiceHost.SetRequestHandler(UpdateLoginRequest.Type, HandleUpdateLoginRequest, true);
|
|
this.ServiceHost.SetRequestHandler(DeleteLoginRequest.Type, HandleDeleteLoginRequest, true);
|
|
this.ServiceHost.SetRequestHandler(InitializeLoginViewRequest.Type, HandleInitializeLoginViewRequest, true);
|
|
this.ServiceHost.SetRequestHandler(DisposeLoginViewRequest.Type, HandleDisposeLoginViewRequest, true);
|
|
|
|
// User request handlers
|
|
this.ServiceHost.SetRequestHandler(InitializeUserViewRequest.Type, HandleInitializeUserViewRequest, true);
|
|
this.ServiceHost.SetRequestHandler(CreateUserRequest.Type, HandleCreateUserRequest, true);
|
|
this.ServiceHost.SetRequestHandler(DeleteUserRequest.Type, HandleDeleteUserRequest, true);
|
|
}
|
|
|
|
|
|
#region "Login Handlers"
|
|
|
|
/// <summary>
|
|
/// Handle request to create a login
|
|
/// </summary>
|
|
internal async Task HandleCreateLoginRequest(CreateLoginParams parameters, RequestContext<object> requestContext)
|
|
{
|
|
ConnectionInfo connInfo;
|
|
string ownerUri;
|
|
contextIdToConnectionUriMap.TryGetValue(parameters.ContextId, out ownerUri);
|
|
ConnectionServiceInstance.TryFindConnection(ownerUri, out connInfo);
|
|
|
|
if (connInfo == null)
|
|
{
|
|
// raise error here
|
|
}
|
|
|
|
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
|
|
LoginPrototype prototype = new LoginPrototype(dataContainer.Server, parameters.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
|
|
}
|
|
}
|
|
|
|
prototype.ApplyGeneralChanges(dataContainer.Server);
|
|
|
|
// TODO move this to LoginData
|
|
// TODO support role assignment for Azure
|
|
LoginPrototype newPrototype = new LoginPrototype(dataContainer.Server, dataContainer.Server.Logins[parameters.Login.Name]);
|
|
var _ =newPrototype.ServerRoles.ServerRoleNames;
|
|
|
|
foreach (string role in parameters.Login.ServerRoles)
|
|
{
|
|
newPrototype.ServerRoles.SetMember(role, true);
|
|
}
|
|
|
|
newPrototype.ApplyServerRoleChanges(dataContainer.Server);
|
|
await requestContext.SendResult(new object());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle request to delete a credential
|
|
/// </summary>
|
|
internal async Task HandleDeleteLoginRequest(DeleteLoginParams parameters, RequestContext<object> requestContext)
|
|
{
|
|
ConnectionInfo connInfo;
|
|
ConnectionServiceInstance.TryFindConnection(parameters.ConnectionUri, out connInfo);
|
|
// if (connInfo == null)
|
|
// {
|
|
// // raise an error
|
|
// }
|
|
|
|
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
|
|
Login login = dataContainer.Server?.Logins[parameters.Name];
|
|
|
|
dataContainer.SqlDialogSubject = login;
|
|
DoDropObject(dataContainer);
|
|
|
|
await requestContext.SendResult(new ResultStatus()
|
|
{
|
|
Success = true,
|
|
ErrorMessage = string.Empty
|
|
});
|
|
}
|
|
|
|
internal async Task HandleUpdateLoginRequest(UpdateLoginParams parameters, RequestContext<object> requestContext)
|
|
{
|
|
ConnectionInfo connInfo;
|
|
string ownerUri;
|
|
contextIdToConnectionUriMap.TryGetValue(parameters.ContextId, out ownerUri);
|
|
ConnectionServiceInstance.TryFindConnection(ownerUri, out connInfo);
|
|
if (connInfo == null)
|
|
{
|
|
// raise error here
|
|
}
|
|
|
|
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
|
|
LoginPrototype prototype = new LoginPrototype(dataContainer.Server, dataContainer.Server.Logins[parameters.Login.Name]);
|
|
|
|
var login = parameters.Login;
|
|
prototype.SqlPassword = login.Password;
|
|
if (0 != String.Compare(login.DefaultLanguage, SR.DefaultLanguagePlaceholder, StringComparison.Ordinal))
|
|
{
|
|
prototype.DefaultLanguage = login.DefaultLanguage.Split(" - ")[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);
|
|
}
|
|
|
|
prototype.ApplyGeneralChanges(dataContainer.Server);
|
|
prototype.ApplyServerRoleChanges(dataContainer.Server);
|
|
prototype.ApplyDatabaseRoleChanges(dataContainer.Server);
|
|
await requestContext.SendResult(new object());
|
|
}
|
|
|
|
internal async Task HandleInitializeLoginViewRequest(InitializeLoginViewRequestParams parameters, RequestContext<LoginViewInfo> requestContext)
|
|
{
|
|
contextIdToConnectionUriMap.Add(parameters.ContextId, parameters.ConnectionUri);
|
|
ConnectionInfo connInfo;
|
|
ConnectionServiceInstance.TryFindConnection(parameters.ConnectionUri, out connInfo);
|
|
if (connInfo == null)
|
|
{
|
|
// raise an error
|
|
}
|
|
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 = GetDefaultLanguageOptions(dataContainer);
|
|
var languageOptionsList = languageOptions.Select(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.Logins[parameters.Name]);
|
|
|
|
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 = 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]
|
|
};
|
|
|
|
await requestContext.SendResult(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 = true,
|
|
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
|
|
});
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
internal async Task HandleDisposeLoginViewRequest(DisposeLoginViewRequestParams parameters, RequestContext<object> requestContext)
|
|
{
|
|
await requestContext.SendResult(new object());
|
|
}
|
|
|
|
private string FormatLanguageDisplay(LanguageDisplay? l)
|
|
{
|
|
if (l == null) return null;
|
|
return string.Format("{0} - {1}", l.Language.Alias, l.Language.Name);
|
|
}
|
|
#endregion
|
|
|
|
#region "User Handlers"
|
|
|
|
internal Task<Tuple<bool, string>> ConfigureUser(
|
|
string? ownerUri,
|
|
UserInfo? user,
|
|
ConfigAction configAction,
|
|
RunType runType)
|
|
{
|
|
return Task<Tuple<bool, string>>.Run(() =>
|
|
{
|
|
try
|
|
{
|
|
ConnectionInfo connInfo;
|
|
ConnectionServiceInstance.TryFindConnection(ownerUri, out connInfo);
|
|
if (connInfo == null)
|
|
{
|
|
throw new ArgumentException("Invalid connection URI '{0}'", ownerUri);
|
|
}
|
|
|
|
var serverConnection = ConnectionService.OpenServerConnection(connInfo, "DataContainer");
|
|
var connectionInfoWithConnection = new SqlConnectionInfoWithConnection();
|
|
connectionInfoWithConnection.ServerConnection = serverConnection;
|
|
|
|
string urn = string.Format(System.Globalization.CultureInfo.InvariantCulture,
|
|
"Server/Database[@Name='{0}']",
|
|
Urn.EscapeString(serverConnection.DatabaseName));
|
|
|
|
ActionContext context = new ActionContext(serverConnection, "new_user", urn);
|
|
DataContainerXmlGenerator containerXml = new DataContainerXmlGenerator(context);
|
|
containerXml.AddProperty("itemtype", "User");
|
|
|
|
XmlDocument xmlDoc = containerXml.GenerateXmlDocument();
|
|
bool objectExists = configAction != ConfigAction.Create;
|
|
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connectionInfoWithConnection, xmlDoc);
|
|
|
|
using (var actions = new UserActions(dataContainer, user, 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());
|
|
}
|
|
});
|
|
}
|
|
|
|
private Dictionary<string, SchemaOwnership> LoadSchemas(string databaseName, string dbroleName, ServerConnection serverConnection)
|
|
{
|
|
bool isPropertiesMode = false;
|
|
Dictionary<string, SchemaOwnership> schemaOwnership = new Dictionary<string, SchemaOwnership>();
|
|
|
|
Enumerator en = new Enumerator();
|
|
Request req = new Request();
|
|
req.Fields = new String[] { "Name", "Owner" };
|
|
req.Urn = "Server/Database[@Name='" + Urn.EscapeString(databaseName) + "']/Schema";
|
|
|
|
DataTable dt = en.Process(serverConnection, req);
|
|
System.Diagnostics.Debug.Assert((dt != null) && (0 < dt.Rows.Count), "enumerator did not return schemas");
|
|
System.Diagnostics.Debug.Assert(!isPropertiesMode || (dbroleName.Length != 0), "role name is not known");
|
|
|
|
foreach (DataRow dr in dt.Rows)
|
|
{
|
|
string schemaName = Convert.ToString(dr["Name"], System.Globalization.CultureInfo.InvariantCulture);
|
|
string schemaOwner = Convert.ToString(dr["Owner"], System.Globalization.CultureInfo.InvariantCulture);
|
|
bool roleOwnsSchema = isPropertiesMode && (0 == String.Compare(dbroleName, schemaOwner, StringComparison.Ordinal));
|
|
if (schemaName != null)
|
|
{
|
|
schemaOwnership[schemaName] = new SchemaOwnership(roleOwnsSchema);
|
|
}
|
|
}
|
|
return schemaOwnership;
|
|
}
|
|
|
|
private string[] LoadDatabaseRoles(ServerConnection serverConnection, string databaseName)
|
|
{
|
|
var server = new Server(serverConnection);
|
|
string dbUrn = "Server/Database[@Name='" + Urn.EscapeString(databaseName) + "']";
|
|
Database? parent = server.GetSmoObject(new Urn(dbUrn)) as Database;
|
|
var roles = new List<string>();
|
|
if (parent != null)
|
|
{
|
|
foreach (DatabaseRole dbRole in parent.Roles)
|
|
{
|
|
var comparer = parent.GetStringComparer();
|
|
if (comparer.Compare(dbRole.Name, "public") != 0)
|
|
{
|
|
roles.Add(dbRole.Name);
|
|
}
|
|
}
|
|
}
|
|
return roles.ToArray();
|
|
}
|
|
|
|
private string[] LoadSqlLogins(ServerConnection serverConnection)
|
|
{
|
|
return LoadItems(serverConnection, "Server/Login");
|
|
}
|
|
|
|
private string[] LoadItems(ServerConnection serverConnection, string urn)
|
|
{
|
|
List<string> items = new List<string>();
|
|
Request req = new Request();
|
|
req.Urn = urn;
|
|
req.ResultType = ResultType.IDataReader;
|
|
req.Fields = new string[] { "Name" };
|
|
|
|
Enumerator en = new Enumerator();
|
|
using (IDataReader reader = en.Process(serverConnection, req).Data as IDataReader)
|
|
{
|
|
if (reader != null)
|
|
{
|
|
string name;
|
|
while (reader.Read())
|
|
{
|
|
// Get the permission name
|
|
name = reader.GetString(0);
|
|
items.Add(name);
|
|
}
|
|
}
|
|
}
|
|
return items.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle request to initialize user view
|
|
/// </summary>
|
|
internal async Task HandleInitializeUserViewRequest(InitializeUserViewParams parameters, RequestContext<UserViewInfo> requestContext)
|
|
{
|
|
ConnectionInfo connInfo;
|
|
ConnectionServiceInstance.TryFindConnection(parameters.ConnectionUri, out connInfo);
|
|
if (connInfo == null)
|
|
{
|
|
throw new ArgumentException("Invalid connection URI '{0}'", parameters.ConnectionUri);
|
|
}
|
|
|
|
if (parameters.ContextId != null && parameters.ConnectionUri != null)
|
|
{
|
|
this.contextIdToConnectionUriMap.Add(parameters.ContextId, parameters.ConnectionUri);
|
|
}
|
|
|
|
var serverConnection = ConnectionService.OpenServerConnection(connInfo, "DataContainer");
|
|
|
|
string databaseName = parameters.Database ?? "master";
|
|
var schemaMap = LoadSchemas(databaseName, string.Empty, serverConnection);
|
|
|
|
UserViewInfo userViewInfo = new UserViewInfo()
|
|
{
|
|
ObjectInfo = new UserInfo()
|
|
{
|
|
Type = DatabaseUserType.WithLogin,
|
|
Name = string.Empty,
|
|
LoginName = string.Empty,
|
|
Password = string.Empty,
|
|
DefaultSchema = string.Empty,
|
|
OwnedSchemas = new string[] { },
|
|
DatabaseRoles = new string[] { },
|
|
},
|
|
SupportContainedUser = true,
|
|
SupportWindowsAuthentication = true,
|
|
SupportAADAuthentication = true,
|
|
SupportSQLAuthentication = true,
|
|
Languages = new string[] { },
|
|
Schemas = schemaMap.Keys.ToArray(),
|
|
Logins = LoadSqlLogins(serverConnection),
|
|
DatabaseRoles = LoadDatabaseRoles(serverConnection, databaseName)
|
|
};
|
|
|
|
await requestContext.SendResult(userViewInfo);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle request to create a user
|
|
/// </summary>
|
|
internal async Task HandleCreateUserRequest(CreateUserParams parameters, RequestContext<CreateUserResult> requestContext)
|
|
{
|
|
if (parameters.ContextId == null || !this.contextIdToConnectionUriMap.ContainsKey(parameters.ContextId))
|
|
{
|
|
throw new ArgumentException("Invalid context ID");
|
|
}
|
|
|
|
string connectionUri = this.contextIdToConnectionUriMap[parameters.ContextId];
|
|
var result = await ConfigureUser(connectionUri,
|
|
parameters.User,
|
|
ConfigAction.Create,
|
|
RunType.RunNow);
|
|
|
|
await requestContext.SendResult(new CreateUserResult()
|
|
{
|
|
User = parameters.User,
|
|
Success = result.Item1,
|
|
ErrorMessage = result.Item2
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle request to delete a user
|
|
/// </summary>
|
|
internal async Task HandleDeleteUserRequest(DeleteUserParams parameters, RequestContext<ResultStatus> requestContext)
|
|
{
|
|
ConnectionInfo connInfo;
|
|
ConnectionServiceInstance.TryFindConnection(parameters.ConnectionUri, out connInfo);
|
|
// if (connInfo == null)
|
|
// {
|
|
// // raise an error
|
|
// }
|
|
|
|
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
|
|
string dbUrn = "Server/Database[@Name='" + Urn.EscapeString(parameters.Database) + "']";
|
|
Database? parent = dataContainer.Server.GetSmoObject(new Urn(dbUrn)) as Database;
|
|
User user = parent.Users[parameters.Name];
|
|
dataContainer.SqlDialogSubject = user;
|
|
DoDropObject(dataContainer);
|
|
|
|
await requestContext.SendResult(new ResultStatus()
|
|
{
|
|
Success = true,
|
|
ErrorMessage = string.Empty
|
|
});
|
|
}
|
|
|
|
private IList<LanguageDisplay> GetDefaultLanguageOptions(CDataContainer dataContainer)
|
|
{
|
|
// this.defaultLanguageComboBox.Items.Clear();
|
|
// this.defaultLanguageComboBox.Items.Add(defaultLanguagePlaceholder);
|
|
|
|
// sort the languages alphabetically by alias
|
|
SortedList sortedLanguages = new SortedList(Comparer.Default);
|
|
|
|
LanguageUtils.SetLanguageDefaultInitFieldsForDefaultLanguages(dataContainer.Server);
|
|
if (dataContainer.Server != null && dataContainer.Server.Languages != null)
|
|
{
|
|
foreach (Language language in dataContainer.Server.Languages)
|
|
{
|
|
LanguageDisplay listValue = new LanguageDisplay(language);
|
|
sortedLanguages.Add(language.Alias, listValue);
|
|
}
|
|
}
|
|
|
|
IList<LanguageDisplay> res = new List<LanguageDisplay>();
|
|
foreach (LanguageDisplay ld in sortedLanguages.Values)
|
|
{
|
|
res.Add(ld);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
// code needs to be ported into the useraction class
|
|
// public void UserMemberships_OnRunNow(object sender, CDataContainer dataContainer)
|
|
// {
|
|
// UserPrototype currentPrototype = UserPrototypeFactory.GetInstance(dataContainer).CurrentPrototype;
|
|
|
|
// //In case the UserGeneral/OwnedSchemas pages are loaded,
|
|
// //those will takes care of applying membership changes also.
|
|
// //Hence, we only need to apply changes in this method when those are not loaded.
|
|
// if (!currentPrototype.IsRoleMembershipChangesApplied)
|
|
// {
|
|
// //base.OnRunNow(sender);
|
|
|
|
// User user = currentPrototype.ApplyChanges();
|
|
|
|
// //this.ExecutionMode = ExecutionMode.Success;
|
|
// dataContainer.ObjectName = currentPrototype.Name;
|
|
// dataContainer.SqlDialogSubject = user;
|
|
// }
|
|
|
|
// //setting back to original after changes are applied
|
|
// currentPrototype.IsRoleMembershipChangesApplied = false;
|
|
// }
|
|
|
|
// /// <summary>
|
|
// /// implementation of OnPanelRunNow
|
|
// /// </summary>
|
|
// /// <param name="node"></param>
|
|
// public void UserOwnedSchemas_OnRunNow(object sender, CDataContainer dataContainer)
|
|
// {
|
|
// UserPrototype currentPrototype = UserPrototypeFactory.GetInstance(dataContainer).CurrentPrototype;
|
|
|
|
// //In case the UserGeneral/Membership pages are loaded,
|
|
// //those will takes care of applying schema ownership changes also.
|
|
// //Hence, we only need to apply changes in this method when those are not loaded.
|
|
// if (!currentPrototype.IsSchemaOwnershipChangesApplied)
|
|
// {
|
|
// //base.OnRunNow(sender);
|
|
|
|
// User user = currentPrototype.ApplyChanges();
|
|
|
|
// //this.ExecutionMode = ExecutionMode.Success;
|
|
// dataContainer.ObjectName = currentPrototype.Name;
|
|
// dataContainer.SqlDialogSubject = user;
|
|
// }
|
|
|
|
// //setting back to original after changes are applied
|
|
// currentPrototype.IsSchemaOwnershipChangesApplied = false;
|
|
// }
|
|
|
|
// how to populate defaults from prototype, will delete once refactored
|
|
// private void InitializeValuesInUiControls()
|
|
// {
|
|
|
|
// IUserPrototypeWithDefaultLanguage defaultLanguagePrototype = this.currentUserPrototype
|
|
// as IUserPrototypeWithDefaultLanguage;
|
|
// if (defaultLanguagePrototype != null
|
|
// && defaultLanguagePrototype.IsDefaultLanguageSupported)
|
|
// {
|
|
// string defaultLanguageAlias = defaultLanguagePrototype.DefaultLanguageAlias;
|
|
|
|
// //If engine returns default language as empty or null, that means the default language of
|
|
// //database will be used.
|
|
// //Default language is not applicable for users inside an uncontained authentication.
|
|
// if (string.IsNullOrEmpty(defaultLanguageAlias)
|
|
// && (this.DataContainer.Server.GetSmoObject(this.parentDbUrn) as Database).ContainmentType != ContainmentType.None)
|
|
// {
|
|
// defaultLanguageAlias = this.defaultLanguagePlaceholder;
|
|
// }
|
|
// this.defaultLanguageComboBox.Text = defaultLanguageAlias;
|
|
// }
|
|
|
|
// IUserPrototypeWithDefaultSchema defaultSchemaPrototype = this.currentUserPrototype
|
|
// as IUserPrototypeWithDefaultSchema;
|
|
// if (defaultSchemaPrototype != null
|
|
// && defaultSchemaPrototype.IsDefaultSchemaSupported)
|
|
// {
|
|
// this.defaultSchemaTextBox.Text = defaultSchemaPrototype.DefaultSchema;
|
|
// }
|
|
// IUserPrototypeWithPassword userWithPwdPrototype = this.currentUserPrototype
|
|
// as IUserPrototypeWithPassword;
|
|
// if (userWithPwdPrototype != null
|
|
// && !this.DataContainer.IsNewObject)
|
|
// {
|
|
// this.passwordTextBox.Text = FAKE_PASSWORD;
|
|
// this.confirmPwdTextBox.Text = FAKE_PASSWORD;
|
|
// }
|
|
// }
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
#region "Credential Handlers"
|
|
|
|
/// <summary>
|
|
/// Handle request to create a credential
|
|
/// </summary>
|
|
internal async Task HandleCreateCredentialRequest(CreateCredentialParams parameters, RequestContext<CredentialResult> requestContext)
|
|
{
|
|
var result = await ConfigureCredential(parameters.OwnerUri,
|
|
parameters.Credential,
|
|
ConfigAction.Create,
|
|
RunType.RunNow);
|
|
|
|
await requestContext.SendResult(new CredentialResult()
|
|
{
|
|
Credential = parameters.Credential,
|
|
Success = result.Item1,
|
|
ErrorMessage = result.Item2
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle request to update a credential
|
|
/// </summary>
|
|
internal async Task HandleUpdateCredentialRequest(UpdateCredentialParams parameters, RequestContext<CredentialResult> requestContext)
|
|
{
|
|
var result = await ConfigureCredential(parameters.OwnerUri,
|
|
parameters.Credential,
|
|
ConfigAction.Update,
|
|
RunType.RunNow);
|
|
|
|
await requestContext.SendResult(new CredentialResult()
|
|
{
|
|
Credential = parameters.Credential,
|
|
Success = result.Item1,
|
|
ErrorMessage = result.Item2
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle request to delete a credential
|
|
/// </summary>
|
|
internal async Task HandleDeleteCredentialRequest(DeleteCredentialParams parameters, RequestContext<ResultStatus> requestContext)
|
|
{
|
|
var result = await ConfigureCredential(parameters.OwnerUri,
|
|
parameters.Credential,
|
|
ConfigAction.Drop,
|
|
RunType.RunNow);
|
|
|
|
await requestContext.SendResult(new ResultStatus()
|
|
{
|
|
Success = result.Item1,
|
|
ErrorMessage = result.Item2
|
|
});
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Handle request to get all credentials
|
|
/// </summary>
|
|
internal async Task HandleGetCredentialsRequest(GetCredentialsParams parameters, RequestContext<GetCredentialsResult> requestContext)
|
|
{
|
|
var result = new GetCredentialsResult();
|
|
try
|
|
{
|
|
ConnectionInfo connInfo;
|
|
ConnectionServiceInstance.TryFindConnection(parameters.OwnerUri, out connInfo);
|
|
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
|
|
|
|
var credentials = dataContainer.Server?.Credentials;
|
|
int credentialsCount = credentials != null ? credentials.Count : 0;
|
|
CredentialInfo[] credentialsInfos = new CredentialInfo[credentialsCount];
|
|
if (credentials != null)
|
|
{
|
|
for (int i = 0; i < credentialsCount; ++i)
|
|
{
|
|
credentialsInfos[i] = new CredentialInfo();
|
|
credentialsInfos[i].Name = credentials[i].Name;
|
|
credentialsInfos[i].Identity = credentials[i].Identity;
|
|
credentialsInfos[i].Id = credentials[i].ID;
|
|
credentialsInfos[i].DateLastModified = credentials[i].DateLastModified;
|
|
credentialsInfos[i].CreateDate = credentials[i].CreateDate;
|
|
credentialsInfos[i].ProviderName = credentials[i].ProviderName;
|
|
}
|
|
}
|
|
result.Credentials = credentialsInfos;
|
|
result.Success = true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
result.Success = false;
|
|
result.ErrorMessage = ex.ToString();
|
|
}
|
|
|
|
await requestContext.SendResult(result);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Disposes the service
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
if (!disposed)
|
|
{
|
|
disposed = true;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region "Helpers"
|
|
|
|
internal Task<Tuple<bool, string>> ConfigureCredential(
|
|
string ownerUri,
|
|
CredentialInfo credential,
|
|
ConfigAction configAction,
|
|
RunType runType)
|
|
{
|
|
return Task<Tuple<bool, string>>.Run(() =>
|
|
{
|
|
try
|
|
{
|
|
ConnectionInfo connInfo;
|
|
ConnectionServiceInstance.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());
|
|
}
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// this is the main method that is called by DropAllObjects for every object
|
|
/// in the grid
|
|
/// </summary>
|
|
/// <param name="objectRowNumber"></param>
|
|
private void DoDropObject(CDataContainer dataContainer)
|
|
{
|
|
// if a server isn't connected then there is nothing to do
|
|
if (dataContainer.Server == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var executionMode = dataContainer.Server.ConnectionContext.SqlExecutionModes;
|
|
var subjectExecutionMode = executionMode;
|
|
|
|
//For Azure the ExecutionManager is different depending on which ExecutionManager
|
|
//used - one at the Server level and one at the Database level. So to ensure we
|
|
//don't use the wrong execution mode we need to set the mode for both (for on-prem
|
|
//this will essentially be a no-op)
|
|
SqlSmoObject sqlDialogSubject = null;
|
|
try
|
|
{
|
|
sqlDialogSubject = dataContainer.SqlDialogSubject;
|
|
}
|
|
catch (System.Exception)
|
|
{
|
|
//We may not have a valid dialog subject here (such as if the object hasn't been created yet)
|
|
//so in that case we'll just ignore it as that's a normal scenario.
|
|
}
|
|
if (sqlDialogSubject != null)
|
|
{
|
|
subjectExecutionMode =
|
|
sqlDialogSubject.ExecutionManager.ConnectionContext.SqlExecutionModes;
|
|
}
|
|
|
|
Urn objUrn = sqlDialogSubject?.Urn;
|
|
System.Diagnostics.Debug.Assert(objUrn != null);
|
|
|
|
SfcObjectQuery objectQuery = new SfcObjectQuery(dataContainer.Server);
|
|
|
|
IDroppable droppableObj = null;
|
|
string[] fields = null;
|
|
|
|
foreach (object obj in objectQuery.ExecuteIterator(new SfcQueryExpression(objUrn.ToString()), fields, null))
|
|
{
|
|
System.Diagnostics.Debug.Assert(droppableObj == null, "there is only one object");
|
|
droppableObj = obj as IDroppable;
|
|
}
|
|
|
|
// For Azure databases, the SfcObjectQuery executions above may have overwritten our desired execution mode, so restore it
|
|
dataContainer.Server.ConnectionContext.SqlExecutionModes = executionMode;
|
|
if (sqlDialogSubject != null)
|
|
{
|
|
sqlDialogSubject.ExecutionManager.ConnectionContext.SqlExecutionModes = subjectExecutionMode;
|
|
}
|
|
|
|
if (droppableObj == null)
|
|
{
|
|
string objectName = objUrn.GetAttribute("Name");
|
|
objectName ??= string.Empty;
|
|
throw new Microsoft.SqlServer.Management.Smo.MissingObjectException("DropObjectsSR.ObjectDoesNotExist(objUrn.Type, objectName)");
|
|
}
|
|
|
|
//special case database drop - see if we need to delete backup and restore history
|
|
SpecialPreDropActionsForObject(dataContainer, droppableObj,
|
|
deleteBackupRestoreOrDisableAuditSpecOrDisableAudit: false,
|
|
dropOpenConnections: false);
|
|
|
|
droppableObj.Drop();
|
|
|
|
//special case Resource Governor reconfigure - for pool, external pool, group Drop(), we need to issue
|
|
SpecialPostDropActionsForObject(dataContainer, droppableObj);
|
|
|
|
}
|
|
|
|
private void SpecialPreDropActionsForObject(CDataContainer dataContainer, IDroppable droppableObj,
|
|
bool deleteBackupRestoreOrDisableAuditSpecOrDisableAudit, bool dropOpenConnections)
|
|
{
|
|
// if a server isn't connected then there is nothing to do
|
|
if (dataContainer.Server == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Database db = droppableObj as Database;
|
|
|
|
if (deleteBackupRestoreOrDisableAuditSpecOrDisableAudit)
|
|
{
|
|
if (db != null)
|
|
{
|
|
dataContainer.Server.DeleteBackupHistory(db.Name);
|
|
}
|
|
else
|
|
{
|
|
// else droppable object should be a server or database audit specification
|
|
ServerAuditSpecification sas = droppableObj as ServerAuditSpecification;
|
|
if (sas != null)
|
|
{
|
|
sas.Disable();
|
|
}
|
|
else
|
|
{
|
|
DatabaseAuditSpecification das = droppableObj as DatabaseAuditSpecification;
|
|
if (das != null)
|
|
{
|
|
das.Disable();
|
|
}
|
|
else
|
|
{
|
|
Audit aud = droppableObj as Audit;
|
|
if (aud != null)
|
|
{
|
|
aud.Disable();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// special case database drop - drop existing connections to the database other than this one
|
|
if (dropOpenConnections)
|
|
{
|
|
if (db?.ActiveConnections > 0)
|
|
{
|
|
// force the database to be single user
|
|
db.DatabaseOptions.UserAccess = DatabaseUserAccess.Single;
|
|
db.Alter(TerminationClause.RollbackTransactionsImmediately);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SpecialPostDropActionsForObject(CDataContainer dataContainer, IDroppable droppableObj)
|
|
{
|
|
// if a server isn't connected then there is nothing to do
|
|
if (dataContainer.Server == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (droppableObj is Policy)
|
|
{
|
|
Policy policyToDrop = (Policy)droppableObj;
|
|
if (!string.IsNullOrEmpty(policyToDrop.ObjectSet))
|
|
{
|
|
ObjectSet objectSet = policyToDrop.Parent.ObjectSets[policyToDrop.ObjectSet];
|
|
objectSet.Drop();
|
|
}
|
|
}
|
|
|
|
ResourcePool rp = droppableObj as ResourcePool;
|
|
ExternalResourcePool erp = droppableObj as ExternalResourcePool;
|
|
WorkloadGroup wg = droppableObj as WorkloadGroup;
|
|
|
|
if (null != rp || null != erp || null != wg)
|
|
{
|
|
// Alter() Resource Governor to reconfigure
|
|
dataContainer.Server.ResourceGovernor.Alter();
|
|
}
|
|
}
|
|
|
|
#endregion // "Helpers"
|
|
|
|
// some potentially useful code for working with server & db roles to be refactored later
|
|
#region "Roles"
|
|
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;
|
|
}
|
|
}
|
|
|
|
// private void DbRole_LoadMembership(string databaseName, string dbroleName, ServerConnection serverConnection)
|
|
// {
|
|
// var roleMembers = new HybridDictionary();
|
|
// bool isPropertiesMode = false;
|
|
// if (isPropertiesMode)
|
|
// {
|
|
// Enumerator enumerator = new Enumerator();
|
|
// Urn urn = String.Format(System.Globalization.CultureInfo.InvariantCulture,
|
|
// "Server/Database[@Name='{0}']/Role[@Name='{1}']/Member",
|
|
// Urn.EscapeString(databaseName),
|
|
// Urn.EscapeString(dbroleName));
|
|
// string[] fields = new string[] { "Name" };
|
|
// OrderBy[] orderBy = new OrderBy[] { new OrderBy("Name", OrderBy.Direction.Asc)};
|
|
// Request request = new Request(urn, fields, orderBy);
|
|
// DataTable dt = enumerator.Process(serverConnection, request);
|
|
|
|
// foreach (DataRow dr in dt.Rows)
|
|
// {
|
|
// string memberName = dr["Name"].ToString();
|
|
// if (memberName != null)
|
|
// {
|
|
// roleMembers[memberName] = new RoleMembership(true);
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// /// <summary>
|
|
// /// sends to server user changes related to membership
|
|
// /// </summary>
|
|
// private void DbRole_SendToServerMembershipChanges(Database db, DatabaseRole dbrole)
|
|
// {
|
|
// var roleMembers = new HybridDictionary();
|
|
// IDictionaryEnumerator enumerator = roleMembers.GetEnumerator();
|
|
// enumerator.Reset();
|
|
|
|
// while (enumerator.MoveNext())
|
|
// {
|
|
// DictionaryEntry entry = enumerator.Entry;
|
|
// string memberName = entry.Key.ToString();
|
|
// RoleMembership membership = (RoleMembership) entry.Value;
|
|
// if (membership != null)
|
|
// {
|
|
// if (!membership.initiallyAMember && membership.currentlyAMember)
|
|
// {
|
|
// dbrole.AddMember(memberName);
|
|
// }
|
|
// else if (membership.initiallyAMember && !membership.currentlyAMember)
|
|
// {
|
|
// dbrole.DropMember(memberName);
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// private void InitProp(ServerConnection serverConnection, string serverName, string databaseName,
|
|
// string dbroleName, string dbroleUrn, bool isPropertiesMode)
|
|
// {
|
|
// System.Diagnostics.Debug.Assert(serverName!=null);
|
|
// System.Diagnostics.Debug.Assert((databaseName!=null) && (databaseName.Trim().Length!=0));
|
|
|
|
// // LoadSchemas();
|
|
// // LoadMembership();
|
|
|
|
// if (isPropertiesMode == true)
|
|
// {
|
|
// // initialize from enumerator in properties mode
|
|
// System.Diagnostics.Debug.Assert(dbroleName!=null);
|
|
// System.Diagnostics.Debug.Assert(dbroleName.Trim().Length !=0);
|
|
// System.Diagnostics.Debug.Assert(dbroleUrn!=null);
|
|
// System.Diagnostics.Debug.Assert(dbroleUrn.Trim().Length != 0);
|
|
|
|
// Enumerator en = new Enumerator();
|
|
// Request req = new Request();
|
|
// req.Fields = new String [] { "Owner" };
|
|
|
|
// if ((dbroleUrn!=null) && (dbroleUrn.Trim().Length != 0))
|
|
// {
|
|
// req.Urn = dbroleUrn;
|
|
// }
|
|
// else
|
|
// {
|
|
// req.Urn = "Server/Database[@Name='" + Urn.EscapeString(databaseName) + "']/Role[@Name='" + Urn.EscapeString(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;
|
|
// }
|
|
// }
|
|
|
|
// private void DbRole_SendDataToServer(CDataContainer dataContainer, string databaseName,
|
|
// string dbroleName, string ownerName, string initialOwner, string roleName, bool isPropertiesMode)
|
|
// {
|
|
// System.Diagnostics.Debug.Assert(databaseName != null && databaseName.Trim().Length != 0, "database name is empty");
|
|
// System.Diagnostics.Debug.Assert(dataContainer.Server != null, "server is null");
|
|
|
|
// Database database = dataContainer.Server.Databases[databaseName];
|
|
// System.Diagnostics.Debug.Assert(database!= null, "database is null");
|
|
|
|
// DatabaseRole role;
|
|
|
|
// if (isPropertiesMode == true) // in properties mode -> alter role
|
|
// {
|
|
// System.Diagnostics.Debug.Assert(dbroleName != null && dbroleName.Trim().Length != 0, "role name is empty");
|
|
|
|
// role = database.Roles[dbroleName];
|
|
// System.Diagnostics.Debug.Assert(role != null, "role is null");
|
|
|
|
// if (0 != String.Compare(ownerName, initialOwner, StringComparison.Ordinal))
|
|
// {
|
|
// role.Owner = ownerName;
|
|
// role.Alter();
|
|
// }
|
|
// }
|
|
// else // not in properties mode -> create role
|
|
// {
|
|
// role = new DatabaseRole(database, roleName);
|
|
// if (ownerName.Length != 0)
|
|
// {
|
|
// role.Owner = ownerName;
|
|
// }
|
|
|
|
// role.Create();
|
|
// }
|
|
|
|
// // SendToServerSchemaOwnershipChanges(database, role);
|
|
// // SendToServerMembershipChanges(database, role);
|
|
// }
|
|
|
|
|
|
// /// <summary>
|
|
// /// sends to server changes related to schema ownership
|
|
// /// </summary>
|
|
// private void DbRole_SendToServerSchemaOwnershipChanges(CDataContainer dataContainer, Database db, DatabaseRole dbrole)
|
|
// {
|
|
// if (dataContainer.Server == null)
|
|
// {
|
|
// return;
|
|
// }
|
|
|
|
// HybridDictionary schemaOwnership = new HybridDictionary();
|
|
// if (9 <= dataContainer.Server.Information.Version.Major)
|
|
// {
|
|
// IDictionaryEnumerator enumerator = 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 != null && (ownership.currentlyOwned && !ownership.initiallyOwned))
|
|
// {
|
|
// Schema schema = db.Schemas[schemaName];
|
|
// schema.Owner = dbrole.Name;
|
|
// schema.Alter();
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
#endregion
|
|
|
|
}
|
|
}
|