DDL Trigger For Logon Insert data to a table - sql-server

I have a below DDL Trigger on my server
CREATE TRIGGER [DDLForLogin] ON ALL SERVER
FOR LOGON
AS BEGIN
DECLARE #data XML
SET #data = EVENTDATA()
IF EXISTS(SELECT * FROM sys.Databases WHERE NAME = 'DatabaseMaintenance') Begin
INSERT INTO DatabaseMaintenance.dbo.MyTable (UserName, HostName, ApplicationName, EventDataValue)
VALUES (CURRENT_USER, HOST_Name(), APP_NAME(),#Data)
END
END;
a login as Windows Authentication insert one row on MyTable but a login as SQL Server Authentication raised below error :
Logon failed for login 'xxx' due to trigger execution.
Changed database context to 'master'.
Changed language setting to us_english. (Microsofr SQL Server, Error: 17892)
EDIT
I GRANT INSERT on my table to PUBLIC.
but no change on raised error.
EDIT2
I change my trigger and add With Execute AS 'sa' on the trigger
but no change on raised error.

Try adding an appropriate EXECUTE AS clause to your trigger - the default is CALLER, so unless that user has permissions to insert into your audit table1, the trigger will fail.
Also, then use ORIGINAL_LOGIN() in the trigger to get the correct login information
1 Which you normally don't want - because otherwise every user can forge entries alleging that other users have logged in.

Must change trigger to below code:
CREATE TRIGGER [DDLForLogin] ON ALL SERVER
WITH EXECUTE AS 'sa'
FOR LOGON
AS BEGIN
DECLARE #data XML
SET #data = EVENTDATA()
DECLARE #IsPooled int
SET #IsPooled = #data.value('(/EVENT_INSTANCE/IsPooled)[1]', 'int')
IF EXISTS(SELECT * FROM sys.Databases WHERE NAME = 'DatabaseMaintenance')AND (#IsPooled=0) Begin
insert into DatabaseMaintenance.dbo.Login (UserName, HostName, ApplicationName, EventDataValue)
values (ORIGINAL_LOGIN(), HOST_Name(), APP_NAME(),#Data)
END
END;

Related

Automate SNAPSHOT isolation level for a particular SQL user

I need to have some users that are systematiically in the TRANSACTION ISOLATION LEVEL SNAPSHOT when they logs to the database.
I tried to use a LOGON trigger to set the user session to that isolation level when the ORIGINAL_LOGIN() was the user I wanted to promote... But this does not works...
I tried to see if it was possible to configure this in the ODBC connection strings.... but I found nothing
This is my actual code to test...
First part the source database :
CREATE DATABASE DB_SOURCE;
GO
ALTER DATABASE DB_SOURCE
SET ALLOW_SNAPSHOT_ISOLATION ON;
GO
USE DB_SOURCE;
GO
CREATE TABLE T (C INT);
GO
INSERT INTO T VALUES (1), (2), (3);
GO`enter code here`
Second part, the login and the user :
USE master;
GO
CREATE LOGIN CNX_USER_TEST
WITH PASSWORD = 'foo',
DEFAULT_DATABASE = DB_SOURCE;
GO
USE DB_SOURCE;
GO
CREATE USER USR_USER_TEST
FROM LOGIN CNX_USER_TEST;
GO
GRANT SELECT TO USR_USER_TEST;
GO
Third part, the logon trigger :
USE master;
GO
CREATE TRIGGER E_LOGON
ON ALL SERVER WITH EXECUTE AS N'sa'
FOR LOGON
AS
BEGIN
IF ORIGINAL_LOGIN()= N'CNX_USER_TEST'
BEGIN
COMMIT;
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
END;
END;
Fourth, the test...
In SSMS window 1 :
BEGIN TRAN ;
UPDATE T SET C = C + 1;
In SSMS in another window connected with CNX_USER_TEST :
SELECT * FROM T;
Blocked !

What's the workaround to pass parameters to triggers?

I have a trigger in "Contracts" and I also have a table called "Audits" (self explanatory).
Everything is working fine. If I insert, edit or delete, a row is inserted into Audits table by the trigger...
The problem here is that Trigger does not accept parameters... and I have a table column called "TriggeredBy" inside of the Audits table... which is supposed to have the User's ID (whoever did the insert, delete or UPDATE).
Is there a workaround that I can use so I can pass that value to that trigger?
If you have the db connection opened for the duration of the application, you can keep track of who is associated with the current db session by having a table with session if, user id.
SessionId int,
UserId varchar(20)
At login time, use ##SPID to store the session ID and associated user.
The trigger can then use ##SPID and retrieve the user ID from the table and insert it into the log table.
Option 2:
Use an application role. Allow users to connect to SQL server database using Windows Integrated Security. Call sp_setapprole to set the role. Users should be given no access to any table. The app role should have insert update delete.
You can now determine the user in your trigger.
If the desktop application used Windows authentication, you could simply use ORIGINAL_LOGIN() or SUSER_SNAME() to get the end user account name in trigger code.
With a shared SQL login, one method is to store the end user name in SQL session context for use by the trigger. Session context allows you to store name/value pairs using the sp_set_session_context procedure and read current session values with the SESSION_CONTEXT function. Call sp_set_session_context with the current user name after opening a new SQL connection so that it can be used by triggers to identify the end user.
Example T-SQL code below. Also, see this answer for other methods to set/use session level values.
CREATE TRIGGER TR_YourTable
ON dbo.YourTable
FOR INSERT, UPDATE, DELETE
AS
DECLARE #TriggeredBy sysname = COALESCE(CAST(SESSION_CONTEXT(N'end-user-name') AS sysname), N'unknown');
IF EXISTS(SELECT 1 FROM inserted) AND EXISTS(SELECT 1 FROM deleted)
BEGIN
INSERT INTO dbo.YourAuditTable (Action, SomeColumn, TriggeredBy)
SELECT 'updated', SomeColumn, #TriggeredBy
FROM deleted;
END
ELSE
BEGIN
IF EXISTS(SELECT 1 FROM inserted)
BEGIN
INSERT INTO dbo.YourAuditTable (Action, SomeColumn, TriggeredBy)
SELECT 'inserted', SomeColumn, #TriggeredBy
FROM inserted;
END
ELSE
BEGIN
INSERT INTO dbo.YourAuditTable (Action, SomeColumn, TriggeredBy)
SELECT 'deleted', SomeColumn, #TriggeredBy
FROM deleted;
END;
END;
GO
--Example T-SQL usage. Queries should be parameterized in application code.
EXEC sp_set_session_context N'end-user-name', N'me';
INSERT INTO dbo.YourTable (SomeColumn) VALUES('example');
GO

How to update the SSRS Users table after a username change?

My user name has changed from jdoe to john.doe (for example). The reference in the [dbo].[Users] table shows my old user name, jdoe.
However, with my new user name, john.doe, the subscriptions fail to run and throw the error:
Failure sending mail: The user or group name 'ABCDE\jdoe' is not
recognized.Mail will not be resent.
The [dbo].[Subscriptions].[OwnerID] value references the [dbo].[Users].[UserID] for jdoe.
Can I simply change the [dbo].[Users].[UserName] value to my new username, leaving the [dbo].[Users].[UserID] and [dbo].[Users].[Sid] columns as is?
On your SQL Server, go to Security -> Logins. Then, rename the old username with the new username (just right click on it and select "Rename").
Next, go to the "Report Server" database, select the "Users" table, and update the "UserName" column for your old user.
This script should do the work:
UPDATE [ReportServer].[dbo].[Users] SET UserName='domain\newUser' WHERE UserName='domain\OldUser'
The SSRS Subscriptions and other SSRS features should now work properly.
I would use a MERGE statement for this. Check the transaction on a rollback to test with first. You'll also need UPDATE permission on the dbo.Users table.
SET XACT_ABORT ON
BEGIN TRANSACTION;
;WITH
users_list
AS
(
SELECT users_list.* FROM (VALUES
('DOMAIN\OldUser1', 'DOMAIN\New.User1')
, ('DOMAIN\OldUser2', 'DOMAIN\New.User2')
) users_list ([UserName], [NewUserName])
)
,
users_source -- don't add users that already exist to prevent duplicates
AS
(
SELECT
[UserName]
, [NewUserName]
FROM
users_list
WHERE
1=1
AND [NewUserName] NOT IN(SELECT [UserName] FROM [ReportServer].[dbo].[Users])
)
MERGE [ReportServer].[dbo].[Users] AS T
USING users_source AS S
ON T.[UserName] = S.[UserName]
WHEN MATCHED
THEN UPDATE SET
T.[UserName] = S.[NewUserName]
OUTPUT ##SERVERNAME AS [Server Name], DB_NAME() AS [Database Name], $action, inserted.*, deleted.*;
ROLLBACK TRANSACTION;
--COMMIT TRANSACTION;
GO
I have tried renaming the dbo.Users.UserName to the new DOMAIN/JDoe, but that still produces errors.
However, renaming the SQL Server's Security Username from DOMAIN/OldName to DOMAIN/NewName did the trick.

Logon Trigger for auditing

Using SQL Server 2008R2
I am working on a LOGON trigger to capture users who have logged in. I have created a trigger, but it is capturing the SQL Agent account and is inserting it like mad. I would like to exclude it from the list, but I'm not sure how to modify the trigger to make it so. The different attempts locked me out and I had to use DAC to drop the trigger.
The original code:
USE B_DBA;
GO
CREATE TRIGGER LogonTrigger ON ALL SERVER FOR LOGON
AS
BEGIN
IF SUSER_SNAME() <> 'sa'
INSERT INTO B_DBA.dbo.LogonAudit (UserName, LogonDate, spid)
VALUES (SUSER_SNAME(), GETDATE(), ##SPID)
END;
GO
ENABLE TRIGGER LogonTrigger ON ALL SERVER;
What I'd like to add is basically a where clause, but I haven't been able to add it successfully and it would not work on the IF section.
Where not in (Select service_account from sys.dm_server_services)
Any help would be appreciated.
Untested, but it seems like you should be able to modify your IF statement to check sys.dm_server_services:
USE B_DBA;
GO
CREATE TRIGGER LogonTrigger ON ALL SERVER FOR LOGON
AS
BEGIN
IF SUSER_SNAME() <> 'sa'
AND NOT EXISTS (SELECT service_account FROM sys.dm_server_services WHERE service_account = SUSER_SNAME())
INSERT INTO B_DBA.dbo.LogonAudit (UserName, LogonDate, spid)
VALUES (SUSER_SNAME(), GETDATE(), ##SPID)
END;
GO
ENABLE TRIGGER LogonTrigger ON ALL SERVER;

Trigger fire on each inserted row to insert into remote Table

I have two different SQL 2008 servers, I don't have permission to create a linked server in any of them.
i created a trigger on server1.table1 to insert the same record to the remote server server2.table1 using OPENROWSET
i created a stored procedure to insert this record, and i have no issue when i execute the stored procedure. it insert the recored into the remote server.
the problem is when i call the stored procedure from trigger, i get an error message.
can anyone help me please to solve this issue
Stored Procedure:
USE [DB1]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
alter PROCEDURE [dbo].[InsertIntoRemoteTable]
#Description nvarchar(50)
AS
insert into
OPENROWSET(
'SQLNCLI', 'Server=Server2;UID=MySRV2user;PWD=MySRV2Password',
'SELECT Description FROM [RemoteDB].[dbo].[Table_1]')
SELECT #Description
Trigger:
ALTER TRIGGER [dbo].[InsertTable1]
ON [DB1].[dbo].[Table_1]
for insert
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Desc nvarchar(50)
Select #Desc = i.Descr from INSERTED i;
EXEC InsertIntoRemoteTable #Desc
END
When i try to insert a new description value in the table i got the following error message:
"No row was updated
the data in row 1 was not committed
Error source .Net SqlClient Data provider.
Error Message: the operation could not be performed because OLE DB
provider "SQLNCLI10" for linked server "(null)" returned message "The partner transaction manager has disabled its support for remote/network transaction.".
correct the errors entry or press ESC to cancel the change(s).
can anyone help please on this
thanks
I think you have over complicated this. There is really no need for a stored procedure here to do this. You can do this insert into another database directly in your trigger with a simple insert statement. This removes a ton of complexity and it is set based.
ALTER TRIGGER [dbo].[InsertTable1]
ON [DB1].[dbo].[Table_1]
for insert
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO RemoteDB].[dbo].[Table_1](Description)
Select i.Descr
from INSERTED i;
END

Resources