Read Only User

prodlife | Aug 14, 2008 17:10 -0600

“Oh mighty DBA” said the developer, “You are wise and very attractive. Can I please have access to the TEST system of my application? I need to debug an issue that QA found and I cannot reproduce”.

(I’m paraphrasing a bit).

“No way. You will change few things while you are there and ruin the test cycle. Even if you won’t, QA will think you did and this will ruin the test cycle.”

“I see your point, wise and attractive DBA. In this case, can I have just read-only access to the system?”

(I hate this part in the conversation. When a request sounds innocent and trivial, but is in-fact complicated and full of trickery.)

“I’ll see what I can do. But you don’t get to execute any functions. Just select on table and views. And you’ll need to prefix the table names with the name of the original user.”

(Well, actually giving synonyms is easy enough. If he’ll insist, I’ll add them).

I wrote the function that does it. It is horrible in several ways:

  1. It uses “execute immediate”.
  2. No bind variables. I don’t think I can use them in this case, but it still means I’m doing bad things to the library cache.
  3. I add “with grant” privilege to all object privileges the original user had. I did not want to touch the original user, but as I explained before, I had no choice. I could have been more selective here, though.

I hope it will be useful, but I post it mostly as a rough reference. Think twice before you use it.


CREATE OR REPLACE
PROCEDURE GRANT_READ_ONLY(RO_USER in varchar2, ORIG_USER in varchar2,with_grant in BOOLEAN
default true) AUTHID current_user AS stmt varchar2(4000);
get_grant_objs varchar2(4000);
TYPE cur_typ IS REF CURSOR;
c_cursor cur_typ;
Type user_priv_typ is record (
    table_name all_tab_privs.table_name%type,
    privilege all_tab_privs.privilege%type,
    grantor all_tab_privs.grantor%type);
user_priv_rec user_priv_typ;

BEGIN
  if (with_grant) then
      get_grant_objs := 'select table_name,privilege,grantor from dba_TAB_PRIVS dtp
          join dba_objects do on dtp.grantor=do.owner and dtp.table_name=do.object_name
          where grantee=''' || ORIG_USER || '''  order by TABLE_NAME';
      open c_cursor for get_grant_objs;
      LOOP
        FETCH c_cursor into user_priv_rec;
        EXIT WHEN c_cursor%NOTFOUND;
        stmt := 'GRANT ' || user_priv_rec.privilege || ' ON ' || user_priv_rec.grantor || '.' ||
        user_priv_rec.table_name || ' TO ' || ORIG_USER || ' WITH GRANT OPTION';
        execute immediate stmt;
        --dbms_output.put_line(stmt);
  END LOOP;
  CLOSE c_cursor;
  end if;
  for obj in
  (
  select object_name from all_objects
  where owner=ORIG_USER
  and object_type in ('SEQUENCE','TABLE','VIEW','MATERIALIZED VIEW'))
  LOOP
    stmt := 'GRANT SELECT ON ' || ORIG_USER || '.' || obj.object_name || ' TO ' || RO_USER;
    execute immediate stmt;
    --dbms_output.put_line(stmt);
  END LOOP;
END;

Alert Log Aliases for 11g

prodlife | Aug 11, 2008 13:00 -0600

I love having short cuts to frequently used commands. 11g forced me to change few of them:

alias tailalert=’tail -f $ORACLE_BASE/admin/$ORACLE_DB/bdump/alert_$ORACLE_SID.log’
Became
alias tailalert=’adrci exec=”set editor vim;set home diag/rdbms/`echo $ORACLE_DB | tr A-Z a-z`/$ORACLE_SID; show alert -tail -f”‘

and

alias vialert=’vi $ORACLE_BASE/admin/$ORACLE_DB/bdump/alert_$ORACLE_SID.log’
Became
alias vialert=’adrci exec=”set editor vim;set home diag/rdbms/`echo $ORACLE_DB | tr A-Z a-z`/$ORACLE_SID; show alert”‘

I’m not sure why the DB Name appears as lower case in the path while the SID is upper case. Maybe a mistake I’ve made during the installation, but in other places that use DB name as a parameter (such as datafile path), it appears in upper case (as it should).

Grants on Views

prodlife | Aug 7, 2008 12:50 -0600

I’ve been bitten by a well known, but not well documented issue with granting “select” privileges on views.

User A has three tables and two functions, user B has “select” privilege on the tables and “execute” privilege on the functions. User B created a view using these tables and functions, and he now wants to allow user C to select from this table. What privileges user C should have in order to select from the view?

  1. Select on user B’s view, select on user A’s table and execute on user A’s functions
  2. Select on user B’s view
  3. Select on user A’s table and execute on user A’s functions
  4. Select on user B’s view, provided that user B has “with grant option”
  5. Select on user B’s view given “with hierarchy option”

The correct answer is #4. You can grant a user “select” privilege on a view without giving him any privileges on the underlying tables and functions. This is actually a terrific feature, allowing for clever information hiding tricks. However this means that when user B grants “select” on his view to user C, he is actually giving him permission to view data from user A’s tables and also permission execute user A’s functions, so user B must have the ability to give user C these priviliges. Hence, “with grant option” is needed.

It makes a lot of sense that answer #1 would also work. After all, if user C already has permission on user A’s tables and functions, why not allow him to select from the view?
Unfortunately, this does not work. As I discovered while debugging “ORA-01031: insufficient privileges”. Incidentally, this is one of the very few ways to get “insufficient privileges” from a select statement. Usually Oracle prefers to pretend that the view does not exist, if a user with no privileges attempts to select from it.