Модель безопасности 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
утверждение для базовой таблицы возвращает ошибку:
Сообщение 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 ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ пользователь 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
функции.