Sql server deny drop view to role - sql-server

I use SQL Server 2008 R2 and want to deny drop any view to developer role.
How can add deny permission for drop any view to this role.
By database Trigger can prevent drop any view for all user.

The problem here is that the deny permission applies to the following securables:
Synonyms
Tables and columns
Views and columns
So you could use:
USE dbName
GO
DENY DELETE TO [developerRole]
GO
The problem is that I assume that you will want some of the developers to be able to drop items, this will prevent anyone in the developer role from dropping any of the above securables. You could instead apply this permission directly to the user, but if you want them to be able to drop tables or columns, but not views, then it won't work either.
Another method would be to move all your views into a separate schema, then deny the user DELETE on that schema:
USE [dbName]
GO
DENY DELETE ON SCHEMA::[viewsSchema] TO [developerRole]
GO
The problem here is that it's messy and not really the best use of a schema, not really an option I would go for.
I think the best option really is a trigger, if it really is just one user you can do the following:
CREATE TRIGGER noViewDrop
ON DATABASE
FOR DROP_VIEW
AS
IF USER_ID() = USER_ID('Bob')
BEGIN
PRINT 'Bob Can''t Drop Views';
ROLLBACK
END
This means that Bob can't drop any views in the database, but can drop any of the other securables listed above. If you want to apply this to a database role rather than an individual user, simply use the IS_ROLEMEMBER() function like so:
CREATE TRIGGER noViewDrop
ON DATABASE
FOR DROP_VIEW
AS
IF IS_ROLEMEMBER ('developerRole') = 1
BEGIN
PRINT 'Members of the developerRole Can''t Drop Views';
ROLLBACK
END
Meaning anyone in the developer role will be caught by the trigger when dropping views, but if they need to drop a column they will still be able to do this. Anyone who is not in the developerRole will still be able to drop views.

Trigger
CREATE TRIGGER NO_DROP_VIEW
ON DATABASE
FOR DROP_VIEW
AS
PRINT 'Dropping VIEWs are not allowed'
ROLLBACK

To deny drop view you can simply include DENY DELETE
use Your_Database
GO
DENY DELETE TO [SQLUserTest]
GO
When user is denied DELETE permission, that user is no longer able to drop or delete any object in that database.

Related

Grant all tables (except 1) Read permission on a Role while not knowing which tables there are every day

Every day in our datawarehouse (that will be dynamically changing) the tables are dropped and rebuilt. Also is it possible that some developer in our organisation will create more tables in that database.
Because of that I can not give permissions to the database that are persistent.
Question:
I want to make some kind of a job that runs every day, that lists all the table names (that are existing at that time) in a database like 'Select * FROM sys.tables'
Then I want the tables names as an input value to a script that runs trough all table names and places them in a script like :
GRANT SELECT TO [Tablename1] TO [ROLE_READALLTABLES Except 1 table],
GRANT SELECT TO [Tablenaam2] TO [ROLE_READALLTABLES Except 1 table]
and so go on in a loop until all existing tables are readable.
So all tables (except 1 table ) in the entire database should get the GRANT SELECT permission.
I have looked around all the related answers, but I cannot seem to get a good idea how to get this to work.
I hope someone can help me with this.
UPDATE
I use Microsoft SQL Server 2014, and I work through SQL Management Studio 2014
UPDATE 2 :
There is one exception. This table has schema [dbo]. like all other tables
You can use the db_datareader role to grant access to all tables generally, then a specific role with a DENY rule to exclude access to the one table that's the exception.
The steps would be roughly like this:
1) Create your "Read all except 1 role":
CREATE ROLE [ROLE_READALLEXCEPT1]
2) Create your "deny" role like so:
CREATE ROLE [ROLE_DENY]
GO
DENY SELECT, INSERT, UPDATE, DELETE ON myTable TO [ROLE_DENY]
GO
3) Then add your "except 1" role to it:
EXEC sp_addrolemember #rolename = 'ROLE_DENY', #membername = 'ROLE_READALLEXCEPT1'
4) Add your role to db_datareader:
EXEC sp_addrolemember #rolename = 'db_datareader', #membername = 'ROLE_READALLEXCEPT1'
The deny role should override db_datareader, and the net effect is that your role now has access to all tables (including new ones) except for those explicitly denied.
You can then add your users to "ROLE_READALLEXCEPT1" and they will have access to everything except the one exception table.
there is no information about the excluded table so i assume is always the same.I also assume that all the other tables are on the schema dbo; this is not a relevant constraint or limitation because the logic can be easily applied to more than one schema.
the easiest solution is granting permission at the schema level. move the single table on a separate schema with restricted permissions and grant full read on the whole schema where the user tables reside:
GRANT SELECT ON SCHEMA::dbo TO [relevant role/user];
now the developers can create all the table they feel like on the schema dbo and the permission are inherited by the schema.
should you need to grant access to more than one schema the permission are easily applied once and then every new table will get proper permission.
the huge pro of this solution is that it is fire and forget: once in place there is no maintenance, no jobs, no script to run daily/weekly/whatever.
this advantage is to be evaluated and weighted against the move of the excluded table (or the other way round: move the user tables): maybe is used by just a couple of internal applications so it is a quick patch or is used by a whole bunch of services accessible worldwide instead and that would be a nightmare.

