Sybase - How to create read-only user only can SELECT all tables? - sybase

I need to create an user which only can SELECT all tables in Sybase database.
The way is :
1. Create role which can SELECT all tables.
2. Grant role to this user.
My Sybase version is : Adaptive Server Enterprise/15.0.2

create a readonly role
grant only the select permission to the new role
add the user to that role

While the other answer is technically correct, it omits a crucial detail: you cannot simply grant the permissions for all tables to a single user or group - you have to specify all table (and procedure, view etc.) names yourself. Wildcards do not work and result in generic and not very helpful Error -131: Syntax error messages.
I've adapted a very good workaround by marc_s from SQL Server 2008 for Sybase (tested on SYL Anywhere, but should work on ASE too, if the documentation is correct).
It could of course be improved to be fully automatic and account for changed tables, but if you just want to quickly give some users read-only access to a relatively static database (be it the sales department or an application), then this just works well enough:
Create a user (you could also create a group with CREATE ROLE):
CREATE USER readonly IDENTIFIED BY 'secretpassword';
Check which tables you need to include (if you want to exclude the system tables, for example). To do that, look into systable for all tables/views/procedures and sysuserperm for user details:
SELECT
t.table_id,
t.table_name,
t.table_type,
t.creator,
u.user_name
FROM systable AS t
JOIN sysuserperm AS u ON t.creator = u.user_id
ORDER BY t.table_id ASC;
Depending on your data and needs, you can modify the query to just return the table_names that you need. In this example we get all views and tables that are owned by DBA (owner = schema). Now create the GRANT statements:
SELECT
t.table_name,
GrantCmd = 'GRANT SELECT ON dba.' + t.table_name + ' TO readonly;',
RevokeCmd = 'REVOKE SELECT ON dba.' + t.table_name + ' FROM readonly;'
FROM systable AS t
WHERE t.creator = 1
AND (t.table_type = 'BASE' OR t.table_type = 'VIEW');
Copy the resulting statements, wrap them in a transaction and execute it:
BEGIN TRANSACTION;
GRANT SELECT ON dba.table1 TO readonly;
GRANT SELECT ON dba.table2 TO readonly;
GRANT SELECT ON dba.table3 TO readonly;
<...>
COMMIT TRANSACTION;
Now you can execute SELECT statements. You have to manually add the context to the tables though:
SELECT * from table1; -- Error -141: table not found
SELECT * from dba.table1; -- works as expected
DELETE FROM dba.table1; -- Error -121: permission denied
If you want to revoke access, just use the revoke part from before.

Related

Using application role to set default schema - commands unexpectedly uses dbo

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

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 to Grant Permission to IMPERSONATE any other user?

In order to log the usage of an application I am developing I need every user using my application to execute queries against my SQL Server Database under their own credentials.
In order to not be storing their passwords in a retrievable fashion, I can't be creating a connection on a per-user basis (because that would entail knowing their password past the brief window when they log-in).
The, seemingly obvious, solution to this problem (which may be sub-optimal) is to run all sensitive queries as a generic "Application" user, impersonating the logged in user (requiring me to only associate the logged in user with a username...which is not a bad thing).
My problem is that I'm not sure how to grant impersonate to all users of a certain role, or all users in general (not the brightest idea, because I don't want the app impersonating a sysadmin, for instance).
grant impersonate on all to ApplicationLogin
doesn't work, and there's no documentation that I can find that suggests granting impersonation on members of a role would be doable...
Any ideas?
You can use Dynamic sql .
the code below fetches all users related to a specific role and then grant permission impersonate on a user.
You should create a user on application login to relate it with database , then grant permission to impersonate on all members of specific role.
This is the code:
CREATE TRIGGER S2
ON DATABASE
FOR CREATE_USER
AS
CREATE TABLE #T
(PRINCIPAL_NAME NVARCHAR(100),ROLE_NAME NVARCHAR(100));
WITH L AS (SELECT *
FROM (SELECT P.name AS 'PRINCIPAL_NAME',R.role_principal_id AS 'GH'
FROM SYS.database_principals P,sys.database_role_members R
WHERE P.principal_id=R.member_principal_id OR P.principal_id=R.role_principal_id
AND type<>'R') S INNER JOIN (SELECT P.name AS 'ROLE_NAME',P.principal_id AS 'GHA'
FROM SYS.database_principals P,sys.database_role_members R
WHERE P.principal_id=R.member_principal_id OR P.principal_id=R.role_principal_id
AND type='R') D
ON D.GHA=S.GH)
INSERT INTO #T
SELECT DISTINCT PRINCIPAL_NAME,ROLE_NAME
FROM L
------------ ENTER ROLE NAME HERE
WHERE ROLE_NAME LIKE '%%'
------------
DECLARE #P NVARCHAR(100),#TEXT NVARCHAR(MAX)=''
------------------------- CHANGE IT TO YOUR DESIRED NAME OF YOUR APPLICATION USER
DECLARE #APPUSER NVARCHAR(100)='APPLICATION_USER'
-------------------------
DECLARE C CURSOR FOR SELECT PRINCIPAL_NAME FROM #T
OPEN C
FETCH NEXT FROM C INTO #P
WHILE(##FETCH_STATUS=0)
BEGIN
SET #TEXT+='GRANT IMPERSONATE ON USER::['+#P+'] TO '+#APPUSER+' '
FETCH NEXT FROM C INTO #P
END
CLOSE C
DEALLOCATE C
DROP TABLE #T
EXEC(#TEXT)
I hope it work for You.
You could create your users via a stored procedure. The last line of your stored procedure would be to grant impersonation.
To set up the current users, you would have to cycle through them all and set the grant impersonation this one time.

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