2009-02-03 11 views
0

Je sécurise la base de données en autorisant uniquement l'interaction avec la base de données via une série de Sprocs; tarif assez commun.Autorisations GRANTing entre différentes bases de données (schémas)

J'ai déterré et modifié un script qui boucle et attribue l'autorisation EXECUTE à tous les SProcs non-système. Il fonctionne un régal, sauf que j'aimerais idéalement l'ajouter à la base de données principale afin que je puisse facilement l'utiliser pour tous les projets ultérieurs. Oui, je pourrais enregistrer simple comme un fichier .sql mais je préfère de cette façon.

Le problème est que je ne sais pas comment faire référence dynamiquement aux objets dans un autre DB. Par exemple, je peux facilement interroger sur MyDB.dbo.INFORMATION_SCHEMA.ROUTINES, mais si le nom de la base de données est dynamique (par exemple @MyDBName), comment puis-je interroger les objets de cette base de données?

Edit: Merci aux affiches ci-dessous, j'ai maintenant une solution de travail:

USE [master] 
GO 

SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 


ALTER PROCEDURE [dbo].[spGrantExec] 
@User sysname, 
@DB varchar(50), 
@Target varchar(50) 
AS 
/*---------------------------- SQL 2005 + -------------------------------*/ 

SET NOCOUNT ON 

-- 1 - Variable declarations 
DECLARE @SQL varchar(8000) 

-- 2 - Create temporary table 
Set @SQL = 
'USE @DB 

DECLARE @MAXOID int 
DECLARE @OwnerName varchar(128) 
DECLARE @ObjectName varchar(128) 
DECLARE @CMD1 varchar(8000) 

CREATE TABLE #StoredProcedures 
(OID int IDENTITY (1,1), 
StoredProcOwner varchar(128) NOT NULL, 
StoredProcName varchar(128) NOT NULL) 

-- 3 - Populate temporary table 

INSERT INTO #StoredProcedures (StoredProcOwner, StoredProcName) 
SELECT ROUTINE_SCHEMA, ROUTINE_NAME 
FROM INFORMATION_SCHEMA.ROUTINES 
WHERE ROUTINE_NAME LIKE ''' + @Target + '%'' 
AND ROUTINE_TYPE = ''PROCEDURE'' 

-- 4 - Capture the @MAXOID value 
SELECT @MAXOID = MAX(OID) FROM #StoredProcedures 

-- 5 - WHILE loop 
WHILE @MAXOID > 0 
BEGIN 

    -- 6 - Initialize the variables 
    SELECT @OwnerName = StoredProcOwner, 
    @ObjectName = StoredProcName 
    FROM #StoredProcedures 
    WHERE OID = @MAXOID 

    -- 7 - Build the string 

    SELECT @CMD1 = ''GRANT EXEC ON '' + ''['' + @OwnerName + '']'' + ''.'' + ''['' + @ObjectName + '']'' + '' TO @user'' 

    -- 8 - Execute the string 
    Print @CMD1 
    EXEC(@CMD1) 

    -- 9 - Decrement @MAXOID 
    SET @MAXOID = @MAXOID - 1 
END 

-- 10 - Drop the temporary table 
DROP TABLE #StoredProcedures' 

Set @SQL = REPLACE(REPLACE(REPLACE(@SQL, '@DB', @DB), '@User', @User), '@Target', @Target) 
--Select @SQL 
--Print @SQL 
Exec (@SQL) 
SET NOCOUNT OFF 
+0

Vous pouvez regarder le plus propre que j'ai trouvé: SELECT @sp_executesql = QUOTENAME (@dbname) + '..sp_executesql' EXEC @sp_executesql @ CMD1 –

Répondre

2

@ réponse similaire à de Cade, la façon de le faire est d'utiliser SQL dynamique. Avant chaque appel à une table de base de données, ajoutez '@DbName.' Remplacez ensuite le @DbName par le nom de la base de données (le nom de la base de données ne peut pas être passé en tant que variable dans SQL, vous devez donc le remplacer).

De même, les curseurs sont normalement considérés comme mal pour des raisons de performances, mais il est logique d'en utiliser un dans ce cas. D'une part, cela simplifierait grandement la procédure, et puisque vous ne l'exécuterez qu'une seule fois lors des mises à jour d'applications, vous ne remarquerez probablement pas de coup de performance, même si cela ajoute une seconde ou deux supplémentaires (ce que je doute ajouter n'importe où près de cela).

ALTER PROCEDURE [dbo].[spGrantExec] 
@User SysName, 
@DbName VarChar(512) 
AS 
BEGIN 
DECLARE @Sql VarChar(1024)

SET @Sql = 'DECLARE @OwnerName varchar(128) DECLARE @ObjectName varchar(128) DECLARE @Cmd1 VarChar(128) DECLARE ProcCursor CURSOR FOR SELECT ROUTINE SCHEMA, ROUTINE NAME FROM @DbName.INFORMATION SCHEMA.ROUTINES WHERE ROUTINENAME NOT LIKE ''dt %'' AND ROUTINE TYPE = ''PROCEDURE'' OPEN ProcCursor FETCH NEXT FROM ProcCursor INTO @OwnerName, @ObjectName WHILE @@FETCH STATUS = 0 BEGIN SET @CMD1 = ''GRANT EXEC ON '' + ''['' + @OwnerName + '']'' + ''.'' + ''['' + @ObjectName + '']'' + '' TO '' + ''@user'' EXEC (@CMD1)

FETCH NEXT FROM ProcCursor INTO @OwnerName, @ObjectName END CLOSE ProcCursor DEALLOCATE ProcCursor '

SET @Sql = Replace(Replace(@Sql, '@DbName', @DbName), '@user', @User) EXEC (@Sql)

END



Vous pouvez appeler cela en utilisant: EXEC [spGrantExec] 'bob', l'espacement Désolé 'Northwind'

est un peu hors du sp. Développé à l'aide Sql 2005.

1

Vous pouvez utiliser the double exec technique.

Dans votre cas, au lieu de simplement:

EXEC(@CMD1) 

vous auriez:

SET @CMD1 = 
    'USE OtherDatabase; 
    EXEC (''' + REPLACE(@CMD1, '''', '''''') + ''')' 
EXEC(@CMD1) 
+0

Intéressant. Je peux voir le principe, mais je n'ai pas encore digéré les détails les plus fins. mais ça promet ... – CJM

+0

J'aurais aimé pouvoir partager le prix pour la réponse acceptée - merci quand même. – CJM

1

J'ai trouvé another technique, which I think is cleaner:

SELECT @sql = 'CREATE VIEW ...' 
SELECT @sp_executesql = quotename(@dbname) + '..sp_executesql' 
EXEC @sp_executesql @sql 

Cela repose sur la définition du contexte de la base de données en appelant sp_executesql dans l'autre base de données (comme l'on pourrait appeler un SP dans une base de données).

Dans votre cas, il serait équivalent à:

SELECT @sp_executesql = quotename(@dbname) + '..sp_executesql' 
EXEC @sp_executesql @CMD1 
+0

Merci. Je vais jeter un coup d'oeil à cela la semaine prochaine. – CJM

Questions connexes