Add securable search and permission setting to User Management (#2052)

This commit is contained in:
Hai Cao
2023-05-10 18:20:06 -07:00
committed by GitHub
parent c5bc4ebc92
commit d525e88672
38 changed files with 2981 additions and 168 deletions

View File

@@ -0,0 +1,36 @@
//
// 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.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts
{
public class SearchRequestParams : GeneralRequestDetails
{
/// <summary>
/// The context id.
/// </summary>
public string? ContextId { get; set; }
public string[]? ObjectTypes { get; set; }
public string? SearchText { get; set; }
public string? Schema { get; set; }
public string? Database { get; set; }
}
public class SearchResultItem
{
public string? Name { get; set; }
public string? Schema { get; set; }
public string? Type { get; set; }
}
public class SearchRequest
{
public static readonly RequestType<SearchRequestParams, SearchResultItem[]> Type = RequestType<SearchRequestParams, SearchResultItem[]>.Create("objectManagement/search");
}
}

View File

@@ -11,6 +11,7 @@ using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts;
using System.Collections.Generic;
using System.Collections.Concurrent;
using Microsoft.SqlTools.ServiceLayer.Management;
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
{
@@ -64,6 +65,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
this.serviceHost.SetRequestHandler(SaveObjectRequest.Type, HandleSaveObjectRequest, true);
this.serviceHost.SetRequestHandler(ScriptObjectRequest.Type, HandleScriptObjectRequest, true);
this.serviceHost.SetRequestHandler(DisposeViewRequest.Type, HandleDisposeViewRequest, true);
this.serviceHost.SetRequestHandler(SearchRequest.Type, HandleSearchRequest, true);
}
internal async Task HandleRenameRequest(RenameRequestParams requestParams, RequestContext<RenameRequestResponse> requestContext)
@@ -116,6 +118,68 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
await requestContext.SendResult(new DisposeViewRequestResponse());
}
internal async Task HandleSearchRequest(SearchRequestParams requestParams, RequestContext<SearchResultItem[]> requestContext)
{
var context = this.GetContext(requestParams.ContextId);
ConnectionInfo connInfo;
ConnectionService.Instance.TryFindConnection(context.Parameters.ConnectionUri, out connInfo);
if (connInfo == null)
{
throw new ArgumentException("Invalid ConnectionUri");
}
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
List<SearchResultItem> res = new List<SearchResultItem>();
foreach (string type in requestParams.ObjectTypes)
{
SearchableObjectCollection result = new SearchableObjectCollection();
SearchableObjectType searchableObjectType = SecurableUtils.ConvertPotentialSqlObjectTypeToSearchableObjectType(type);
if (searchableObjectType == SearchableObjectType.LastType)
{
continue;
}
SearchableObjectTypeDescription desc = SearchableObjectTypeDescription.GetDescription(searchableObjectType);
if (requestParams.SearchText != null)
{
if (desc.IsDatabaseObject)
{
SearchableObject.Search(result, searchableObjectType, dataContainer.ConnectionInfo, context.Parameters.Database, requestParams.SearchText, false, true);
}
else
{
SearchableObject.Search(result, searchableObjectType, dataContainer.ConnectionInfo, requestParams.SearchText, false, true);
}
}
else
{
if (desc.IsDatabaseObject)
{
SearchableObject.Search(result, searchableObjectType, dataContainer.ConnectionInfo, context.Parameters.Database, true);
}
else
{
SearchableObject.Search(result, searchableObjectType, dataContainer.ConnectionInfo, true);
}
}
foreach (SearchableObject obj in result)
{
res.Add(new SearchResultItem
{
Name = obj.Name,
Type = type
});
}
}
await requestContext.SendResult(res.ToArray());
}
private IObjectTypeHandler GetObjectTypeHandler(SqlObjectType objectType)
{
foreach (var handler in objectTypeHandlers)

View File

@@ -75,12 +75,14 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
Value = item.Value
}).ToArray(),
OwnedSchemas = prototype.SchemasOwned,
SecurablePermissions = prototype.SecurablePermissions
};
var viewInfo = new AppRoleViewInfo()
{
ObjectInfo = appRoleInfo,
Schemas = prototype.Schemas
Schemas = prototype.Schemas,
SupportedSecurableTypes = SecurableUtils.GetSecurableTypeMetadata(SqlObjectType.ApplicationRole, dataContainer.Server.Version, parameters.Database, dataContainer.Server.DatabaseEngineType, dataContainer.Server.DatabaseEngineEdition)
};
var context = new AppRoleViewContext(parameters, dataContainer.ServerConnection);

View File

@@ -8,7 +8,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
/// <summary>
/// a class for storing various application role properties
/// </summary>
public class AppRoleInfo : SqlObject
public class AppRoleInfo : SecurityPrincipalObject
{
public string? DefaultSchema { get; set; }
public string? Password { get; set; }

View File

@@ -8,7 +8,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
/// <summary>
/// a class for storing various application role view properties
/// </summary>
public class AppRoleViewInfo : SqlObjectViewInfo
public class AppRoleViewInfo : SecurityPrincipalViewInfo
{
public string[]? Schemas { get; set; }
}

View File

@@ -74,12 +74,14 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
}).ToArray(),
Members = prototype.Members.ToArray(),
OwnedSchemas = prototype.SchemasOwned.ToArray(),
SecurablePermissions = prototype.SecurablePermissions
};
var viewInfo = new DatabaseRoleViewInfo()
{
ObjectInfo = DatabaseRoleInfo,
Schemas = prototype.Schemas
Schemas = prototype.Schemas,
SupportedSecurableTypes = SecurableUtils.GetSecurableTypeMetadata(SqlObjectType.DatabaseRole, dataContainer.Server.Version, parameters.Database, dataContainer.Server.DatabaseEngineType, dataContainer.Server.DatabaseEngineEdition)
};
var context = new DatabaseRoleViewContext(parameters, dataContainer.ServerConnection);

View File

