Предоставление разрешений для разных баз данных (схем) - PullRequest
0 голосов
/ 03 февраля 2009

Я защищаю БД, разрешая взаимодействие с БД только через серию Sprocs; довольно распространенный тариф.

Я выкопал и изменил скрипт, который просматривает и назначает пользователю разрешение EXECUTE для всех несистемных SProcs. Это работает, за исключением того, что я в идеале хотел бы добавить его в Master DB, чтобы я мог легко использовать его для любых последующих проектов. Да, я могу сохранить простой файл в формате .sql, но я бы предпочел его таким образом.

Проблема в том, что я не знаю, как динамически ссылаться на объекты в другой БД. Например, я могу легко запросить MyDB.dbo.INFORMATION_SCHEMA.ROUTINES, но если имя БД динамическое (например, @MyDBName), как я могу запросить объекты в этой БД?

Редактировать: Благодаря постерам ниже, у меня теперь есть рабочее решение:

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

Ответы [ 3 ]

2 голосов
/ 03 февраля 2009

Похоже на ответ @ Cade, способ сделать это - использовать динамический sql. Перед каждым вызовом таблицы базы данных добавляйте '@DbName.' Затем замените @DbName реальным именем базы данных (имя базы данных не может быть передано как переменная в SQL, поэтому вы должны выполнить замену).

Также курсоры обычно считаются злыми по соображениям производительности, однако использование одного в этом случае имеет смысл. С одной стороны, это значительно упростит процедуру, плюс, поскольку вы будете запускать ее только один раз во время обновлений приложения, вы, вероятно, не заметите снижения производительности, даже если добавите дополнительную секунду или две (что, я сомневаюсь, будет добавить где-нибудь рядом с этим много).

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

<p>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)</p>

<p>FETCH NEXT FROM ProcCursor INTO @OwnerName, @ObjectName
END
CLOSE ProcCursor
DEALLOCATE ProcCursor
'</p>

<p>SET @Sql = Replace(Replace(@Sql, '@DbName', @DbName), '@user', @User)
EXEC (@Sql)</p>

<p>END


Вы можете вызвать это, используя: EXEC [spGrantExec] 'bob', 'Northwind'

Извините, расстояние немного увеличено. Разработано с использованием Sql 2005.

1 голос
/ 06 февраля 2009

Я нашел другую технику, которая, на мой взгляд, чище :

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

Это зависит от установки контекста базы данных путем вызова sp_executesql в другой базе данных (точно так же, как можно вызвать SP в любой базе данных).

В вашем случае это будет эквивалентно:

SELECT @sp_executesql = quotename(@dbname) + '..sp_executesql'
EXEC @sp_executesql @CMD1 
1 голос
/ 03 февраля 2009

Вы можете использовать технику двойного исполнения .

В вашем случае вместо просто:

EXEC(@CMD1)

Вы бы получили:

SET @CMD1 =
    'USE OtherDatabase;
    EXEC (''' + REPLACE(@CMD1, '''', '''''') + ''')'
EXEC(@CMD1)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...