GRANT SELECT ON sys.server_principals 给 usersys 角色。如何?

GRANT SELECT ON sys.server_principals to usersys role. How?

我找不到这个问题的答案。问题是我需要使用 master 数据库然后我不知道如何指定我将此 select 授予另一个数据库中的角色。使用 "use master" 时它不起作用,因为主体在另一个数据库中并且您不能添加例如Database.dbo.role 原则前缀。我怎么做?即使通过 SSMS UI 授予也不起作用。

我需要这个:GRANT SELECT ON Syf.sys.server_principals to Syf.dbo.usersys

我错过了什么?我是不是想错了?

即使我在 master 数据库上与用户一起尝试并调用 "execute as user = 'user'",然后从 sys.server_principals 调用 select,它仍然 return 只有几条记录。我显然不明白这些权限是如何工作的,这超出了我的逻辑。似乎还有一些其他对象需要授予权限。

我需要使用 "with execute as 'privilegedUser'",当我这样做时,我们处于该 SP 数据库的用户上下文中,并且该用户无权访问 sys.server_principals。?我需要这个,因为如果存在同名登录,SP 会删除用户和登录。我需要与特权用户一起执行的原因是因为我的数据库具有多租户并且每个用户都绑定到一个 TenantId 并且当他或其他用户去删除用户时,安全策略抱怨他无权这样做。

我找到了一种方法,我必须将 select 授予 master db 上的来宾用户 sys.server_principals,这会赋予来宾更多权限。

根据 sys.server_principals 上的文档,所需的权限是:

Any login can see their own login name, the system logins, and the fixed server roles. To see other logins, requires ALTER ANY LOGIN, or a permission on the login. To see user-defined server roles, requires ALTER ANY SERVER ROLE, or membership in the role.

The visibility of the metadata in catalog views is limited to securables that a user either owns or on which the user has been granted some permission. For more information, see Metadata Visibility Configuration.

但并非一无所有。我们可以使用模块签名来创建一个存储过程,它可以让你做你需要做的事情。

use master;

create master key encryption by password = 'an unguessable password!'
alter master key add encryption by service master key
create certificate [CodeSigningCert]
    with expiry_date = '2018-12-31',
        subject = 'Code signing'
go
create login [CodeSigningLogin] from certificate [CodeSigningCert]
grant alter any login to [CodeSigningLogin]
go

SELECT 'CREATE CERTIFICATE ' + QUOTENAME([name]) 
    + ' AUTHORIZATION ' + USER_NAME([c].[principal_id]) 
    + ' FROM BINARY = ' + CONVERT(VARCHAR(MAX), CERTENCODED([c].[certificate_id]), 1)
    + ' WITH PRIVATE KEY (BINARY = ' 
    + CONVERT(VARCHAR(MAX), CERTPRIVATEKEY([c].[certificate_id], 'f00bar!23'), 1)
    + ', DECRYPTION BY PASSWORD = ''f00bar!23'')'
FROM [sys].[certificates] AS [c]
WHERE [name] = 'CodeSigningCert'

use tempdb
go
create master key encryption by password = 'foobar!23'
-- c/p the create certificate code generated above here
-- to create the same certificate in tempdb
create user CodeSigningUser from certificate CodeSigningCert
go
create login [foobar] with password = 'foobar!23'
create user [foobar]
go
create procedure dbo.listServerPrincipals
as
begin
    select *
    from sys.server_principals
end
go
grant execute on dbo.listServerPrincipals to foobar
go
execute as login = 'foobar'
go
exec dbo.listServerPrincipals
revert
go

add signature to dbo.listServerPrincipals by certificate [CodeSigningCert]
go

execute as login = 'foobar'
go
exec dbo.listServerPrincipals
revert
go

看起来很多,但本质上你在做以下事情:

  1. 在 master 中创建证书
  2. 从该证书创建登录
  3. 在您的用户数据库中创建相同的证书(我在这里使用 tempdb 作为替代)
  4. 为该证书创建一个用户
  5. 创建一个 login/user 来代表您的应用程序用户
  6. 创建一个执行 select
  7. 的过程
  8. 尝试以应用登录的方式执行。它“有效”,但看起来与您自己完成 select
  9. 没有什么不同
  10. 向程序添加签名
  11. 再次尝试执行程序。这次,它应该return所有数据

我找到了一个解决方案,我不需要在第一个过程中使用 "with execute as owner",但在第一个过程调用的第二个过程中。在第一个中,我可以 select 从 sys 表中获取我需要的所有信息,并将信息传递给第二个具有 "with execute as owner" 的第二个,该模式在用户禁止的模式中。

更好的解决方案:

 alter trigger [dbo].[AfterInsertUpdateTenant_Korisnici]
    on [dbo].[Korisnici]
    with execute as owner
    for insert 
    as 
    execute as user = original_login();
    declare @TenantId int = dbo.GetCurrentTenantId();
    revert;

    update dbo.Korisnici
    set TenantId = @TenantId
    from Inserted i
    where dbo.Korisnici.Id = i.Id;