@@ -8,7 +8,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
/// <summary>
/// a class for storing various database role properties
/// </summary>
public class DatabaseRoleInfo : SqlObject
public class DatabaseRoleInfo : SecurityPrincipalObject
{
public string? Owner { get; set; }
public string[]? OwnedSchemas { get; set; }

View File

@@ -8,7 +8,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
/// <summary>
/// a class for storing various database role view properties
/// </summary>
public class DatabaseRoleViewInfo : SqlObjectViewInfo
public class DatabaseRoleViewInfo : SecurityPrincipalViewInfo
{
public string[]? Schemas { get; set; }
}

View File

@@ -55,8 +55,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
}
string[] languages = languageOptionsList.ToArray();
LoginPrototype prototype = parameters.IsNewObject
? new LoginPrototype(dataContainer.Server)
: new LoginPrototype(dataContainer.Server, dataContainer.Server.GetSmoObject(parameters.ObjectUrn) as Login);
? new LoginPrototype(dataContainer)
: new LoginPrototype(dataContainer, dataContainer.Server.GetSmoObject(parameters.ObjectUrn) as Login);
List<string> loginServerRoles = new List<string>();
foreach (string role in prototype.ServerRoles.ServerRoleNames)
@@ -82,7 +82,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
ConnectPermission = prototype.WindowsGrantAccess,
IsEnabled = !prototype.IsDisabled,
IsLockedOut = prototype.IsLockedOut,
UserMapping = new ServerLoginDatabaseUserMapping[0]
UserMapping = new ServerLoginDatabaseUserMapping[0],
SecurablePermissions = prototype.SecurablePermissions
};
var supportedAuthTypes = new List<LoginAuthenticationType>();
@@ -104,7 +105,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
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
SupportAdvancedOptions = dataContainer.Server.DatabaseEngineType == DatabaseEngineType.Standalone || dataContainer.Server.DatabaseEngineEdition == DatabaseEngineEdition.SqlManagedInstance,
SupportedSecurableTypes = SecurableUtils.GetSecurableTypeMetadata(SqlObjectType.ServerLevelLogin, dataContainer.Server.Version, "", dataContainer.Server.DatabaseEngineType, dataContainer.Server.DatabaseEngineEdition)
};
var context = new LoginViewContext(parameters);
return Task.FromResult(new InitializeViewResult()
@@ -189,7 +191,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
}
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
LoginPrototype prototype = new LoginPrototype(dataContainer.Server, dataContainer.Server.Logins[login.Name]);
LoginPrototype prototype = new LoginPrototype(dataContainer, dataContainer.Server.Logins[login.Name]);
prototype.SqlPassword = login.Password;
if (0 != string.Compare(login.DefaultLanguage, SR.DefaultLanguagePlaceholder, StringComparison.Ordinal))
@@ -207,6 +209,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
prototype.IsDisabled = !login.IsEnabled;
prototype.MustChange = login.EnforcePasswordPolicy ? login.MustChangePassword : false;
prototype.WindowsGrantAccess = login.ConnectPermission;
prototype.SecurablePermissions = login.SecurablePermissions;
if (prototype.LoginType == SqlServer.Management.Smo.LoginType.SqlLogin)
{
@@ -256,7 +259,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
}
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
LoginPrototype prototype = new LoginPrototype(dataContainer.Server, login);
LoginPrototype prototype = new LoginPrototype(dataContainer, login);
if (prototype.LoginType == SqlServer.Management.Smo.LoginType.SqlLogin)
{

View File

@@ -35,7 +35,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
/// <summary>
/// a class for storing various login properties
/// </summary>
public class LoginInfo : SqlObject
public class LoginInfo : SecurityPrincipalObject
{
public LoginAuthenticationType AuthenticationType { get; set; }

View File

@@ -6,7 +6,7 @@
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
{
public class LoginViewInfo : SqlObjectViewInfo
public class LoginViewInfo : SecurityPrincipalViewInfo
{
public LoginAuthenticationType[] AuthenticationTypes { get; set; }
public bool CanEditLockedOutState { get; set; }

View File

@@ -12,6 +12,7 @@ using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.ServiceLayer.Management;
using System.Collections.Generic;
using System.Linq;
using Microsoft.SqlTools.ServiceLayer.ObjectManagement.PermissionsData;
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
{
@@ -32,7 +33,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
private bool exists;
private AppRolePrototypeData currentState;
private AppRolePrototypeData originalState;
private SecurablePermissions[] securablePermissions = null;
private Principal principal = null;
#endregion
#region Trace support
@@ -139,6 +141,18 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
this.currentState.ExtendedProperties = value;
}
}
public SecurablePermissions[] SecurablePermissions
{
get
{
return this.securablePermissions;
}
set
{
this.securablePermissions = value;
}
}
#endregion
#region Constructors / Dispose
@@ -149,6 +163,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
this.dataContainer = context;
this.currentState = new AppRolePrototypeData(context, database);
this.originalState = (AppRolePrototypeData)this.currentState.Clone();
this.securablePermissions = new SecurablePermissions[0];
}
/// <summary>
@@ -161,6 +176,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
this.databaseName = database;
this.currentState = new AppRolePrototypeData(context, database);
this.originalState = (AppRolePrototypeData)this.currentState.Clone();
this.principal = SecurableUtils.CreatePrincipal(false, PrincipalType.ApplicationRole, null, roleInfo.Name, context, database);
this.ApplyInfoToPrototype(roleInfo);
}
@@ -175,6 +191,9 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
this.databaseName = database;
this.currentState = new AppRolePrototypeData(context, database, role);
this.originalState = (AppRolePrototypeData)this.currentState.Clone();
this.securablePermissions = SecurableUtils.GetSecurablePermissions(true, PrincipalType.ApplicationRole, role, context);
this.principal = SecurableUtils.CreatePrincipal(true, PrincipalType.ApplicationRole, role, null, context, database);
this.principal.AddExistingSecurables();
}
#endregion
@@ -223,6 +242,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
SendToServerSchemaOwnershipChanges(db, approle);
SendToServerExtendedPropertiesChange();
SecurableUtils.SendToServerPermissionChanges(this.exists, this.Name, this.SecurablePermissions, this.principal, this.dataContainer, this.databaseName);
}
else // not in properties mode -> create role
{
@@ -236,6 +256,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
SendToServerSchemaOwnershipChanges(db, approle);
SendToServerExtendedPropertiesChange();
SecurableUtils.SendToServerPermissionChanges(this.exists, this.Name, this.SecurablePermissions, this.principal, this.dataContainer, this.databaseName);
}
}
@@ -327,6 +348,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
this.SchemasOwned = roleInfo.OwnedSchemas.ToArray();
this.Password = roleInfo.Password;
this.ExtendedProperties = roleInfo.ExtendedProperties.Select(ep => new KeyValuePair<string, string>(ep.Name, ep.Value)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
this.securablePermissions = roleInfo.SecurablePermissions;
}
private class AppRolePrototypeData : ICloneable

View File

@@ -12,6 +12,7 @@ using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.ServiceLayer.Management;
using System.Collections.Generic;
using System.Linq;
using Microsoft.SqlTools.ServiceLayer.ObjectManagement.PermissionsData;
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
{
@@ -32,7 +33,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
private bool exists;
private DatabaseRolePrototypeData currentState;
private DatabaseRolePrototypeData originalState;
private SecurablePermissions[] securablePermissions = null;
private Principal principal = null;
#endregion
#region Trace support
@@ -138,6 +140,18 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
return this.dataContainer.Server.VersionMajor >= 9;
}
}
public SecurablePermissions[] SecurablePermissions
{
get
{
return this.securablePermissions;
}
set
{
this.securablePermissions = value;
}
}
#endregion
#region Constructors / Dispose
@@ -148,6 +162,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
this.dataContainer = context;
this.currentState = new DatabaseRolePrototypeData(context, database);
this.originalState = (DatabaseRolePrototypeData)this.currentState.Clone();
this.securablePermissions = new SecurablePermissions[0];
}
/// <summary>
@@ -160,6 +175,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
this.dataContainer = context;
this.currentState = new DatabaseRolePrototypeData(context, database);
this.originalState = (DatabaseRolePrototypeData)this.currentState.Clone();
this.principal = SecurableUtils.CreatePrincipal(false, PrincipalType.DatabaseRole, null, roleInfo.Name, context, database);
this.ApplyInfoToPrototype(roleInfo);
}
@@ -174,6 +190,9 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
this.dataContainer = context;
this.currentState = new DatabaseRolePrototypeData(context, database, role);
this.originalState = (DatabaseRolePrototypeData)this.currentState.Clone();
this.principal = SecurableUtils.CreatePrincipal(true, PrincipalType.DatabaseRole, role, null, context, database);
this.principal.AddExistingSecurables();
this.securablePermissions = SecurableUtils.GetSecurablePermissions(true, PrincipalType.DatabaseRole, role, context);
}
#endregion
@@ -219,6 +238,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
}
SendToServerSchemaOwnershipChanges(db, databaseRole);
SendToServerExtendedPropertiesChange();
SecurableUtils.SendToServerPermissionChanges(this.exists, this.Name, this.SecurablePermissions, this.principal, this.dataContainer, this.databaseName);
}
#endregion
@@ -341,6 +361,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
this.Members = roleInfo.Members.ToList();
this.SchemasOwned = roleInfo.OwnedSchemas.ToArray();
this.ExtendedProperties = roleInfo.ExtendedProperties.Select(ep => new KeyValuePair<string, string>(ep.Name, ep.Value)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
this.securablePermissions = roleInfo.SecurablePermissions;
}
private class DatabaseRolePrototypeData : ICloneable

View File

@@ -0,0 +1,200 @@
//
// 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.Data;
using System.Globalization;
using Microsoft.SqlServer.Management.Sdk.Sfc;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlTools.ServiceLayer.Management;
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
{
/// <summary>
/// Data model for the EffectivePermmisions UI
/// </summary>
internal class EffectivePermissionsData
{
#region fields
private CDataContainer dataContainer;
// urn we are targetting
private Urn urn;
// securable we are targetting
private SupportedSecurable securable;
// the principal we will execute as
private string principalName = string.Empty;
// indicates whether or not we should execute as a login or user
private bool executeAsLogin;
#endregion
#region properties
/// <summary>
/// True if the query for this principal will return column information
/// </summary>
public bool HasColumnInformation
{
get
{
// STrace.Assert(this.securable != null, "invalid object state");
return this.securable.HasColumnInformation;
}
}
/// <summary>
/// The name of the principal we are checking permissions for
/// </summary>
public string PrincipalName
{
get
{
return this.principalName;
}
}
/// <summary>
/// the display name of the securable we are querying. If the securable has a schema it will be
/// schema.securable, otherwise just securable
/// </summary>
public string SecurableDisplayName
{
get
{
// STrace.Assert(this.securable != null, "invalid object state");
string displayName;
if (this.securable.Schema.Length > 0)
{
displayName = String.Format(CultureInfo.CurrentCulture
, "{0}.{1}"
, this.securable.Schema
, this.securable.Name);
}
else
{
displayName = this.securable.Name;
}
return displayName;
}
}
#endregion
#region constructors
/// <summary>
/// Construct an EffectivePermissionsData object
/// </summary>
/// <param name="dataContainer">CDataContainer that represents the principal we are querying</param>
public EffectivePermissionsData(CDataContainer dataContainer)
{
if (dataContainer == null)
{
throw new ArgumentNullException("dataContainer");
}
this.dataContainer = dataContainer;
Initialize();
}
#endregion
#region public methods
/// <summary>
/// Query the effective permissions for principal against the securable
/// </summary>
/// <returns>Dataset representing the permissions</returns>
public DataSet QueryEffectivePermissions()
{
// get a connection
ServerConnection serverConnection = this.dataContainer.ServerConnection;
// see if we need to set the context to a particular db
string databaseName = GetDatabaseName(this.urn);
if (databaseName.Length > 0)
{
serverConnection.ExecuteNonQuery(
String.Format(CultureInfo.InvariantCulture
, "USE [{0}]"
, SecurableUtils.EscapeString(databaseName, "]")));
}
// get the securable query
string securableQuery = this.securable.GetPermissionsForSecurableSyntax();
// merge the securableQuery with the EXECUTE AS context
string sqlQuery =
String.Format(CultureInfo.InvariantCulture,
@"EXECUTE AS {0} = N'{1}';
{2}
REVERT;"
, this.executeAsLogin ? "LOGIN" : "USER"
, Urn.EscapeString(this.principalName)
, securableQuery);
return serverConnection.ExecuteWithResults(sqlQuery);
}
#endregion
#region implementation
/// <summary>
/// Initialize the object
/// </summary>
private void Initialize()
{
// STrace.Assert(this.dataContainer != null);
STParameters parameters = new STParameters(this.dataContainer.Document);
// get the Urn of the securable. This must be set
string securableUrn = string.Empty;
parameters.GetParam("urn", ref securableUrn);
// cannot proceed if there is no object to work on
if (securableUrn == null || securableUrn.Length == 0)
{
throw new InvalidOperationException();
}
this.urn = new Urn(securableUrn);
// get a supported securable for this object
this.securable = new SupportedSecurable(this.urn, this.dataContainer.Server);
// get the user we will be executing as
parameters.GetParam("executeas", ref this.principalName);
string executeAsType = String.Empty;
parameters.GetParam("executetype", ref executeAsType);
this.executeAsLogin = (executeAsType == "login");
// if no override is supplied then we will just execute as self
if (this.principalName == null || this.principalName.Length == 0)
{
// STrace.Assert(false, "Principal was not supplied. Defaulting to login");
this.principalName = this.dataContainer.ServerConnection.TrueLogin;
this.executeAsLogin = true;
}
}
/// <summary>
/// Get the database name if any that contains the securable
/// </summary>
/// <param name="urn">Securable</param>
/// <returns>Database name that contains the securable, or an empty string if this is a server
/// scoped object</returns>
private string GetDatabaseName(Urn urn)
{
String databaseName = string.Empty;
// otherwise try and find the database
while (urn != null && urn.Type != "Database")
{
urn = urn.Parent;
}
if (urn != null)
{
databaseName = urn.GetAttribute("Name");
}
return databaseName;
}
#endregion
}
}

