I am in the process of configuring database users for some new developers and I am running into some difficulties as I am reading lots of articles and it's not working out too well for me. I have tried various configurations manually and with T-SQL but I need a more efficient method.
My objective:
Some TSQL I can launch to give a database user the following permissions:
Grant permission to execute all Stored Procedures within the Database
Deny permission to View Definition of all of these stored procedures
Grant permission to SELECT, UPDATE, INSERT, DELETE from all tables within the database
Deny permission to View Definition of all of the tables in the database (I don't want them to view the data)
What I have tried:
I have achieved this manually but I have 200+ stored procedures and 100+ tables so I don't want to do it manually. From the T-SQL aspect I have managed to get the following to work:
USE database_name;
GRANT EXECUTE TO [security_account];
This works and allows the users to run the stored procedures but they cannot view the actual query code. I need the same logic for the tables as described above.
Thank you for your help.
I am not quite sure if this is a viable solution to your problem. But maybe it will get you at least closer to what you want. So, here is the setup I'd propose:
Do not grant anybody any permissions on any table.
Use stored procedures for DML.
Grant execute on all these stored procedures to public.
Setup one table in your database which lists all users which have access to your database including their login (suser_sname()) and their permissions (for example MayAlterTableUsers).
Implement into all stored procedures a check similar to this
if (
select isnull(MayAlterTableUsers, 0)
from tblUsers
where LoginName = suser_sname()
) > 0
begin
select N'Implementing the requested changes to tblUsers.'
end
else
begin
select N'You don''t have the required permission.'
end
Setup you views similar and grant select on all views to public
create view vShowAllUsers as
select *
from dbo.tblUsers
cross apply (
select MaySeeAllUsers
from dbo.tblUsers
where LoginName = suser_sname()
) as p
where p.MaySeeAllUsers = 1
In the end all views and all stored procedures will be publicly available but the handling of permission will be within each one of them. All permissions themselves will be within this table tblUsers. Since nobody has the possibility to alter this table tblUsers unless (of course) they are in this table with the appropriate permission, this DB setup is self-contained.
Related
We've built a collection of views, under a specific schema as we wanted to set the SELECT permission only to some developers for that particular schema, and perform the same logic for other developers working on data from other schemas.
The challenge we're facing now is that, e.g., View_X, under schema Schema_X (owner = dbo), selects data from Table_X under Schema_Y (owner = dbo).
The setup at the moment is built this way:
We created Role A with Developer_A and Developer_B as members;
For which we've DENIED all permissions for securable SCHEMA Schema_Y and other schemas, as we only want these members to select data from Schema_X. When Developer_A or Developer_B
query View_X, they get the error saying they don't have permissions for Schema_Y
Is there a way to prevent our developers to query Table_X or other tables from Schema_Y, via SQL Server permissions? Or any other way via some other user management logic?
There's something else going on. A DENY won't prevent ownership chains; ownership chains suppress permissions checking entirely. eg
create schema X
go
create schema Y
go
create table Y.tt(id int)
go
create view X.vtt as select * from Y.tt
go
create user xv without login
grant select on schema::X to xv
deny select on schema::Y to xv
go
execute as user='xv'
select * from X.vtt
revert
Please can any one advise if it is possible to have a stored procedure in the [dbo] schema select data from one table in one schema for users accessing via one database role and for it to select data from a like-named table in another schema for users accessing via another database role?
For example if I have three schemas in my database:
dbo
green
red
I have two database logins [RedLogin] and [GreenLogin]. These connect to my database using respective database users [RedUser] and [GreenUser]. These users are members of the respective database roles [RedRole] and [GreenRole].
[RedUser] has a default schema of [red].
[GreenUser] has a default schema of [green].
[RedRole] has execute permission on [dbo] and select permission on
the [red] schema.
[GreenRole] has execute permission on [dbo] and select permission on
the [green] schema.
In the [green] schema I have a table called [User].
In the [red] schema I have a table called [User].
In the [dbo] schema I have a stored procedure called [User_GetAll]
that runs.
SELECT * FROM USER;
What I would like is:
For users who login with [Redlogin] and call the
[User_GetAll] get all users from the [red].[User] table.
For users who login with [Greenlogin] and call the
[User_GetAll] get all users from the [green].[User] table.
So I have a question. For example:
-[dbo] schema in the past, I had 100 stored procedures. And now, I don't want to change code in stored procedure because It's so much, so How can I do to address the problem? Please help me.
....................................................................................
Update:
For simple example:
I have a schema [dbo], and in that schema, I have created a stored procedure dbo.GetAccount:
CREATE PROCEDURE dbo.GetAccount
AS
BEGIN
SELECT * FROM tblAccountNet
END
Then, I have created a schema [ABC] with user named UserABC.
Now, I would like to login with UserABC and execute dbo.GetAccount for schema [ABC] to get all user of it and don't want to change code of dbo.GetAccount. So, how can I do?
These are my ideas to resolve it:
Create another stored procedure in [dbo] schema, and use it to read all other procedure to make them execute against schema with user when login. Can I do that? So, how can I do that?
Create a stored procedure to change schema of all [dbo] procedure to [ABC]. Can I do that?
Thanks for your help.
Your best bet here would be to use dynamic SQL. That is something which allows you to pass string variables into a script which then gets executed against the SQL engine. For example, if you had variables #dynamicsql and #usertype, you would build a dyanmic SQL string like:
#dynamicsql = 'SELECT * FROM '+#usertype+'.tblAccountNet'
Then you would execute this code in a stored procedure using EXEC(#dynamicsql). This would probably work, but it requires additional permissions for the user, and also opens you up to a whole world of security concerns, with the biggest one being SQL Injection attacks. So this would probably work, but it might be more trouble than it is worth.
http://xkcd.com/327/
I need to restrict user access to SELECT, INSERT, UPDATE and DELETE, so that user should manage data only using stored procedures I provide.
So, for instance
SELECT * FROM Table1
should return
The SELECT permission was denied on the object 'Table1'
however, if there is stored procedure SelectTable1 defined as
CREATE PROCEDURE SelectTable1
AS
BEGIN
SELECT * FROM Table1
END
(the real one contains filtering and parameters, so it is not meaningless, like the one above)
user should execute it successfully and get the resultset.
But obviously, I have no success implementing this set of permissions. Can anybody point me to some specific tutorial? MSDN was not very helpful.
Database is SQL Server 2012 and all objects (tables and stored procedures) are in custom schema.
You can do it using GRANT EXEC either on specific procedures or on schemas or on a database.
The following example grants EXECUTE permission on stored procedure
HumanResources.uspUpdateEmployeeHireInfo to an application role called
Recruiting11.
USE AdventureWorks2012;
GRANT EXECUTE ON OBJECT::HumanResources.uspUpdateEmployeeHireInfo
TO Recruiting11;
GO
Thanks to Igor I've got to the right MSDN page, and followed rights links.
However, using ownership chains suggested was too complicated for me, so I used
WITH EXECUTE AS OWNER
on my stored procedures and that works very good. When I log on using restricted user I see only procedures, no tables at all and I can execute procedures, but not even select from tables.
Also, I want to mention this concept is very similar to setuid and thus was familiar to me.
I mark Igors reply as answer, because ownership chains seem to be more generic way, just wanted to share info I found.
I'm trying to create a role to give a few users permission to create and alter views, procedures and tables.
I don't want these users to be able to select from/update/delete/alter etc. any table in the database, there are tables we want to keep control of - but they should have full permissions on any objects they create.
I've given the users permissions to create views etc. and that works fine, but they can't then select from views they then create. Is it possible to do this?
-- ADDED 25/july/2013
Example:
An example user Mike has specific permissions granted on a handful of tables. All Grant, no Deny.
No other database level permissions beyond "connect"
Plus is a member of public (not altered - no denys), plus 3 other roles we have set up
Role: Standard_Reader
Specific Select permissions on a number of tables. All Grant, no Deny.
No other database level permissions
Role: SensitiveDemographicsReader
Specific Select permissions on sensitive tables. All Grant, no Deny
Role: Analyst
No Specific securables
Database level permissions:
Create Function
Create Procedure
Create Table
Create View
This user can create a table or view, but once created, can't select from it.
Is it possible to set up SQL server so that whenever a user user creates a table or view they then have permissions to select from it (assuming they have permissions on underlying tables in view)
-- EDIT
After some investigation it has become apparent that for some reason in our database, ownership of objects is not acruing to their creators.
Found using this code
select so.name, su.name, so.crdate from sysobjects so join sysusers su on so.uid = su.uid
order by so.crdate
All owners, with a couple of exceptions are DBO.
I can't understand why ownership is not passing to the creators of objects. Any idea what could cause this?
Sounds like what you're using to deny them in the first place is overriding the default settings. Can you post more information on what permissions the users have?
Can't comment :(
I would comment but lack privileges; have you taken a look at MySQL table permissions? It's a rather good system.
you need to grant SELECT on the schema to user/group:
GRANT SELECT ON SCHEMA::dbo TO User/Group;
Inspired by various schema related questions I've seen...
Ownership chaining allows me to GRANT EXECUTE on a stored procedure without explicit permissions on tables I use, if both stored procedure and tables are in the same schema.
If we use separate schemas then I'd have to explicitly GRANT XXX on the the different-schema tables. The ownership chaining example demonstrates that. This means the stored proc executing user can read/write your tables directly.
This would be like having direct access to your instance variables in a class, bypassing getter/setters, breaking encapsulation.
We also use row level security to restrict what someone sees and we apply this in the stored procedures.
So, how can we maintain schema separation and prevent direct table access?
Of course, the question won't apply if you use an ORM or don't use stored procs. But I'm not asking if I should use an ORM or stored proc in case anyone feels the need to enlighten me...
Edit, example
CREATE USER OwnsMultiSchema WITHOUT LOGIN
GO
CREATE SCHEMA MultiSchema1 AUTHORIZATION OwnsMultiSchema
GO
CREATE SCHEMA MultiSchema2 AUTHORIZATION OwnsMultiSchema
GO
CREATE USER OwnsOtherSchema WITHOUT LOGIN
GO
CREATE SCHEMA OtherSchema AUTHORIZATION OwnsOtherSchema
GO
CREATE TABLE MultiSchema1.T1 (foo int)
GO
CREATE TABLE MultiSchema2.T2 (foo int)
GO
CREATE TABLE OtherSchema.TA (foo int)
GO
CREATE PROC MultiSchema1.P1
AS
SELECT * FROM MultiSchema1.T1
SELECT * FROM MultiSchema2.T2
SELECT * FROM OtherSchema.TA
Go
EXEC AS USER = 'OwnsMultiSchema'
GO
--gives error on OtherSchema
EXEC MultiSchema1.P1
GO
REVERT
GO
CREATE PROC OtherSchema.PA
AS
SELECT * FROM MultiSchema1.T1
SELECT * FROM MultiSchema2.T2
SELECT * FROM OtherSchema.TA
Go
GRANT EXEC ON OtherSchema.PA TO OwnsMultiSchema
GO
EXEC AS USER = 'OwnsMultiSchema'
GO
--works
EXEC OtherSchema.PA
GO
REVERT
GO
Edit 2:
We don't use "cross database ownership chaining"
Row level security is a red herring and irrelevant: we don't use it everywhere
I fear that either your description or your conception of Ownership Chaining is unclear, so let me start with that:
"Ownership Chaining" simply refers to that fact that when executing a Stored Procedure (or View) on SQL Server, the currently executing batch temporarily acquires the rights/permissions of the sProc's Owner (or the sProc's schema's Owner) while executing that SQL code. So in the case of a sProc, the User cannot use those privs to do anything that the sProc code does not implement for them. Note especially that it never acquires the Identity of the Owner, only it's rights, temporarily (however, EXECUTE AS... does do this).
So the typical approach to leverage this for security is to:
Put all of the Data Tables (and all non-security Views as well) into their own Schema, let's call it [data] (though typically [dbo] is used because it's already there and too privileged for the User's schema). Make sure that no existing Users, Schemas or Owners have access to this [data] schema.
Create a schema called [exec] for all of the sProcs (and/or possibly any security Views). Make sure that the owner of this schema has access to the [data] schema (this is easy if you make dbo the owner of this schema).
Create a new db-Role called "Users" and give it EXECUTE access to the [exec] schema. Now add all users to this role. Make sure that your users only have Connect rights and have no granted access to any other schema, including [dbo].
Now your users can access the data only by executing the sProcs in [exec]. They cannot access any other data or execute any other objects.
I am not sure if this answers your question (because I was uncertain what the question was exactly), so feel free to redirect me.
As for row-level security, here is how I always do it with the security scheme above:
I always implement row-level security as a series of Views that mirror-wrap every table and compare the User's identity (usually with Suser_Sname() or one of the others) to a security list keyed from a security code in the row itself. These are the Security-Views.
Create a new schema called [rows], give it's owner access to the [data] schema and nothing else. Put all of the Security-Views in this schema.
Revoke the [exec] owner's access to the [data] schema and instead grant it data access to the [rows] schema.
Done. Now row-level security has been implemented by transparently slipping it between the sProcs and the tables.
Finally, here is a stored procure that I use to help me remember how much of this obscure security stuff works and interacts with itself (oops, corrected version of code):
CREATE proc [TestCnxOnly].[spShowProc_Security_NoEX] as
--no "With Execute as Owner" for this version
--create User [UserNoLogin] without login
--Grant connect on database :: TestSecurity to Guest
--alter database TestSecurity set trustworthy on
--Show current user context:
select current_user as current_
, session_user as session
, user_name() as _name
, suser_name() as [suser (sproc)]
, suser_sname() as sname
, system_user as system_
--Execute As Login = 'UserNoLogin'
select current_user as current_
, session_user as session
, user_name() as _name
, suser_name() as [suser (after exec as)]
, suser_sname() as sname
, system_user as system_
EXEC('select current_user as current_
, session_user as session
, user_name() as _name
, suser_name() as [suser (in Exec(sql))]
, suser_sname() as sname
, system_user as system_')
EXEC sp_ExecuteSQL N'select current_user as current_
, session_user as session
, user_name() as _name
, suser_name() as [suser (in sp_Executesql)]
, suser_sname() as sname
, system_user as system_'
--Revert
select current_user as current_
, session_user as session
, user_name() as _name
, suser_name() as [suser (aftr revert)]
, suser_sname() as sname
, system_user as system_
[EDIT: corrected version of code)
My 2c: Ownership chaining is legacy. It dates from days when there was no alternatives, and compared with today's alternatives is unsecure and coarse.
I say the alternative is not schema permissions, the alternative is code signing. With code signing you can grant the needed permissions on the signature of the procedure, and grant wide execute access on the procedure while the data access is tightly controlled. Code signing offers more granular and more precise control, and it cannot be abused the way ownership chaining can. It works inside the schema, it works across the schema, it works across the database and does not require the huge security hole of cross database ownership chaining to be open. And it doesn't require the hijacking of the object ownership for access purposes: the owner of the procedure can be any user.
As for your second question about row level security: row level security doesn't really exist in SQL Server versions 2014 and earlier, as a feature offered by the engine. You have various workarounds, and those workarounds work actually better with code signing than with ownership chaining. Since sys.login_token contains the context signatures and countersignatures, you can actually do more complex checks than you could in an ownership chaining context.
Since version 2016 SQL Server fully supports row level security.
You can:
Grant Execute On Schema::[schema_name] To [user_name]
to allow the user to execute any procedures in the schema. If you don't want him to be able to execute all of them, you can explicitly deny execute on a particular procedure to the user. Deny will take precedence in this case.