From b9277c3830ae7f37d72a078943f75ac589879b28 Mon Sep 17 00:00:00 2001 From: Hai Cao Date: Thu, 6 Jul 2023 16:19:50 -0700 Subject: [PATCH] Add explicit CREATE TABLE permission check for table designer (#2128) --- .../Localization/sr.cs | 16 +++++ .../Localization/sr.resx | 10 +++ .../Localization/sr.strings | 2 + .../Localization/sr.xlf | 11 ++++ .../TableDesigner/TableDesignerService.cs | 63 ++++++++++++++++++- 5 files changed, 101 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs index d93f16b7..887baaca 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs @@ -12916,6 +12916,16 @@ namespace Microsoft.SqlTools.ServiceLayer return Keys.GetString(Keys.NonClusteredColumnStoreIndexMustHaveColumnsRuleDescription, indexName); } + public static string TableDesignerCreateTablePermissionDenied(string db) + { + return Keys.GetString(Keys.TableDesignerCreateTablePermissionDenied, db); + } + + public static string TableDesignerAlterTablePermissionDenied(string table) + { + return Keys.GetString(Keys.TableDesignerAlterTablePermissionDenied, table); + } + public static string SqlProjectModelNotFound(string projectUri) { return Keys.GetString(Keys.SqlProjectModelNotFound, projectUri); @@ -16871,6 +16881,12 @@ namespace Microsoft.SqlTools.ServiceLayer public const string TableDesignerConfirmationText = "TableDesignerConfirmationText"; + public const string TableDesignerCreateTablePermissionDenied = "TableDesignerCreateTablePermissionDenied"; + + + public const string TableDesignerAlterTablePermissionDenied = "TableDesignerAlterTablePermissionDenied"; + + public const string SqlProjectModelNotFound = "SqlProjectModelNotFound"; diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx index eae3dac2..336e93be 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx @@ -5440,6 +5440,16 @@ The Query Processor estimates that implementing the following index could improv I have read the summary and understand the potential risks. + + CREATE TABLE permission denied in database '{0}'. + . + Parameters: 0 - db (string) + + + ALTER TABLE permission denied for table '{0}'. + . + Parameters: 0 - table (string) + Could not find SQL model from project: {0}. . diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings index 2883e583..97eb2e91 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings @@ -2451,6 +2451,8 @@ HashIndexMustHaveBucketCountRuleDescription(string indexName) = Hash index '{0}' ColumnCanOnlyAppearOnceInNonClusteredColumnStoreIndexRuleDescription(string columnName, string indexName, int rowNumber) = Column with name '{0}' has already been added to the non-clustered columnstore index '{1}'. Row number: {2}. NonClusteredColumnStoreIndexMustHaveColumnsRuleDescription(string indexName) = Non-clustered columnstore index '{0}' does not have any columns associated with it. TableDesignerConfirmationText = I have read the summary and understand the potential risks. +TableDesignerCreateTablePermissionDenied(string db) = CREATE TABLE permission denied in database '{0}'. +TableDesignerAlterTablePermissionDenied(string table) = ALTER TABLE permission denied for table '{0}'. ############################################################################ # TSql Model diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf index 5060a6fa..8b4344ab 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf @@ -8321,6 +8321,17 @@ The Query Processor estimates that implementing the following index could improv None + + CREATE TABLE permission denied in database '{0}'. + CREATE TABLE permission denied in database '{0}'. + + + + ALTER TABLE permission denied for table '{0}'. + ALTER TABLE permission denied for table '{0}'. + . + Parameters: 0 - table (string) + \ No newline at end of file diff --git a/src/Microsoft.SqlTools.ServiceLayer/TableDesigner/TableDesignerService.cs b/src/Microsoft.SqlTools.ServiceLayer/TableDesigner/TableDesignerService.cs index 4d2f8330..32d94799 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/TableDesigner/TableDesignerService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/TableDesigner/TableDesignerService.cs @@ -18,6 +18,7 @@ using Dac = Microsoft.Data.Tools.Sql.DesignServices.TableDesigner; using STSHost = Microsoft.SqlTools.ServiceLayer.Hosting.ServiceHost; using Microsoft.SqlTools.ServiceLayer.SqlContext; using Microsoft.SqlTools.ServiceLayer.Connection; +using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection; namespace Microsoft.SqlTools.ServiceLayer.TableDesigner { @@ -31,6 +32,8 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner private Dictionary idTableMap = new Dictionary(); private bool disposed = false; private static readonly Lazy instance = new Lazy(() => new TableDesignerService()); + private const string CheckCreateTablePermissionInDbQuery = "SELECT HAS_PERMS_BY_NAME(QUOTENAME(@dbname), 'DATABASE', 'CREATE TABLE')"; + private const string CheckAlterTablePermissionQuery = "SELECT HAS_PERMS_BY_NAME(QUOTENAME(@schema) + '.' + QUOTENAME(@table), 'OBJECT', 'ALTER')"; public TableDesignerService() { @@ -1807,7 +1810,16 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner // Set Access Token only when authentication mode is not specified. var accessToken = connectionStringBuilder.Authentication == SqlAuthenticationMethod.NotSpecified ? tableInfo.AccessToken : null; - tableDesigner = new Dac.TableDesigner(connectionString, accessToken, tableInfo.Schema, tableInfo.Name, tableInfo.IsNewTable, tableDesignerOptions); + + try + { + tableDesigner = new Dac.TableDesigner(connectionString, accessToken, tableInfo.Schema, tableInfo.Name, tableInfo.IsNewTable, tableDesignerOptions); + } + catch (Exception ex) + { + CheckPermissions(tableInfo, connectionString); + throw ex; + } } else { @@ -1855,6 +1867,55 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner return metadata; } + private void CheckPermissions(TableInfo tableInfo, string connectionString) + { + CheckCreateAlterTablePermission(tableInfo, connectionString); + } + + private void CheckCreateAlterTablePermission(TableInfo tableInfo, string connectionString) + { + if (tableInfo.IsNewTable) + { + + ReliableConnectionHelper.ExecuteReader(connectionString, CheckCreateTablePermissionInDbQuery, (reader) => + { + reader.Read(); + if (reader.IsDBNull(0) || (int)reader[0] != 1) + { + throw new Exception(SR.TableDesignerCreateTablePermissionDenied(tableInfo.Database)); + } + }, (cmd) => + { + var dbNameParameter = cmd.CreateParameter(); + dbNameParameter.ParameterName = "@dbname"; + dbNameParameter.Value = tableInfo.Database; + cmd.Parameters.Add(dbNameParameter); + }); + } + else + { + ReliableConnectionHelper.ExecuteReader(connectionString, CheckAlterTablePermissionQuery, (reader) => + { + reader.Read(); + if (reader.IsDBNull(0) || (int)reader[0] != 1) + { + throw new Exception(SR.TableDesignerAlterTablePermissionDenied(tableInfo.Name)); + } + }, (cmd) => + { + var schemaParameter = cmd.CreateParameter(); + schemaParameter.ParameterName = "@schema"; + schemaParameter.Value = tableInfo.Schema; + cmd.Parameters.Add(schemaParameter); + + var tableParameter = cmd.CreateParameter(); + tableParameter.ParameterName = "@table"; + tableParameter.Value = tableInfo.Name; + cmd.Parameters.Add(tableParameter); + }); + } + } + /// /// Disposes the table designer Service ///