Sol*_*ard 7 sql-server permissions role deployment
In SQL Server, I would like to create a role that has the ability to manipulate database objects as well as create other roles and grant those roles subsets of its permissions.
CREATE ROLE deploymentRole;
CREATE ROLE subRole;
GRANT SELECT TO deploymentRole WITH GRANT OPTION;
CREATE TABLE dbo.testTable (id INT NULL);
CREATE USER deployUser WITHOUT LOGIN;
ALTER ROLE deploymentRole ADD MEMBER deployUser;
EXECUTE AS USER = 'deployUser';
--This works
SELECT * FROM dbo.testTable
--This also works
GRANT SELECT TO subRole AS deploymentRole;
--This does not work
GRANT SELECT ON dbo.testTable TO subRole AS deploymentRole;
-- Cannot find the object 'testTable', because it does not exist
-- or you do not have permission.
Run Code Online (Sandbox Code Playgroud)
While having SELECT
(or another permission) on the entire database implies having SELECT
on an object within the database, having the GRANT OPTION
for SELECT
on the entire database does not imply you have the GRANT OPTION
for SELECT
on an object in the database. Said another way, the GRANT OPTION
does not seem to be recursive down the hierarchy.
In this case, I would like members of the deploymentRole
to be able to grant subsets of their permissions to other principals without having to give explicit GRANT OPTION
s on all permissions for all objects now and in the future. Is there a way to apply the GRANT OPTION
recursively or another paradigm I should be considering?
I do not want to grant permissions on individual objects as our schema is constantly changing. I want my role to be able to have database level permissions and also grant smaller subsets of its permissions without having to enumerate objects.
虽然拥有
SELECT
整个数据库(或其他权限)意味着拥有SELECT
数据库中的某个对象,但拥有整个数据库的GRANT OPTION
forSELECT
并不意味着您拥有数据库中的对象的GRANT OPTION
for 。SELECT
正确的。“实际”/“显式”权限和“有效”权限之间存在差异。在数据库级别授予SELECT
权限与在该数据库中的一个或多个对象授予权限截然不同SELECT
。在系统目录视图中查看时您会看到这种差异sys.database_permissions
。因此,SELECT
单个表上的权限并不是数据库级SELECT
权限的实际子集。这同样适用于SELECT
表的权限,而不是SELECT
该表中各个列的权限。
现在,虽然获得有关此请求的总体目标的一些反馈/澄清会有所帮助,但至少根据问题中的示例代码,我们希望有一个部署/推出登录(或登录) )不属于sysadmin
固定服务器角色。为此,您仍然可以使用模块签名来完成此类设置。这个想法是创建一个证书和基于证书的登录名以及关联的用户,该用户不能被模拟(即无法登录或通过 成为当前登录名/用户EXECUTE AS
),但可以持有执行该任务所需的所有必需权限。部署流程需要做的事情。然后,您创建一个或多个将执行所需命令的存储过程。使用用于创建登录名和用户的同一证书对存储过程进行签名将允许授予基于证书的登录名和用户的权限应用于存储过程中的代码。鉴于操作的动态性质,需要通过动态 SQL 来完成,但这对于模块签名来说效果很好。
这种方法允许您拥有一组通用权限(分配给基于证书的登录/用户),可用于分配其他权限。目前,将用户置于db_owner
固定数据库角色中似乎是最简单的,因为这样可以执行您需要执行的任何操作。由于模块签名将权限授予代码,而不是执行模块的登录/用户/角色(这就是通过模拟进行的工作方式EXECUTE AS
),因此任何可以执行该代码的人都只能执行代码设置的操作。通过这种方式,模块签名允许对扩展权限进行比模拟更精细的控制(授予基于证书的登录/用户的权限可以做什么并不重要,只重要签名的代码实际做什么)。如果有人更改了已签名的代码,则该模块将丢失签名,并提醒您发生了更改,此时您可以查看更改,如果可以,则重新签署该模块。
请参阅下面的示例,该示例允许将SELECT
特定对象授予给定角色。扩展示例以允许创建新角色以及部署过程中需要完成的任何其他操作非常容易。
解决方案设置,第 1 部分(一般支撑结构)
USE [master];
GO
CREATE CERTIFICATE [DeploymentPermissionsKey]
ENCRYPTION BY PASSWORD = 'not_really_a_password'
WITH SUBJECT = 'Deployment Permissions';
CREATE LOGIN [DeploymentPermissions]
FROM CERTIFICATE [DeploymentPermissionsKey];
GRANT VIEW SERVER STATE TO [DeploymentPermissions];
DECLARE @Certificate VARBINARY(2000),
@PrivateKey VARBINARY(2000);
SELECT @Certificate = CERTENCODED(CERT_ID(N'DeploymentPermissionsKey')),
@PrivateKey = CERTPRIVATEKEY(CERT_ID(N'DeploymentPermissionsKey'),
'EncryptPass', 'not_really_a_password');
---
USE [tempdb];
--DROP CERTIFICATE [DeploymentPermissionsKey];
DECLARE @CertSQL NVARCHAR(MAX) = N'
CREATE CERTIFICATE [DeploymentPermissionsKey]
FROM BINARY = ' + CONVERT(NVARCHAR(MAX), @Certificate, 1) + N'
WITH PRIVATE KEY ( BINARY = ' + CONVERT(NVARCHAR(MAX), @PrivateKey, 1) + N',
DECRYPTION BY PASSWORD = ''EncryptPass'',
ENCRYPTION BY PASSWORD = ''not_really_a_password''
);
';
PRINT @CertSQL;
EXEC(@CertSQL);
--DROP ROLE [DeploymentRole];
CREATE ROLE [DeploymentRole];
--DROP USER [MrDeploy];
CREATE USER [MrDeploy] WITHOUT LOGIN;
ALTER ROLE [DeploymentRole] ADD MEMBER [MrDeploy];
--DROP USER [DeploymentPermissions];
CREATE USER [DeploymentPermissions]
FROM LOGIN [DeploymentPermissions];
ALTER ROLE [db_owner] ADD MEMBER [DeploymentPermissions];
Run Code Online (Sandbox Code Playgroud)
解决方案设置,第 2 部分(允许向角色授予权限)
@RoleName
请注意,由于需要使用动态 SQL,我们需要通过检查/验证字符串输入参数:和 来防止 SQL 注入@ObjectName
。
USE [tempdb];
GO
CREATE
--ALTER
PROCEDURE dbo.GrantObjectPermissionsToRole
(
@RoleName [sysname],
@ObjectName NVARCHAR(261),
@PermissionID TINYINT
)
AS
SET NOCOUNT ON;
DECLARE @SQL NVARCHAR(MAX);
-- Check parameter values to prevent errors and SQL Injection:
IF (NOT EXISTS(
SELECT dp.*
FROM sys.database_principals dp
WHERE dp.[type] = 'R' -- DATABASE_ROLE
AND dp.[name] = @RoleName
)
)
BEGIN
RAISERROR(N'Role does not exist: %s', 16, 1, @RoleName);
RETURN -1;
END;
IF (OBJECT_ID(@ObjectName) IS NULL)
BEGIN
RAISERROR(N'Object does not exist: %s', 16, 1, @ObjectName);
RETURN -2;
END;
SET @SQL = N'GRANT ' + CASE @PermissionID
WHEN 1 THEN N'SELECT'
WHEN 2 THEN N'EXECUTE'
ELSE N'Invalid_PermissionID_'
+ CONVERT(NVARCHAR(10), @PermissionID)
END
+ N' ON ' + @ObjectName
+ N' TO ' + @RoleName
+ N' AS [dbo];';
PRINT @SQL; -- debug
EXEC(@SQL);
RETURN;
GO
GRANT EXECUTE ON dbo.[GrantObjectPermissionsToRole] TO [DeploymentRole];
ADD SIGNATURE TO dbo.[GrantObjectPermissionsToRole]
BY CERTIFICATE [DeploymentPermissionsKey]
WITH PASSWORD = 'not_really_a_password';
Run Code Online (Sandbox Code Playgroud)
设置示例
USE [tempdb];
GO
CREATE TABLE [dbo].[TestTable] ([ID] INT NULL);
CREATE ROLE [TestRole];
CREATE USER [TestUser] WITHOUT LOGIN;
ALTER ROLE [TestRole] ADD MEMBER [TestUser];
Run Code Online (Sandbox Code Playgroud)
测试
测试 1显示,最初TestUser
位于 的TestRole
没有SELECT
来自的权限TestTable
。
测试 2显示部署用户MrDeploy
也没有 的SELECT
权限TestTable
。此外,MrDeploy
甚至没有能力授予TestRole
. 但是,可以执行授予权限的MrDeploy
存储过程。SELECT
TestRole
测试 3显示TestUser
,通过其 的成员资格TestRole
,现在确实SELECT
拥有TestTable
. 使用sys.fn_my_permissions()
我们可以看到“有效”权限,这些权限向下级联到各个列。但是,从中选择sys.database_permissions
我们可以看到“实际”权限,该权限仅在表本身上(表明实际权限不会级联)。
测试 4授予数据库级SELECT
权限MrDeploy
,以便可以针对sys.fn_my_permissions()
函数(以查看权限如何级联到子对象)和sys.database_permissions
系统目录视图(以查看根本没有对象级权限)进行测试。这再次说明了“有效”和“实际”/“显式”权限之间的区别。
--- Test # 1 ---
EXECUTE AS USER = 'TestUser';
SELECT SESSION_USER AS [CurrentUser];
SELECT * FROM [dbo].[TestTable]; -- error:
-- The SELECT permission was denied on the object 'TestTable',
-- database 'tempdb', schema 'dbo'.
REVERT;
SELECT SESSION_USER AS [CurrentUser];
--- Test # 2 ---
EXECUTE AS USER = 'MrDeploy';
SELECT SESSION_USER AS [CurrentUser];
SELECT * FROM [dbo].[TestTable]; -- error:
-- The SELECT permission was denied on the object 'TestTable',
-- database 'tempdb', schema 'dbo'.
GRANT SELECT ON [dbo].[TestTable] TO [TestUser]; -- error:
-- Cannot find the object 'TestTable', because it does not exist or you
-- do not have permission.
GRANT SELECT ON [dbo].[TestTable] TO [TestUser] AS [dbo]; -- error:
-- Cannot find the user 'dbo', because it does not exist or you do not have permission.
SELECT dp.*
FROM sys.database_principals dp
WHERE dp.[type] = 'R' -- DATABASE_ROLE
AND dp.[name] = N'TestRole'; -- no rows due to no VIEW SERVER STATE permission
EXEC dbo.[GrantObjectPermissionsToRole]
@RoleName = N'TestRole',
@ObjectName = N'TestTable',
@PermissionID = 1;
-- Success!
SELECT * FROM [dbo].[TestTable]; -- error (still):
-- The SELECT permission was denied on the object 'TestTable',
-- database 'tempdb', schema 'dbo'.
REVERT;
SELECT SESSION_USER AS [CurrentUser];
--- Test # 3 ---
EXECUTE AS USER = 'TestUser';
SELECT SESSION_USER AS [CurrentUser];
SELECT * FROM [dbo].[TestTable]; -- Success!!
SELECT * FROM sys.fn_my_permissions(N'dbo.TestTable', 'OBJECT');
SELECT *,
USER_NAME([grantee_principal_id]) AS [Grantee],
USER_NAME([grantor_principal_id]) AS [Grantor]
FROM sys.database_permissions
WHERE [class] = 1 -- OBJECT_OR_COLUMN
AND [major_id] = OBJECT_ID(N'dbo.TestTable');
REVERT;
SELECT SESSION_USER AS [CurrentUser];
--- Test # 4 ---
GRANT SELECT TO [MrDeploy];
EXECUTE AS USER = 'MrDeploy';
SELECT SESSION_USER AS [CurrentUser];
SELECT * FROM sys.fn_my_permissions(N'dbo.TestTable', 'OBJECT');
SELECT *,
USER_NAME([grantee_principal_id]) AS [Grantee],
USER_NAME([grantor_principal_id]) AS [Grantor]
FROM sys.database_permissions
WHERE [class] = 1 -- OBJECT_OR_COLUMN
AND [major_id] = OBJECT_ID(N'dbo.TestTable');
REVERT;
SELECT SESSION_USER AS [CurrentUser];
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
900 次 |
最近记录: |