GRANT Permissions seem not to work on a VIEW - sql

I have the following view (called view3)
I created a role and granted it SELECT and UPDATE rights just on two columns.
CREATE ROLE Testrole
GRANT SELECT (Doc_ID, [Total Attentions]) ON view3 TO Testrole
Then I assigned the role to a user (test) already created
ALTER ROLE Testrole ADD MEMBER test
But when the transaction checking if all is ok is executed, all the columns are shown instead of the two required (the same image above).
This is the script
CREATE LOGIN logtest
WITH PASSWORD = 'logtest'
CREATE USER test
FOR LOGIN logtest
CREATE ROLE Testrole
GRANT SELECT (Doc_ID, [Total Attentions]) ON view3 TO Testrole
ALTER ROLE Testrole ADD MEMBER test
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
BEGIN TRANSACTION
EXECUTE AS USER = 'test'
SELECT * /*This should give an error*/
FROM view3
SELECT Doc_ID, [Total Attentions] /*This should work just fine*/
FROM view3
REVERT
ROLLBACK

GRANT permissions are cumulative. These symptoms suggest that view-level (all columns) permissions exist, inherited from this or other roles. Run the query below to see if this is the case.
SELECT
permission_name
, OBJECT_NAME(major_id) AS ObjectName
, CASE WHEN c.name IS NULL THEN 'All Columns' ELSE c.name END AS ColumnName
, USER_NAME(grantee_principal_id) AS Gratee
FROM sys.database_permissions AS p
LEFT JOIN sys.columns AS c ON
c.object_id = p.major_id
AND c.column_id = p.minor_id
WHERE
major_id = OBJECT_ID(N'view3');

Related

Cross Database Trigger with EXECUTE AS not working - permission problem

I have 2 Databases. Database A belongs to an ERP System and Database B is my own database.
I've created an AFTER INSERT DML Trigger in a table which belongs to Database A.
Every single ERP User is associated with a DB User.
At the moment I copy all the users from Database A to Database B to grant permission for my tables.
It was the only solution I could find to make it work but it's bad because a new ERP User will get an Error Message when there's no user in Database B.
So now I tried it once again to use WITH EXECUTE AS .... I tried OWNER and different DB users but nothing works and I have absolutely no idea what's wrong.
I even granted all permissions for the user on the tables I use in the trigger.
Let's say I have a user called "triggerUser" when I do WITH EXECUTE AS 'triggerUser' it doesn't work but when I remove the EXECUTE AS .... and use the triggerUser for the ERP Login it works so the user got the permissions to write my tables.
Here's my trigger. Maybe you have an idea what I'm missing.
CREATE TRIGGER [dbo].[TR_manAddAutomatikQueueForLager_AfterInsert]
ON [SL_MWAWI].[dbo].[LAGERPROTOKOLL]
WITH EXECUTE AS 'moep'
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON
DECLARE #Mandant int
SET #Mandant = (SELECT MANDANT_ID FROM SL_Daten.dbo.MANDANT WHERE Datenbankname = 'SL_MWAWI')
-- insert
IF EXISTS (SELECT * FROM inserted) AND NOT EXISTS(SELECT * FROM deleted)
BEGIN
INSERT INTO maniacSellerGen2.dbo.manAutomatikQueue
SELECT #Mandant, ISNULL(wsa.WebShopId, wsav.WebShopId), ISNULL(wsav.VaterArtikelnummer, wsa.Artikelnummer), 'Lager', GETDATE()
FROM inserted
LEFT JOIN maniacSellerGen2.dbo.manWebShopArtikel wsa
ON wsa.Artikelnummer COLLATE DATABASE_DEFAULT = inserted.Artikelnummer COLLATE DATABASE_DEFAULT
AND #Mandant = wsa.Mandant
LEFT JOIN maniacSellerGen2.dbo.manWebShopArtikelVarianten wsav
ON wsav.Artikelnummer COLLATE DATABASE_DEFAULT = inserted.Artikelnummer COLLATE DATABASE_DEFAULT
AND #Mandant = wsav.Mandant
WHERE ((wsa.Artikelnummer IS NULL AND wsav.Artikelnummer IS NOT NULL)
OR (wsa.Artikelnummer IS NOT NULL AND wsav.Artikelnummer IS NULL))
END
END
Here's a screenshot from the permissions for the user in every database it has db_datareader and db_datawriter.
Permissions
I also did tried this:
GRANT INSERT ON dbo.manAutomatikQueue TO moep
GRANT DELETE ON dbo.manAutomatikQueue TO moep
GRANT UPDATE ON dbo.manAutomatikQueue TO moep
GRANT SELECT ON dbo.manAutomatikQueue TO moep
Thanks a lot!

How to allow a view to read from multiple schemas

