web-dev-qa-db-fra.com

SQL Server: accorder un accès sélectif à un utilisateur dans une vue et non dans ses tables

J'ai une instance SQL Server 2012 avec quelques bases de données. Dans l'un d'eux, j'ai créé une vue, qui sélectionne des tables dans plus d'une base de données.

Je veux qu'un utilisateur puisse sélectionner cette vue, mais il ne doit pas sélectionner ses tables. La vue a été créée exactement parce que l'utilisateur ne peut pas sélectionner les tables.

J'ai lu https://stackoverflow.com/questions/368414/grant-select-on-a-view-not-base-table et http://msdn.Microsoft .com/en-us/library/ms188676.aspx et cela ne fonctionne toujours pas.

Si je fais un GRANT SELECT TABLE TO USER à toutes les tables, l'utilisateur peut sélectionner la vue. Mais si je révoque une table, cela échoue.

Cela devrait être une procédure facile à faire, mais j'ai du mal à le faire fonctionner. Je l'ai déjà vu (le propriétaire d'une instance m'a donné accès à une vue et ne l'a pas fait à ses tables) mais je ne peux pas le faire ou trouver quelqu'un qui sait comment.

Quelqu'un pourrait-il me fournir un tutoriel sur la façon de le faire, ou un exemple de code?


Lorsque l'utilisateur SELECTs la vue, je reçois le message:

L'autorisation SELECT a été refusée sur l'objet <TABLE>, base de données <DB>, schéma dbo.

Si j'accorde la sélection à cette table, le message d'erreur change le nom de la table en une autre table que la vue lit.

11
Hikari

Si vous souhaitez que les utilisateurs choisissent dans la vue, pourquoi accordez-vous à la table? Par "révoquer", voulez-vous dire explicitement révoquer/refuser? Le refus remplacera l'octroi, il y a donc votre problème ... vous devriez être en mesure d'accomplir cela en ajoutant l'octroi à la vue et en ne faisant rien de toute façon sur les tables.

Voici un exemple rapide où SELECT n'a pas été explicitement accordé sur la table, mais l'a été dans la vue. L'utilisateur peut sélectionner dans la vue mais pas dans la table.

CREATE USER foo WITHOUT LOGIN;
GO
CREATE TABLE dbo.a(id INT);
CREATE TABLE dbo.b(id INT);
GO
CREATE VIEW dbo.v 
AS 
  SELECT a.id FROM a INNER JOIN b ON a.id = b.id;
GO
GRANT SELECT ON dbo.v TO foo;
GO
EXECUTE AS USER = N'foo';
GO
-- works:
SELECT id FROM dbo.v;
GO
-- Msg 229, SELECT denied:
SELECT id FROM dbo.a;
GO
REVERT;

Notez que cela suppose que foo n'a pas reçu de privilèges élevés via des autorisations explicites sur le schéma ou la base de données, ou via l'appartenance à un rôle ou à un groupe.

Comme vous utilisez des tables dans plusieurs bases de données (désolé d'avoir manqué la fin de cette première phrase au départ), vous pouvez également avoir besoin d'autorisations explicites sur les tables de la base de données où la vue n'existe pas. Afin d'éviter d'octroyer une sélection aux tables, vous pouvez créer une vue dans chaque base de données, puis joindre les vues.

Créez deux bases de données et un identifiant:

CREATE DATABASE d1;
GO
CREATE DATABASE d2;
GO
USE [master];
GO
CREATE LOGIN blat WITH PASSWORD = 'x', CHECK_POLICY = OFF;
GO

Dans la base de données d1, créez un utilisateur, puis créez une table et une vue simple sur cette table. Accordez une sélection à l'utilisateur uniquement par rapport à la vue:

USE d1;
GO
CREATE USER blat FROM LOGIN blat;
GO
CREATE TABLE dbo.t1(id INT);
GO
CREATE VIEW dbo.v1
AS
  SELECT id FROM dbo.t1;
GO
GRANT SELECT ON dbo.v1 TO blat;
GO

Maintenant, dans la deuxième base de données, créez l'utilisateur, puis créez une autre table et une vue qui joint cette table à la vue dans d1. Accordez la sélection uniquement à la vue.

USE d2;
GO
CREATE USER blat FROM LOGIN blat;
GO
CREATE TABLE dbo.t2(id INT);
GO
CREATE VIEW dbo.v2
AS
  SELECT v1.id FROM dbo.t2 
    INNER JOIN d1.dbo.v1 AS v1
    ON t2.id = v1.id;
GO
GRANT SELECT ON dbo.v2 TO blat;
GO

Lancez maintenant une nouvelle fenêtre de requête et modifiez les informations d'identification pour la connexion blat (EXECUTE AS ne fonctionne pas ici). Exécutez ensuite ce qui suit à partir du contexte de l'une ou l'autre base de données et cela devrait fonctionner correctement:

SELECT id FROM d1.dbo.v2;

Ceux-ci devraient tous deux générer des erreurs Msg 229:

SELECT id FROM d1.dbo.t1;
GO
SELECT id FROM d2.dbo.t2;

Résultats:

Msg 229, niveau 14, état 5, ligne 1
L'autorisation SELECT a été refusée sur l'objet 't1', la base de données 'd1', le schéma 'dbo'.
Msg 229, niveau 14, état 5, ligne 3
L'autorisation SELECT a été refusée sur l'objet 't2', la base de données 'd2', le schéma 'dbo'.

21
Aaron Bertrand

Réponse wiki communautaire initialement ajouté à la question par son auteur:

C'est ce que j'ai fait:

  1. Création d'une vue dans la base de données A, joignant toutes les tables qu'elle contient.
  2. Accordé à SELECT l'accès à l'utilisateur sur cette vue, et NON à aucune de ses tables. L'utilisateur a réussi à interroger la vue et non les tables.
  3. Création d'une vue dans la base de données B, joignant les tables de cette base de données avec la vue dans la base de données A.
  4. Accordé à SELECT l'accès à l'utilisateur sur cette deuxième vue, et également à AUCUNE table. L'utilisateur a réussi à interroger cette vue finale et à voir les données.

Je pense qu'il est étrange qu'une vue soit capable d'interroger des tables dans sa base de données que l'utilisateur n'a pas d'accès direct mais ne puisse pas le faire dans des tables d'autres bases de données. Au moins ça a marché.

1
user126897

Si vous activez Cross database ownership chaining pour le serveur que les vues croisées de base de données fonctionneront correctement.

https://docs.Microsoft.com/en-us/dotnet/framework/data/adonet/sql/enabling-cross-database-access-in-sql-server

attention aux risques

0
user2461271