What would be the correct answer to the below? I would have thought EXECUTE permission is enough?? Thanks!
The database has a table named Customers owned by UserA and another table named Orders owned by UserB. You also have a stored procedure GetCustomerOrderInfo owned by UserB. GetCustomerOrderInfo selects data from both tables. You create a new user UserC. You need to ensure that UserC can call the GetCustomerOrderInfo stored procedure. You also need to assign only the minimum required permissions to UserC
Permissions on tables are not checked if the tables and the procedure have the the same owner. This is called ownership chaining.
Note that "ownership" in this context means "schema owner". For example, the table TestDB.Schema1.Table1 is owned by the user that owns of Schema1.
Because Orders has the same owner as GetCustomerOrderInfo, the stored procedure has implicit rights to read from Orders.
But Customers has a different owner, so you have to grant permission on that explicitly.
Here is a test script to demonstrate the issue:
use Test
go
if exists (select * from sys.syslogins where name = 'UserA')
drop login UserA
create login UserA with password = 'Welcome'
if exists (select * from sys.syslogins where name = 'UserB')
drop login UserB
create login UserB with password = 'Welcome'
if exists (select * from sys.syslogins where name = 'UserC')
drop login UserC
create login UserC with password = 'Welcome'
if exists (select * from sys.tables where name = 'Customers' and schema_name(schema_id) = 'SchemaA')
drop table SchemaA.Customers
if exists (select * from sys.schemas where name = 'SchemaA')
drop schema SchemaA
if exists (select * from sys.sysusers where name = 'UserA')
drop user UserA
if exists (select * from sys.tables where name = 'Orders' and schema_name(schema_id) = 'SchemaB')
drop table SchemaB.Orders
if exists (select * from sys.procedures where name = 'GetCustomerOrderInfo' and schema_name(schema_id) = 'SchemaB')
drop procedure SchemaB.GetCustomerOrderInfo
if exists (select * from sys.schemas where name = 'SchemaB')
drop schema SchemaB
if exists (select * from sys.sysusers where name = 'UserB')
drop user UserB
if exists (select * from sys.sysusers where name = 'UserC')
drop user UserC
create user UserA for login UserA
alter role db_owner add member UserA
go
create schema SchemaA authorization UserA
go
create user UserB for login UserB
alter role db_owner add member UserB
go
create schema SchemaB authorization UserB
go
create user UserC for login UserC
create table SchemaA.Customers (id int identity)
create table SchemaB.Orders (id int identity, CustomerId int)
go
create procedure SchemaB.GetCustomerOrderInfo
as
select *
from SchemaB.Orders o
join SchemaA.Customers c
on c.id = o.CustomerId
go
When we're all set up, we can test the procedure with different permissions. First we'll need execute permission on the stored procedure, then read permission on Customers. After that, the stored procedure works, even though we did not grant read access on Orders.
execute as login = 'UserC' -- Login as UserC
exec SchemaB.GetCustomerOrderInfo
-- The EXECUTE permission was denied on the object 'GetCustomerOrderInfo', database 'Test', schema 'SchemaB'
revert -- Revert back to our original login
grant execute on SchemaB.GetCustomerOrderInfo to UserC
execute as login = 'UserC'
exec SchemaB.GetCustomerOrderInfo
-- The SELECT permission was denied on the object 'Customers', database 'Test', schema 'SchemaA'.
revert
grant select on SchemaA.Customers to UserC
execute as login = 'UserC'
exec SchemaB.GetCustomerOrderInfo
-- (0 row(s) affected)
revert
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With