diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs index 977e16e9..13638cff 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs @@ -9877,6 +9877,582 @@ namespace Microsoft.SqlTools.ServiceLayer } } + public static string objectType_functionTable_plural + { + get + { + return Keys.GetString(Keys.objectType_functionTable_plural); + } + } + + public static string objectType_externalDataSource_singular + { + get + { + return Keys.GetString(Keys.objectType_externalDataSource_singular); + } + } + + public static string objectType_serverRole_singular + { + get + { + return Keys.GetString(Keys.objectType_serverRole_singular); + } + } + + public static string objectType_xmlSchemaCollection_singular + { + get + { + return Keys.GetString(Keys.objectType_xmlSchemaCollection_singular); + } + } + + public static string objectType_storedProcedure_plural + { + get + { + return Keys.GetString(Keys.objectType_storedProcedure_plural); + } + } + + public static string objectType_endpoint_plural + { + get + { + return Keys.GetString(Keys.objectType_endpoint_plural); + } + } + + public static string objectType_sequence_singular + { + get + { + return Keys.GetString(Keys.objectType_sequence_singular); + } + } + + public static string objectType_userDefinedDataType_singular + { + get + { + return Keys.GetString(Keys.objectType_userDefinedDataType_singular); + } + } + + public static string objectType_fullTextCatalog_plural + { + get + { + return Keys.GetString(Keys.objectType_fullTextCatalog_plural); + } + } + + public static string objectType_credential_singular + { + get + { + return Keys.GetString(Keys.objectType_credential_singular); + } + } + + public static string objectType_databaseRole_plural + { + get + { + return Keys.GetString(Keys.objectType_databaseRole_plural); + } + } + + public static string objectType_endpoint_singular + { + get + { + return Keys.GetString(Keys.objectType_endpoint_singular); + } + } + + public static string objectType_view_plural + { + get + { + return Keys.GetString(Keys.objectType_view_plural); + } + } + + public static string objectType_assembly_singular + { + get + { + return Keys.GetString(Keys.objectType_assembly_singular); + } + } + + public static string objectType_functionScalar_singular + { + get + { + return Keys.GetString(Keys.objectType_functionScalar_singular); + } + } + + public static string objectType_server_plural + { + get + { + return Keys.GetString(Keys.objectType_server_plural); + } + } + + public static string objectType_table_singular + { + get + { + return Keys.GetString(Keys.objectType_table_singular); + } + } + + public static string objectType_serviceQueue_singular + { + get + { + return Keys.GetString(Keys.objectType_serviceQueue_singular); + } + } + + public static string objectType_login_plural + { + get + { + return Keys.GetString(Keys.objectType_login_plural); + } + } + + public static string objectType_storedProcedure_singular + { + get + { + return Keys.GetString(Keys.objectType_storedProcedure_singular); + } + } + + public static string objectType_default_plural + { + get + { + return Keys.GetString(Keys.objectType_default_plural); + } + } + + public static string objectType_symmetricKey_singular + { + get + { + return Keys.GetString(Keys.objectType_symmetricKey_singular); + } + } + + public static string objectType_userDefinedTableType_singular + { + get + { + return Keys.GetString(Keys.objectType_userDefinedTableType_singular); + } + } + + public static string objectType_functionInline_singular + { + get + { + return Keys.GetString(Keys.objectType_functionInline_singular); + } + } + + public static string objectType_serverRole_plural + { + get + { + return Keys.GetString(Keys.objectType_serverRole_plural); + } + } + + public static string objectType_agentjob_singular + { + get + { + return Keys.GetString(Keys.objectType_agentjob_singular); + } + } + + public static string objectType_databaseRole_singular + { + get + { + return Keys.GetString(Keys.objectType_databaseRole_singular); + } + } + + public static string objectType_synonym_singular + { + get + { + return Keys.GetString(Keys.objectType_synonym_singular); + } + } + + public static string objectType_AvailabilityGroup_singular + { + get + { + return Keys.GetString(Keys.objectType_AvailabilityGroup_singular); + } + } + + public static string objectType_externalFileFormat_plural + { + get + { + return Keys.GetString(Keys.objectType_externalFileFormat_plural); + } + } + + public static string objectType_database_singular + { + get + { + return Keys.GetString(Keys.objectType_database_singular); + } + } + + public static string objectType_symmetricKey_plural + { + get + { + return Keys.GetString(Keys.objectType_symmetricKey_plural); + } + } + + public static string objectType_securityPolicy_singular + { + get + { + return Keys.GetString(Keys.objectType_securityPolicy_singular); + } + } + + public static string objectType_externalFileFormat_singular + { + get + { + return Keys.GetString(Keys.objectType_externalFileFormat_singular); + } + } + + public static string objectType_applicationRole_singular + { + get + { + return Keys.GetString(Keys.objectType_applicationRole_singular); + } + } + + public static string objectType_assembly_plural + { + get + { + return Keys.GetString(Keys.objectType_assembly_plural); + } + } + + public static string objectType_sequence_plural + { + get + { + return Keys.GetString(Keys.objectType_sequence_plural); + } + } + + public static string objectType_userDefinedDataType_plural + { + get + { + return Keys.GetString(Keys.objectType_userDefinedDataType_plural); + } + } + + public static string objectType_server_singular + { + get + { + return Keys.GetString(Keys.objectType_server_singular); + } + } + + public static string objectType_aggregateFunction_singular + { + get + { + return Keys.GetString(Keys.objectType_aggregateFunction_singular); + } + } + + public static string objectType_fullTextCatalog_singular + { + get + { + return Keys.GetString(Keys.objectType_fullTextCatalog_singular); + } + } + + public static string objectType_certificate_plural + { + get + { + return Keys.GetString(Keys.objectType_certificate_plural); + } + } + + public static string objectType_AvailabilityGroup_plural + { + get + { + return Keys.GetString(Keys.objectType_AvailabilityGroup_plural); + } + } + + public static string objectType_database_plural + { + get + { + return Keys.GetString(Keys.objectType_database_plural); + } + } + + public static string objectType_view_singular + { + get + { + return Keys.GetString(Keys.objectType_view_singular); + } + } + + public static string objectType_schema_plural + { + get + { + return Keys.GetString(Keys.objectType_schema_plural); + } + } + + public static string objectType_rule_plural + { + get + { + return Keys.GetString(Keys.objectType_rule_plural); + } + } + + public static string objectType_certificate_singular + { + get + { + return Keys.GetString(Keys.objectType_certificate_singular); + } + } + + public static string objectType_user_singular + { + get + { + return Keys.GetString(Keys.objectType_user_singular); + } + } + + public static string objectType_agentjob_plural + { + get + { + return Keys.GetString(Keys.objectType_agentjob_plural); + } + } + + public static string objectType_synonym_plural + { + get + { + return Keys.GetString(Keys.objectType_synonym_plural); + } + } + + public static string objectType_aggregateFunction_plural + { + get + { + return Keys.GetString(Keys.objectType_aggregateFunction_plural); + } + } + + public static string objectType_functionTable_singular + { + get + { + return Keys.GetString(Keys.objectType_functionTable_singular); + } + } + + public static string objectType_rule_singular + { + get + { + return Keys.GetString(Keys.objectType_rule_singular); + } + } + + public static string objectType_serviceQueue_plural + { + get + { + return Keys.GetString(Keys.objectType_serviceQueue_plural); + } + } + + public static string objectType_asymmetricKey_plural + { + get + { + return Keys.GetString(Keys.objectType_asymmetricKey_plural); + } + } + + public static string objectType_applicationRole_plural + { + get + { + return Keys.GetString(Keys.objectType_applicationRole_plural); + } + } + + public static string objectType_extendedStoredProcedure_singular + { + get + { + return Keys.GetString(Keys.objectType_extendedStoredProcedure_singular); + } + } + + public static string objectType_login_singular + { + get + { + return Keys.GetString(Keys.objectType_login_singular); + } + } + + public static string objectType_functionInline_plural + { + get + { + return Keys.GetString(Keys.objectType_functionInline_plural); + } + } + + public static string objectType_user_plural + { + get + { + return Keys.GetString(Keys.objectType_user_plural); + } + } + + public static string objectType_externalDataSource_plural + { + get + { + return Keys.GetString(Keys.objectType_externalDataSource_plural); + } + } + + public static string objectType_functionScalar_plural + { + get + { + return Keys.GetString(Keys.objectType_functionScalar_plural); + } + } + + public static string objectType_table_plural + { + get + { + return Keys.GetString(Keys.objectType_table_plural); + } + } + + public static string objectType_credential_plural + { + get + { + return Keys.GetString(Keys.objectType_credential_plural); + } + } + + public static string objectType_schema_singular + { + get + { + return Keys.GetString(Keys.objectType_schema_singular); + } + } + + public static string objectType_userDefinedTableType_plural + { + get + { + return Keys.GetString(Keys.objectType_userDefinedTableType_plural); + } + } + + public static string objectType_securityPolicy_plural + { + get + { + return Keys.GetString(Keys.objectType_securityPolicy_plural); + } + } + + public static string objectType_xmlSchemaCollection_plural + { + get + { + return Keys.GetString(Keys.objectType_xmlSchemaCollection_plural); + } + } + + public static string objectType_extendedStoredProcedure_plural + { + get + { + return Keys.GetString(Keys.objectType_extendedStoredProcedure_plural); + } + } + + public static string objectType_asymmetricKey_singular + { + get + { + return Keys.GetString(Keys.objectType_asymmetricKey_singular); + } + } + + public static string objectType_default_singular + { + get + { + return Keys.GetString(Keys.objectType_default_singular); + } + } + public static string Permission_Alter { get @@ -15159,6 +15735,222 @@ namespace Microsoft.SqlTools.ServiceLayer public const string ResetPasswordWhileUnlocking = "ResetPasswordWhileUnlocking"; + public const string objectType_functionTable_plural = "objectType_functionTable_plural"; + + + public const string objectType_externalDataSource_singular = "objectType_externalDataSource_singular"; + + + public const string objectType_serverRole_singular = "objectType_serverRole_singular"; + + + public const string objectType_xmlSchemaCollection_singular = "objectType_xmlSchemaCollection_singular"; + + + public const string objectType_storedProcedure_plural = "objectType_storedProcedure_plural"; + + + public const string objectType_endpoint_plural = "objectType_endpoint_plural"; + + + public const string objectType_sequence_singular = "objectType_sequence_singular"; + + + public const string objectType_userDefinedDataType_singular = "objectType_userDefinedDataType_singular"; + + + public const string objectType_fullTextCatalog_plural = "objectType_fullTextCatalog_plural"; + + + public const string objectType_credential_singular = "objectType_credential_singular"; + + + public const string objectType_databaseRole_plural = "objectType_databaseRole_plural"; + + + public const string objectType_endpoint_singular = "objectType_endpoint_singular"; + + + public const string objectType_view_plural = "objectType_view_plural"; + + + public const string objectType_assembly_singular = "objectType_assembly_singular"; + + + public const string objectType_functionScalar_singular = "objectType_functionScalar_singular"; + + + public const string objectType_server_plural = "objectType_server_plural"; + + + public const string objectType_table_singular = "objectType_table_singular"; + + + public const string objectType_serviceQueue_singular = "objectType_serviceQueue_singular"; + + + public const string objectType_login_plural = "objectType_login_plural"; + + + public const string objectType_storedProcedure_singular = "objectType_storedProcedure_singular"; + + + public const string objectType_default_plural = "objectType_default_plural"; + + + public const string objectType_symmetricKey_singular = "objectType_symmetricKey_singular"; + + + public const string objectType_userDefinedTableType_singular = "objectType_userDefinedTableType_singular"; + + + public const string objectType_functionInline_singular = "objectType_functionInline_singular"; + + + public const string objectType_serverRole_plural = "objectType_serverRole_plural"; + + + public const string objectType_agentjob_singular = "objectType_agentjob_singular"; + + + public const string objectType_databaseRole_singular = "objectType_databaseRole_singular"; + + + public const string objectType_synonym_singular = "objectType_synonym_singular"; + + + public const string objectType_AvailabilityGroup_singular = "objectType_AvailabilityGroup_singular"; + + + public const string objectType_externalFileFormat_plural = "objectType_externalFileFormat_plural"; + + + public const string objectType_database_singular = "objectType_database_singular"; + + + public const string objectType_symmetricKey_plural = "objectType_symmetricKey_plural"; + + + public const string objectType_securityPolicy_singular = "objectType_securityPolicy_singular"; + + + public const string objectType_externalFileFormat_singular = "objectType_externalFileFormat_singular"; + + + public const string objectType_applicationRole_singular = "objectType_applicationRole_singular"; + + + public const string objectType_assembly_plural = "objectType_assembly_plural"; + + + public const string objectType_sequence_plural = "objectType_sequence_plural"; + + + public const string objectType_userDefinedDataType_plural = "objectType_userDefinedDataType_plural"; + + + public const string objectType_server_singular = "objectType_server_singular"; + + + public const string objectType_aggregateFunction_singular = "objectType_aggregateFunction_singular"; + + + public const string objectType_fullTextCatalog_singular = "objectType_fullTextCatalog_singular"; + + + public const string objectType_certificate_plural = "objectType_certificate_plural"; + + + public const string objectType_AvailabilityGroup_plural = "objectType_AvailabilityGroup_plural"; + + + public const string objectType_database_plural = "objectType_database_plural"; + + + public const string objectType_view_singular = "objectType_view_singular"; + + + public const string objectType_schema_plural = "objectType_schema_plural"; + + + public const string objectType_rule_plural = "objectType_rule_plural"; + + + public const string objectType_certificate_singular = "objectType_certificate_singular"; + + + public const string objectType_user_singular = "objectType_user_singular"; + + + public const string objectType_agentjob_plural = "objectType_agentjob_plural"; + + + public const string objectType_synonym_plural = "objectType_synonym_plural"; + + + public const string objectType_aggregateFunction_plural = "objectType_aggregateFunction_plural"; + + + public const string objectType_functionTable_singular = "objectType_functionTable_singular"; + + + public const string objectType_rule_singular = "objectType_rule_singular"; + + + public const string objectType_serviceQueue_plural = "objectType_serviceQueue_plural"; + + + public const string objectType_asymmetricKey_plural = "objectType_asymmetricKey_plural"; + + + public const string objectType_applicationRole_plural = "objectType_applicationRole_plural"; + + + public const string objectType_extendedStoredProcedure_singular = "objectType_extendedStoredProcedure_singular"; + + + public const string objectType_login_singular = "objectType_login_singular"; + + + public const string objectType_functionInline_plural = "objectType_functionInline_plural"; + + + public const string objectType_user_plural = "objectType_user_plural"; + + + public const string objectType_externalDataSource_plural = "objectType_externalDataSource_plural"; + + + public const string objectType_functionScalar_plural = "objectType_functionScalar_plural"; + + + public const string objectType_table_plural = "objectType_table_plural"; + + + public const string objectType_credential_plural = "objectType_credential_plural"; + + + public const string objectType_schema_singular = "objectType_schema_singular"; + + + public const string objectType_userDefinedTableType_plural = "objectType_userDefinedTableType_plural"; + + + public const string objectType_securityPolicy_plural = "objectType_securityPolicy_plural"; + + + public const string objectType_xmlSchemaCollection_plural = "objectType_xmlSchemaCollection_plural"; + + + public const string objectType_extendedStoredProcedure_plural = "objectType_extendedStoredProcedure_plural"; + + + public const string objectType_asymmetricKey_singular = "objectType_asymmetricKey_singular"; + + + public const string objectType_default_singular = "objectType_default_singular"; + + public const string Permission_Alter = "Permission_Alter"; diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx index b03d90b1..026379e7 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx @@ -5475,6 +5475,294 @@ The Query Processor estimates that implementing the following index could improv Reset password for the login while unlocking. + + Table-valued functions + + + + External Data Source + + + + Server role + + + + XML schema collection + + + + Stored procedures + + + + Endpoints + + + + Sequence + + + + User-defined data type + + + + Full-text catalogs + + + + Credential + + + + Database roles + + + + Endpoint + + + + Views + + + + Assembly + + + + Scalar function + + + + Servers + + + + Table + + + + Queue + + + + Logins + + + + Stored procedure + + + + Defaults + + + + Symmetric key + + + + User-defined table type + + + + Inline function + + + + Server roles + + + + Agent job + + + + Database role + + + + Synonym + + + + Availability Group + + + + External File Formats + + + + Database + + + + Symmetric keys + + + + Security Policy + + + + External File Format + + + + Application role + + + + Assemblies + + + + Sequences + + + + User-defined data types + + + + Server + + + + Aggregate function + + + + Full-text catalog + + + + Certificates + + + + Availability Groups + + + + Databases + + + + View + + + + Schemas + + + + Rules + + + + Certificate + + + + User + + + + Agent jobs + + + + Synonyms + + + + Aggregate functions + + + + Table-valued function + + + + Rule + + + + Queues + + + + Asymmetric keys + + + + Application roles + + + + Extended stored procedure + + + + Login + + + + Inline functions + + + + Users + + + + External Data Sources + + + + Scalar functions + + + + Tables + + + + Credentials + + + + Schema + + + + User-defined table types + + + + Security Policies + + + + XML schema collections + + + + Extended stored procedures + + + + Asymmetric key + + + + Default + + Alter diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings index 12d0fa38..225b4928 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings @@ -2470,6 +2470,80 @@ ObjectNotRenamable(string urn) = The object could not be renamed. URN: '{0}'. DefaultLanguagePlaceholder = ResetPasswordWhileUnlocking = Reset password for the login while unlocking. +#Search +objectType_functionTable_plural = Table-valued functions +objectType_externalDataSource_singular = External Data Source +objectType_serverRole_singular = Server role +objectType_xmlSchemaCollection_singular = XML schema collection +objectType_storedProcedure_plural = Stored procedures +objectType_endpoint_plural = Endpoints +objectType_sequence_singular = Sequence +objectType_userDefinedDataType_singular = User-defined data type +objectType_fullTextCatalog_plural = Full-text catalogs +objectType_credential_singular = Credential +objectType_databaseRole_plural = Database roles +objectType_endpoint_singular = Endpoint +objectType_view_plural = Views +objectType_assembly_singular = Assembly +objectType_functionScalar_singular = Scalar function +objectType_server_plural = Servers +objectType_table_singular = Table +objectType_serviceQueue_singular = Queue +objectType_login_plural = Logins +objectType_storedProcedure_singular = Stored procedure +objectType_default_plural = Defaults +objectType_symmetricKey_singular = Symmetric key +objectType_userDefinedTableType_singular = User-defined table type +objectType_functionInline_singular = Inline function +objectType_serverRole_plural = Server roles +objectType_agentjob_singular = Agent job +objectType_databaseRole_singular = Database role +objectType_synonym_singular = Synonym +objectType_AvailabilityGroup_singular = Availability Group +objectType_externalFileFormat_plural = External File Formats +objectType_database_singular = Database +objectType_symmetricKey_plural = Symmetric keys +objectType_securityPolicy_singular = Security Policy +objectType_externalFileFormat_singular = External File Format +objectType_applicationRole_singular = Application role +objectType_assembly_plural = Assemblies +objectType_sequence_plural = Sequences +objectType_userDefinedDataType_plural = User-defined data types +objectType_server_singular = Server +objectType_aggregateFunction_singular = Aggregate function +objectType_fullTextCatalog_singular = Full-text catalog +objectType_certificate_plural = Certificates +objectType_AvailabilityGroup_plural = Availability Groups +objectType_database_plural = Databases +objectType_view_singular = View +objectType_schema_plural = Schemas +objectType_rule_plural = Rules +objectType_certificate_singular = Certificate +objectType_user_singular = User +objectType_agentjob_plural = Agent jobs +objectType_synonym_plural = Synonyms +objectType_aggregateFunction_plural = Aggregate functions +objectType_functionTable_singular = Table-valued function +objectType_rule_singular = Rule +objectType_serviceQueue_plural = Queues +objectType_asymmetricKey_plural = Asymmetric keys +objectType_applicationRole_plural = Application roles +objectType_extendedStoredProcedure_singular = Extended stored procedure +objectType_login_singular = Login +objectType_functionInline_plural = Inline functions +objectType_user_plural = Users +objectType_externalDataSource_plural = External Data Sources +objectType_functionScalar_plural = Scalar functions +objectType_table_plural = Tables +objectType_credential_plural = Credentials +objectType_schema_singular = Schema +objectType_userDefinedTableType_plural = User-defined table types +objectType_securityPolicy_plural = Security Policies +objectType_xmlSchemaCollection_plural = XML schema collections +objectType_extendedStoredProcedure_plural = Extended stored procedures +objectType_asymmetricKey_singular = Asymmetric key +objectType_default_singular = Default + #Object permission names Permission_Alter = Alter Permission_Connect = Connect diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf index 233d9961..51e74413 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf @@ -7217,6 +7217,365 @@ The Query Processor estimates that implementing the following index could improv Service {0} was not found in the service provider + + Table-valued functions + Table-valued functions + + + + External Data Source + External Data Source + + + + Server role + Server role + + + + XML schema collection + XML schema collection + + + + Stored procedures + Stored procedures + + + + Endpoints + Endpoints + + + + Sequence + Sequence + + + + User-defined data type + User-defined data type + + + + Full-text catalogs + Full-text catalogs + + + + Credential + Credential + + + + Database roles + Database roles + + + + Endpoint + Endpoint + + + + Views + Views + + + + Assembly + Assembly + + + + Scalar function + Scalar function + + + + Servers + Servers + + + + Table + Table + + + + Queue + Queue + + + + Logins + Logins + + + + Stored procedure + Stored procedure + + + + Defaults + Defaults + + + + Symmetric key + Symmetric key + + + + User-defined table type + User-defined table type + + + + Inline function + Inline function + + + + Server roles + Server roles + + + + Agent job + Agent job + + + + Database role + Database role + + + + Synonym + Synonym + + + + Availability Group + Availability Group + + + + External File Formats + External File Formats + + + + Database + Database + + + + Symmetric keys + Symmetric keys + + + + Security Policy + Security Policy + + + + External File Format + External File Format + + + + Application role + Application role + + + + Assemblies + Assemblies + + + + Sequences + Sequences + + + + User-defined data types + User-defined data types + + + + Server + Server + + + + Aggregate function + Aggregate function + + + + Full-text catalog + Full-text catalog + + + + Certificates + Certificates + + + + Availability Groups + Availability Groups + + + + Databases + Databases + + + + View + View + + + + Schemas + Schemas + + + + Rules + Rules + + + + Certificate + Certificate + + + + User + User + + + + Agent jobs + Agent jobs + + + + Synonyms + Synonyms + + + + Aggregate functions + Aggregate functions + + + + Table-valued function + Table-valued function + + + + Rule + Rule + + + + Queues + Queues + + + + Asymmetric keys + Asymmetric keys + + + + Application roles + Application roles + + + + Extended stored procedure + Extended stored procedure + + + + Login + Login + + + + Inline functions + Inline functions + + + + Users + Users + + + + External Data Sources + External Data Sources + + + + Scalar functions + Scalar functions + + + + Tables + Tables + + + + Credentials + Credentials + + + + Schema + Schema + + + + User-defined table types + User-defined table types + + + + Security Policies + Security Policies + + + + XML schema collections + XML schema collections + + + + Extended stored procedures + Extended stored procedures + + + + Asymmetric key + Asymmetric key + + + + Default + Default + In Primary Key In Primary Key diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/Contracts/SearchRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/Contracts/SearchRequest.cs new file mode 100644 index 00000000..73cdaeb6 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/Contracts/SearchRequest.cs @@ -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 + { + /// + /// The context id. + /// + 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 Type = RequestType.Create("objectManagement/search"); + } +} \ No newline at end of file diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectManagementService.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectManagementService.cs index f6f21927..cc954d49 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectManagementService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectManagementService.cs @@ -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 requestContext) @@ -116,6 +118,68 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement await requestContext.SendResult(new DisposeViewRequestResponse()); } + internal async Task HandleSearchRequest(SearchRequestParams requestParams, RequestContext 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 res = new List(); + + 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) diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/AppRole/AppRoleHandler.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/AppRole/AppRoleHandler.cs index 3fc9d3e9..e0d69011 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/AppRole/AppRoleHandler.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/AppRole/AppRoleHandler.cs @@ -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); diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/AppRole/AppRoleInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/AppRole/AppRoleInfo.cs index c60882d0..2b64bfc1 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/AppRole/AppRoleInfo.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/AppRole/AppRoleInfo.cs @@ -8,7 +8,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement /// /// a class for storing various application role properties /// - public class AppRoleInfo : SqlObject + public class AppRoleInfo : SecurityPrincipalObject { public string? DefaultSchema { get; set; } public string? Password { get; set; } diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/AppRole/AppRoleViewInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/AppRole/AppRoleViewInfo.cs index 42456615..829ffbf6 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/AppRole/AppRoleViewInfo.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/AppRole/AppRoleViewInfo.cs @@ -8,7 +8,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement /// /// a class for storing various application role view properties /// - public class AppRoleViewInfo : SqlObjectViewInfo + public class AppRoleViewInfo : SecurityPrincipalViewInfo { public string[]? Schemas { get; set; } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/DatabaseRole/DatabaseRoleHandler.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/DatabaseRole/DatabaseRoleHandler.cs index 28519cb9..5bb42d69 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/DatabaseRole/DatabaseRoleHandler.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/DatabaseRole/DatabaseRoleHandler.cs @@ -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); diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/DatabaseRole/DatabaseRoleInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/DatabaseRole/DatabaseRoleInfo.cs index 9f733367..bff6263e 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/DatabaseRole/DatabaseRoleInfo.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/DatabaseRole/DatabaseRoleInfo.cs @@ -8,7 +8,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement /// /// a class for storing various database role properties /// - public class DatabaseRoleInfo : SqlObject + public class DatabaseRoleInfo : SecurityPrincipalObject { public string? Owner { get; set; } public string[]? OwnedSchemas { get; set; } diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/DatabaseRole/DatabaseRoleViewInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/DatabaseRole/DatabaseRoleViewInfo.cs index 7ac14dad..0abd67a9 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/DatabaseRole/DatabaseRoleViewInfo.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/DatabaseRole/DatabaseRoleViewInfo.cs @@ -8,7 +8,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement /// /// a class for storing various database role view properties /// - public class DatabaseRoleViewInfo : SqlObjectViewInfo + public class DatabaseRoleViewInfo : SecurityPrincipalViewInfo { public string[]? Schemas { get; set; } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Login/LoginHandler.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Login/LoginHandler.cs index 3b3c4860..27b01a7f 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Login/LoginHandler.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Login/LoginHandler.cs @@ -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 loginServerRoles = new List(); 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(); @@ -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) { diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Login/LoginInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Login/LoginInfo.cs index 82c80856..63b51062 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Login/LoginInfo.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Login/LoginInfo.cs @@ -35,7 +35,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement /// /// a class for storing various login properties /// - public class LoginInfo : SqlObject + public class LoginInfo : SecurityPrincipalObject { public LoginAuthenticationType AuthenticationType { get; set; } diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Login/LoginViewInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Login/LoginViewInfo.cs index 62678012..ba43d9f0 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Login/LoginViewInfo.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Login/LoginViewInfo.cs @@ -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; } diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/AppRoleData.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/AppRoleData.cs index c089d7d8..cfe63ce2 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/AppRoleData.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/AppRoleData.cs @@ -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]; } /// @@ -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(ep.Name, ep.Value)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + this.securablePermissions = roleInfo.SecurablePermissions; } private class AppRolePrototypeData : ICloneable diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/DatabaseRoleData.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/DatabaseRoleData.cs index 1c4f51e5..9c792c7b 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/DatabaseRoleData.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/DatabaseRoleData.cs @@ -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]; } /// @@ -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(ep.Name, ep.Value)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + this.securablePermissions = roleInfo.SecurablePermissions; } private class DatabaseRolePrototypeData : ICloneable diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/EffectivePermissionsData.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/EffectivePermissionsData.cs new file mode 100644 index 00000000..901cffcc --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/EffectivePermissionsData.cs @@ -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 +{ + /// + /// Data model for the EffectivePermmisions UI + /// + 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 + /// + /// True if the query for this principal will return column information + /// + public bool HasColumnInformation + { + get + { + // STrace.Assert(this.securable != null, "invalid object state"); + return this.securable.HasColumnInformation; + } + } + /// + /// The name of the principal we are checking permissions for + /// + public string PrincipalName + { + get + { + return this.principalName; + } + } + /// + /// the display name of the securable we are querying. If the securable has a schema it will be + /// schema.securable, otherwise just securable + /// + 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 + /// + /// Construct an EffectivePermissionsData object + /// + /// CDataContainer that represents the principal we are querying + public EffectivePermissionsData(CDataContainer dataContainer) + { + if (dataContainer == null) + { + throw new ArgumentNullException("dataContainer"); + } + + this.dataContainer = dataContainer; + + Initialize(); + } + #endregion + + #region public methods + /// + /// Query the effective permissions for principal against the securable + /// + /// Dataset representing the permissions + 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 + /// + /// Initialize the object + /// + 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; + } + } + /// + /// Get the database name if any that contains the securable + /// + /// Securable + /// Database name that contains the securable, or an empty string if this is a server + /// scoped object + 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 + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/LoginActions.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/LoginActions.cs index 0fb39022..4916d562 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/LoginActions.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/LoginActions.cs @@ -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(); } } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/LoginData.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/LoginData.cs index 7a99d672..adedb9e9 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/LoginData.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/LoginData.cs @@ -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; + } + } + /// /// Get the database roles collection for the user in a particular database /// @@ -2056,13 +2072,16 @@ INNER JOIN sys.sql_logins AS sql_logins /// constructor /// /// The server on which we are creating a login - 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]; } /// @@ -2070,21 +2089,28 @@ INNER JOIN sys.sql_logins AS sql_logins /// /// The server on which we are modifying a login /// The login we are modifying - 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(); } /// /// constructor /// /// The server on which we are creating a login - 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); + } + /// /// Create the login or modify the login's access type, default database, default language, /// and password diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/ServerRoleData.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/ServerRoleData.cs index 8ea8f364..4c955dfc 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/ServerRoleData.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/ServerRoleData.cs @@ -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 /// 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]; } /// @@ -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 diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/SqlObjectSearchData.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/SqlObjectSearchData.cs index ed9a0dca..d7c69228 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/SqlObjectSearchData.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/SqlObjectSearchData.cs @@ -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 /// /// Bitmap for the object type - /// The key to look up localized type names, e.g. "objectType.functionTable" + /// The key to look up localized type names, e.g. "objectType_functionTable" /// The URN object type substring, e.g. "UserDefinedFunction" /// Any special clauses needed for the URN, e.g. "@FunctionType='2'" /// Clause to restrict selection to non-system objects, e.g. "@IsSystemObject='false'" @@ -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 /// /// Bitmap for the object type - /// The key to look up localized type names, e.g. "objectType.functionTable" + /// The key to look up localized type names, e.g. "objectType_functionTable" /// The URN object type substring, e.g. "UserDefinedFunction" /// Whether the object is contained by a database /// Whether the object is contained byt a schema @@ -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); } /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/SupportedSecurable.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/SupportedSecurable.cs new file mode 100644 index 00000000..1a9c0c27 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/SupportedSecurable.cs @@ -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 +{ + /// + /// Represents a Securable in SQL that can generate the TSQL to list + /// its permissions. + /// + 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 + /// + /// SMO server we are targetting. This is needed so we can generate the correct + /// query against Table valued functions + /// + private SqlServer.Management.Smo.Server server; + /// + /// Securable we are targetting + /// + private Urn securable; + /// + /// Name of the securable + /// + private string securableName; + /// + /// Schema of the securable. If the securable does not have a schema this will be an Empty string + /// + private string securableSchema; + /// + /// The SMO type of the securable. + /// + private string securableType; + #endregion + + #region public properties + /// + /// Indicates whether or not the securable will return column information or just a + /// list of permissions + /// + 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; + } + } + /// + /// name of the securable + /// + public string Name + { + get + { + return this.securableName; + } + } + /// + /// schema of the securable + /// + public string Schema + { + get + { + return this.securableSchema; + } + } + #endregion + + #region Construction + /// + /// Create a new SupportedSecurable object. + /// + /// The securable we are targetting + /// The server where the securable resides. Can be null + 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 + /// + /// Generate a SQL query that would query the permissions on this securable + /// + /// a string that represents the query + 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 + /// + /// Finds a type for a Urn that can be passed to fn_my_permissions + /// + /// Urn + /// tsql type + 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 + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/UserActions.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/UserActions.cs index 7320c156..467687c1 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/UserActions.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/UserActions.cs @@ -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); } // /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/UserData.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/UserData.cs index 96c5bfa7..9bf3c81e 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/UserData.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Security/UserData.cs @@ -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; /// /// 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 /// @@ -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; } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/ServerRole/ServerRoleHandler.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/ServerRole/ServerRoleHandler.cs index c483559d..9f00cffa 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/ServerRole/ServerRoleHandler.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/ServerRole/ServerRoleHandler.cs @@ -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); diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/ServerRole/ServerRoleInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/ServerRole/ServerRoleInfo.cs index e4b63c3a..0900290f 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/ServerRole/ServerRoleInfo.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/ServerRole/ServerRoleInfo.cs @@ -8,7 +8,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement /// /// a class for storing various server role properties /// - public class ServerRoleInfo : SqlObject + public class ServerRoleInfo : SecurityPrincipalObject { public string? Owner { get; set; } public string[]? Members { get; set; } diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/ServerRole/ServerRoleViewInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/ServerRole/ServerRoleViewInfo.cs index e62407d4..9bad8137 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/ServerRole/ServerRoleViewInfo.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/ServerRole/ServerRoleViewInfo.cs @@ -8,7 +8,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement /// /// a class for storing various server role view properties /// - public class ServerRoleViewInfo : SqlObjectViewInfo + public class ServerRoleViewInfo : SecurityPrincipalViewInfo { public bool IsFixedRole { get; set; } public string[]? ServerRoles { get; set; } diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/User/UserHandler.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/User/UserHandler.cs index ee5a9d57..81a5544f 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/User/UserHandler.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/User/UserHandler.cs @@ -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 }; diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/User/UserInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/User/UserInfo.cs index 10465c89..aa01678e 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/User/UserInfo.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/User/UserInfo.cs @@ -33,7 +33,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement /// /// a class for storing various user properties /// - public class UserInfo : SqlObject + public class UserInfo : SecurityPrincipalObject { public DatabaseUserType? Type { get; set; } diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/User/UserViewInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/User/UserViewInfo.cs index 99a4a66f..631b45ac 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/User/UserViewInfo.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/User/UserViewInfo.cs @@ -8,7 +8,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement /// /// The information required to render the user view. /// - public class UserViewInfo : SqlObjectViewInfo + public class UserViewInfo : SecurityPrincipalViewInfo { public DatabaseUserType[]? UserTypes { get; set; } diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/PermissionMetadata.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/PermissionMetadata.cs new file mode 100644 index 00000000..20e75ca8 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/PermissionMetadata.cs @@ -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; } + } +} \ No newline at end of file diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/SecurablePermissions.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/SecurablePermissions.cs new file mode 100644 index 00000000..72d13ed1 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/SecurablePermissions.cs @@ -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; } + } +} \ No newline at end of file diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/SecurableTypeMetadata.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/SecurableTypeMetadata.cs new file mode 100644 index 00000000..b0cb2be7 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/SecurableTypeMetadata.cs @@ -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; } + } +} \ No newline at end of file diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/SecurableUtils.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/SecurableUtils.cs new file mode 100644 index 00000000..0d741a18 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/SecurableUtils.cs @@ -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 = @" + + + + sql + + + + + + sqlmgmt.dll + +"; + + public static SecurableTypeMetadata[] GetSecurableTypeMetadata(SqlObjectType objectType, Version serverVersion, string databaseName,DatabaseEngineType databaseEngineType, DatabaseEngineEdition engineEdition) + { + List res = new List(); + 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 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 res = new List(); + 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 permissionItemsDict = new Dictionary(); + 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 res = new List(); + 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(); + } + + /// + /// Form the xml to query effective permissions data + /// + /// + 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; + } + + /// + /// Create principal object for server level principals + /// + 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); + } + } + + /// + /// Create principal object for database level principals + /// + 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; + } + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/SecurityPrincipalObject.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/SecurityPrincipalObject.cs new file mode 100644 index 00000000..599c996b --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/SecurityPrincipalObject.cs @@ -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; } + } +} \ No newline at end of file diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/SecurityPrincipalViewInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/SecurityPrincipalViewInfo.cs new file mode 100644 index 00000000..ccf1cca1 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/SecurityPrincipalViewInfo.cs @@ -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; } + } +} \ No newline at end of file diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/ObjectManagement/ObjectManagementTestUtils.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/ObjectManagement/ObjectManagementTestUtils.cs index b1008234..d2244fea 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/ObjectManagement/ObjectManagementTestUtils.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/ObjectManagement/ObjectManagementTestUtils.cs @@ -71,7 +71,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement Password = "placeholder" + new Random().NextInt64(10000000, 90000000).ToString() + "!*PLACEHOLDER", OldPassword = "placeholder" + new Random().NextInt64(10000000, 90000000).ToString() + "!*PLACEHOLDER", DefaultLanguage = "English - us_english", - DefaultDatabase = "master" + DefaultDatabase = "master", + SecurablePermissions = new SecurablePermissions[0] }; } @@ -84,7 +85,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement LoginName = loginName, Password = "placeholder" + new Random().NextInt64(10000000, 90000000).ToString() + "!*PLACEHOLDER", DefaultSchema = "dbo", - OwnedSchemas = new string[] { "" } + OwnedSchemas = new string[] { "" }, + SecurablePermissions = new SecurablePermissions[0] }; }