I have two tables in different schemas, one owned by dbo, the other not. I have a third schema where I'm trying to create a view that reads from those two tables. My understanding is that if dbo is the owner of the view, because it has grant options to all objects in the database, users with only access to the view should be able to query it. But that isn't working, even when I explicitly grant dbo SELECT WITH GRANT to the underlying table in the schema it doesn't own.
I've read a few other posts, but don't see an answer that works: https://stackoverflow.com/a/4134892/1499015, Ownership Chaining and Tutorial: Ownership Chains and Context Switching
I assume I'm doing something wrong with permission chaining, but I can't figure out what the problem is.
--use master
--GO
--create login testuser with password='******************'
-- principal3 will own schema3
--create login principal3 with password='*****************'
--use testingDB
--GO
create user testuser for login testuser
create user principal3 for login principal3
GO
-- testrole that will only have select permissions on schema2
CREATE ROLE testrole
exec sp_addrolemember 'testrole', 'testuser'
GO
-- the first schema is owned by dbo
CREATE SCHEMA schema1 authorization dbo
GO
-- the schema where the view will live, also owned by dbo
create schema schema2 authorization dbo
GO
-- schema3 owned by principal3
create schema schema3 authorization principal3
go
-- tables in schema1 and schema3
CREATE TABLE schema1.testtable1 (
id int
, testvalue sysname
)
GO
CREATE TABLE schema3.testtable3 (
id int
, testvalue sysname
)
GO
-- some base data
INSERT schema1.testtable1
SELECT 1, 'a'
UNION SELECT 2, 'b'
UNION SELECT 3, 'c'
INSERT schema3.testtable3
SELECT 1, 'hot dogs rule'
UNION SELECT 2, 'pizza sucks'
UNION SELECT 3, 'haw just kidding that''s obviously backwards'
GO
-- view in schema2 that queries tables in schema1 and schema3, owned by dbo
CREATE VIEW schema2.testview
AS
SELECT t1.testvalue as v1
, t3.testvalue as v3
FROM schema1.testtable1 t1
INNER JOIN schema3.testtable3 t3 ON t1.id = t3.id
GO
GRANT SELECT ON schema::schema1 TO dbo WITH GRANT OPTION
GRANT SELECT ON schema::schema3 TO dbo WITH GRANT OPTION
ALTER AUTHORIZATION ON schema2.testview TO dbo
-- give testrole permission to query the view
GRANT SELECT ON schema2.testview to testrole
GO
EXECUTE AS USER = 'testuser';
SELECT USER_NAME();
BEGIN TRY
select top 100 * from schema2.testview
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber
,ERROR_MESSAGE() AS ErrorMessage;
END CATCH
REVERT;
SELECT USER_NAME();
When I run this code, I get
The SELECT permission was denied on the object 'testtable3', database 'testingDb', schema 'schema3'.
I've also tried to create a schema owner user for the schema that the view lives in, and granting that user select with grant to the underlying tables, but it's still not working.
Any ideas what I'm doing wrong?
Thanks!
The documentation says:
When an object is accessed through a chain, SQL Server first compares
the object's owner to the owner of the calling object (the previous
link in the chain). If both objects have the same owner, permissions
on the referenced object are not checked. Whenever an object accesses
another object that has a different owner, the ownership chain is
broken and SQL Server must check the caller's security context.
In this case, when the object testtable3 is accessed, SQL Server first compares testtable3 owner to the owner of the testview. If both objects have the same owner, permissions on the referenced object are not checked. It does not matter if you GRANT dbo rights on testtable3, even WITH GRANT (he this right anyway). It matters that the owner of testtable3 is the same as the owner of the testview.
For example, try:
ALTER AUTHORIZATION ON SCHEMA::schema3 TO dbo
And it will work.
If the owners are different, the ownership chain is broken and SQL Server must check the caller's security context. In this case: does testuser have rights to select from testtable3?.
Therefore, if the owners are different you need to do this to make it work:
GRANT SELECT ON schema3.testtable3 TO testuser

Find all databases where particular user exists and its role

I have a huge instance containing 1000+ databases. I need to find a way to query entire instance and find databases that contain particular user and what role this user has. I am not interested whether the user is orphanded. I just want to know which databases have this user and which do not.
Lets say that my user is called TestUser. Databases that do not contain this user should return NULL.
I would like the results in the following format:
Column1 - Database Name
Column2 - UserName (if exists or else NULL)
Column3 - UserRole (if exists or else NULL)
Under the assumption that you are not looking for issuing 1000+ selects, one (extremely ugly) solution would be:
SELECT 'DB_1' , UserName , UserRole
FROM DB_1.UsersTable
WHERE Username = 'TestUser'
UNION
SELECT 'DB_2' , UserName , UserRole
FROM DB_2.UsersTable
WHERE Username = 'TestUser'
:
:
Another solution is to use DYNAMIC SQL:
Collect the list of all the DBs that you to check,
Build a string hosting a select statement like the one above,
Execute the statement.
Again, both methods are shameful.
create table #temp
(
dbname sysname,
dbrole sysname,
dbuser sysname
)
Exec sp_msforeachdb '
if db_id()>4
Begin
insert into #temp
select db_name(), rp.name as database_role, mp.name as database_user
from sys.database_role_members drm
join sys.database_principals rp on (drm.role_principal_id = rp.principal_id)
join sys.database_principals mp on (drm.member_principal_id = mp.principal_id)
End
'
The Roles Part is referenced from here:
Get list of all database users with specified role

