O modelo de segurança do SQL Server permite conceder acesso a uma exibição sem conceder acesso às tabelas subjacentes.
Como o código de exemplo é uma ótima maneira de mostrar um conceito, considere o seguinte, com uma LoginDetails
tabela e a visualização correspondente:
CREATE TABLE dbo.LoginDetails
(
Username nvarchar(100) NOT NULL
, EmailAddress nvarchar(256) NOT NULL
, LastLoggedInAt datetime NULL
);
GO
CREATE VIEW dbo.LoginDetailsView
AS
SELECT ld.Username
, ld.EmailAddress
, ld.LastLoggedInAt
FROM dbo.LoginDetails ld
WHERE ld.LastLoggedInAt IS NOT NULL;
GO
Criaremos um logon e um usuário, em seguida, atribuiremos a esse usuário os direitos para selecionar linhas da exibição, sem ter direitos para exibir a tabela em si.
CREATE LOGIN RemoteUser
WITH PASSWORD = '2q1345lkjsadfgsa0(*';
CREATE USER RemoteUser
FOR LOGIN RemoteUser
WITH DEFAULT_SCHEMA = dbo;
GRANT SELECT ON dbo.LoginDetailsView TO RemoteUser;
Agora, vamos inserir duas linhas de teste:
INSERT INTO dbo.LoginDetails(Username, EmailAddress, LastLoggedInAt)
VALUES ('user x', 'x@y.com', NULL)
, ('user y', 'y@y.com', GETDATE());
Isso testa o modelo de segurança. A primeira SELECT
instrução é bem-sucedida, pois é selecionada na visualização, enquanto a segunda SELECT
instrução falha porque o usuário não tem acesso direto à tabela.
EXECUTE AS LOGIN = 'RemoteUser';
SELECT *
FROM dbo.LoginDetailsView;
╔══════════╦══════════════╦═══════════════════════ ══╗
║ Nome de usuário ║ EndereçoEmail ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ user y ║ y@y.com -15 2018-02-15 07: 36: 54.490 ║
╚══════════╩══════════════╩═══════════════════════ ══╝
SELECT *
FROM dbo.LoginDetails;
REVERT
Observe que os resultados da exibição excluem a linha em que o LastLoggedInAt
valor está NULL
, conforme necessário em sua pergunta.
A segunda SELECT
instrução na tabela subjacente retorna um erro:
Msg 229, Nível 14, Estado 5, Linha 28
A permissão SELECT foi negada no objeto 'LoginDetails', banco de dados 'tempdb', esquema 'dbo'.
Limpar:
DROP USER RemoteUser;
DROP LOGIN RemoteUser;
DROP VIEW dbo.LoginDetailsView;
DROP TABLE dbo.LoginDetails;
Como alternativa, se você tiver o SQL Server 2016 ou mais recente, poderá usar um predicado de segurança no nível da linha para impedir que certos usuários vejam linhas com um LastLoggedInAt
valor NULL .
Primeiro, criamos a tabela, um logon, um usuário para esse logon e concedemos acesso à tabela:
CREATE TABLE dbo.LoginDetails
(
Username nvarchar(100) NOT NULL
, EmailAddress nvarchar(256) NOT NULL
, LastLoggedInAt datetime NULL
);
GO
CREATE LOGIN RemoteUser
WITH PASSWORD = '2q1345lkjsadfgsa0(*';
CREATE USER RemoteUser
FOR LOGIN RemoteUser
WITH DEFAULT_SCHEMA = dbo;
GRANT SELECT ON dbo.LoginDetails TO RemoteUser;
Em seguida, inserimos algumas linhas de amostra. Uma linha com um valor nulo LastLoggedInAt
e outra com um valor não nulo para essa coluna.
INSERT INTO dbo.LoginDetails(Username, EmailAddress, LastLoggedInAt)
VALUES ('user x', 'x@y.com', NULL)
, ('user y', 'y@y.com', GETDATE());
Aqui, estamos criando uma função com valor de tabela vinculada ao esquema que retorna uma linha com 0 ou 1, dependendo do valor das variáveis @LastLoggedInAt
e @username
que são passadas para a função. Essa função será usada por um predicado de filtro para eliminar as linhas que queremos ocultar de certos usuários.
CREATE FUNCTION dbo.fn_LoginDetailsRemoteUserPredicate
(
@LastLoggedInAt datetime
, @username sysname
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN SELECT 1 AS fn_securitypredicate_result
WHERE (@username = N'RemoteUser' AND @LastLoggedInAt IS NOT NULL)
OR @username <> N'RemoteUser';
GO
Este é o filtro de segurança que elimina as linhas das SELECT
instruções executadas na dbo.LoginDetails
tabela:
CREATE SECURITY POLICY LoginDetailsRemoteUserPolicy
ADD FILTER PREDICATE dbo.fn_LoginDetailsRemoteUserPredicate(LastLoggedInAt, USER_NAME())
ON dbo.LoginDetails
WITH (STATE=ON);
O filtro acima usa a dbo.fn_LoginDetailsRemoteUserPredicate
função passando o nome do usuário atual, juntamente com os valores de cada linha da LastLoggedInAt
coluna da dbo.LoginDetails
tabela.
Se consultarmos a tabela como um usuário normal:
SELECT *
FROM dbo.LoginDetails
vemos todas as linhas:
╔══════════╦══════════════╦═══════════════════════ ══╗
║ Nome de usuário ║ EndereçoEmail ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ usuário x ║ x@y.com ║ NULL ║
║ user y ║ y@y.com -15 2018-02-15 13: 53: 42.577 ║
╚══════════╩══════════════╩═══════════════════════ ══╝
No entanto, se testarmos como RemoteUser
:
EXECUTE AS LOGIN = 'RemoteUser';
SELECT *
FROM dbo.LoginDetails
REVERT
vemos apenas linhas "válidas":
╔══════════╦══════════════╦═══════════════════════ ══╗
║ Nome de usuário ║ EndereçoEmail ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ user y ║ y@y.com -15 2018-02-15 13: 42: 02.023 ║
╚══════════╩══════════════╩═══════════════════════ ══╝
E limpamos:
DROP SECURITY POLICY LoginDetailsRemoteUserPolicy;
DROP FUNCTION dbo.fn_LoginDetailsRemoteUserPredicate;
DROP USER RemoteUser;
DROP LOGIN RemoteUser;
DROP TABLE dbo.LoginDetails;
Esteja ciente de que a ligação de um esquema a uma função dessa tabela impossibilita modificar a definição da tabela sem primeiro descartar o predicado do filtro e a dbo.fn_LoginDetailsRemoteUserPredicate
função.