View File

@@ -31,9 +31,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
{
if (this.configAction != ConfigAction.Drop)
{
prototype.ApplyGeneralChanges(this.DataContainer.Server);
prototype.ApplyServerRoleChanges(this.DataContainer.Server);
prototype.ApplyDatabaseRoleChanges(this.DataContainer.Server);
prototype.SendChangeToServer();
}
}
}

View File

@@ -16,6 +16,7 @@ using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Sdk.Sfc;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.ServiceLayer.Management;
using Microsoft.SqlTools.ServiceLayer.ObjectManagement.PermissionsData;
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
{
@@ -1547,11 +1548,14 @@ INNER JOIN sys.sql_logins AS sql_logins
}
private bool exists;
private CDataContainer context;
private string machineName;
private LoginPrototypeData currentState;
private LoginPrototypeData originalState;
private bool mapToCredential;
private SecurablePermissions[] securablePermissions = null;
private Principal principal = null;
public bool MapToCredential
{
set
@@ -1905,6 +1909,18 @@ INNER JOIN sys.sql_logins AS sql_logins
}
}
public SecurablePermissions[] SecurablePermissions
{
get
{
return securablePermissions;
}
set
{
securablePermissions = value;
}
}
/// <summary>
/// Get the database roles collection for the user in a particular database
/// </summary>
@@ -2056,13 +2072,16 @@ INNER JOIN sys.sql_logins AS sql_logins
/// constructor
/// </summary>
/// <param name="server">The server on which we are creating a login</param>
public LoginPrototype(Microsoft.SqlServer.Management.Smo.Server server)
public LoginPrototype(CDataContainer context)
{
this.context = context;
var server = context.Server;
this.exists = false;
this.machineName = server.ConnectionContext.TrueName.ToUpperInvariant();
this.currentState = new LoginPrototypeData(server);
this.originalState = (LoginPrototypeData) this.currentState.Clone();
this.comparer = new SqlCollationSensitiveStringComparer(server.Information.Collation);
this.securablePermissions = new SecurablePermissions[0];
}
/// <summary>
@@ -2070,21 +2089,28 @@ INNER JOIN sys.sql_logins AS sql_logins
/// </summary>
/// <param name="server">The server on which we are modifying a login</param>
/// <param name="login">The login we are modifying</param>
public LoginPrototype(Microsoft.SqlServer.Management.Smo.Server server, Login login)
public LoginPrototype(CDataContainer context, Login login)
{
this.context = context;
var server = context.Server;
this.exists = true;
this.machineName = server.ConnectionContext.TrueName.ToUpperInvariant();
this.currentState = new LoginPrototypeData(server, login);
this.originalState = (LoginPrototypeData) this.currentState.Clone();
this.comparer = new SqlCollationSensitiveStringComparer(server.Information.Collation);
this.securablePermissions = SecurableUtils.GetSecurablePermissions(this.exists, PrincipalType.Login, login, context);
this.principal = SecurableUtils.CreatePrincipal(true, PrincipalType.Login, login, null, context);
this.principal.AddExistingSecurables();
}
/// <summary>
/// constructor
/// </summary>
/// <param name="server">The server on which we are creating a login</param>
public LoginPrototype(Microsoft.SqlServer.Management.Smo.Server server, LoginInfo login)
public LoginPrototype(CDataContainer context, LoginInfo login)
{
this.context = context;
var server = context.Server;
this.exists = false;
this.machineName = server.ConnectionContext.TrueName.ToUpperInvariant();
this.currentState = new LoginPrototypeData(server);
@@ -2112,6 +2138,8 @@ INNER JOIN sys.sql_logins AS sql_logins
this.IsDisabled = !login.IsEnabled;
this.MustChange = login.EnforcePasswordPolicy ? login.MustChangePassword : false;
this.WindowsGrantAccess = login.ConnectPermission;
this.securablePermissions = login.SecurablePermissions;
this.principal = SecurableUtils.CreatePrincipal(false, PrincipalType.Login, null, login.Name, context);
}
private LoginType GetLoginType(LoginInfo loginInfo)
@@ -2154,6 +2182,14 @@ INNER JOIN sys.sql_logins AS sql_logins
}
public void SendChangeToServer()
{
ApplyGeneralChanges(this.context.Server);
ApplyServerRoleChanges(this.context.Server);
ApplyDatabaseRoleChanges(this.context.Server);
SecurableUtils.SendToServerPermissionChanges(this.exists, this.LoginName, this.SecurablePermissions, this.principal, this.context, null);
}
/// <summary>
/// Create the login or modify the login's access type, default database, default language,
/// and password

View File

@@ -10,6 +10,7 @@ using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.ServiceLayer.Management;
using System.Collections.Generic;
using System.Linq;
using Microsoft.SqlTools.ServiceLayer.ObjectManagement.PermissionsData;
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
{
@@ -26,6 +27,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
/// as a hash table where one can manipulate custom data
/// </summary>
private CDataContainer dataContainer = null;
private Principal principal = null;
private SecurablePermissions[] securablePermissions = null;
private bool exists;
private ServerRolePrototypeData currentState;
@@ -109,6 +112,18 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
return this.currentState.IsFixedRole;
}
}
public SecurablePermissions[] SecurablePermissions
{
get
{
return securablePermissions;
}
set
{
securablePermissions = value;
}
}
#endregion
#region Constructors / Dispose
@@ -118,6 +133,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
this.dataContainer = context;
this.currentState = new ServerRolePrototypeData(context);
this.originalState = (ServerRolePrototypeData)this.currentState.Clone();
this.securablePermissions = new SecurablePermissions[0];
}
/// <summary>
@@ -129,6 +145,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
this.dataContainer = context;
this.currentState = new ServerRolePrototypeData(context);
this.originalState = (ServerRolePrototypeData)this.currentState.Clone();
this.principal = SecurableUtils.CreatePrincipal(false, PrincipalType.ServerRole, null, roleInfo.Name, context);
this.ApplyInfoToPrototype(roleInfo);
}
@@ -142,6 +159,9 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
this.dataContainer = context;
this.currentState = new ServerRolePrototypeData(context, role);
this.originalState = (ServerRolePrototypeData)this.currentState.Clone();
this.principal = SecurableUtils.CreatePrincipal(true, PrincipalType.ServerRole, role, null, context);
this.principal.AddExistingSecurables();
this.securablePermissions = SecurableUtils.GetSecurablePermissions(this.exists, PrincipalType.ServerRole, role, this.dataContainer);
}
#endregion
@@ -183,6 +203,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
SendToServerMemberChanges(serverRole);
SendToServerMembershipChanges(serverRole);
SecurableUtils.SendToServerPermissionChanges(this.exists, this.Name, this.SecurablePermissions, this.principal, this.dataContainer, null);
}
#endregion
@@ -250,13 +271,13 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
}
}
public void ApplyInfoToPrototype(ServerRoleInfo roleInfo)
{
this.Name = roleInfo.Name;
this.Owner = roleInfo.Owner;
this.Members = roleInfo.Members.ToList();
this.Memberships = roleInfo.Memberships.ToList();
this.SecurablePermissions = roleInfo.SecurablePermissions;
}
private class ServerRolePrototypeData : ICloneable

View File

