我有mssql2008 r2 sql server
问题: 用户对表有一些列权限。他可以更新表中的一些列(不是全部)。我们需要创建UPDATE语句,以便它不会违反权限。 最好没有动态查询。
MSSQL服务器中是否有此功能?
答案 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...
;