对象 sp_start_job 的执行权限被拒绝

Man*_*eld 10 sql-server sql-server-agent sql-server-2014

我需要允许用户在没有任何能力启动其他代理的情况下启动特定的代理作业。为此,我创建了以下过程(简化):

ALTER PROCEDURE [dbo].[RunJob]
    @job_name nvarchar(200)
WITH EXECUTE AS 'sysadminaccount'
AS
BEGIN
    --SET NOCOUNT ON;
    BEGIN TRY
        EXEC msdb.dbo.sp_start_job @job_name = @job_name 

        -- Wait for job to finish
        DECLARE @job_history_id AS INT = NULL
        DECLARE @job_result AS INT = NULL

        WHILE 1=1
        BEGIN
            SELECT TOP 1 @job_history_id = activity.job_history_id
            FROM msdb.dbo.sysjobs jobs
            INNER JOIN msdb.dbo.sysjobactivity activity ON activity.job_id = jobs.job_id
            WHERE jobs.name = @job_name
            ORDER BY activity.start_execution_date DESC

            IF @job_history_id IS NULL
            BEGIN
                WAITFOR DELAY '00:00:01'
                CONTINUE
            END
            ELSE
                BREAK
        END

        -- Check exit code
        SET @job_result = (SELECT history.run_status
        FROM msdb.dbo.sysjobhistory history
        WHERE history.instance_id = @job_history_id)

        RETURN @job_result;

    END TRY
    BEGIN CATCH
        THROW;
        RETURN;
    END CATCH
END
Run Code Online (Sandbox Code Playgroud)

但是,当我调用此过程时(已通过“sysadminaccount”验证它正在运行),我收到以下错误消息:

消息 229,级别 14,状态 5,过程 sp_start_job,第 1 行 对象“sp_start_job”、数据库“msdb”、架构“dbo”上的 EXECUTE 权限被拒绝。

该帐户是 sysadmin 角色的成员,因此据我所知,启动工作应该没有任何问题。我已经验证它是 msdb 中三个 sqlagent 角色的成员,并且这些角色都对sp_start_job.

我怎样才能给这个帐户适当的权限?由于模拟,还有其他需要做的事情吗?

Aar*_*and 13

我不喜欢这个TRUSTWORTHY选项,因为它会显着增加你接触各种事物的机会。正如 Remus 在这个答案中解释的那样,它基本上将 any 提升db_ownersysadmin. 一些其他的事情值得一读的是一系列TRUSTWORTHY塞巴斯蒂安MEINE,在BOL主题和知识库文章(即使组件部分可能不是与您无关,在这种情况下):

(还有很多其他帖子警告不要盲目使用这个属性——仅仅因为它有效并且很容易并不意味着它是正确的做法——事实上这应该让你更加质疑它。)所以我建议采用不同的方法(还有其他方法,例如使用证书签名,但这对我来说一直有效):

  1. 中创建过程msdb
  2. 为该用户的登录创建一个用户msdb

    USE msdb;
    GO
    CREATE USER floobarama FROM LOGIN floobarama;
    
    Run Code Online (Sandbox Code Playgroud)
  3. 授予用户对存储过程的执行权限:

    GRANT EXECUTE ON [dbo].[RunJob] TO floobarama;
    
    Run Code Online (Sandbox Code Playgroud)
  4. 测试它 - 通过从另一个数据库调用过程:

    USE tempdb;
    GO
    EXECUTE AS LOGIN = N'floobarama';
    GO
    EXEC msdb.dbo.RunJob @job_name = N'whatever';
    GO
    REVERT;
    
    Run Code Online (Sandbox Code Playgroud)

    或者更简单的测试,以防您现在不想运行任何作业并且不想等到该用户执行它来确定他们是否有足够的访问权限msdb

    USE msdb;
    GO
    CREATE PROCEDURE dbo.whatever
    WITH EXECUTE AS N'sysadminaccount'
    AS
    BEGIN
      SET NOCOUNT ON;
      SELECT [I am really...] = SUSER_SNAME();
    END
    GO
    GRANT EXECUTE ON dbo.whatever TO floobarama;
    
    USE tempdb;
    GO
    EXECUTE AS LOGIN = N'floobarama';
    GO
    EXEC msdb.dbo.whatever;
    GO
    REVERT;
    
    Run Code Online (Sandbox Code Playgroud)

    结果应该是:

    I am really...
    ---------------
    sysadminaccount
    
    Run Code Online (Sandbox Code Playgroud)
  5. 验证这不会msdb向该用户公开任何其他内容:

    USE tempdb;
    GO
    EXECUTE AS LOGIN = N'floobarama';
    GO
    SELECT job_id FROM msdb.dbo.sysjobs;
    GO
    REVERT;
    
    Run Code Online (Sandbox Code Playgroud)

    结果应该是...

    消息 229,级别 14,状态 5,第 21 行
    对象“sysjobs”、数据库“msdb”、架构“dbo”的 SELECT 权限被拒绝。

    ...因为在数据库中创建用户并没有赋予他们对该数据库中任何内容的自动权限;您需要为该用户或他们所在的角色或组(包括public)明确这样做。