SQL Server Zeile für Zeile Zugriff

10

Ich habe eine Tabelle so strukturiert (vereinfacht)

Name, EMail, LastLoggedInAt

Ich habe einen Benutzer in SQL Server (RemoteUser), der nur Daten sehen kann (über eine ausgewählte Abfrage), bei denen das Feld LastLoggdInAt nicht null ist.

Es sieht so aus, als könnte ich das schaffen? Ist es möglich?

LiamB
quelle
Hier ist das Online- Buchthema
David Browne - Microsoft

Antworten:

32

Mit dem SQL Server-Sicherheitsmodell können Sie Zugriff auf eine Ansicht gewähren, ohne Zugriff auf die zugrunde liegenden Tabellen zu gewähren.

Da Beispielcode eine hervorragende Möglichkeit ist, ein Konzept LoginDetailsanzuzeigen, sollten Sie Folgendes mit einer Tabelle und der entsprechenden Ansicht berücksichtigen :

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

Wir erstellen ein Login und einen Benutzer und weisen diesem Benutzer dann die Rechte zu, Zeilen aus der Ansicht auszuwählen, ohne über Rechte zum Anzeigen der Tabelle selbst zu verfügen.

CREATE LOGIN RemoteUser 
WITH PASSWORD = '2q1345lkjsadfgsa0(*';

CREATE USER RemoteUser
FOR LOGIN RemoteUser
WITH DEFAULT_SCHEMA = dbo;

GRANT SELECT ON dbo.LoginDetailsView TO RemoteUser;

Jetzt fügen wir zwei Testzeilen ein:

INSERT INTO dbo.LoginDetails(Username, EmailAddress, LastLoggedInAt)
VALUES ('user x', '[email protected]', NULL)
    , ('user y', '[email protected]', GETDATE());

Dies testet das Sicherheitsmodell. Die erste SELECTAnweisung ist erfolgreich, da sie aus der Ansicht ausgewählt wird, während die zweite SELECTAnweisung fehlschlägt, weil der Benutzer keinen direkten Zugriff auf die Tabelle hat.

EXECUTE AS LOGIN = 'RemoteUser';

SELECT *
FROM dbo.LoginDetailsView;
╔══════════╦══════════════╦═══════════════════════ ══╗
║ Benutzername ║ EmailAddress ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ Benutzer y ║ [email protected] ║ 15.02.2018 07: 36: 54.490 ║
╚══════════╩══════════════╩═══════════════════════ ══╝
SELECT *
FROM dbo.LoginDetails;

REVERT

Beachten Sie, dass die Ergebnisse aus der Ansicht die Zeile ausschließen, in der sich der LastLoggedInAtWert befindet NULL, wie in Ihrer Frage erforderlich.

Die zweite SELECTAnweisung für die zugrunde liegende Tabelle gibt einen Fehler zurück:

Nachricht 229, Ebene 14,
Status 5, Zeile 28 Die SELECT-Berechtigung wurde für das Objekt 'LoginDetails', Datenbank 'tempdb', Schema 'dbo' verweigert.

Aufräumen:

DROP USER RemoteUser;
DROP LOGIN RemoteUser;
DROP VIEW dbo.LoginDetailsView;
DROP TABLE dbo.LoginDetails;

Wenn Sie über SQL Server 2016 oder höher verfügen, können Sie alternativ ein Sicherheitsprädikat auf Zeilenebene verwenden, um zu verhindern, dass bestimmte Benutzer Zeilen mit einem NULL- LastLoggedInAtWert sehen.

Zuerst erstellen wir die Tabelle, ein Login, einen Benutzer für dieses Login und gewähren Zugriff auf die Tabelle:

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;

Als nächstes fügen wir einige Beispielzeilen ein. Eine Zeile mit einer Null LastLoggedInAtund eine mit einem Wert ungleich Null für diese Spalte.

INSERT INTO dbo.LoginDetails(Username, EmailAddress, LastLoggedInAt)
VALUES ('user x', '[email protected]', NULL)
    , ('user y', '[email protected]', GETDATE());

