Обновить оператор с неприкасаемым полем - PullRequest
0 голосов
/ 26 января 2012

У меня mssql2008 r2 sql server

Проблема: у пользователя есть некоторые разрешения для столбцов в таблице.Он мог обновить некоторые столбцы таблицы (не все).Нам нужно создать оператор UPDATE, чтобы он не нарушал права доступа.Желательно без динамического запроса.

Есть ли эта возможность на сервере MSSQL?

1 Ответ

2 голосов
/ 26 января 2012

Без динамического SQL (или динамического построения запроса на уровне приложения или API)? Я не думаю, что это будет очень красиво. Команда UPDATE не имеет каких-либо внутренних знаний о том, какие разрешения может иметь пользователь для соответствующих столбцов. Он собирается отправить запрос в движок и надеяться на лучшее. Если у пользователя нет прав доступа ко всем столбцам, он вернет ошибку, а не попытается ее обойти, изменив предполагаемый оператор. Я думаю, что на самом деле было бы очень плохо продолжать обновление, хотя не все запланированные столбцы были обновлены.

Тем не менее, я полагаю, вы могли бы сделать что-то подобное, но это совсем не будет красиво - на самом деле будет намного проще, если вы не полагаетесь на принципалов базы данных:

DECLARE 
    @dpid INT = DATABASE_PRINCIPAL_ID(),
    @obj  INT = OBJECT_ID('dbo.foo'),
    @col  SYSNAME = N'bar';

UPDATE dbo.foo SET bar = CASE 
  WHEN EXISTS -- check they've been granted UPDATE at column or table level:
  (
    SELECT 1 
      FROM sys.database_permissions AS dp
      INNER JOIN sys.objects AS o 
        ON dp.major_id = o.[object_id]
      LEFT OUTER JOIN  sys.columns AS c
        ON dp.minor_id = COALESCE(c.column_id, 0)
      WHERE dp.grantee_principal_id = @dpid
      AND o.[object_id] = @obj
      AND (c.name = @col OR c.column_id IS NULL)
      AND dp.[permission_name] = 'UPDATE'
      AND dp.[state] = 'G' -- GRANT
  ) 
  AND NOT EXISTS -- since DENY trumps GRANT, make sure that doesn't also exist:
  (
    SELECT 1
      FROM sys.database_permissions AS dp
      INNER JOIN sys.objects AS o
        ON dp.major_id = o.[object_id]
      LEFT OUTER JOIN  sys.columns AS c
        ON dp.minor_id = COALESCE(c.column_id, 0)
      WHERE dp.grantee_principal_id = @dpid
      AND o.[object_id] = @obj
      AND (c.name = @col OR c.column_id IS NULL)
      AND dp.[permission_name] = 'UPDATE'
      AND dp.[state] = 'D' -- DENY
)
THEN @bar ELSE bar END
-- WHERE...
;

Это не совсем то, что вы просите; технически он обновляет столбец, но устанавливает его себе (таким образом, он все равно будет указан, например, как обновленный столбец в триггере), но предотвращает применение ввода к таблице. Я также не проверял разрешения, предоставленные способами, отличными от явного ОБНОВЛЕНИЯ ГРАНТА или ОБНОВЛЕНИЯ ДЕНЬГИ для указанного пользователя или роли - например, GRANT ALL или разрешения, унаследованные членством в группе AD, могут усложнить это. Конечно, управлять этим будет не очень весело, если у вас есть несколько столбцов для проверки.

Возможно, вы захотите добавить другие условия в предложение WHEN, например, чтобы избежать проверки для dbo (who) или пользователей, которым вы хотите явно обойти проверку, вы можете иметь:

CASE 
  WHEN DATABASE_PRINCIPAL_ID() = 1 THEN @bar
  WHEN SUSER_SNAME = 'some_user' THEN @bar
  WHEN (...stuff from above...)
  ELSE bar
END
-- WHERE...
;
...