The basic concept is to use GRANT/DENY Schema Permissions. You can efficiently manage permissions by creating a role and then adding members to it.
Below is an example that will explain you in detail
use master
go
--Create Logins
CREATE LOGIN UserA WITH Password='UserA123';
go
CREATE LOGIN UserB WITH Password='UserB123';
use AdventureWorks2008R2
go
--Create Database Users
CREATE USER UserA;
go
CREATE USER UserB;
go
--Create the Test Schemas
CREATE SCHEMA SchemaA AUTHORIZATION UserA
go
CREATE SCHEMA SchemaB AUTHORIZATION UserB
go
-- create test tables
create table schemaA.TableA (fname char(5))
go
insert into schemaA.TableA (fname) values ('Kin-A')
go
create table SchemaB.TableB (fname char(5))
go
insert into SchemaB.TableB (fname) values ('Kin-B')
go
Now test :
--Test for UserA in SchemaA
EXEC('select * from schemaA.TableA') AS USER = 'UserA'
go
--Kin-A
-- Test for UserB in SchemaB == this should fail
EXEC('select * from SchemaB.TableB') AS USER = 'UserA'
go
--Msg 229, Level 14, State 5, Line 1
--The SELECT permission was denied on the object 'TableB', database 'AdventureWorks2008R2', schema 'SchemaB'.
Now create Stored Procedures :
CREATE PROCEDURE SchemaB.proc_SelectUserB
AS
select * from schemaA.TableA;
go
create procedure schemaA.proc_SchemaA
as
select * from schemaA.TableA
Now Grant execute permissions to UserA on schemaB's SP
GRANT EXECUTE ON OBJECT::[SchemaB].[proc_SelectUserB] TO [UserA]
go
Test it .. to see if UserA is able to run SP from schemaB. This will PASS
EXECUTE AS LOGIN='UserA';
Exec SchemaB.proc_SelectUserB;
revert;
go
--- Kin-A
But UserA wont be able to see data from SchemaB
EXECUTE AS LOGIN='UserA';
select * from SchemaB.TableB
revert;
go
--- Msg 229, Level 14, State 5, Line 3
--- The SELECT permission was denied on the object 'TableB', database 'AdventureWorks2008R2', schema 'SchemaB'.
Alternatively you can use DATABASE ROLE and just add users to it for better manageability of permissions:
EXEC sp_addrole 'SchemaBUsesSchemaAProc'
go
EXEC sp_addrolemember 'SchemaBUsesSchemaAProc','UserA';
go
Below statement will make sure that UserA is able to see schemaA and NOT schemaB. The good thing is that you can just add users to SchemaBUsesSchemaAProc
role and they will inherit all the permissions granted to that role.
GRANT SELECT ON SCHEMA::SchemaA TO SchemaBUsesSchemaAProc;
go
If you only want to allow UserA to execute SP's which are owned by SchemaB then below statement will do the job:
GRANT EXECUTE ON OBJECT::[SchemaB].[proc_SelectUserB] TO [SchemaBUsesSchemaAProc]
go
This way, UserA is not able to see SchemaB's tables, but still can execute procs from SchemaB.
Below will explain the permission hierarchy :
This will list explicitly granted permissions on table types, but not those granted implicitly through role or group membership, or permissions granted against the containing schema.
SELECT
[schema] = s.name,
[type] = t.name,
[user] = u.name,
p.permission_name,
p.state_desc
FROM sys.database_permissions AS p
INNER JOIN sys.database_principals AS u
ON p.grantee_principal_id = u.principal_id
INNER JOIN sys.types AS t
ON p.major_id = t.user_type_id--.[object_id]
INNER JOIN sys.schemas AS s
ON t.[schema_id] = s.[schema_id]
WHERE p.class = 6; -- TYPE
I am curious what type of explicit permissions you are using in your system for table types? From the documentation there isn't a whole lot you need to implement for standard runtime query support (you can't grant SELECT
directly, for example). It seems this is mostly there for metadata / control.
Best Answer
I believe this is because when you ran:
Grant
was given to that particular user. But when you tried to revoke, that was at the schema level.(2nd run). However when initially you gave the GRANT access , it was not specified at object level.SinceREVOKE
has no affect if the specified permission does not already exist.I believe starting with creation of user and then assigning the roles accordingly can help here.
Please read Understanding GRANT, DENY, and REVOKE in SQL Server which will help you in this scenario.