Search code examples
sql-servert-sqlimpersonationexecute-as

Is it possible to call EXECUTE AS OWNER later in object?


Consider this code:

ALTER TRIGGER [dbo].[AfterInsertUpdateTenant_Korisnici] 
ON [dbo].[Korisnici]
WITH EXECUTE AS OWNER
FOR INSERT
AS
    DECLARE @TenantId INT = dbo.GetCurrentTenantId();

    EXECUTE AS LOGIN = 'sa';

    UPDATE Korisnici
    SET TenantId = @TenantId
    FROM Inserted i
    WHERE Korisnici.Id = i.Id;

    REVERT;

On every table, there is a security policy that doesn't allow change of the TenantId column on the table other than the one that is bound to the database user, but the policy will allow it for the "sa" user.

Problem is TenantId, I need to get the TenantId with the calling principal because TenantId is bound to the database user. I know one solution: without with execute in a trigger and calling a procedure that has with execute after I get TenantId and pass it into a procedure, but then I'd have to copy table Inserted into temp table so that I can access it in the procedure.

I would like better a solution where I can call execute as owner later in object, after I get the TenantId. Logically it's valid, but the question is; have they implemented it? execute as login = 'sa' that you can see in the code is not a good solution because it requires that current login is able to impersonate the sa login.

Point of this trigger is to allow a user not to specify TenantId on an insert. I don't have to even mention TenantId in the application, everything is only on the SQL Server.

Another solution would be if the creator of an object could write some info on an object that I could read in security policy and based on that info allow the user to update the TenantId. It may even be permissions on the object but the question is how can I read which object caused the call of the security policy?


Solution

  • I've figured out a solution where trigger calls the procedure that is in the schema forbidden to a user. The trigger has no "with execute" and can get TenantId and forward that info to the procedure. In the procedure, I have "with execute as owner" and is able to change records regarding any Tenant. Also, I am copying needed columns of the Inserted table into the temp table so that the procedure can see it, I don't see a way around that.

    Even better solution:

    alter trigger [dbo].[AfterInsertUpdateTenant_Korisnici]
    on [dbo].[Korisnici]
    with execute as owner
    for insert 
    as 
    execute as user = original_login();
    declare @TenantId int = dbo.GetCurrentTenantId();
    revert;
    
    update dbo.Korisnici
    set TenantId = @TenantId
    from Inserted i
    where dbo.Korisnici.Id = i.Id;