Строка SQL Server по доступу к строке

10

У меня есть таблица, структурированная так (упрощенно)

Name, EMail, LastLoggedInAt

У меня есть пользователь в SQL Server (RemoteUser), который должен видеть только данные (с помощью запроса выбора), где поле LastLoggdInAt не является нулевым.

Похоже, я могу это сделать? Является ли это возможным?

LiamB
источник
Вот тема "Книги в Интернете" по безопасности на уровне строк
Дэвид Браун - Microsoft,

Ответы:

32

Модель безопасности 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функции.

Макс Вернон
источник
Блестящий ответ - спасибо! Какое влияние на производительность этих двух методов. Мы обнаружили, что наше веб-приложение работает в 5 раз медленнее, когда мы используем эту функцию. Нужно посмотреть на метод просмотра.
LiamB
Функция защиты на уровне строк оценивается для каждой строки, считанной из таблицы; Я ожидаю, что это значительно замедлит доступ к этой таблице. С другой стороны, представление должно оказывать незначительное влияние на производительность, если вы создадите полезный индекс для LastLoggedInAtстолбца.
Макс Вернон
Это имеет смысл - я сейчас посмотрю на вид, похоже, работает хорошо! Если бы мы хотели, чтобы пользователь мог редактировать только пользовательские данные для этих строк, которые соответствуют критериям, было бы это возможно с представлением?
LiamB
Красиво, все работает - спасибо за помощь в этом
LiamB
Да, вы можете редактировать строки через представление
Макс Вернон,