Isso é fácil de ser feito de uma maneira muito segura, usando a assinatura do módulo. Isso será semelhante às duas respostas a seguir, também aqui no DBA.StackExchange, que dão exemplos de como fazer isso:
Segurança de procedimento armazenado com execução como, consultas entre bancos de dados e assinatura de módulo
Permissões em gatilhos ao usar certificados entre bancos de dados
A diferença para essa pergunta em particular é que ela lida com uma Visualização e as Visualizações não podem ser assinadas. Portanto, você precisará alterar a Visualização em uma função com valor de tabela (TVF) de múltiplas instruções, pois elas podem ser assinadas e podem ser acessadas como uma Visualização (bem, para SELECT
acesso).
O código de exemplo a seguir mostra exatamente o que está sendo solicitado na pergunta, pois o Login / Usuário "RestrictedUser" só tem acesso ao "DatabaseA" e ainda pode obter dados do "DatabaseB". Isso funciona apenas selecionando esse TVF e apenas devido ao fato de ter sido assinado.
Para realizar esse tipo de acesso ao banco de dados enquanto ainda estiver usando uma Visualização, e sem conceder permissões adicionais ao Usuário, seria necessário ativar o Encadeamento de Propriedade entre Bancos de Dados. Isso é muito menos seguro porque é completamente aberto para todos os objetos entre os dois bancos de dados (não pode ser restrito a determinados objetos e / ou usuários). A assinatura do módulo permite que apenas este SELECT
TVF tenha acesso entre bancos de dados (o usuário não tem permissão, o TVF) e os usuários que não podem do TVF não têm acesso ao "Banco de DadosB".
USE [master];
CREATE LOGIN [RestrictedUser] WITH PASSWORD = 'No way? Yes way!';
GO
---
USE [DatabaseA];
CREATE USER [RestrictedUser] FOR LOGIN [RestrictedUser];
GO
CREATE FUNCTION dbo.DataFromOtherDB()
RETURNS @Results TABLE ([SomeValue] INT)
AS
BEGIN
INSERT INTO @Results ([SomeValue])
SELECT [SomeValue]
FROM DatabaseB.dbo.LotsOfValues;
RETURN;
END;
GO
GRANT SELECT ON dbo.[DataFromOtherDB] TO [RestrictedUser];
GO
---
USE [DatabaseB];
CREATE TABLE dbo.[LotsOfValues]
(
[LotsOfValuesID] INT IDENTITY(1, 1) NOT NULL
CONSTRAINT [PK_LotsOfValues] PRIMARY KEY,
[SomeValue] INT
);
INSERT INTO dbo.[LotsOfValues] VALUES
(1), (10), (100), (1000);
GO
---
USE [DatabaseA];
SELECT * FROM dbo.[DataFromOtherDB]();
EXECUTE AS LOGIN = 'RestrictedUser';
SELECT * FROM dbo.[DataFromOtherDB]();
/*
Msg 916, Level 14, State 1, Line XXXXX
The server principal "RestrictedUser" is not able to access
the database "DatabaseB" under the current security context.
*/
REVERT;
Todas as etapas acima recriam a situação atual: o usuário tem acesso ao banco de dados A, tem permissão para interagir com um objeto no banco de dados A, mas recebe um erro devido a esse objeto no banco de dados A acessando algo no banco de dados B onde o usuário não tem acesso.
Os passos abaixo configuram o canto do módulo. Faz o seguinte:
- cria um certificado no DatabaseA
- Assina o TVF com o certificado
- Copia o certificado (sem a chave privada) no banco de dados B
- Cria um usuário no banco de dadosB a partir do certificado
- Concede
SELECT
permissão à Tabela no Banco de DadosB ao Usuário Baseado em Certificado
Configuração de assinatura do módulo:
CREATE CERTIFICATE [AccessOtherDB]
ENCRYPTION BY PASSWORD = 'SomePassword'
WITH SUBJECT = 'Used for accessing other DB',
EXPIRY_DATE = '2099-12-31';
ADD SIGNATURE
TO dbo.[DataFromOtherDB]
BY CERTIFICATE [AccessOtherDB]
WITH PASSWORD = 'SomePassword';
---
DECLARE @CertificatePublicKey NVARCHAR(MAX) =
CONVERT(NVARCHAR(MAX), CERTENCODED(CERT_ID(N'AccessOtherDB')), 1);
SELECT @CertificatePublicKey AS [Cert / PublicKey]; -- debug
EXEC (N'USE [DatabaseB];
CREATE CERTIFICATE [AccessOtherDB] FROM BINARY = ' + @CertificatePublicKey + N';');
---
EXEC (N'
USE [DatabaseB];
CREATE USER [AccessOtherDbUser] FROM CERTIFICATE [AccessOtherDB];
GRANT SELECT ON dbo.[LotsOfValues] TO [AccessOtherDbUser];
');
---
EXECUTE AS LOGIN = 'RestrictedUser';
SELECT * FROM dbo.[DataFromOtherDB]();
-- Success!!
SELECT * FROM [DatabaseB].[dbo].[LotsOfValues];
/*
Msg 916, Level 14, State 1, Line XXXXX
The server principal "RestrictedUser" is not able to access
the database "DatabaseB" under the current security context.
*/
REVERT;
SE O ACESSO PRECISA SER ATRAVÉS DE UMA VISTA, por qualquer motivo, você pode simplesmente criar uma Visualização que selecione a partir do TVF mostrado acima. E, nessa situação, o SELECT
acesso não precisa ser concedido ao TVF, apenas ao View, conforme demonstrado abaixo:
GO
CREATE VIEW dbo.[DataFromTVF]
AS
SELECT [SomeValue]
FROM dbo.DataFromOtherDB();
GO
-- Remove direct access to the TVF as it is no longer needed:
REVOKE SELECT ON dbo.[DataFromOtherDB] FROM [RestrictedUser];
GRANT SELECT ON dbo.[DataFromTVF] TO [RestrictedUser];
E agora para testá-lo:
EXECUTE AS LOGIN = 'RestrictedUser';
SELECT * FROM dbo.[DataFromOtherDB]();
/*
Msg 229, Level 14, State 5, Line XXXXX
The SELECT permission was denied on the object 'DataFromOtherDB',
database 'DatabaseA', schema 'dbo'.
*/
SELECT * FROM [OwnershipChaining].[dbo].[LotsOfValues];
/*
Msg 916, Level 14, State 1, Line XXXXX
The server principal "RestrictedUser" is not able to access
the database "DatabaseB" under the current security context.
*/
SELECT * FROM dbo.[DataFromTVF];
-- Success!!
REVERT;
Para mais informações sobre a assinatura do módulo, visite: https://ModuleSigning.Info/