@@ -9,7 +9,6 @@ using System;
using System.Collections;
using System.Collections.Specialized;
using System.Data;
using System.Resources;
using System.Text;
using System.Globalization;
using Microsoft.SqlServer.Management.Smo;
@@ -281,7 +280,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
/// Constructor
/// </summary>
/// <param name="image">Bitmap for the object type</param>
/// <param name="typeNameKey">The key to look up localized type names, e.g. "objectType.functionTable"</param>
/// <param name="typeNameKey">The key to look up localized type names, e.g. "objectType_functionTable"</param>
/// <param name="urnObjectType">The URN object type substring, e.g. "UserDefinedFunction"</param>
/// <param name="specialRestrictions">Any special clauses needed for the URN, e.g. "@FunctionType='2'"</param>
/// <param name="disallowSystemObjectsRestriction">Clause to restrict selection to non-system objects, e.g. "@IsSystemObject='false'"</param>
@@ -294,8 +293,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
string specialRestrictions,
string disallowSystemObjectsRestriction,
bool isDatabaseObject,
bool isSchemaObject,
ResourceManager resourceManager)
bool isSchemaObject)
{
// STrace.Assert(image != null, "image is null");
// STrace.Assert((typeNameKey != null) && (typeNameKey.Length != 0), "typeNameKey is null or empty");
@@ -309,9 +307,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
this.disallowSystemObjectsRestriction = disallowSystemObjectsRestriction;
this.isDatabaseObject = isDatabaseObject;
this.isSchemaObject = isSchemaObject;
this.typeNamePlural = resourceManager.GetString(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}.plural", typeNameKey));
this.typeNameSingular = resourceManager.GetString(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}.singular", typeNameKey));
this.typeNamePlural = SR.Keys.GetString(string.Format("{0}_plural", typeNameKey));
this.typeNameSingular = SR.Keys.GetString(string.Format("{0}_singular", typeNameKey));
// STrace.Assert((this.typeNamePlural != null) && (this.typeNamePlural.Length != 0), "could not get plural type name");
// STrace.Assert((this.typeNameSingular != null) && (this.typeNameSingular.Length != 0), "could not get singular type name");
@@ -321,7 +318,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
/// Constructor
/// </summary>
/// <param name="image">Bitmap for the object type</param>
/// <param name="typeNameKey">The key to look up localized type names, e.g. "objectType.functionTable"</param>
/// <param name="typeNameKey">The key to look up localized type names, e.g. "objectType_functionTable"</param>
/// <param name="urnObjectType">The URN object type substring, e.g. "UserDefinedFunction"</param>
/// <param name="isDatabaseObject">Whether the object is contained by a database</param>
/// <param name="isSchemaObject">Whether the object is contained byt a schema</param>
@@ -330,8 +327,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
string typeNameKey,
string urnObjectType,
bool isDatabaseObject,
bool isSchemaObject,
ResourceManager resourceManager)
bool isSchemaObject)
{
// STrace.Assert(image != null, "image is null");
// STrace.Assert((typeNameKey != null) && (typeNameKey.Length != 0), "typeNameKey is null or empty");
@@ -344,8 +340,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
this.isDatabaseObject = isDatabaseObject;
this.isSchemaObject = isSchemaObject;
this.typeNamePlural = resourceManager.GetString(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}.plural", typeNameKey));
this.typeNameSingular = resourceManager.GetString(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}.singular", typeNameKey));
this.typeNamePlural = SR.Keys.GetString(string.Format("{0}_plural", typeNameKey));
this.typeNameSingular = SR.Keys.GetString(string.Format("{0}_singular", typeNameKey));
// STrace.Assert((this.typeNamePlural != null) && (this.typeNamePlural.Length != 0), "could not get plural type name");
// STrace.Assert((this.typeNameSingular != null) && (this.typeNameSingular.Length != 0), "could not get singular type name");
@@ -620,14 +616,11 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
{
if (SearchableObjectTypeDescription.firstVersionSpecificTypeInfoUpdate)
{
ResourceManager resourceManager = new ResourceManager("Microsoft.SqlServer.Management.SqlMgmt.SqlObjectSearchStrings", typeof(SearchableObjectTypeDescription).Assembly);
// Color transparent = ResourceUtils.StandardBitmapTransparentColor;
//Re-writing the value of SearchableObjectType.ServerRole in the Dictionary.
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.ServerRole] =
new SearchableObjectTypeDescription(
// // DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("Flexible_server_role.ico")).ToBitmap(),
"objectType.serverRole",
"objectType_serverRole",
"Role",
string.Empty,
Utils.IsSql11OrLater(serverVersion.Major)
@@ -636,8 +629,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
? "@ID=2" //Public server role's ID is 2.
: string.Empty,
false,
false,
resourceManager);
false);
firstVersionSpecificTypeInfoUpdate = false;
}
@@ -653,364 +645,324 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
SearchableObjectTypeDescription.typeToDescription = new HybridDictionary(25);
}
ResourceManager resourceManager = new ResourceManager("Microsoft.SqlServer.Management.SqlMgmt.SqlObjectSearchStrings", typeof(SearchableObjectTypeDescription).Assembly);
// Color transparent = ResourceUtils.StandardBitmapTransparentColor;
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.AggregateFunction] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("ScalarValuedFunction.ico")).ToBitmap(),
"objectType.aggregateFunction",
"objectType_aggregateFunction",
"UserDefinedAggregate",
true,
true,
resourceManager);
true);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.ApplicationRole] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("application_role_16x.ico")).ToBitmap(),
"objectType.applicationRole",
"objectType_applicationRole",
"ApplicationRole",
true,
false,
resourceManager);
false);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.Assembly] =
new SearchableObjectTypeDescription(
// // DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("assemblies.ico")).ToBitmap(),
"objectType.assembly",
"objectType_assembly",
"SqlAssembly",
true,
false,
resourceManager);
false);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.AsymmetricKey] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("asymmetric_key.ico")).ToBitmap(),
"objectType.asymmetricKey",
"objectType_asymmetricKey",
"AsymmetricKey",
true,
false,
resourceManager);
false);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.Certificate] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("certificate.ico")).ToBitmap(),
"objectType.certificate",
"objectType_certificate",
"Certificate",
true,
false,
resourceManager);
false);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.Database] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("database.ico")).ToBitmap(),
"objectType.database",
"objectType_database",
"Database",
String.Empty,
"@IsSystemObject=false()",
false,
false,
resourceManager);
false);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.AgentJob] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("jobs.ico")).ToBitmap(),
"objectType.agentjob",
"objectType_agentjob",
"JobServer/Job",
false,
false,
resourceManager);
false);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.DatabaseRole] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("database_roles_16x.ico")).ToBitmap(),
"objectType.databaseRole",
"objectType_databaseRole",
"Role",
String.Empty,
"@IsFixedRole=false()",
true,
false,
resourceManager);
false);
//Without version info, we can't have system object Urn as it differs with version.
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.ServerRole] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("Flexible_server_role.ico")).ToBitmap(),
"objectType.serverRole",
"objectType_serverRole",
"Role",
false,
false,
resourceManager);
false);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.Endpoint] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("endpoint.ico")).ToBitmap(),
"objectType.endpoint",
"objectType_endpoint",
"Endpoint",
false,
false,
resourceManager);
false);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.ExtendedStoredProcedure] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("user_extended_stored_proc.ico")).ToBitmap(),
"objectType.extendedStoredProcedure",
"objectType_extendedStoredProcedure",
"ExtendedStoredProcedure",
String.Empty,
"@IsSystemObject=false()",
true,
true,
resourceManager);
true);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.ExternalDataSource] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("ExternalDataSource.ico")).ToBitmap(),
"objectType.externalDataSource",
"objectType_externalDataSource",
"ExternalDataSource",
true,
false,
resourceManager);
false);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.ExternalFileFormat] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("ExternalFileFormat.ico")).ToBitmap(),
"objectType.externalFileFormat",
"objectType_externalFileFormat",
"ExternalFileFormat",
true,
false,
resourceManager);
false);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.FullTextCatalog] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("full_text_catalog.ico")).ToBitmap(),
"objectType.fullTextCatalog",
"objectType_fullTextCatalog",
"FullTextCatalog",
true,
false,
resourceManager);
false);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.FunctionInline] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("table_valued_function.ico")).ToBitmap(),
"objectType.functionInline",
"objectType_functionInline",
"UserDefinedFunction",
String.Format(System.Globalization.CultureInfo.InvariantCulture, "@FunctionType='{0}'", (int)UserDefinedFunctionType.Inline),
"@IsSystemObject=false()",
true,
true,
resourceManager);
true);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.FunctionScalar] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("ScalarValuedFunction.ico")).ToBitmap(),
"objectType.functionScalar",
"objectType_functionScalar",
"UserDefinedFunction",
String.Format(System.Globalization.CultureInfo.InvariantCulture, "@FunctionType='{0}'", (int)UserDefinedFunctionType.Scalar),
"@IsSystemObject=false()",
true,
true,
resourceManager);
true);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.FunctionTable] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("table_valued_function.ico")).ToBitmap(),
"objectType.functionTable",
"objectType_functionTable",
"UserDefinedFunction",
String.Format(System.Globalization.CultureInfo.InvariantCulture, "@FunctionType='{0}'", (int)UserDefinedFunctionType.Table),
"@IsSystemObject=false()",
true,
true,
resourceManager);
true);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.Login] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("log_in_16x.ico")).ToBitmap(),
"objectType.login",
"objectType_login",
"Login",
String.Empty,
"@IsSystemObject=false()",
false,
false,
resourceManager);
false);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.LoginOnly] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("log_in_16x.ico")).ToBitmap(),
"objectType.login",
"objectType_login",
"Login",
"@LoginType = 2 or @LoginType = 0",
"@IsSystemObject=false()",
false,
false,
resourceManager);
false);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.Schema] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("database_schema.ico")).ToBitmap(),
"objectType.schema",
"objectType_schema",
"Schema",
true,
false,
resourceManager);
false);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.Server] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("server.ico")).ToBitmap(),
"objectType.server",
"objectType_server",
String.Empty,
false,
false,
resourceManager);
false);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.SecurityPolicy] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("securitypolicy.ico")).ToBitmap(),
"objectType.securityPolicy",
"objectType_securityPolicy",
"SecurityPolicy",
true,
true,
resourceManager);
true);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.ServiceQueue] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("queue.ico")).ToBitmap(),
"objectType.serviceQueue",
"objectType_serviceQueue",
"ServiceBroker/ServiceQueue",
true,
true,
resourceManager);
true);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.StoredProcedure] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("stored_procedure.ico")).ToBitmap(),
"objectType.storedProcedure",
"objectType_storedProcedure",
"StoredProcedure",
String.Empty,
"@IsSystemObject=false()",
true,
true,
resourceManager);
true);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.Synonym] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("synonym.ico")).ToBitmap(),
"objectType.synonym",
"objectType_synonym",
"Synonym",
true,
true,
resourceManager);
true);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.Sequence] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("sequence.ico")).ToBitmap(),
"objectType.sequence",
"objectType_sequence",
"Sequence",
true,
true,
resourceManager);
true);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.Table] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("table.ico")).ToBitmap(),
"objectType.table",
"objectType_table",
"Table",
String.Empty,
"@IsSystemObject=false()",
true,
true,
resourceManager);
true);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.User] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("user_16x.ico")).ToBitmap(),
"objectType.user",
"objectType_user",
"User",
String.Empty,
"(@IsSystemObject=false() or @Name='guest')",
true,
false,
resourceManager);
false);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.UserDefinedDataType] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("user_defined_data_type.ico")).ToBitmap(),
"objectType.userDefinedDataType",
"objectType_userDefinedDataType",
"UserDefinedDataType",
true,
true,
resourceManager);
true);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.View] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("view.ico")).ToBitmap(),
"objectType.view",
"objectType_view",
"View",
String.Empty,
"@IsSystemObject=false()",
true,
true,
resourceManager);
true);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.XmlSchemaCollection] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("XML_schemas.ico")).ToBitmap(),
"objectType.xmlSchemaCollection",
"objectType_xmlSchemaCollection",
"XmlSchemaCollection",
true,
true,
resourceManager);
true);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.Rule] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("rule.ico")).ToBitmap(),
"objectType.rule",
"objectType_rule",
"Rule",
true,
true,
resourceManager);
true);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.Default] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("defaults_16x.ico")).ToBitmap(),
"objectType.default",
"objectType_default",
"Default",
true,
true,
resourceManager);
true);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.Credential] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("credential.ico")).ToBitmap(),
"objectType.credential",
"objectType_credential",
"Credential",
false,
false,
resourceManager);
false);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.SymmetricKey] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("symmetric_key.ico")).ToBitmap(),
"objectType.symmetricKey",
"objectType_symmetricKey",
"SymmetricKey",
true,
false,
resourceManager);
false);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.UserDefinedTableType] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("table.ico")).ToBitmap(),
"objectType.userDefinedTableType",
"objectType_userDefinedTableType",
"UserDefinedTableType",
true,
true,
resourceManager);
true);
SearchableObjectTypeDescription.typeToDescription[SearchableObjectType.AvailabilityGroup] =
new SearchableObjectTypeDescription(
// DpiUtil.GetScaledIcon(ResourceUtils.LoadIcon("Availability_Group.ico")).ToBitmap(),
"objectType.AvailabilityGroup",
"objectType_AvailabilityGroup",
"AvailabilityGroup",
false,
false,
resourceManager);
false);
}
/// <summary>

