Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQL Server User Permissions on Stored Procedure and Underlying Tables

Tags:

sql

sql-server

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

like image 685
K09 Avatar asked Jan 21 '26 00:01

K09


1 Answers

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
like image 189
Andomar Avatar answered Jan 22 '26 15:01

Andomar