Hier erstellen wir eine schemagebundene Tabellenwertfunktion, die abhängig vom Wert der @LastLoggedInAtund der @usernameVariablen, die an die Funktion übergeben werden , eine Zeile mit 0 oder 1 zurückgibt . Diese Funktion wird von einem Filterprädikat verwendet, um die Zeilen zu entfernen, die wir vor bestimmten Benutzern verbergen möchten.

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

Dies ist der Sicherheitsfilter, der Zeilen aus SELECTAnweisungen entfernt, die für die dbo.LoginDetailsTabelle ausgeführt werden:

CREATE SECURITY POLICY LoginDetailsRemoteUserPolicy
ADD FILTER PREDICATE dbo.fn_LoginDetailsRemoteUserPredicate(LastLoggedInAt, USER_NAME())
ON dbo.LoginDetails
WITH (STATE=ON);

Der obige Filter verwendet die dbo.fn_LoginDetailsRemoteUserPredicateFunktion, indem er den Namen des aktuellen Benutzers zusammen mit den Werten aus jeder Zeile für die LastLoggedInAtSpalte aus der dbo.LoginDetailsTabelle übergibt .

Wenn wir die Tabelle als normaler Benutzer abfragen:

SELECT *
FROM dbo.LoginDetails

wir sehen alle Zeilen:

╔══════════╦══════════════╦═══════════════════════ ══╗
║ Benutzername ║ EmailAddress ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ Benutzer x ║ [email protected] ║ NULL ║
║ Benutzer y ║ [email protected] ║ 2018-02-15 13: 53: 42.577 ║
╚══════════╩══════════════╩═══════════════════════ ══╝

Wenn wir jedoch Folgendes testen RemoteUser:

EXECUTE AS LOGIN = 'RemoteUser';

SELECT *
FROM dbo.LoginDetails

REVERT

Wir sehen nur "gültige" Zeilen:

╔══════════╦══════════════╦═══════════════════════ ══╗
║ Benutzername ║ EmailAddress ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ Benutzer y ║ [email protected] ║ 15.02.2018 13: 42: 02.023 ║
╚══════════╩══════════════╩═══════════════════════ ══╝

Und wir räumen auf:

DROP SECURITY POLICY LoginDetailsRemoteUserPolicy;
DROP FUNCTION dbo.fn_LoginDetailsRemoteUserPredicate;
DROP USER RemoteUser;
DROP LOGIN RemoteUser;
DROP TABLE dbo.LoginDetails;

Beachten Sie, dass das Schema, das eine Funktion auf diese Weise an die Tabelle bindet, es unmöglich macht, die Definition der Tabelle zu ändern, ohne zuerst das Filterprädikat und die dbo.fn_LoginDetailsRemoteUserPredicateFunktion zu löschen.

Max Vernon
quelle
Geniale Antwort - danke! Welche Auswirkungen haben diese beiden Methoden auf die Leistung? Wir haben festgestellt, dass unsere Web-App bei Verwendung der Funktion bis zu fünfmal langsamer ist. Sie müssen sich die Ansichtsmethode ansehen.
LiamB
Die Sicherheitsfunktion auf Zeilenebene wird für jede aus der Tabelle gelesene Zeile ausgewertet. Ich würde erwarten, dass der Zugriff auf diese Tabelle erheblich verlangsamt wird. Die Ansicht sollte jedoch einen vernachlässigbaren Einfluss auf die Leistung haben, vorausgesetzt, Sie erstellen einen nützlichen Index für die LastLoggedInAtSpalte.
Max Vernon
Das macht Sinn - ich schaue mir jetzt die Aussicht an, scheint gut zu funktionieren! Wenn wir möchten, dass der Benutzer nur Benutzerdaten für diese Zeilen bearbeiten kann, die den Kriterien entsprechen, wäre dies mit der Ansicht möglich?
LiamB
Es ist wunderschön, alles funktioniert - danke für die Hilfe dabei
LiamB
Ja, Sie können Zeilen über die Ansicht bearbeiten
Max Vernon