View File

@@ -0,0 +1,296 @@
//
// 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.Globalization;
using Microsoft.SqlServer.Management.Sdk.Sfc;
using Microsoft.SqlServer.Management.Smo;
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
{
/// <summary>
/// Represents a Securable in SQL that can generate the TSQL to list
/// its permissions.
/// </summary>
internal class SupportedSecurable
{
#region constants
private const string queryWithColumn = @"SELECT
permission_name AS [Permission]
,subentity_name AS [Column]
FROM fn_my_permissions(N'{0}', N'{1}')
ORDER BY permission_name, subentity_name;";
private const string queryWithoutColumn = @"SELECT
permission_name AS [Permission]
FROM fn_my_permissions(N'{0}', N'{1}')
ORDER BY permission_name;";
private const string queryWithoutSecurableName = @"SELECT
permission_name AS [Permission]
FROM fn_my_permissions(NULL, N'{1}')
ORDER BY permission_name;";
#endregion
#region fields
/// <summary>
/// SMO server we are targetting. This is needed so we can generate the correct
/// query against Table valued functions
/// </summary>
private SqlServer.Management.Smo.Server server;
/// <summary>
/// Securable we are targetting
/// </summary>
private Urn securable;
/// <summary>
/// Name of the securable
/// </summary>
private string securableName;
/// <summary>
/// Schema of the securable. If the securable does not have a schema this will be an Empty string
/// </summary>
private string securableSchema;
/// <summary>
/// The SMO type of the securable.
/// </summary>
private string securableType;
#endregion
#region public properties
/// <summary>
/// Indicates whether or not the securable will return column information or just a
/// list of permissions
/// </summary>
public bool HasColumnInformation
{
get
{
bool hasColumnInformation = false;
if (securable.Type == "Table" || securable.Type == "View")
{
hasColumnInformation = true;
}
else if (securable.Type == "UserDefinedFunction" && this.server != null)
{
UserDefinedFunction function = server.GetSmoObject(this.securable) as UserDefinedFunction;
// STrace.Assert(function != null, "Could not get correct SMO object");
hasColumnInformation = (function != null && function.FunctionType == UserDefinedFunctionType.Table);
}
return hasColumnInformation;
}
}
/// <summary>
/// name of the securable
/// </summary>
public string Name
{
get
{
return this.securableName;
}
}
/// <summary>
/// schema of the securable
/// </summary>
public string Schema
{
get
{
return this.securableSchema;
}
}
#endregion
#region Construction
/// <summary>
/// Create a new SupportedSecurable object.
/// </summary>
/// <param name="securable">The securable we are targetting</param>
/// <param name="server">The server where the securable resides. Can be null</param>
public SupportedSecurable(Urn securable, SqlServer.Management.Smo.Server server)
{
if (securable == null)
{
throw new ArgumentNullException("securable");
}
this.securable = securable;
this.server = server;
// get the name
this.securableName = this.securable.GetAttribute("Name");
// get the schema from the urn
this.securableSchema = this.securable.GetAttribute("Schema");
// convert null to string.empty
this.securableSchema ??= String.Empty;
// get the type
this.securableType = this.securable.Type;
// check that we were passed good information
// STrace.Assert(this.securableName != null && this.securableName.Length > 0, "No usable object name available");
// STrace.Assert(this.securableType != null && this.securableType.Length > 0, "No usable object type available");
}
#endregion
#region public methods
/// <summary>
/// Generate a SQL query that would query the permissions on this securable
/// </summary>
/// <returns>a string that represents the query</returns>
public string GetPermissionsForSecurableSyntax()
{
string sqlQuery;
string fullSecurableName = null;
// get the class we will pass
string securableClass = GetSecurableClassForUrn(this.securable);
// Do not pass securable name for SERVER securables
if (securableClass == "SERVER")
{
sqlQuery = queryWithoutSecurableName;
}
else
{
if (this.HasColumnInformation)
{
sqlQuery = queryWithColumn;
}
else
{
sqlQuery = queryWithoutColumn;
}
if (this.securableSchema.Length > 0)
{
fullSecurableName = String.Format(CultureInfo.InvariantCulture, "[{0}].[{1}]"
, SecurableUtils.EscapeString(SecurableUtils.EscapeString(this.securableSchema, "]"), "'")
, SecurableUtils.EscapeString(SecurableUtils.EscapeString(this.securableName, "]"), "'"));
}
else
{
fullSecurableName = String.Format(CultureInfo.InvariantCulture, "[{0}]"
, SecurableUtils.EscapeString(SecurableUtils.EscapeString(this.securableName, "]"), "'"));
}
}
// return the select query
return String.Format(CultureInfo.InvariantCulture, sqlQuery, fullSecurableName, securableClass);
}
#endregion
#region implementation
/// <summary>
/// Finds a type for a Urn that can be passed to fn_my_permissions
/// </summary>
/// <param name="type">Urn</param>
/// <returns>tsql type</returns>
private static string GetSecurableClassForUrn(Urn securable)
{
string securableType;
// just use a simple switch. If we wanted to be more sophisticated we could use a
// chain-of-responsibility pattern
switch (securable.Type)
{
case "ApplicationRole":
securableType = "APPLICATION ROLE";
break;
case "SqlAssembly":
securableType = "ASSEMBLY";
break;
case "AsymmetricKey":
securableType = "ASYMMETRIC KEY";
break;
case "Certificate":
securableType = "CERTIFICATE";
break;
case "ServiceContract":
securableType = "CONTRACT";
break;
case "Database":
securableType = "DATABASE";
break;
case "Endpoint":
securableType = "ENDPOINT";
break;
case "ExternalDataSource":
securableType = "EXTERNAL DATA SOURCE";
break;
case "ExternalFileFormat":
securableType = "EXTERNAL FILE FORMAT";
break;
case "FullTextCatalog":
securableType = "FULLTEXT CATALOG";
break;
case "Login":
securableType = "LOGIN";
break;
case "MessageType":
securableType = "MESSAGE TYPE";
break;
case "AvailabilityGroup":
securableType = "AVAILABILITY GROUP";
break;
// the following types map to OBJECT
case "UserDefinedAggregate":
case "Check":
case "Default":
case "ForeignKey":
// index is for index and primary key constraints
case "Index":
case "StoredProcedure":
case "UserDefinedFunction":
case "Rule":
case "Synonym":
case "Sequence":
case "ServiceQueue":
case "Trigger":
case "DdlTrigger":
case "Table":
case "View":
case "ExtendedStoredProcedure":
securableType = "OBJECT";
break;
case "RemoteServiceBinding":
securableType = "REMOTE SERVICE BINDING";
break;
case "Role":
securableType = (securable.Parent.Type == "Database") ? "ROLE" : "SERVER ROLE";
break;
case "ServiceRoute":
securableType = "ROUTE";
break;
case "Schema":
securableType = "SCHEMA";
break;
case "SecurityPolicy":
securableType = "SECURITY POLICY";
break;
case "Server":
securableType = "SERVER";
break;
case "BrokerService":
securableType = "SERVICE";
break;
case "SymmetricKey":
securableType = "SYMMETRIC KEY";
break;
case "UserDefinedDataType":
securableType = "TYPE";
break;
case "User":
securableType = "USER";
break;
case "XmlSchemaCollection":
securableType = "XML SCHEMA COLLECTION";
break;
default:
// throw if we don't know about something
throw new InvalidOperationException();
}
return securableType;
}
#endregion
}
}

