I have this setup
- I created two databases (source and target).
- Source database has stored procedure that is accessing table in target database.
- Stored procedure is executed as service account
- Service account login has user account in both databases
- Service account has connect and authenticate permission to target database
- Service account has read permission on target database schema
- Both databases has trustworthy on
still I got an error
The server principal "ServiceAccount" is not able to access the database "TargetDatabase" under the current security context.
SQL script
-- Create logins
CREATE LOGIN SourceDatabaseOwner WITH PASSWORD = 'Pa$$w0rd'
CREATE LOGIN TargetDatabaseOwner WITH PASSWORD = 'Pa$$w0rd'
CREATE LOGIN ServiceAccount WITH PASSWORD = 'Pa$$w0rd'
-- Create databases
CREATE DATABASE SourceDatabase
CREATE DATABASE TargetDatabase
-- Setup trustworthy
ALTER DATABASE SourceDatabase SET TRUSTWORTHY ON;
ALTER DATABASE TargetDatabase SET TRUSTWORTHY ON;
-- Setup database owners
USE SourceDatabase
GO
EXEC dbo.sp_changedbowner @loginame = N'SourceDatabaseOwner'
USE TargetDatabase
GO
EXEC dbo.sp_changedbowner @loginame = N'TargetDatabaseOwner'
-- Add ServiceAccount to source database
USE SourceDatabase
GO
CREATE USER ServiceAccount FOR LOGIN ServiceAccount;
-- Add ServiceAccount to target database
USE TargetDatabase
GO
CREATE USER ServiceAccount FOR LOGIN ServiceAccount;
-- Enable ServiceAccount to authenticate to target database
USE TargetDatabase
GO
GRANT AUTHENTICATE TO ServiceAccount;
-- Grant permissions
USE TargetDatabase
GO
GRANT SELECT ON SCHEMA::dbo TO ServiceAccount
-- Create table with data
USE TargetDatabase
GO
create table dbo.InterestingData (Id int identity primary key, Content nvarchar(255))
insert into dbo.InterestingData (Content) values ('Foo'), ('Bar')
-- Create stored procedure executing under service account and accessing target database
USE SourceDatabase
GO
create or alter procedure dbo.GetData
with execute as 'ServiceAccount' as
begin
select id, content from TargetDatabase.dbo.InterestingData
end
-- Execution does not work under service account
-- The server principal "ServiceAccount" is not able to access the database "TargetDatabase" under the current security context.
exec dbo.GetData
So I continued and tried to sign the procedure with the certificate and setup permissions on the target table to the login of that certificate
-- create certificate in master
use master
go
CREATE CERTIFICATE SignProcedureCert
ENCRYPTION BY PASSWORD = 'Pa$$w0rd'
WITH SUBJECT = 'Certificate for signing stored procedures'
GO
-- backup certificate
BACKUP CERTIFICATE SignProcedureCert TO FILE ='C:\Certs\SignProcedureCert.cer'
WITH PRIVATE KEY
(
FILE = 'C:\Certs\SignProcedureCert.pvk',
DECRYPTION BY PASSWORD = 'Pa$$w0rd',
ENCRYPTION BY PASSWORD = 'Pa$$w0rd'
)
GO
-- create login from the certificate
create login SignProcedureLogin from certificate SignProcedureCert
-- Import certificate to the source database
CREATE CERTIFICATE SignProcedureCert
FROM FILE = 'C:\Certs\SignProcedureCert.cer'
WITH PRIVATE KEY (FILE = 'C:\Certs\SignProcedureCert.pvk',
ENCRYPTION BY PASSWORD = 'Pa$$w0rd',
DECRYPTION BY PASSWORD = 'Pa$$w0rd')
-- Sign the procedure
ADD SIGNATURE TO dbo.GetData
BY CERTIFICATE SignProcedureCert
WITH PASSWORD = 'Pa$$w0rd';
GO
-- Setup permission for SignProcedureLogin to target database
USE TargetDatabase
GO
CREATE USER SignProcedureLogin FOR LOGIN SignProcedureLogin;
GRANT SELECT ON SCHEMA::dbo TO SignProcedureLogin
so now I should have this setup
but when I execute
USE SourceDatabase
GO
exec dbo.GetData
I got the same error
The server principal "ServiceAccount" is not able to access the database "TargetDatabase" under the current security context.
What am I missing?
Update
When I remove the with execute as
clause it does work
-- Setup trustworthy
ALTER DATABASE SourceDatabase SET TRUSTWORTHY OFF;
ALTER DATABASE TargetDatabase SET TRUSTWORTHY OFF;
USE SourceDatabase
GO
create or alter procedure dbo.GetData
--with execute as 'ServiceAccount'
as
begin
select id, content from TargetDatabase.dbo.InterestingData
end
GRANT EXECUTE ON SCHEMA::dbo TO ServiceAccount
execute as login = 'ServiceAccount'
exec dbo.GetData
revert
but still it's possible to somehow execute the procedure with the with execute as
clause?
trustworthy on
works for me only when I usewith execute as owner
but it does not work when I usewith execute as 'ServiceAccount'
. Withoutwith execute as
clause I can assign select permissions to the target table directly to the ServiceAccount and it works even without the certificate. Thewith execute as
is very handy feature I would like to use, because then anyone with execute authorization for the procedure can run it and it will always behave the same and there is no need for manual impersonation. However, I'm not sure if what I want is even possible.execute as owner
withtrustworthy on
works very well. I don't know about caching, I will look into that.