SQL Server User Permissions on Stored Procedure and Underlying Tables

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
As explained above the schema owner of SP and underlying objects should be the same.
Check schema owners by:
select name, USER_NAME(s.principal_id) AS Schema_Owner from sys.schemas s
To change the owner of an schema you can:
ALTER AUTHORIZATION ON SCHEMA::YOUR_SCHEMA TO YOUR_USER;
Examples:
ALTER AUTHORIZATION ON SCHEMA::Claim TO dbo
ALTER AUTHORIZATION ON SCHEMA::datix TO user1;
Finally if within your SP you are truncating a table or changing structure you may want to add WITH EXECUTE AS OWNER in your SP:
ALTER procedure [myProcedure]
WITH EXECUTE AS OWNER
as
truncate table etl.temp

How can I view all grants for an SQL Database?

I am using SQL Server 2005, I want to find out what all the grants are on a specific database for all tables. It would also help to find out all tables where the delete grant has been given for a specific user.
Note: this may be similar to this question, but I could not get the selected answer's solution working (if someone could provide a better example of how to use that, it would help as well)
The given solution does not cover where the permission is granted against the schema or the database itself, which do grant permissions against the tables as well. This will give you those situations, too. You can use a WHERE clause against permission_name to restrict to just DELETE.
SELECT
class_desc
, CASE WHEN class = 0 THEN DB_NAME()
WHEN class = 1 THEN OBJECT_NAME(major_id)
WHEN class = 3 THEN SCHEMA_NAME(major_id) END [Securable]
, USER_NAME(grantee_principal_id) [User]
, permission_name
, state_desc
FROM sys.database_permissions
Also, db_datawriter would need to be checked for membership because it gives implicit INSERT, UPDATE, and DELETE rights, meaning you won't see it show up in the permission DMVs or their derivatives.
I liked the answer from K. Brian Kelly but I wanted a little more information (like the schema) as well as generating the corresponding GRANT and REVOKE statements so I could apply them in different environments (e.g. dev/test/prod).
note you can easily exclude system objects, see commented where clause
select
class_desc
,USER_NAME(grantee_principal_id) as user_or_role
,CASE WHEN class = 0 THEN DB_NAME()
WHEN class = 1 THEN ISNULL(SCHEMA_NAME(o.uid)+'.','')+OBJECT_NAME(major_id)
WHEN class = 3 THEN SCHEMA_NAME(major_id) END [Securable]
,permission_name
,state_desc
,'revoke ' + permission_name + ' on ' +
isnull(schema_name(o.uid)+'.','')+OBJECT_NAME(major_id)+ ' from [' +
USER_NAME(grantee_principal_id) + ']' as 'revokeStatement'
,'grant ' + permission_name + ' on ' +
isnull(schema_name(o.uid)+'.','')+OBJECT_NAME(major_id)+ ' to ' +
'[' + USER_NAME(grantee_principal_id) + ']' as 'grantStatement'
FROM sys.database_permissions dp
LEFT OUTER JOIN sysobjects o
ON o.id = dp.major_id
-- where major_id >= 1 -- ignore sysobjects
order by
class_desc desc
,USER_NAME(grantee_principal_id)
,CASE WHEN class = 0 THEN DB_NAME()
WHEN class = 1 THEN isnull(schema_name(o.uid)+'.','')+OBJECT_NAME(major_id)
WHEN class = 3 THEN SCHEMA_NAME(major_id) end
,permission_name
To list all the permissions that can be controlled you can use the function fn_my_permission. This query lists all permissions on server:
select * from fn_my_permissions(NULL, NULL)
You have to login using an account that has sysadmin role.
You can refine the function calls using following parameters.
For all permissions on database:
select * from fn_my_permissions(NULL, 'database')
For all permissions on the dbo schema:
select * from fn_my_permissions('dbo', 'schema')
For all permissions on a table:
select * from fn_my_permissions('dbo.test', 'object')
To view all grants on a specific database use this:
Select * from INFORMATION_SCHEMA.TABLE_PRIVILEGES
To just view delete grants on a specific database use this:
Select * from INFORMATION_SCHEMA.TABLE_PRIVILEGES WHERE PRIVILEGE_TYPE = 'DELETE'
To see the grants on an entire DB, select the DB in question, open a new query window, enter - sp_helprotect, execute query