使用nontouchable字段更新语句

时间:2012-01-26 04:54:50

标签: sql sql-server-2008 tsql database-permissions

我有mssql2008 r2 sql server

问题: 用户对表有一些列权限。他可以更新表中的一些列(不是全部)。我们需要创建UPDATE语句,以便它不会违反权限。 最好没有动态查询。

MSSQL服务器中是否有此功能?

1 个答案:

答案 0 :(得分:2)

没有动态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 UPDATE或DENY UPDATE方式授予的权限给指定的用户或角色 - 例如GRANT ALL,或AD组成员资格继承的权限,可能会使此复杂化。当然,如果您要检查多个列,那么管理它本身并不会很有趣。

您可能希望在WHEN子句中添加其他条件,例如为了避免检查dbo(谁)或想要明确绕过支票的用户,你可以:

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