SQL 代理作业位于用户级别之上,需要将登录名分配给所有者。但它不接受组登录作为可接受的参数。我需要使用 Windows AD 组作为所有者,因为我有不同的 SQL 用户,其中一些用户应该只能看到特定的作业。现在我已经使用 SQLAgentUserRole 为每个用户创建了单独的作业,这肯定不好,并且数据库充满了 1:1 作业,每个作业都有不同的所有者,以避免看到其他作业。
全图: 假设数据库中有 10 个不同的作业。其中一项作业名为 UserJob。我希望特定用户在连接到数据库时并展开作业部分以仅查看名为“UserJob”的作业并能够启动它。我不需要通过存储过程等。我只需要通过 SSMS 启动作业(右键单击,启动作业,如果需要,输入参数)。谢谢。
根据文档 SSMS 检查以下数据库角色中的用户成员身份以显示 SQL Server 代理树节点:
我使用 SQL Server Profiler 来查找当您首次在对象浏览器中连接到数据库并展开各个节点时执行的查询。
对于 SQL Server 代理,它使用
SELECT * FROM msdb.dbo.sysjobs_view
视图来列出作业。此视图可以修改。
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 可以从此表中读取参数。用户应该很容易右键单击 -> 在表格上编辑并修改数据。
对于表结构我建议如下:
我对 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