¿Cómo agregar un usuario con acceso a una sola vista?

14

Estoy trabajando con MSSQL Server Management Studio 2008 y necesito exponer una vista a un tercero para su reconciliación de datos. He creado la vista adecuada, pero tengo problemas para crear un usuario y darle a ese usuario los permisos adecuados para seleccionar de la vista.

Seguí a los asistentes para crear un inicio de sesión y un usuario y luego agregué mi vista en la sección Asegurables con el cuadro de concesión marcado para seleccionar. Todo parecía estar bien, pero cuando inicié sesión como ese usuario e intenté hacer un "Seleccionar * de MyViewName" me dijo que se había denegado el permiso de selección.

Acabo de recrear al usuario (esta vez solo usando SQL en lugar del asistente) y otorgué permisos de selección explícitamente y ahora me está dando el error: Msg 916, Level 14, State 1, Line 2 The server principal "username" is not able to access the database "unrelated_db" under the current security context.(No sé por qué está intentando acceder a la base de datos no relacionada ...)

Realmente no sé a dónde ir desde aquí. Nuevamente, básicamente todo lo que necesito es crear un usuario que pueda darle al tercero para que se conecte a nuestra base de datos y seleccione desde esta vista.

velojason
fuente
3
¿La vista hace referencia a tablas base en otras bases de datos? Si es así, tendrá que lidiar con el encadenamiento de propiedad. Además, asegúrese de tener actualmente el contexto de la base de datos establecido en la base de datos deseada.
Thomas Stringer el
¿Hay alguna forma de ver las dependencias para una vista? Quiero decir, hasta donde yo sé, todo lo de la vista debe estar contenido en la base de datos principal. La vista consta de solo tres campos, dos procedentes de una tabla de detalles de pago y uno procedente de una combinación de la tabla de detalles de la cuenta, pero ambas tablas están en la misma base de datos (y la misma base de datos que la vista).
velojason

Respuestas:

16

Por favor, no use la interfaz de usuario para esto. Es un desastre confuso.

Me parece que lo que quieres es crear un usuario en una base de datos, para un inicio de sesión específico, que solo tiene permisos para seleccionar desde una vista. Entonces, como ya ha creado el inicio de sesión:

USE your_db;
GO
CREATE USER username FROM LOGIN username;
GO
GRANT SELECT ON dbo.MyViewName TO username;
GO

EDITAR aquí es un ejemplo de una secuencia de comandos que conducirá al error que mencionas.

Primero, cree una tabla en el unrelated_db:

CREATE DATABASE unrelated_db;
GO
USE unrelated_db;
GO
CREATE TABLE dbo.foo(bar INT);
GO

Ahora cree un inicio de sesión relativamente restringido:

USE [master];
GO
CREATE LOGIN username WITH PASSWORD='foo', CHECK_POLICY = OFF;
GO

Ahora cree una base de datos donde vivirá la vista y agregue el inicio de sesión como usuario:

CREATE DATABASE velojason;
GO
USE velojason;
GO
CREATE USER username FROM LOGIN username;
GO

Ahora cree una función que haga referencia a la tabla en la otra base de datos, y un sinónimo de la otra tabla:

CREATE FUNCTION dbo.checkbar()
RETURNS INT
AS 
BEGIN
    RETURN 
    (
      SELECT TOP (1) bar 
        FROM unrelated_db.dbo.foo 
        ORDER BY bar
    );
END
GO
CREATE SYNONYM dbo.foo FOR unrelated_db.dbo.foo;
GO

Ahora cree una tabla local:

CREATE TABLE dbo.PaymentDetails
(
  PaymentID INT
);
GO

Ahora cree una vista que haga referencia a la tabla, la función y el sinónimo, y otorgue SELECTa username:

CREATE VIEW dbo.SomeView
AS
  SELECT 
    p.PaymentID, 
    x = dbo.checkbar(), -- function that pulls from other DB
    y = (SELECT bar FROM dbo.foo) -- synonym to other DB
    FROM dbo.PaymentDetails AS p;
