是否可以向需要用户没有权限的触发器授予权限?
Is it possible to grant permission to a trigger that requires permission user does not have?
过去我使用数据库触发器来记录所有架构更改。这是光荣的。但是,它要求任何需要修改架构的人都具有特定权限。我相信需要的一个是查看服务器状态。记不清了,但是这个 post.
需要哪一个并不重要
问题是,如果有些用户可以更改架构但不应该拥有该权限怎么办?这完全破坏了我的工具,因为触发器只会导致他们得到这个错误。我可以将它包装在一个 try catch 中,但如果它不记录更改,那显然会破坏整个目的。
有没有办法让这个触发器对每个人 运行 即使我不能给每个人所有必需的权限?我猜答案是否定的,因为我不知道如何在某种模拟下触发触发器,而且我想不出任何其他解决方案。但我希望你们知道一些我不知道的事情!
这是触发器:
CREATE TRIGGER [LogSchemaEvent]
ON DATABASE
FOR CREATE_TABLE, ALTER_TABLE, DROP_TABLE,
CREATE_VIEW, ALTER_VIEW, DROP_VIEW,
CREATE_PROCEDURE, ALTER_PROCEDURE, DROP_PROCEDURE,
CREATE_FUNCTION, ALTER_FUNCTION, DROP_FUNCTION,
CREATE_TRIGGER, ALTER_TRIGGER, DROP_TRIGGER,
CREATE_SCHEMA, ALTER_SCHEMA, DROP_SCHEMA,
CREATE_INDEX, ALTER_INDEX, DROP_INDEX,
CREATE_TYPE, DROP_TYPE
AS
BEGIN
SET NOCOUNT ON
DECLARE @EventData XML = EVENTDATA()
DECLARE @ip VARCHAR(32) = (
SELECT client_net_address
FROM sys.dm_exec_connections
WHERE session_id = @@SPID)
INSERT SchemaEvents (
EventType,
EventDDL,
EventXML,
DatabaseName,
SchemaName,
ObjectName,
HostName,
IPAddress,
ProgramName,
LoginName)
SELECT
@EventData.value('(/EVENT_INSTANCE/EventType)[1]', 'NVARCHAR(100)'),
@EventData.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'NVARCHAR(MAX)'),
@EventData,
DB_NAME(),
@EventData.value('(/EVENT_INSTANCE/SchemaName)[1]', 'NVARCHAR(255)'),
@EventData.value('(/EVENT_INSTANCE/ObjectName)[1]', 'NVARCHAR(255)'),
HOST_NAME(),
@ip,
PROGRAM_NAME(),
SUSER_SNAME();
END
通过使用与具有所需权限的用户关联的证书对其进行签名,可以将提升的权限授予模块(过程、函数、触发器)。这适用于架构范围内的对象,但不适用于 DDL 触发器,因为它们无法直接签名。
但是,您可以创建一个具有所需功能的模式范围的过程,使用证书对其进行签名,然后从您的 DDL 触发器中调用它。这种方法的唯一缺点是数据库必须是可信的。我通常建议避免使用 TRUSTWORTHY,但在开发数据库的情况下可能是可以接受的。
下面是一个示例脚本。
USE YourDatabase;
GO
CREATE OR ALTER PROCEDURE dbo.LogDDLEvent
@EventData xml
AS
INSERT SchemaEvents (
EventType,
EventDDL,
EventXML,
DatabaseName,
SchemaName,
ObjectName,
HostName,
IPAddress,
ProgramName,
LoginName)
SELECT
@EventData.value('(/EVENT_INSTANCE/EventType)[1]', 'NVARCHAR(100)'),
@EventData.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'NVARCHAR(MAX)'),
@EventData,
DB_NAME(),
@EventData.value('(/EVENT_INSTANCE/SchemaName)[1]', 'NVARCHAR(255)'),
@EventData.value('(/EVENT_INSTANCE/ObjectName)[1]', 'NVARCHAR(255)'),
HOST_NAME(),
(SELECT TOP 1 client_net_address FROM sys.dm_exec_connections WHERE session_id = @@SPID),
PROGRAM_NAME(),
SUSER_SNAME();
GO
GRANT EXECUTE ON dbo.LogDDLEvent TO public;
GO
CREATE OR ALTER TRIGGER [LogSchemaEvent]
ON DATABASE
FOR CREATE_TABLE, ALTER_TABLE, DROP_TABLE,
CREATE_VIEW, ALTER_VIEW, DROP_VIEW,
CREATE_PROCEDURE, ALTER_PROCEDURE, DROP_PROCEDURE,
CREATE_FUNCTION, ALTER_FUNCTION, DROP_FUNCTION,
CREATE_TRIGGER, ALTER_TRIGGER, DROP_TRIGGER,
CREATE_SCHEMA, ALTER_SCHEMA, DROP_SCHEMA,
CREATE_INDEX, ALTER_INDEX, DROP_INDEX,
CREATE_TYPE, DROP_TYPE
AS
SET NOCOUNT ON;
DECLARE @EventData XML = EVENTDATA();
EXECUTE dbo.LogDDLEvent @EventData = @EventData;
GO
USE master
GO
-- Create certificate in master.
CREATE CERTIFICATE LogSchemaEventCertificate
ENCRYPTION BY PASSWORD = 'ephemoral password'
WITH SUBJECT = 'For view server state';
CREATE LOGIN LogSchemaEventCertificateUser FROM CERTIFICATE LogSchemaEventCertificate;
GRANT VIEW SERVER STATE TO LogSchemaEventCertificateUser;
-- Copy cert to user database
DECLARE @cert_id int = cert_id('LogSchemaEventCertificate')
DECLARE @public_key varbinary(MAX) = certencoded(@cert_id),
@private_key varbinary(MAX) =
certprivatekey(@cert_id,
'ephemoral password',
'ephemoral password');
SELECT @cert_id, @public_key, @private_key; --these values should be non-null
DECLARE @sql nvarchar(MAX) =
'CREATE CERTIFICATE LogSchemaEventCertificate
FROM BINARY = ' + convert(varchar(MAX), @public_key, 1) + '
WITH PRIVATE KEY (BINARY = ' +
convert(varchar(MAX), @private_key, 1) + ',
DECRYPTION BY PASSWORD = ''ephemoral password'',
ENCRYPTION BY PASSWORD = ''ephemoral password'')';
EXEC YourDatabase.sys.sp_executesql @sql;
ALTER CERTIFICATE LogSchemaEventCertificate REMOVE PRIVATE KEY;
ALTER DATABASE YourDatabase SET TRUSTWORTHY ON;
GO
USE YourDatabase;
ADD SIGNATURE TO dbo.LogDDLEvent BY CERTIFICATE LogSchemaEventCertificate WITH PASSWORD = 'ephemoral password';
ALTER CERTIFICATE LogSchemaEventCertificate REMOVE PRIVATE KEY;
GO
过去我使用数据库触发器来记录所有架构更改。这是光荣的。但是,它要求任何需要修改架构的人都具有特定权限。我相信需要的一个是查看服务器状态。记不清了,但是这个 post.
需要哪一个并不重要问题是,如果有些用户可以更改架构但不应该拥有该权限怎么办?这完全破坏了我的工具,因为触发器只会导致他们得到这个错误。我可以将它包装在一个 try catch 中,但如果它不记录更改,那显然会破坏整个目的。
有没有办法让这个触发器对每个人 运行 即使我不能给每个人所有必需的权限?我猜答案是否定的,因为我不知道如何在某种模拟下触发触发器,而且我想不出任何其他解决方案。但我希望你们知道一些我不知道的事情!
这是触发器:
CREATE TRIGGER [LogSchemaEvent]
ON DATABASE
FOR CREATE_TABLE, ALTER_TABLE, DROP_TABLE,
CREATE_VIEW, ALTER_VIEW, DROP_VIEW,
CREATE_PROCEDURE, ALTER_PROCEDURE, DROP_PROCEDURE,
CREATE_FUNCTION, ALTER_FUNCTION, DROP_FUNCTION,
CREATE_TRIGGER, ALTER_TRIGGER, DROP_TRIGGER,
CREATE_SCHEMA, ALTER_SCHEMA, DROP_SCHEMA,
CREATE_INDEX, ALTER_INDEX, DROP_INDEX,
CREATE_TYPE, DROP_TYPE
AS
BEGIN
SET NOCOUNT ON
DECLARE @EventData XML = EVENTDATA()
DECLARE @ip VARCHAR(32) = (
SELECT client_net_address
FROM sys.dm_exec_connections
WHERE session_id = @@SPID)
INSERT SchemaEvents (
EventType,
EventDDL,
EventXML,
DatabaseName,
SchemaName,
ObjectName,
HostName,
IPAddress,
ProgramName,
LoginName)
SELECT
@EventData.value('(/EVENT_INSTANCE/EventType)[1]', 'NVARCHAR(100)'),
@EventData.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'NVARCHAR(MAX)'),
@EventData,
DB_NAME(),
@EventData.value('(/EVENT_INSTANCE/SchemaName)[1]', 'NVARCHAR(255)'),
@EventData.value('(/EVENT_INSTANCE/ObjectName)[1]', 'NVARCHAR(255)'),
HOST_NAME(),
@ip,
PROGRAM_NAME(),
SUSER_SNAME();
END
通过使用与具有所需权限的用户关联的证书对其进行签名,可以将提升的权限授予模块(过程、函数、触发器)。这适用于架构范围内的对象,但不适用于 DDL 触发器,因为它们无法直接签名。
但是,您可以创建一个具有所需功能的模式范围的过程,使用证书对其进行签名,然后从您的 DDL 触发器中调用它。这种方法的唯一缺点是数据库必须是可信的。我通常建议避免使用 TRUSTWORTHY,但在开发数据库的情况下可能是可以接受的。
下面是一个示例脚本。
USE YourDatabase;
GO
CREATE OR ALTER PROCEDURE dbo.LogDDLEvent
@EventData xml
AS
INSERT SchemaEvents (
EventType,
EventDDL,
EventXML,
DatabaseName,
SchemaName,
ObjectName,
HostName,
IPAddress,
ProgramName,
LoginName)
SELECT
@EventData.value('(/EVENT_INSTANCE/EventType)[1]', 'NVARCHAR(100)'),
@EventData.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'NVARCHAR(MAX)'),
@EventData,
DB_NAME(),
@EventData.value('(/EVENT_INSTANCE/SchemaName)[1]', 'NVARCHAR(255)'),
@EventData.value('(/EVENT_INSTANCE/ObjectName)[1]', 'NVARCHAR(255)'),
HOST_NAME(),
(SELECT TOP 1 client_net_address FROM sys.dm_exec_connections WHERE session_id = @@SPID),
PROGRAM_NAME(),
SUSER_SNAME();
GO
GRANT EXECUTE ON dbo.LogDDLEvent TO public;
GO
CREATE OR ALTER TRIGGER [LogSchemaEvent]
ON DATABASE
FOR CREATE_TABLE, ALTER_TABLE, DROP_TABLE,
CREATE_VIEW, ALTER_VIEW, DROP_VIEW,
CREATE_PROCEDURE, ALTER_PROCEDURE, DROP_PROCEDURE,
CREATE_FUNCTION, ALTER_FUNCTION, DROP_FUNCTION,
CREATE_TRIGGER, ALTER_TRIGGER, DROP_TRIGGER,
CREATE_SCHEMA, ALTER_SCHEMA, DROP_SCHEMA,
CREATE_INDEX, ALTER_INDEX, DROP_INDEX,
CREATE_TYPE, DROP_TYPE
AS
SET NOCOUNT ON;
DECLARE @EventData XML = EVENTDATA();
EXECUTE dbo.LogDDLEvent @EventData = @EventData;
GO
USE master
GO
-- Create certificate in master.
CREATE CERTIFICATE LogSchemaEventCertificate
ENCRYPTION BY PASSWORD = 'ephemoral password'
WITH SUBJECT = 'For view server state';
CREATE LOGIN LogSchemaEventCertificateUser FROM CERTIFICATE LogSchemaEventCertificate;
GRANT VIEW SERVER STATE TO LogSchemaEventCertificateUser;
-- Copy cert to user database
DECLARE @cert_id int = cert_id('LogSchemaEventCertificate')
DECLARE @public_key varbinary(MAX) = certencoded(@cert_id),
@private_key varbinary(MAX) =
certprivatekey(@cert_id,
'ephemoral password',
'ephemoral password');
SELECT @cert_id, @public_key, @private_key; --these values should be non-null
DECLARE @sql nvarchar(MAX) =
'CREATE CERTIFICATE LogSchemaEventCertificate
FROM BINARY = ' + convert(varchar(MAX), @public_key, 1) + '
WITH PRIVATE KEY (BINARY = ' +
convert(varchar(MAX), @private_key, 1) + ',
DECRYPTION BY PASSWORD = ''ephemoral password'',
ENCRYPTION BY PASSWORD = ''ephemoral password'')';
EXEC YourDatabase.sys.sp_executesql @sql;
ALTER CERTIFICATE LogSchemaEventCertificate REMOVE PRIVATE KEY;
ALTER DATABASE YourDatabase SET TRUSTWORTHY ON;
GO
USE YourDatabase;
ADD SIGNATURE TO dbo.LogDDLEvent BY CERTIFICATE LogSchemaEventCertificate WITH PASSWORD = 'ephemoral password';
ALTER CERTIFICATE LogSchemaEventCertificate REMOVE PRIVATE KEY;
GO