I'm attempting to use an application role with a specific default schema to facilitate using SQL commands without schema designations, i.e. unqualified names. I need to create and work with 4+ complete sets of tables+views+etc. (each 50-100 objects) that all should work exclusively within their own schema.
To test it, I've created an application role with a suitable default schema. I then use 'sp_setapprole' to attach this role, and 'sp_unsetapprole' to revert to my previous role. This seems to be working, as SCHEMA_NAME() returns the expected schema name (and 'dbo' before 'set' and after 'unset').
While in my approle, I attempt to drop and create a table, and then insert a record into the created table. The drop/creation works fine, but the insert fails, as it attempts to insert the record into a table with a similar name in the 'dbo' schema (which it hasn't been given permission to do).
Why is it, that my (purposedly) unqualified table reference suddenly reverts to 'dbo' even though my approle dictated default schema is another ?
It also fails when I do a 'select', which again defaults to 'dbo'.
I have granted all possible permissions for my approle to the designated schema, and only SELECT to dbo.
Any help is appreciated.
Cheers
Lars
This is tricky because the object resolution is based on the default schema active when the batch is parsed. So a batch like this:
DECLARE #cookie varbinary(8000);
exec sp_setapprole a_role,'P#ssword', #fCreateCookie = true, #cookie = #cookie output
--go
select * from t
Will reference dbo.t, even if a_role has a different default schema. If you break it into two batches, then t will resolve to a.t.
Either that or you're using static SQL in a stored procedure, which is resolved relative to the procedure's schema, rather than any user's default_schema.
Apart from that, I don't think Application Roles are the right approach. You can accomplish this more simply by using impersonation than using Application Roles. EG grant the application users the right to impersonate a user who has rights only in a specific schema. Something like:
create role app_users
create user a_user without login with default_schema=a
grant select, insert, update, delete, execute on schema::a to a_user
grant impersonate on user::a_user to app_users
go
execute as user='a_user'
select * from t --resolves to a.t
select * from dbo.t --fails
revert
Related
I have a store procedure that have several selects for checking and balances in our order process, long story short, this store procedure reads (SELECT) about 20 tables with are spread in 3 databases.
CREATE USER [stageUsrOrder] FOR LOGIN [domain\[my user]]
GO
CREATE ROLE OrderSpecRole AUTHORIZATION [stageUsrOrder]
GO
GRANT EXECUTE on [orders].[ValidateOrderById] TO pmdSchedulerRole
GO
I thought that this statements would give me read access to all the tables inside the store procedure.
I have try
GRANT EXECUTE on [AVIS].[spReportValidationByLAN] TO pmdSchedulerRole WITH GRANT OPTION
GO
But it didn't work.
So, How do I grant execute access to the store procedure and SELECT to all the tables inside?
You don't. One of the value propositions of stored procedures is that you can restrict access to certain patterns. For example, if you want people to only search by ID, you give then a stored procedure that takes ID as a parameter.
But if you want to give them arbitrary select access to the underlying tables, you have to do that explicitly.
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.
I want to know what are the default permissions for sql server stored procedures.
For example, I created a user in database but no role or no permissions granted except execute permission like:
GRANT EXECUTE ON SCHEMA :: [dbo] TO [newUser]
later on me (as a sa login) created a stored procedure that makes just a select from a table like:
CREATE PROCEDURE dbo.selectX AS
BEGIN
SELECT ID, NAME FROM MyTable
END
and my user [newUser] can execute and see the result of select statement like:
EXEC dbo.selectX
up until now, I can assume that stored procedures have default permissions that my user don't have.
I tried other dmls like UPDATE, INSERT, DELETE and user managed to execute procedures successfully.
But! when I created a proceure with ddl as create table .. such as:
CREATE PROCEDURE dbo.crtNT AS
BEGIN
CREATE TABLE del(id int) --for test
END
and when the newUser executes this procedure error occurs like:
CREATE TABLE permission denied in database 'MyDb'
And here is my question: By default, do stored procedures have permission with DML(select, insert, update vs.) but not with DDL(create, drop table vs.)? Because I didn't see any explanation about this situation in Books Online or anywhere else.
Any comments will be welcomed.
You are correct in your assumption that DDL statements are treated differently.
The procedures with DML statements work through the mechanism of Ownership Chaining however as this quote from the EXECUTE AS documentation states
Remember that ownership chaining applies only to DML statements.
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;
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.