GO
GRANT SELECT ON dbo.SomeView TO username;
GO

Ahora intente ejecutar como usernamey seleccione solo la columna local de la vista:

EXECUTE AS USER = 'username';
GO
  -- even though I don't reference any of the columns 
  -- in the other DB, I am denied SELECT on the view:
SELECT PaymentID FROM dbo.SomeView;
GO
REVERT;
GO

Resultado:

Mensaje 916, Nivel 14, Estado 1, Línea 3
El "nombre de usuario" principal del servidor no puede acceder a la base de datos "unrelated_db" en el contexto de seguridad actual.

Ahora cambie la vista para no hacer referencia a ningún objeto externo y ejecute lo anterior SELECTnuevamente, y funciona:

ALTER VIEW dbo.SomeView
AS
  SELECT 
    p.PaymentID 
    --x = dbo.checkbar(),
    --y = (SELECT bar FROM dbo.foo)
    FROM dbo.PaymentDetails AS p;
GO

A menos que nos muestre los scripts para los detalles de pago, detalles de cuenta y objetos MyView, tal vez pueda informarnos si esta consulta devuelve algún resultado. Puede encontrar referencias a varios objetos a través de la vista de catálogo sys.sql_expression_dependencies, pero esta vista no es perfecta; creo que depende de que todas las vistas se actualicen (en el caso de que las vistas hagan referencia a otras vistas, por ejemplo, o el esquema subyacente haya cambiado) en orden ser preciso.

DECLARE 
  @dbname   SYSNAME = N'unrelated_db',
  @viewname SYSNAME = N'dbo.SomeView';

SELECT DISTINCT 
    [This object] = 
    OBJECT_SCHEMA_NAME([referencing_id]) 
      + '.' + OBJECT_NAME([referencing_id]), 
    [references this object] = 
    OBJECT_SCHEMA_NAME([referenced_id]) 
      + '.' + OBJECT_NAME([referenced_id]), 
    [and touches this database] = referenced_database_name,
    [and is a(n)] = o.type_desc,
    [if synonym, it references] = s.base_object_name
FROM sys.sql_expression_dependencies AS d
LEFT OUTER JOIN sys.objects AS o
ON o.[object_id] = d.referenced_id
LEFT OUTER JOIN sys.synonyms AS s
ON d.referenced_id = s.[object_id]
AND s.base_object_name LIKE '%[' + @dbname + ']%'
WHERE OBJECT_ID(@viewname) IN (
        referenced_id, 
        referencing_id, 
        (SELECT referencing_id FROM sys.sql_expression_dependencies 
        WHERE referenced_database_name = @dbname)
) OR referenced_database_name = @dbname;

SQL Server no solo intentará acceder unrelated_dbpor diversión ... debe haber algún vínculo con esa base de datos desde la vista que está tratando de usar. Desafortunadamente, si no podemos ver la definición de la vista y más detalles sobre los objetos que toca, todo lo que podemos hacer es especular. Las dos cosas principales en las que puedo pensar son sinónimos o funciones que usan nombres de tres partes, pero ver los scripts reales nos dará una idea mucho mejor en lugar de adivinar. :-)

Es posible que también desee verificar sys.dm_sql_referenced_entities, sin embargo, esta función no devuelve nada útil en el ejemplo anterior.

Aaron Bertrand
fuente
6
create login YourTpvLogin with password = 'enter new password here'
go

use SomeDb
go

create user YourTpvUser for login YourTpvLogin
go

grant select on YourView to YourTpvUser
go

Puede probar esto haciendo lo siguiente:

execute as user = 'YourTpvUser'
go

select *
from YourView

revert
go
Thomas Stringer
fuente
Aparentemente, esto no otorga privilegios recursivamente. El usuario puede seleccionar "YourView", pero si "YourView" depende de otras relaciones / bases de datos, fallará: el servidor principal "YourTpvUser" no puede acceder a la base de datos "OtherDb" en el contexto de seguridad actual.
lilalinux