允许 Windows AD 组拥有 SQL 作业

问题描述 投票:0回答:2

SQL 代理作业位于用户级别之上,需要将登录名分配给所有者。但它不接受组登录作为可接受的参数。我需要使用 Windows AD 组作为所有者,因为我有不同的 SQL 用户,其中一些用户应该只能看到特定的作业。现在我已经使用 SQLAgentUserRole 为每个用户创建了单独的作业,这肯定不好,并且数据库充满了 1:1 作业,每个作业都有不同的所有者,以避免看到其他作业。

全图: 假设数据库中有 10 个不同的作业。其中一项作业名为 UserJob。我希望特定用户在连接到数据库时并展开作业部分以仅查看名为“UserJob”的作业并能够启动它。我不需要通过存储过程等。我只需要通过 SSMS 启动作业(右键单击,启动作业,如果需要,输入参数)。谢谢。

sql-server sql-server-2012 ssms sql-agent
2个回答
1
投票

根据文档 SSMS 检查以下数据库角色中的用户成员身份以显示 SQL Server 代理树节点:

  • SQLAgent用户角色
  • SQLAgentReader角色
  • SQLAgentOperator角色

我使用 SQL Server Profiler 来查找当您首次在对象浏览器中连接到数据库并展开各个节点时执行的查询。

对于 SQL Server 代理,它使用

SELECT * FROM msdb.dbo.sysjobs_view
视图来列出作业。此视图可以修改。

变化

  1. 在msdb数据库中创建一个新的数据库角色。我将其称为“CustomJobRole”。
  2. 然后我创建了一个名为“TestJob”的新作业(我假设您已经有一个作业)
  3. 创建一个低权限用户,该用户应该只能查看和运行“TestJob”。
  4. 将此用户添加到“CustomJobRole”和“SQLAgentReaderRole”和/或“SQLAgentOperatorRole”(有关详细信息,请参阅上面的链接文档)
  5. 修改
    sysjobs_view
    如下:

(见代码中的注释)

ALTER VIEW sysjobs_view
AS
SELECT jobs.job_id,
       svr.originating_server,
       jobs.name,
       jobs.enabled,
       jobs.description,
       jobs.start_step_id,
       jobs.category_id,
       jobs.owner_sid,
       jobs.notify_level_eventlog,
       jobs.notify_level_email,
       jobs.notify_level_netsend,
       jobs.notify_level_page,
       jobs.notify_email_operator_id,
       jobs.notify_netsend_operator_id,
       jobs.notify_page_operator_id,
       jobs.delete_level,
       jobs.date_created,
       jobs.date_modified,
       jobs.version_number,
       jobs.originating_server_id,
       svr.master_server
FROM msdb.dbo.sysjobs as jobs
  JOIN msdb.dbo.sysoriginatingservers_view as svr
    ON jobs.originating_server_id = svr.originating_server_id
  --LEFT JOIN msdb.dbo.sysjobservers js ON jobs.job_id = js.job_id
WHERE 
    -- Custom: Add Condition for your Custom Role and Job Name
    ( (ISNULL(IS_MEMBER(N'CustomJobRole'), 0) = 1) AND jobs.name = 'TestJob' )
    OR (owner_sid = SUSER_SID())
   OR (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)
   -- Custom: In order for users to be able to see and start Jobs they have to be members of SQLAgentReaderRole/SQLAgentOperatorRole
   -- but these roles gives them ability to see all jobs so add an exclusion
   OR ( ISNULL(IS_MEMBER(N'SQLAgentReaderRole'), 0) = 1 AND ISNULL( IS_MEMBER(N'CustomJobRole'), 0 ) = 0 ) 
   OR ( (ISNULL(IS_MEMBER(N'TargetServersRole'), 0) = 1) AND
        (EXISTS(SELECT * FROM msdb.dbo.sysjobservers js 
         WHERE js.server_id <> 0 AND js.job_id = jobs.job_id))) -- filter out local jobs

注意:注释掉了

LEFT JOIN
是原始代码,与解决方案无关。

总结

这种方法是“hacky”,因为它只修改某些用户的作业列表,实际上并没有阻止他们通过代码运行其他作业,换句话说,这不提供任何安全性,只是提供干净的 UI 的便利。实现很简单,但显然不可扩展:作业名称是硬编码的,并且使用负成员身份(即

AND ISNULL( IS_MEMBER(N'CustomJobRole'), 0 ) = 0
)。 IMO,这是最简单、最可靠(副作用最小)的方法。

已测试

SSMS v18.9.2 + SQL Server 2014 SP3

编辑作业步骤解决方法

除非您是作业所有者或系统管理员,否则无法修改作业步骤。 解决此问题的一种甚至更“hacky”的方法是创建一个表来保存所有输入参数并为用户提供对该表的插入/更新访问权限。然后,您的 SP 可以从此表中读取参数。用户应该很容易右键单击 -> 在表格上编辑并修改数据。

对于表结构我建议如下:

  • 假设您的参数相对较少,我建议您为每个参数创建一列。这样你就可以为每个数据类型提供正确的数据类型 参数。
  • 为表添加插入/删除后触发器以确保 该表始终只有一行数据。

0
投票

我对 sysjobs_view 使用以下修改。 它允许使用任何 msdb 自定义角色名称。相应的代理类别名称必须与角色名称完全匹配。 然后它的功能就像 Alex 提供的解决方案一样,只是可以具有多个角色/类别。