View File

@@ -40,7 +40,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
currentUserType = UserActions.GetCurrentUserTypeForExistingUser(dataContainer.Server.GetSmoObject(dataContainer.ObjectUrn) as User);
}
this.userPrototype = UserPrototypeFactory.GetUserPrototype(dataContainer, user, originalData, currentUserType);
this.userPrototype = UserPrototypeFactory.GetUserPrototype(dataContainer, user, originalData, currentUserType, dataContainer.IsNewObject);
}
// /// <summary>

View File

@@ -12,6 +12,7 @@ using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Sdk.Sfc;
using Microsoft.SqlTools.ServiceLayer.Management;
using Microsoft.SqlTools.ServiceLayer.Utility;
using Microsoft.SqlTools.ServiceLayer.ObjectManagement.PermissionsData;
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
{
@@ -93,6 +94,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
public SecureString passwordConfirm = new SecureString();
public SecureString oldPassword = new SecureString();
public bool isOldPasswordRequired = false;
public bool isNewObject = false;
/// <summary>
/// Used for creating clone of a UserPrototypeData.
@@ -363,6 +365,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
private bool exists = false;
private Database parent;
private CDataContainer context;
private SecurablePermissions[] securablePermissions = new SecurablePermissions[0];
private Principal principal = null;
public bool IsRoleMembershipChangesApplied { get; set; } //default is false
public bool IsSchemaOwnershipChangesApplied { get; set; } //default is false
@@ -483,6 +487,18 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
this.currentState.isMember[roleName] = isMember;
}
public SecurablePermissions[] SecurablePermissions
{
get
{
return this.securablePermissions;
}
set
{
this.securablePermissions = value;
}
}
#endregion
/// <summary>
@@ -501,6 +517,19 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
Database? parent = context.Server.GetSmoObject(new Urn(context.ParentUrn)) as Database ?? throw new ArgumentException("Context ParentUrn is invalid");
this.parent = parent;
var userName = this.currentState.name;
if (current.isNewObject)
{
this.securablePermissions = new SecurablePermissions[0];
this.principal = SecurableUtils.CreatePrincipal(false, PrincipalType.DatabaseRole, null, userName, context, parent.Name);
}
else
{
this.securablePermissions = SecurableUtils.GetSecurablePermissions(true, PrincipalType.User, parent.Users[userName], context);
this.principal = SecurableUtils.CreatePrincipal(true, PrincipalType.User, parent.Users[userName], null, context, parent.Name);
this.principal.AddExistingSecurables();
}
this.roleNames = this.PopulateRoles();
this.schemaNames = this.PopulateSchemas();
}
@@ -564,6 +593,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
this.ApplyRoleMembershipChanges(parentDb, user);
this.IsRoleMembershipChangesApplied = true;
SecurableUtils.SendToServerPermissionChanges(!string.IsNullOrEmpty(this.currentState.name), this.Name, this.SecurablePermissions, this.principal, context, parentDb.Name);
}
return user;
@@ -1031,10 +1062,11 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
{
public static UserPrototype GetUserPrototype(
CDataContainer context, UserInfo? user,
UserPrototypeData? originalData, ExhaustiveUserTypes userType)
UserPrototypeData? originalData, ExhaustiveUserTypes userType, bool isNewObject)
{
UserPrototype currentPrototype = null;
UserPrototypeData currentData = new UserPrototypeData(context, user);
currentData.isNewObject = isNewObject;
switch (userType)
{
case ExhaustiveUserTypes.AsymmetricKeyMappedUser:
@@ -1067,6 +1099,10 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
currentPrototype = null;
break;
}
if (user != null && user.SecurablePermissions != null)
{
currentPrototype.SecurablePermissions = user.SecurablePermissions;
}
return currentPrototype;
}
}

View File

@@ -56,14 +56,16 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
Name = prototype.Name,
Owner = prototype.Owner,
Members = prototype.Members.ToArray(),
Memberships = prototype.Memberships.ToArray()
Memberships = prototype.Memberships.ToArray(),
SecurablePermissions = prototype.SecurablePermissions
};
var viewInfo = new ServerRoleViewInfo()
{
ObjectInfo = ServerRoleInfo,
IsFixedRole = prototype.IsFixedRole,
ServerRoles = serverRoles.ToArray()
ServerRoles = serverRoles.ToArray(),
SupportedSecurableTypes = SecurableUtils.GetSecurableTypeMetadata(SqlObjectType.ServerRole, dataContainer.Server.Version, "", dataContainer.Server.DatabaseEngineType, dataContainer.Server.DatabaseEngineEdition)
};
var context = new ServerRoleViewContext(parameters);

View File

@@ -8,7 +8,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
/// <summary>
/// a class for storing various server role properties
/// </summary>
public class ServerRoleInfo : SqlObject
public class ServerRoleInfo : SecurityPrincipalObject
{
public string? Owner { get; set; }
public string[]? Members { get; set; }

View File

@@ -8,7 +8,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
/// <summary>
/// a class for storing various server role view properties
/// </summary>
public class ServerRoleViewInfo : SqlObjectViewInfo
public class ServerRoleViewInfo : SecurityPrincipalViewInfo
{
public bool IsFixedRole { get; set; }
public string[]? ServerRoles { get; set; }

View File

@@ -98,7 +98,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
}
// generate a user prototype
UserPrototype currentUserPrototype = UserPrototypeFactory.GetUserPrototype(dataContainer, userInfo, originalData: null, userType);
UserPrototype currentUserPrototype = UserPrototypeFactory.GetUserPrototype(dataContainer, userInfo, originalData: null, userType, parameters.IsNewObject);
// get the default schema if available
string defaultSchema = null;
@@ -212,13 +212,15 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
DefaultSchema = defaultSchema,
OwnedSchemas = schemaNames.ToArray(),
DatabaseRoles = databaseRoles.ToArray(),
DefaultLanguage = defaultLanguage
DefaultLanguage = defaultLanguage,
SecurablePermissions = currentUserPrototype.SecurablePermissions
},
UserTypes = supportedUserTypes.ToArray(),
Languages = languageOptionsList.ToArray(),
Schemas = currentUserPrototype.SchemaNames.ToArray(),
Logins = logins,
DatabaseRoles = currentUserPrototype.DatabaseRoleNames.ToArray()
DatabaseRoles = currentUserPrototype.DatabaseRoleNames.ToArray(),
SupportedSecurableTypes = SecurableUtils.GetSecurableTypeMetadata(SqlObjectType.User, dataContainer.Server.Version, parameters.Database, dataContainer.Server.DatabaseEngineType, dataContainer.Server.DatabaseEngineEdition)
};
var context = new UserViewContext(parameters, dataContainer.ServerConnection, currentUserPrototype.CurrentState);
return new InitializeViewResult { ViewInfo = userViewInfo, Context = context };

View File

