Il modello di sicurezza di SQL Server consente di concedere l'accesso a una vista senza concedere l'accesso alle tabelle sottostanti.
Poiché il codice di esempio è un ottimo modo per mostrare un concetto, considera quanto segue, con una LoginDetails
tabella e una vista corrispondente:
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
Creeremo un account di accesso e un utente, quindi assegneremo a quell'utente i diritti per selezionare le righe dalla vista, senza disporre dei diritti per visualizzare la tabella stessa.
CREATE LOGIN RemoteUser
WITH PASSWORD = '2q1345lkjsadfgsa0(*';
CREATE USER RemoteUser
FOR LOGIN RemoteUser
WITH DEFAULT_SCHEMA = dbo;
GRANT SELECT ON dbo.LoginDetailsView TO RemoteUser;
Ora inseriremo due righe di test:
INSERT INTO dbo.LoginDetails(Username, EmailAddress, LastLoggedInAt)
VALUES ('user x', 'x@y.com', NULL)
, ('user y', 'y@y.com', GETDATE());
Questo verifica il modello di sicurezza. La prima SELECT
istruzione ha SELECT
esito positivo, poiché viene selezionata dalla vista, mentre la seconda istruzione ha esito negativo poiché l'utente non ha accesso diretto alla tabella.
EXECUTE AS LOGIN = 'RemoteUser';
SELECT *
FROM dbo.LoginDetailsView;
╔══════════╦══════════════╦═══════════════════════ ══╗
║ Nome utente ║ Indirizzo e-mail ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ utente y ║ y@y.com ║ 15/02/2018 07: 36: 54.490 ║
╚══════════╩══════════════╩═══════════════════════ ══╝
SELECT *
FROM dbo.LoginDetails;
REVERT
Nota i risultati dalla vista escludono la riga in cui si trova il LastLoggedInAt
valore NULL
, come richiesto nella tua domanda.
La seconda SELECT
istruzione rispetto alla tabella sottostante restituisce un errore:
Messaggio 229, livello 14, stato 5, riga 28
L'autorizzazione SELECT è stata negata sull'oggetto "LoginDetails", database "tempdb", schema "dbo".
Pulire:
DROP USER RemoteUser;
DROP LOGIN RemoteUser;
DROP VIEW dbo.LoginDetailsView;
DROP TABLE dbo.LoginDetails;
In alternativa, se si dispone di SQL Server 2016 o versione successiva, è possibile utilizzare un predicato di sicurezza a livello di riga per impedire a determinati utenti di visualizzare righe con un LastLoggedInAt
valore NULL .
Innanzitutto, creiamo la tabella, un login, un utente per tale login e garantiamo l'accesso alla tabella:
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;
Quindi, inseriamo un paio di righe di esempio. Una riga con un valore null LastLoggedInAt
e una con un valore non null per quella colonna.
INSERT INTO dbo.LoginDetails(Username, EmailAddress, LastLoggedInAt)
VALUES ('user x', 'x@y.com', NULL)
, ('user y', 'y@y.com', GETDATE());
Qui, stiamo creando una funzione con valori di tabella associata allo schema che restituisce una riga con 0 o 1 a seconda del valore delle variabili @LastLoggedInAt
e @username
che vengono passate nella funzione. Questa funzione verrà utilizzata da un predicato filtro per eliminare le righe che vogliamo nascondere a determinati utenti.
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
Questo è il filtro di sicurezza che elimina le righe dalle SELECT
istruzioni eseguite sulla dbo.LoginDetails
tabella:
CREATE SECURITY POLICY LoginDetailsRemoteUserPolicy
ADD FILTER PREDICATE dbo.fn_LoginDetailsRemoteUserPredicate(LastLoggedInAt, USER_NAME())
ON dbo.LoginDetails
WITH (STATE=ON);
Il filtro sopra utilizza la dbo.fn_LoginDetailsRemoteUserPredicate
funzione passando il nome dell'utente corrente, insieme ai valori di ciascuna riga per la LastLoggedInAt
colonna dalla dbo.LoginDetails
tabella.
Se eseguiamo una query sulla tabella come utente normale:
SELECT *
FROM dbo.LoginDetails
vediamo tutte le righe:
╔══════════╦══════════════╦═══════════════════════ ══╗
║ Nome utente ║ Indirizzo e-mail ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ utente x ║ x@y.com ║ NULL ║
║ utente y ║ y@y.com ║ 2018-02-15 13: 53: 42.577 ║
╚══════════╩══════════════╩═══════════════════════ ══╝
Tuttavia, se testiamo come RemoteUser
:
EXECUTE AS LOGIN = 'RemoteUser';
SELECT *
FROM dbo.LoginDetails
REVERT
vediamo solo righe "valide":
╔══════════╦══════════════╦═══════════════════════ ══╗
║ Nome utente ║ Indirizzo e-mail ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ utente y ║ y@y.com ║ 2018-02-15 13: 42: 02.023 ║
╚══════════╩══════════════╩═══════════════════════ ══╝
E puliamo:
DROP SECURITY POLICY LoginDetailsRemoteUserPolicy;
DROP FUNCTION dbo.fn_LoginDetailsRemoteUserPredicate;
DROP USER RemoteUser;
DROP LOGIN RemoteUser;
DROP TABLE dbo.LoginDetails;
Tenere presente che l'associazione di uno schema a una funzione in questo modo rende impossibile modificare la definizione della tabella senza prima eliminare il predicato del filtro e la dbo.fn_LoginDetailsRemoteUserPredicate
funzione.