How can I use t-sql permissions to show someone a subset of records from a table?

I have a table with many records in Microsoft SQL Server 2008. Some of the records have boolean flag set. Others do not.
I want to a user to be able to ONLY see records where the flag is set. I made a view that uses a select statement to query these records and I gave the user read permissions on this view. But because the view selects from the original table SQL server is saying the user does not have enough permissions to look at the view.
The view and the table are in the same database.
How can I set permissions (either using a view or using some other method) so that the user can only see the subset of records from this table?
This works exactly as it should:
USE tempdb;
GO
CREATE TABLE dbo.MyFlags(a INT, flag BIT);
GO
INSERT dbo.MyFlags VALUES(1,0),(2,1),(3,1);
GO
CREATE VIEW dbo.vMyFlags
AS
SELECT a, flag FROM dbo.MyFlags WHERE flag = 1;
GO
CREATE LOGIN smudge WITH PASSWORD = 'floob', CHECK_POLICY = OFF;
GO
CREATE USER smudge FROM LOGIN smudge;
GO
GRANT SELECT ON dbo.vMyFlags TO smudge;
GO
EXECUTE AS user = 'smudge';
GO
-- from view (succeeds):
SELECT a,flag FROM dbo.vMyFlags;
GO
-- from table (error):
SELECT a,flag FROM dbo.MyFlags;
GO
Perhaps you missed a step there, or created or referenced objects without the correct schema prefix. Always, always, always use the schema prefix.

Create User from a different DB in SQL Server

Is there any way I can create a user on a database from the master DB(or any other DB)
IF EXISTS (SELECT 1 FROM j_test.sys.database_principals WHERE name = N'test_user')
DROP USER j_test.[test_user]; --doesnt't work.
You either need to change the context to that database or dynamically go there:
EXEC j_test..sp_executesql N'DROP USER test_user;';
Logins are done at the server level, I usually create them from the [master] database. Just my convention.
Users are done at the database level. You need to have your context set to that database. The USE command allows you to switch between databases.
This snippet is from my blog that shows a hypothetical database [BSA] with a schema named [STAGE].
-- Which database to use.
USE [master]
GO
-- Delete existing login.
IF EXISTS (SELECT * FROM sys.server_principals WHERE name = N'BSA_ADMIN')
DROP LOGIN [BSA_ADMIN]
GO
-- Add new login.
CREATE LOGIN [BSA_ADMIN] WITH PASSWORD=N'M0a2r0c9h11$', DEFAULT_DATABASE=[BSA]
GO
-- Which database to use.
USE [BSA]
GO
-- Delete existing user.
IF EXISTS (SELECT * FROM sys.database_principals WHERE name = N'BSA_ADMIN')
DROP USER [BSA_ADMIN]
GO
-- Add new user.
CREATE USER [BSA_ADMIN] FOR LOGIN [BSA_ADMIN] WITH DEFAULT_SCHEMA=[STAGE]
GO

Multi-Schema Privileges for a Table Trigger in an Oracle Database