@@ -33,7 +33,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
/// <summary>
/// a class for storing various user properties
/// </summary>
public class UserInfo : SqlObject
public class UserInfo : SecurityPrincipalObject
{
public DatabaseUserType? Type { get; set; }

View File

@@ -8,7 +8,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
/// <summary>
/// The information required to render the user view.
/// </summary>
public class UserViewInfo : SqlObjectViewInfo
public class UserViewInfo : SecurityPrincipalViewInfo
{
public DatabaseUserType[]? UserTypes { get; set; }

View File

@@ -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.
//
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
{
public class PermissionMetadata
{
public string? Name { get; set; }
public string? DisplayName { get; set; }
}
}

View File

@@ -0,0 +1,24 @@
//
// 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 SecurablePermissionItem
{
public string? Permission { get; set; }
public string? Grantor { get; set; }
public bool? Grant { get; set; }
public bool? WithGrant { get; set; }
}
public class SecurablePermissions
{
public string? Name { get; set; }
public string? Schema { get; set; }
public string? Type { get; set; }
public string[]? EffectivePermissions { get; set; }
public SecurablePermissionItem[]? Permissions { get; set; }
}
}

View File

@@ -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
{
public class SecurableTypeMetadata
{
public string? Name { get; set; }
public string? DisplayName { get; set; }
public PermissionMetadata[]? Permissions { get; set; }
}
}

View File

@@ -0,0 +1,530 @@
//
// 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.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Xml;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.ServiceLayer.Management;
using Microsoft.SqlTools.ServiceLayer.ObjectManagement.PermissionsData;
using Newtonsoft.Json;
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
{
public class SecurableUtils
{
private static readonly SearchableObjectType[] securableTypesForServerLevel = new SearchableObjectType[] {
SearchableObjectType.AvailabilityGroup,
SearchableObjectType.Endpoint,
SearchableObjectType.Login,
SearchableObjectType.ServerRole,
SearchableObjectType.Server
};
private static readonly SearchableObjectType[] securableTypesForDbLevel = new SearchableObjectType[] {
SearchableObjectType.AggregateFunction,
SearchableObjectType.ApplicationRole,
SearchableObjectType.Assembly,
SearchableObjectType.AsymmetricKey,
SearchableObjectType.Certificate,
SearchableObjectType.Database,
SearchableObjectType.DatabaseRole,
SearchableObjectType.ExternalDataSource,
SearchableObjectType.ExternalFileFormat,
SearchableObjectType.FullTextCatalog,
SearchableObjectType.FunctionInline,
SearchableObjectType.ServiceQueue,
SearchableObjectType.FunctionScalar,
SearchableObjectType.Schema,
SearchableObjectType.SecurityPolicy,
SearchableObjectType.Sequence,
SearchableObjectType.StoredProcedure,
SearchableObjectType.SymmetricKey,
SearchableObjectType.Synonym,
SearchableObjectType.Table,
SearchableObjectType.FunctionTable,
SearchableObjectType.UserDefinedDataType,
SearchableObjectType.UserDefinedTableType,
SearchableObjectType.User,
SearchableObjectType.View,
SearchableObjectType.XmlSchemaCollection
};
static internal string launchEffectivePermissions = @"
<formdescription>
<params>
<servername></servername>
<servertype>sql</servertype>
<database></database>
<urn></urn>
<executeas></executeas>
<executetype></executetype>
<A32942B7-FBDE-4ac3-B84E-F5EC89961094 />
<assemblyname>sqlmgmt.dll</assemblyname>
</params>
</formdescription>";
public static SecurableTypeMetadata[] GetSecurableTypeMetadata(SqlObjectType objectType, Version serverVersion, string databaseName,DatabaseEngineType databaseEngineType, DatabaseEngineEdition engineEdition)
{
List<SecurableTypeMetadata> res = new List<SecurableTypeMetadata>();
switch (objectType)
{
case SqlObjectType.ServerLevelLogin:
case SqlObjectType.ServerRole:
AddSecurableTypeMetadata(res, securableTypesForServerLevel, null, serverVersion, databaseName, databaseEngineType, engineEdition);
break;
case SqlObjectType.ApplicationRole:
case SqlObjectType.DatabaseRole:
case SqlObjectType.User:
AddSecurableTypeMetadata(res, securableTypesForDbLevel, null, serverVersion, databaseName, databaseEngineType, engineEdition);
break;
default:
break;
}
return res.ToArray();
}
private static void AddSecurableTypeMetadata(List<SecurableTypeMetadata> res, SearchableObjectType[] supportedTypes, SearchableObjectType[] excludeList, Version serverVersion, string databaseName,DatabaseEngineType databaseEngineType, DatabaseEngineEdition engineEdition)
{
foreach(SearchableObjectType t in supportedTypes)
{
if (t == SearchableObjectType.LastType || (excludeList != null && excludeList.Contains(t)))
{
continue;
}
SecurableType secType = PermissionsData.Securable.GetSecurableType(t);
SearchableObjectTypeDescription desc = SearchableObjectTypeDescription.GetDescription(t);
var pList = PermissionsData.Securable.GetRelevantPermissions(secType, serverVersion, databaseName, databaseEngineType, engineEdition);
var permissions = new PermissionMetadata[pList.Count];
for (int i = 0; i < pList.Count; i++)
{
var p = (Permission)pList[i];
permissions[i] = new PermissionMetadata()
{
Name = p?.Name,
DisplayName = p?.Name
};
}
SecurableTypeMetadata metadata = new SecurableTypeMetadata()
{
Name = desc.DisplayTypeNameSingular,
DisplayName = desc.DisplayTypeNamePlural,
Permissions = permissions
};
res.Add(metadata);
}
res.Sort((x, y) => string.Compare(x.Name, y.Name, StringComparison.InvariantCulture));
}
public static SecurablePermissions[] GetSecurablePermissions(bool principalExists, PrincipalType principalType, SqlSmoObject o, CDataContainer dataContainer)
{
List<SecurablePermissions> res = new List<SecurablePermissions>();
Principal principal;
try
{
principal = CreatePrincipal(principalExists, principalType, o, null, dataContainer);
}
catch(Exception)
{
return new SecurablePermissions[0];
}
principal.AddExistingSecurables();
var securables = principal.GetSecurables(new SecurableComparer(SecurableComparer.DefaultSortingOrder, true));
foreach (Securable s in securables)
{
var permissionStates = principal.GetPermissionStates(s);
Dictionary<string, SecurablePermissionItem> permissionItemsDict = new Dictionary<string, SecurablePermissionItem>();
for (int i = 0; i < permissionStates.Count; i++)
{
var p = permissionStates[i];
string key = p?.Permission.Name ?? string.Empty;
if (!permissionItemsDict.ContainsKey(key) || string.IsNullOrEmpty(permissionItemsDict[key].Grantor))
{
var permissionItem = new SecurablePermissionItem()
{
Permission = p?.Permission.Name,
Grantor = p?.Grantor,
Grant = p?.State == PermissionStatus.Revoke ? null : p?.State == PermissionStatus.Grant || p?.State == PermissionStatus.WithGrant,
WithGrant = p?.State == PermissionStatus.Revoke ? null : p?.State == PermissionStatus.WithGrant,
};
permissionItemsDict[key] = permissionItem;
}
}
var permissions = permissionItemsDict.Values.OrderBy(x => x.Permission, StringComparer.InvariantCulture).ToArray();
SecurablePermissions secPerm = new SecurablePermissions()
{
Name = s.Name,
Schema = s.Schema,
Type = s.TypeName,
Permissions = permissions,
EffectivePermissions = CanHaveEffectivePermissions(principalType) ? GetEffectivePermissions(dataContainer, s, principal) : new string[0]
};
res.Add(secPerm);
}
return res.ToArray();
}
public static bool CanHaveEffectivePermissions(PrincipalType principalType)
{
if (principalType == PrincipalType.ServerRole || principalType == PrincipalType.DatabaseRole || principalType == PrincipalType.ApplicationRole)
{
return false;
}
return true;
}
internal static string[] GetEffectivePermissions(CDataContainer dataContainer, Securable securable, Principal principal)
{
var doc = ReadEffectivePermissionsXml(securable, principal);
dataContainer.Document = doc;
var dataModel = new EffectivePermissionsData(dataContainer);
List<string> res = new List<string>();
DataSet data = dataModel.QueryEffectivePermissions();
// STrace.Assert(data.Tables.Count == 1, "Unknown number of tables returned");
if (data.Tables.Count > 0)
{
DataTable table = data.Tables[0];
// STrace.Assert(table.Columns.Count >= 1 && table.Columns.Count <= 2, "Too many columns returned");
bool hasColumnInformation = dataModel.HasColumnInformation;
// loop through and add rows
foreach (DataRow row in table.Rows)
{
res.Add(row[0].ToString());
}
}
return res.ToArray();
}
/// <summary>
/// Form the xml to query effective permissions data
/// </summary>
/// <returns></returns>
private static XmlDocument ReadEffectivePermissionsXml(Securable securable, Principal principal )
{
if (securable != null && principal != null)
{
string executeas = null;
string executetype = null;
GetPrincipalToExecuteAs(principal,
securable.DatabaseName,
securable.ConnectionInfo,
out executeas,
out executetype);
// build a document
XmlDocument xml = new XmlDocument();
xml.LoadXml(launchEffectivePermissions);
xml.SelectSingleNode("/formdescription/params/urn").InnerText = securable.Urn;
xml.SelectSingleNode("/formdescription/params/servername").InnerText = ((SqlConnectionInfo)securable.ConnectionInfo).ServerName;
xml.SelectSingleNode("/formdescription/params/database").InnerText = securable.DatabaseName;
xml.SelectSingleNode("/formdescription/params/executeas").InnerText = executeas;
xml.SelectSingleNode("/formdescription/params/executetype").InnerText = executetype;
return xml;
}
return null;
}
/// <summary>
/// Create principal object for server level principals
/// </summary>
internal static Principal CreatePrincipal(bool principalExists, PrincipalType principalType, SqlSmoObject o, string? objectName, CDataContainer dataContainer)
{
if (principalExists)
{
NamedSmoObject obj = (NamedSmoObject) o;
return new Principal(obj, dataContainer.ConnectionInfo);
}
else
{
Version serverVersion = Securable.GetServerVersion(dataContainer.ConnectionInfo);
return new Principal(
objectName,
principalType,
principalExists,
dataContainer.ConnectionInfo,
serverVersion);
}
}
/// <summary>
/// Create principal object for database level principals
/// </summary>
internal static Principal CreatePrincipal(bool principalExists, PrincipalType principalType, SqlSmoObject o, string? objectName, CDataContainer dataContainer, string databaseName)
{
if (principalExists)
{
NamedSmoObject obj = (NamedSmoObject)o;
return new Principal(obj, dataContainer.ConnectionInfo);
}
else
{
Version serverVersion = Securable.GetServerVersion(dataContainer.ConnectionInfo);
DatabaseEngineType databaseEngineType = Securable.GetDatabaseEngineType(dataContainer.ConnectionInfo);
DatabaseEngineEdition databaseEngineEdition = Securable.GetDatabaseEngineEdition(dataContainer.ConnectionInfo);
return new Principal(
objectName,
databaseName,
principalType,
principalExists,
dataContainer.ConnectionInfo,
serverVersion,
databaseEngineType,
databaseEngineEdition);
}
}
public static String EscapeString(String s, string esc)
{
if (null == s)
{
return null;
}
string replace = esc + esc;
StringBuilder sb = new StringBuilder(s);
sb.Replace(esc, replace);
return sb.ToString();
}
internal static SearchableObjectType ConvertPotentialSqlObjectTypeToSearchableObjectType(string typeStr)
{
if (typeStr == ConvertSqlObjectTypeToStringValue(SqlObjectType.ApplicationRole))
{
return SearchableObjectType.ApplicationRole;
}
else if (typeStr == ConvertSqlObjectTypeToStringValue(SqlObjectType.Credential))
{
return SearchableObjectType.Credential;
}
else if (typeStr == ConvertSqlObjectTypeToStringValue(SqlObjectType.DatabaseRole))
{
return SearchableObjectType.DatabaseRole;
}
else if (typeStr == ConvertSqlObjectTypeToStringValue(SqlObjectType.ServerLevelLogin))
{
return SearchableObjectType.Login;
}
else if (typeStr == ConvertSqlObjectTypeToStringValue(SqlObjectType.ServerRole))
{
return SearchableObjectType.ServerRole;
}
else if (typeStr == ConvertSqlObjectTypeToStringValue(SqlObjectType.Table))
{
return SearchableObjectType.Table;
}
else if (typeStr == ConvertSqlObjectTypeToStringValue(SqlObjectType.User))
{
return SearchableObjectType.User;
}
else if (typeStr == ConvertSqlObjectTypeToStringValue(SqlObjectType.View))
{
return SearchableObjectType.View;
}
else
{
return ConvertStringToSearchableObjectType(typeStr);
}
}
private static string ConvertSqlObjectTypeToStringValue(SqlObjectType objectType)
{
return JsonConvert.SerializeObject(objectType).Replace("\"", "");
}
private static SearchableObjectType ConvertStringToSearchableObjectType(string typeStr)
{
foreach(SearchableObjectType t in Enum.GetValues(typeof(SearchableObjectType)))
{
if (t == SearchableObjectType.LastType)
{
continue;
}
SecurableType secType = PermissionsData.Securable.GetSecurableType(t);
SearchableObjectTypeDescription desc = SearchableObjectTypeDescription.GetDescription(t);
if (desc.DisplayTypeNameSingular == typeStr || desc.DisplayTypeNamePlural == typeStr)
{
return t;
}
}
return SearchableObjectType.LastType;
}
internal static void GetPrincipalToExecuteAs(Principal principal,
string databaseName,
object connectionInfo,
out string executeas,
out string executetype)
{
executeas = null;
executetype = null;
//
// IF we are a user AND we are mapped to a login,
// then we actually want to calculate effective
// permissions as the login, and not as the user.
// why? because that's the only way the server
// level perms will be taken into account.
//
if (principal.PrincipalType == PrincipalType.User &&
!string.IsNullOrEmpty(databaseName))
{
SqlConnectionInfoWithConnection ci = connectionInfo as SqlConnectionInfoWithConnection;
if (ci != null && ci.ServerConnection != null)
{
Server server = new Server(ci.ServerConnection);
if (server != null)
{
Database db = server.Databases[databaseName];
if (db != null)
{
User u = db.Users[principal.Name];
//
// if the the user is mapped to a certificate or
// or asymmetric key, we should execute as user.
//
if (u != null &&
(u.LoginType == LoginType.SqlLogin ||
u.LoginType == LoginType.WindowsUser ||
u.LoginType == LoginType.WindowsGroup) &&
!string.IsNullOrEmpty(u.Login))
{
executeas = u.Login;
executetype = "login";
}
}
}
}
}
//
// if we couldn't determine what type of user the principal was,
// or if the user is mapped to a certificate or asymmetric key, or if
// the principal was a login, we will default to executing as whatever
// principal type we are (either login or user).
//
if (string.IsNullOrEmpty(executeas) || string.IsNullOrEmpty(executetype))
{
executeas = principal.Name;
executetype = (principal.PrincipalType == PrincipalType.Login) ? "login" : "user";
}
}
internal static SearchableObject ConvertFromSecurableNameToSearchableObject(string securableName, string type, string database, object connectionInfo)
{
SearchableObjectType searchableObjectType = ConvertPotentialSqlObjectTypeToSearchableObjectType(type);
SearchableObjectTypeDescription desc = SearchableObjectTypeDescription.GetDescription(searchableObjectType);
var urn = desc.GetSearchUrn(securableName, true, true);
return SearchableObject.GetSearchableObject(searchableObjectType, connectionInfo, database, securableName);
}
internal static void SendToServerPermissionChanges(bool exists, string name, SecurablePermissions[] securablePermissions, Principal principal, CDataContainer dataContainer, string database)
{
if (securablePermissions == null)
{
return;
}
if (!exists)
{
foreach (SecurablePermissions secPerm in securablePermissions)
{
var securable = principal.AddSecurable(SecurableUtils.ConvertFromSecurableNameToSearchableObject(secPerm.Name, secPerm.Type, database, dataContainer.ConnectionInfo));
var states = principal.GetPermissionStates(securable);
ApplyPermissionStates(secPerm.Permissions, states);
}
}
else
{
var securables = principal.GetSecurables(new SecurableComparer(SecurableComparer.DefaultSortingOrder, true));
foreach (SecurablePermissions secPerm in securablePermissions)
{
var securable = FindMatchedSecurable(securables, secPerm.Name) ?? principal.AddSecurable(SecurableUtils.ConvertFromSecurableNameToSearchableObject(secPerm.Name, secPerm.Type, database, dataContainer.ConnectionInfo));
var states = principal.GetPermissionStates(securable);
ApplyPermissionStates(secPerm.Permissions, states);
}
var newSecurableNames = securablePermissions.Select(s => s.Name).ToHashSet();
foreach (Securable securable in securables)
{
if (!newSecurableNames.Contains(securable.Name))
{
var states = principal.GetPermissionStates(securable);
for (int i = 0; i < states.Count; i++)
{
states[i].Revoke();
}
principal.RemoveSecurable(securable);
}
}
}
principal.ApplyChanges(name, dataContainer.Server);
}
private static Securable FindMatchedSecurable(SecurableList securableList, string name)
{
foreach (Securable securable in securableList)
{
if (securable.Name == name)
{
return securable;
}
}
return null;
}
private static void ApplyPermissionStates(SecurablePermissionItem[] items, PermissionStateCollection states)
{
foreach (var p in items)
{
var key = p.Permission + p.Grantor;
if (p.WithGrant == true)
{
states[key].State = PermissionStatus.WithGrant;
}
else if (p.Grant == true)
{
states[key].State = PermissionStatus.Grant;
}
else if (p.Grant == false)
{
states[key].State = PermissionStatus.Deny;
}
else if (p.Grant == null)
{
states[key].State = PermissionStatus.Revoke;
}
}
var itemNames = items.Select(item => item.Permission).ToHashSet();
for (int i = 0; i < states.Count; i++)
{
var state = states[i];
if (!itemNames.Contains(state.Permission.Name))
{
state.State = PermissionStatus.Revoke;
}
}
}
}
}

View File

@@ -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 SecurityPrincipalObject : SqlObject
{
public SecurablePermissions[]? SecurablePermissions { get; set; }
}
}

View File

@@ -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 SecurityPrincipalViewInfo : SqlObjectViewInfo
{
public SecurableTypeMetadata[]? SupportedSecurableTypes { get; set; }
}
}