Модель безпеки SQL Server дозволяє надати доступ до представлення даних без надання доступу до базових таблиць.
Оскільки приклад коду - чудовий спосіб показати концепцію, розгляньте наступне за допомогою LoginDetails
таблиці та відповідного перегляду:
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
Ми створимо логін та користувача, а потім призначимо цьому користувачу права на вибір рядків із представлення даних, не маючи жодних прав на перегляд самої таблиці.
CREATE LOGIN RemoteUser
WITH PASSWORD = '2q1345lkjsadfgsa0(*';
CREATE USER RemoteUser
FOR LOGIN RemoteUser
WITH DEFAULT_SCHEMA = dbo;
GRANT SELECT ON dbo.LoginDetailsView TO RemoteUser;
Тепер ми вставимо два тестових рядки:
INSERT INTO dbo.LoginDetails(Username, EmailAddress, LastLoggedInAt)
VALUES ('user x', 'x@y.com', NULL)
, ('user y', 'y@y.com', GETDATE());
Це тестує модель безпеки. Перше SELECT
твердження є успішним, оскільки воно вибирається з представлення даних, тоді як друге SELECT
твердження виходить з ладу, оскільки користувач не має прямого доступу до таблиці.
EXECUTE AS LOGIN = 'RemoteUser';
SELECT *
FROM dbo.LoginDetailsView;
╔══════════╦══════════════╦═══════════════════════ ══╗
║ Ім'я користувача ║ Адреса електронної пошти ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ користувач y ║ y@y.com ║ 2018-02-15 07: 36: 54.490 ║
╚══════════╩══════════════╩═══════════════════════ ══╝
SELECT *
FROM dbo.LoginDetails;
REVERT
Зауважте, що результати з подання виключають рядок, у якому це LastLoggedInAt
значення NULL
, як це вимагається у вашому запитанні.
Друге SELECT
твердження проти основної таблиці повертає помилку:
Msg 229, рівень 14, стан 5, рядок 28
Дозвіл SELECT відмовлено в об’єкті "LoginDetails", базі даних "tempdb", схемі "dbo".
Прибирати:
DROP USER RemoteUser;
DROP LOGIN RemoteUser;
DROP VIEW dbo.LoginDetailsView;
DROP TABLE dbo.LoginDetails;
Крім того, якщо у вас є SQL Server 2016 або новішої версії, ви можете використовувати предикат рівня безпеки на рядок, щоб певні користувачі не бачили рядки зі LastLoggedInAt
значенням NULL .
Спочатку ми створюємо таблицю, логін, користувача для цього входу, і надаємо доступ до таблиці:
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;
Далі вставляємо пару зразків рядків. Один рядок з нульовим значенням LastLoggedInAt
, а інший із значенням, що не має значення для цього стовпця.
INSERT INTO dbo.LoginDetails(Username, EmailAddress, LastLoggedInAt)
VALUES ('user x', 'x@y.com', NULL)
, ('user y', 'y@y.com', GETDATE());
Тут ми створюємо функцію, пов’язану з таблицею-значення-значення, яка повертає рядок з 0 або 1 залежно від значення @LastLoggedInAt
та @username
змінних, переданих у функцію. Ця функція буде використана предикатом фільтра для усунення рядків, які ми хочемо приховати від певних користувачів.
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
Це фільтр безпеки, який виключає рядки з SELECT
операторів, розташованих проти dbo.LoginDetails
таблиці:
CREATE SECURITY POLICY LoginDetailsRemoteUserPolicy
ADD FILTER PREDICATE dbo.fn_LoginDetailsRemoteUserPredicate(LastLoggedInAt, USER_NAME())
ON dbo.LoginDetails
WITH (STATE=ON);
Наведений вище фільтр використовує dbo.fn_LoginDetailsRemoteUserPredicate
функцію, передаючи ім’я поточного користувача разом із значеннями кожного рядка для LastLoggedInAt
стовпця з dbo.LoginDetails
таблиці.
Якщо ми запитуємо таблицю як звичайний користувач:
SELECT *
FROM dbo.LoginDetails
ми бачимо всі рядки:
╔══════════╦══════════════╦═══════════════════════ ══╗
║ Ім'я користувача ║ Адреса електронної пошти ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ user x ║ x@y.com ║ NULL ║
║ користувач y ║ y@y.com ║ 2018-02-15 13: 53: 42.577 ║
╚══════════╩══════════════╩═══════════════════════ ══╝
Однак якщо ми перевіримо як RemoteUser
:
EXECUTE AS LOGIN = 'RemoteUser';
SELECT *
FROM dbo.LoginDetails
REVERT
ми бачимо лише "дійсні" рядки:
╔══════════╦══════════════╦═══════════════════════ ══╗
║ Ім'я користувача ║ Адреса електронної пошти ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ користувач y ║ y@y.com ║ 2018-02-15 13: 42: 02.023 ║
╚══════════╩══════════════╩═══════════════════════ ══╝
І ми прибираємо:
DROP SECURITY POLICY LoginDetailsRemoteUserPolicy;
DROP FUNCTION dbo.fn_LoginDetailsRemoteUserPredicate;
DROP USER RemoteUser;
DROP LOGIN RemoteUser;
DROP TABLE dbo.LoginDetails;
Майте на увазі, що прив'язка схеми функції до таблиці таким чином робить неможливим змінити визначення таблиці без попереднього випадання предиката фільтра та dbo.fn_LoginDetailsRemoteUserPredicate
функції.