ALTER VIEW sysjobs_view  
AS  
SELECT jobs.job_id,  
       svr.originating_server,  
       jobs.name,  
       jobs.enabled,  
       jobs.description,  
       jobs.start_step_id,  
       jobs.category_id,  
       jobs.owner_sid,  
       jobs.notify_level_eventlog,  
       jobs.notify_level_email,  
       jobs.notify_level_netsend,  
       jobs.notify_level_page,  
       jobs.notify_email_operator_id,  
       jobs.notify_netsend_operator_id,  
       jobs.notify_page_operator_id,  
       jobs.delete_level,  
       jobs.date_created,  
       jobs.date_modified,  
       jobs.version_number,  
       jobs.originating_server_id,  
       svr.master_server  
FROM msdb.dbo.sysjobs as jobs  
  JOIN msdb.dbo.sysoriginatingservers_view as svr  
    ON jobs.originating_server_id = svr.originating_server_id  
  --LEFT JOIN msdb.dbo.sysjobservers js ON jobs.job_id = js.job_id  
  JOIN msdb.dbo.syscategories AS cats ON (jobs.category_id=cats.category_id)
WHERE (owner_sid = SUSER_SID())  
   OR (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)  
   OR (ISNULL(IS_MEMBER(N'SQLAgentReaderRole'), 0) = 1)  
   OR ( (ISNULL(IS_MEMBER(N'TargetServersRole'), 0) = 1) AND  
        (EXISTS(SELECT * FROM msdb.dbo.sysjobservers js   
         WHERE js.server_id <> 0 AND js.job_id = jobs.job_id))) -- filter out local jobs    
   OR (ISNULL(IS_MEMBER(cats.name), 0) =1)
GO

我在 msdb 中还有一个补充存储过程,它允许同一角色/类别中的任何用户获得作业的所有权。

CREATE OR ALTER PROCEDURE [dbo].[JobTakeOwnership]
    @JobName nvarchar(128) = NULL
WITH EXECUTE AS OWNER
AS
/*********************************************************
This procedure is used to change the owner of a SQL Agent Job, to the account executing this stored procedure.
The account executing this stored procedure must be a member of the category the job belongs to.
---------------------------------
Parameter Definition:
---------------------------------
@JobName = 'job_name'
Name of the SQL Agent Job you wish to take ownership of.
*********************************************************/
SET NOCOUNT ON;

BEGIN
  declare @jobexistsforuser as int
  declare @jobcatmember as int
  declare @currentjobid as uniqueidentifier
  declare @jobcatname as nvarchar(128)
  declare @newownersid as varbinary(85)
  declare @oldownersid as varbinary(85)
  declare @jobownerbefore as sysname
  declare @jobownerafter as sysname

  select @oldownersid=owner_sid, @currentjobid=job_id
    from sysjobs
    where name=@Jobname

  EXECUTE AS CALLER
  select @jobexistsforuser=count(*)
    from sysjobs_view
    where job_id = @currentjobid

  select @jobcatname=b.name
    from sysjobs_view a join syscategories b
    on a.category_id=b.category_id
    where job_id = @currentjobid

  set @newownersid = suser_sid()
  set @jobcatmember=IS_MEMBER(@jobcatname)

  /* allow sysadmins to run this SP and override the job category check */
  if is_srvrolemember('sysadmin')=1 set @jobcatmember=1
  REVERT

  if @currentjobid is NULL
    print 'Job "'+@JobName+'" does not exist.'
  else if @jobexistsforuser=0
    print 'You do not have permission to view or modify this job.'
  else if @newownersid = @oldownersid
    print 'You already own this job, no change required.'
  else if @jobcatmember is NULL  /* 3 possibilities, NULL, 0, 1 */
    print 'This job is not pre-configured for transferring ownership.'
  else if @jobcatmember=0
    print 'You are not a member of the "'+@jobcatname+'" group.'
  else
    begin
      select @jobownerbefore=suser_sname(owner_sid) from sysjobs where name=@Jobname
      print 'Job owner before update: ' +@jobownerbefore

      update sysjobs set owner_sid      = @newownersid
                      , date_modified  = CURRENT_TIMESTAMP
                      , version_number = version_number + 1
                    where job_id = @currentjobid
                    
      /*the owner of job schedules also need be changed, 
      otherwise the new job owner will not be able to modify the schedules of the job.
      Modified by Dicheng Liu on 2012-07-06*/                
      update sysschedules 
      set owner_sid      = @newownersid
        , date_modified  = CURRENT_TIMESTAMP
        , version_number = version_number + 1
      where schedule_id in (select schedule_id from sysjobschedules 
                            where job_id =@currentjobid)

      select @jobownerafter=suser_sname(owner_sid) from sysjobs where name=@Jobname
      print 'Job owner after update: ' +@jobownerafter


      /* SQL agent caches job attributes - force the SQL agent to update cached information */
      print ''
      print 'Refreshing SQL Agent cache for job: ' + @JobName
      EXECUTE AS CALLER
        exec sp_sqlagent_notify @op_type     = N'J'
                              , @job_id      = @currentjobid
                              , @action_type = N'U'
      REVERT
    end
END
GO

GRANT EXECUTE ON [dbo].[JobTakeOwnership] TO [SQLAgentUserRole]
GO
© www.soinside.com 2019 - 2024. All rights reserved.