El modelo de seguridad de SQL Server le permite otorgar acceso a una vista sin otorgar acceso a las tablas subyacentes.
Dado que el código de ejemplo es una excelente manera de mostrar un concepto, considere lo siguiente, con una LoginDetails
tabla y la vista correspondiente:
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
Crearemos un inicio de sesión y un usuario, luego le asignaremos a ese usuario los derechos para seleccionar filas de la vista, sin tener ningún derecho para ver la tabla en sí.
CREATE LOGIN RemoteUser
WITH PASSWORD = '2q1345lkjsadfgsa0(*';
CREATE USER RemoteUser
FOR LOGIN RemoteUser
WITH DEFAULT_SCHEMA = dbo;
GRANT SELECT ON dbo.LoginDetailsView TO RemoteUser;
Ahora, insertaremos dos filas de prueba:
INSERT INTO dbo.LoginDetails(Username, EmailAddress, LastLoggedInAt)
VALUES ('user x', 'x@y.com', NULL)
, ('user y', 'y@y.com', GETDATE());
Esto prueba el modelo de seguridad. La primera SELECT
instrucción tiene éxito, ya que está seleccionando desde la vista, mientras que la segunda SELECT
falla porque el usuario no tiene acceso directo a la tabla.
EXECUTE AS LOGIN = 'RemoteUser';
SELECT *
FROM dbo.LoginDetailsView;
╔══════════╦══════════════╦═══════════════════════ ══╗
║ Nombre de usuario ║ Dirección de correo electrónico ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ usuario y ║ y@y.com ║ 2018-02-15 07: 36: 54.490 ║
╚══════════╩══════════════╩═══════════════════════ ══╝
SELECT *
FROM dbo.LoginDetails;
REVERT
Tenga en cuenta que los resultados de la vista excluyen la fila donde está el LastLoggedInAt
valor NULL
, como se requiere en su pregunta.
La segunda SELECT
declaración contra la tabla subyacente devuelve un error:
Msg 229, Nivel 14, Estado 5, Línea 28
El permiso SELECT fue denegado en el objeto 'LoginDetails', base de datos 'tempdb', esquema 'dbo'.
Limpiar:
DROP USER RemoteUser;
DROP LOGIN RemoteUser;
DROP VIEW dbo.LoginDetailsView;
DROP TABLE dbo.LoginDetails;
Alternativamente, si tiene SQL Server 2016 o más reciente, puede usar un predicado de seguridad de nivel de fila para evitar que ciertos usuarios vean filas con un LastLoggedInAt
valor NULL .
Primero, creamos la tabla, un inicio de sesión, un usuario para ese inicio de sesión y otorgamos acceso a la tabla:
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;
A continuación, insertamos un par de filas de muestra. Una fila con un valor nulo LastLoggedInAt
y otra con un valor no nulo para esa columna.
INSERT INTO dbo.LoginDetails(Username, EmailAddress, LastLoggedInAt)
VALUES ('user x', 'x@y.com', NULL)
, ('user y', 'y@y.com', GETDATE());
Aquí, estamos creando una función de tabla con valores de esquema que devuelve una fila con 0 o 1 dependiendo del valor de las variables @LastLoggedInAt
y @username
que se pasan a la función. Esta función será utilizada por un predicado de filtro para eliminar las filas que queremos ocultar a ciertos usuarios.
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
Este es el filtro de seguridad que elimina las filas de las SELECT
declaraciones ejecutadas en la dbo.LoginDetails
tabla:
CREATE SECURITY POLICY LoginDetailsRemoteUserPolicy
ADD FILTER PREDICATE dbo.fn_LoginDetailsRemoteUserPredicate(LastLoggedInAt, USER_NAME())
ON dbo.LoginDetails
WITH (STATE=ON);
El filtro anterior usa la dbo.fn_LoginDetailsRemoteUserPredicate
función al pasar el nombre del usuario actual, junto con los valores de cada fila para la LastLoggedInAt
columna de la dbo.LoginDetails
tabla.
Si consultamos la tabla como un usuario normal:
SELECT *
FROM dbo.LoginDetails
vemos todas las filas:
╔══════════╦══════════════╦═══════════════════════ ══╗
║ Nombre de usuario ║ Dirección de correo electrónico ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ usuario x ║ x@y.com ║ NULL ║
║ usuario y ║ y@y.com ║ 2018-02-15 13: 53: 42.577 ║
╚══════════╩══════════════╩═══════════════════════ ══╝
Sin embargo, si probamos como RemoteUser
:
EXECUTE AS LOGIN = 'RemoteUser';
SELECT *
FROM dbo.LoginDetails
REVERT
solo vemos filas "válidas":
╔══════════╦══════════════╦═══════════════════════ ══╗
║ Nombre de usuario ║ Dirección de correo electrónico ║ LastLoggedInAt ║
╠══════════╬══════════════╬═══════════════════════ ══╣
║ usuario y ║ y@y.com ║ 2018-02-15 13: 42: 02.023 ║
╚══════════╩══════════════╩═══════════════════════ ══╝
Y, limpiamos:
DROP SECURITY POLICY LoginDetailsRemoteUserPolicy;
DROP FUNCTION dbo.fn_LoginDetailsRemoteUserPredicate;
DROP USER RemoteUser;
DROP LOGIN RemoteUser;
DROP TABLE dbo.LoginDetails;
Tenga en cuenta que el enlace de esquema de una función a la tabla de esta manera hace que sea imposible modificar la definición de la tabla sin eliminar primero el predicado del filtro y la dbo.fn_LoginDetailsRemoteUserPredicate
función.