I'm trying to write a table trigger which queries another table that is outside the schema where the trigger will reside. Is this possible? It seems like I have no problem querying tables in my schema but I get:
Error: ORA-00942: table or view does not exist
when trying trying to query tables outside my schema.
EDIT
My apologies for not providing as much information as possible the first time around. I was under the impression this question was more simple.
I'm trying create a trigger on a table that changes some fields on a newly inserted row based on the existence of some data that may or may not be in a table that is in another schema.
The user account that I'm using to create the trigger does have the permissions to run the queries independently. In fact, I've had my trigger print the query I'm trying to run and was able to run it on it's own successfully.
I should also note that I'm building the query dynamically by using the EXECUTE IMMEDIATE statement. Here's an example:
CREATE OR REPLACE TRIGGER MAIN_SCHEMA.EVENTS
BEFORE INSERT
ON MAIN_SCHEMA.EVENTS REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
DECLARE
rtn_count NUMBER := 0;
table_name VARCHAR2(17) := :NEW.SOME_FIELD;
key_field VARCHAR2(20) := :NEW.ANOTHER_FIELD;
BEGIN
CASE
WHEN (key_field = 'condition_a') THEN
EXECUTE IMMEDIATE 'select count(*) from OTHER_SCHEMA_A.'||table_name||' where KEY_FIELD='''||key_field||'''' INTO rtn_count;
WHEN (key_field = 'condition_b') THEN
EXECUTE IMMEDIATE 'select count(*) from OTHER_SCHEMA_B.'||table_name||' where KEY_FIELD='''||key_field||'''' INTO rtn_count;
WHEN (key_field = 'condition_c') THEN
EXECUTE IMMEDIATE 'select count(*) from OTHER_SCHEMA_C.'||table_name||' where KEY_FIELD='''||key_field||'''' INTO rtn_count;
END CASE;
IF (rtn_count > 0) THEN
-- change some fields that are to be inserted
END IF;
END;
The trigger seams to fail on the EXECUTE IMMEDIATE with the previously mentioned error.
EDIT
I have done some more research and I can offer more clarification.
The user account I'm using to create this trigger is not MAIN_SCHEMA or any one of the OTHER_SCHEMA_Xs. The account I'm using (ME) is given privileges to the involved tables via the schema users themselves. For example (USER_TAB_PRIVS):
GRANTOR GRANTEE TABLE_SCHEMA TABLE_NAME PRIVILEGE GRANTABLE HIERARCHY
MAIN_SCHEMA ME MAIN_SCHEMA EVENTS DELETE NO NO
MAIN_SCHEMA ME MAIN_SCHEMA EVENTS INSERT NO NO
MAIN_SCHEMA ME MAIN_SCHEMA EVENTS SELECT NO NO
MAIN_SCHEMA ME MAIN_SCHEMA EVENTS UPDATE NO NO
OTHER_SCHEMA_X ME OTHER_SCHEMA_X TARGET_TBL SELECT NO NO
And I have the following system privileges (USER_SYS_PRIVS):
USERNAME PRIVILEGE ADMIN_OPTION
ME ALTER ANY TRIGGER NO
ME CREATE ANY TRIGGER NO
ME UNLIMITED TABLESPACE NO
And this is what I found in the Oracle documentation:
To create a trigger in another user's
schema, or to reference a table in
another schema from a trigger in your
schema, you must have the CREATE ANY
TRIGGER system privilege. With this
privilege, the trigger can be created
in any schema and can be associated
with any user's table. In addition,
the user creating the trigger must
also have EXECUTE privilege on the
referenced procedures, functions, or
packages.
Here: Oracle Doc
So it looks to me like this should work, but I'm not sure about the "EXECUTE privilege" it's referring to in the doc.
What you are experiencing is a feature of Oracle's security model. The entire point of using schemas is to control access to the data. The tables in my schema are mine, you cannot even see them until I grant you privileges on them.
The syntax is quite simple: the owner schema issues
grant select, insert on my_table to you
/
Alternatively an account with the GRANT ANY privilege (such as a DBA) can pass privileges on any user's objects.
grant select, insert on apc.my_table to you
/
The grantee can be either a user or a role. However, note that we can only build program units - stored procedures, views, triggers - using privileges which have been granted directly through to our user.
So, if you get the other schema owner to grant you the necessary privileges you will be able to build your trigger.
edit
When referencing an object in another schema we need to qualify the object with the schema name ....
insert into apc.whatever_table values ...
or else we need to create a synonym for it
create synonym whatever for apc.whatever_table;
I feel someone should add the obvious - the other schema's table must be qualified with the schema name or a private/public synonym is needed. I wonder if the original problem was merely a name resolution issue. If not, APC's answer is a good explanation of the Oracle security model.
You should execute this for every table and schema involved:
grant select on OTHER_SCHEMA_%.table_name to MAIN_SCHEMA;

How do you set permissions on a schema that has objects accessing other schemas?

I have 2 schemas and one of the objects in the first schema needs to access an object in the other schema. For example:
CREATE VIEW I.ITest
AS
SELECT 1 as TestColumn
GO
CREATE VIEW O.OTest
AS
SELECT * FROM I.ITest
GO
EXEC ('SELECT * FROM O.OTest') AS USER = 'TestUser'
DROP VIEW O.OTest
DROP VIEW I.ITest
In the above example, TestUser only has access to the 'O' Schema. So the select itself works fine, but because the view is doing a select from another schema 'I' then it fails with the error:
The SELECT permission was denied on the object 'ITest', database 'MyDB', schema 'I'.
To get around this I can give the 'O' schema permission to access the 'I' schema, but this doesn't sound right and looks to be bypassing the schema permissions.
What can be done? Am I doing this all wrong? Whats the best practice in this scenario?
Thanks
UPDATE: My schemas were owned by different db roles so I got around this problem by simply changing the owner of both to dbo and then giving the db role permission to access the schema. This meant that the owner dbo could see everything and I could then give specific permission to the db role only and the rest of the db objects were not available unless via this schema. Thanks for your help
You should wrap the selection of data from the view on the "other" Schema, within a stored procedure. Then grant execute rights on the stored procedure. Although the user will not have direct access to the view they are permitted access via the stored procedure.
Here is an example walkthrough for you demonstrating the security principles as work:
USE DATABASE SANDBOX;
--Create Logins
CREATE LOGIN UserOne WITH Password='Hello123';
CREATE LOGIN UserTwo WITH Password='Hello123';
--Create Database Users
CREATE USER UserOne;
CREATE USER UserTwo;
--Create the Test Schemas
CREATE SCHEMA SchemaOne AUTHORIZATION UserOne;
CREATE SCHEMA SchemaTwo AUTHORIZATION UserTwo;
--Create a View on SchemaOne
CREATE VIEW SchemaOne.ViewOne
AS SELECT 1 as TestColumn;
--Create a View on SchemaTwo
CREATE VIEW SchemaTwo.ViewTwo
AS SELECT * FROM SchemaOne.ViewOne;
--Test that the SchemaOne
EXEC('select * from SchemaOne.ViewOne') AS USER = 'UserOne'
--1
EXEC('select * from SchemaTwo.ViewTwo') AS USER = 'UserOne'
--The SELECT permission was denied on the object 'ViewTwo', database 'SANDBOX', schema 'SchemaTwo'.
--Create a stored procedure to safely expose the view within SchemaTwo to UserOne who's default Schema is
--SchemaOne.
CREATE PROCEDURE SchemaTwo.proc_SelectViewTwo
AS
select * from SchemaTwo.ViewTwo;
--Grant execute rights on the procedure
GRANT EXECUTE ON SchemaTwo.proc_SelectViewTwo TO UserOne;
--Test the
EXECUTE AS LOGIN='UserOne';
Exec SchemaTwo.proc_SelectViewTwo;
revert;
An alternative approach as suggeted in my comments would be to use a Database Role to control access to multiple schemas. Using the principals as defined in the solution above, you could use Database Roles like so:
EXEC sp_addrole 'CrossSchemaRole';
EXEC sp_addrolemember 'CrossSchemaRole','UserOne';
GRANT SELECT ON SCHEMA::SchemaOne TO CrossSchemaRole;
GRANT SELECT ON SCHEMA::SchemaTwo TO CrossSchemaRole;
EXECUTE AS LOGIN='UserOne';
select * from SchemaTwo.ViewTwo;
revert;
Some suggested further reading:
Securing SQL Server
SQL Server Permission Hierarchy
White Paper: Security Overview for
Administrators
Surely you give permissions to users not objects and that's it. If you or the owner of the other schema wants to allow other users to access objects (whether they are tables, views or whatever) within it then it is up to the owner of the other schema. Just because you as the developer can write a procedure that accesses objects in other schemas it does not follow that anyone running your procedure should be allowed to do so too. Roles are the way to go.

Resources