Model zabezpieczeń programu SQL Server umożliwia udzielanie dostępu do widoku bez udzielania dostępu do bazowych tabel.
Ponieważ przykładowy kod to świetny sposób na pokazanie koncepcji, weź pod uwagę następujące kwestie wraz z LoginDetails
tabelą i odpowiednim widokiem:
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
Utworzymy login i użytkownika, a następnie przypiszemy temu użytkownikowi prawa do wyboru wierszy z widoku, bez żadnych uprawnień do przeglądania samej tabeli.
CREATE LOGIN RemoteUser
WITH PASSWORD = '2q1345lkjsadfgsa0(*';
CREATE USER RemoteUser
FOR LOGIN RemoteUser
WITH DEFAULT_SCHEMA = dbo;
GRANT SELECT ON dbo.LoginDetailsView TO RemoteUser;
Teraz wstawimy dwa wiersze testowe:
INSERT INTO dbo.LoginDetails(Username, EmailAddress, LastLoggedInAt)
VALUES ('user x', 'x@y.com', NULL)
, ('user y', 'y@y.com', GETDATE());
To testuje model bezpieczeństwa. Pierwsza SELECT
instrukcja kończy się powodzeniem, ponieważ wybiera z widoku, natomiast druga SELECT
instrukcja kończy się niepowodzeniem, ponieważ użytkownik nie ma bezpośredniego dostępu do tabeli.
EXECUTE AS LOGIN = 'RemoteUser';
SELECT *
FROM dbo.LoginDetailsView;
╔══════════╦══════════════╦═══════════════════════ ══╗
║ Nazwa użytkownika ║ Adres e-mail ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ użytkownik y ║ y@y.com ║ 15.02.2018 07: 36: 54.490 ║
╚══════════╩══════════════╩═══════════════════════ ══╝
SELECT *
FROM dbo.LoginDetails;
REVERT
Uwaga: wyniki z widoku wykluczają wiersz, w którym LastLoggedInAt
znajduje się wartość NULL
, zgodnie z wymaganiami w pytaniu.
Druga SELECT
instrukcja względem tabeli bazowej zwraca błąd:
Msg 229, poziom 14, stan 5, wiersz 28
Odmówiono uprawnienia SELECT do obiektu „LoginDetails”, bazy danych „tempdb”, schematu „dbo”.
Sprzątać:
DROP USER RemoteUser;
DROP LOGIN RemoteUser;
DROP VIEW dbo.LoginDetailsView;
DROP TABLE dbo.LoginDetails;
Alternatywnie, jeśli masz SQL Server 2016 lub nowszy, możesz użyć predykatu zabezpieczeń na poziomie wiersza, aby uniemożliwić niektórym użytkownikom wyświetlanie wierszy o LastLoggedInAt
wartości NULL .
Najpierw tworzymy tabelę, login, użytkownika dla tego loginu i udzielamy dostępu do tabeli:
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;
Następnie wstawiamy kilka przykładowych wierszy. Jeden wiersz z wartością null LastLoggedInAt
i jeden z wartością inną niż null dla tej kolumny.
INSERT INTO dbo.LoginDetails(Username, EmailAddress, LastLoggedInAt)
VALUES ('user x', 'x@y.com', NULL)
, ('user y', 'y@y.com', GETDATE());
W tym miejscu tworzymy funkcję tabelaryczną powiązaną ze schematem, która zwraca wiersz z 0 lub 1 w zależności od wartości @LastLoggedInAt
i @username
zmiennych przekazywanych do funkcji. Ta funkcja będzie używana przez predykat filtra, aby wyeliminować wiersze, które chcemy ukryć przed niektórymi użytkownikami.
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
To jest filtr bezpieczeństwa, który eliminuje wiersze z SELECT
instrukcji uruchamianych względem dbo.LoginDetails
tabeli:
CREATE SECURITY POLICY LoginDetailsRemoteUserPolicy
ADD FILTER PREDICATE dbo.fn_LoginDetailsRemoteUserPredicate(LastLoggedInAt, USER_NAME())
ON dbo.LoginDetails
WITH (STATE=ON);
Powyższy filtr używa tej dbo.fn_LoginDetailsRemoteUserPredicate
funkcji, przekazując nazwę bieżącego użytkownika wraz z wartościami z każdego wiersza dla LastLoggedInAt
kolumny z dbo.LoginDetails
tabeli.
Jeśli zapytamy tabelę jako zwykły użytkownik:
SELECT *
FROM dbo.LoginDetails
widzimy wszystkie rzędy:
╔══════════╦══════════════╦═══════════════════════ ══╗
║ Nazwa użytkownika ║ Adres e-mail ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ użytkownik x ║ x@y.com ║ NULL ║
║ użytkownik y ║ y@y.com ║ 15.02.2018 13: 53: 42.577 ║
╚══════════╩══════════════╩═══════════════════════ ══╝
Jeśli jednak przetestujemy jako RemoteUser
:
EXECUTE AS LOGIN = 'RemoteUser';
SELECT *
FROM dbo.LoginDetails
REVERT
widzimy tylko „prawidłowe” wiersze:
╔══════════╦══════════════╦═══════════════════════ ══╗
║ Nazwa użytkownika ║ Adres e-mail ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ użytkownik y ║ y@y.com ║ 15.02.2018 13: 42: 02.023 ║
╚══════════╩══════════════╩═══════════════════════ ══╝
I sprzątamy:
DROP SECURITY POLICY LoginDetailsRemoteUserPolicy;
DROP FUNCTION dbo.fn_LoginDetailsRemoteUserPredicate;
DROP USER RemoteUser;
DROP LOGIN RemoteUser;
DROP TABLE dbo.LoginDetails;
Należy pamiętać, że powiązanie schematu w ten sposób z tabelą uniemożliwia modyfikację definicji tabeli bez uprzedniego upuszczenia predykatu filtru i dbo.fn_LoginDetailsRemoteUserPredicate
funkcji.