I created a role R_CLIENTE:
CREATE ROLE R_CLIENTE IDENTIFIED BY RolCliente;
Then I granted some privileges on it:
GRANT SELECT ON alquiler.CLIENTE TO R_CLIENTE;
(Schema alquiler and table CLIENTE already exist). Then I created an user U_Cliente1:
CREATE USER U_Cliente1 IDENTIFIED BY Cliente1 DEFAULT TABLESPACE table_def TEMPORARY TABLESPACE table_temp QUOTA 2M ON table_def PASSWORD EXPIRE;
(Both tablespaces already exist). I granted U_Cliente1 to R_CLIENTE privileges:
GRANT R_CLIENTE TO U_Cliente1;
When I login as U_Cliente1 I am not able to select any data from the table alquiler.CLIENTE:
SQL> desc alquiler.CLIENTE;
ERROR:
ORA-04043: object alquiler.CLIENTE does not exist
However, if I grant directly the privilege to the user U_Cliente1:
GRANT SELECT ON alquiler.CLIENTE TO U_Cliente1;
Now I am able to select the table alquiler.CLIENTE:
SQL> desc alquiler.CLIENTE;
Name Null? Type
----------------------------------------- -------- ----------------------------
K_CODCLIENTE NOT NULL NUMBER(5)
N_NOMBRE1 NOT NULL VARCHAR2(15)
N_NOMBRE2 VARCHAR2(15)
N_APELLIDO1 NOT NULL VARCHAR2(15)
N_APELLIDO2 VARCHAR2(15)
N_DIRECCION NOT NULL VARCHAR2(50)
Q_TELEFONO NOT NULL NUMBER(10)
K_CODREF NUMBER(5)
I_TIPOID NOT NULL VARCHAR2(2)
Q_IDENTIFICACION NOT NULL VARCHAR2(10)
How can I properly link a role and user(s) so they can all share the same privileges?
The reason that the direct grant worked the way it did is because direct grants are always active. Roles can be activated and deactivated within a session. In your example you didn't configure the role as a default role, so it must be explicitly activated after you login, like this:
set role r_cliente;
alternatively, after granting the role set it as a default for the user:
alter user U_Cliente1 default role r_cliente;
or
alter user U_Cliente1 default role all;
Then the role will be active automatically when you login.
Background
I'm working these days on organizing the Postgres database of a project in my work.
EDIT:
This database is connected to a NodeJS server that runs Postgraphile on it, in order to expose the GraphQL interface for the client. Therefore, I have to use RLS in order to forbid the user to query and manipulate rows that he/she doesn't have permission.
One of the tasks that I've got is to add a deleted field for each table, and using RLS to hide the records that deleted = true.
Code Example
To explain my problem, I'll add an SQL code for building a fake database:
Roles
For this example, I'll use these roles:
Superuser role named admin
Role called app_users
2 Users inherit from app_users:
bob
alice
CREATE ROLE admin WITH
LOGIN
SUPERUSER
INHERIT
CREATEDB
CREATEROLE
NOREPLICATION
ENCRYPTED PASSWORD 'md5f6fdffe48c908deb0f4c3bd36c032e72'; -- password: admin
GRANT username TO admin;
CREATE ROLE app_users WITH
NOLOGIN
NOSUPERUSER
NOINHERIT
NOCREATEDB
CREATEROLE
NOREPLICATION;
CREATE ROLE bob WITH
LOGIN
NOSUPERUSER
INHERIT
NOCREATEDB
NOCREATEROLE
NOREPLICATION
ENCRYPTED PASSWORD 'md5e8557d12f6551b2ddd26bbdd0395465c';
GRANT app_users TO bob;
CREATE ROLE alice WITH
LOGIN
NOSUPERUSER
INHERIT
NOCREATEDB
NOCREATEROLE
NOREPLICATION
ENCRYPTED PASSWORD 'md5579e43b423b454623383471aeb85cd87';
GRANT app_users TO alice;
Database
This example will hold a database named league for a mock database for an American football league.
CREATE DATABASE league
WITH
OWNER = admin
ENCODING = 'UTF8'
LC_COLLATE = 'en_US.utf8'
LC_CTYPE = 'en_US.utf8'
TABLESPACE = pg_default
CONNECTION LIMIT = -1;
GRANT CREATE, CONNECT ON DATABASE league TO admin;
GRANT TEMPORARY ON DATABASE league TO admin WITH GRANT OPTION;
GRANT TEMPORARY, CONNECT ON DATABASE league TO PUBLIC;
Scheme: public
I've added some minor changes in the scheme, so in default, role app_users allow any command, type, execute function, etcetera.
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT ALL ON TABLES TO app_users;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT ALL ON SEQUENCES TO app_users;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT EXECUTE ON FUNCTIONS TO app_users;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT USAGE ON TYPES TO app_users;
Creating Tables
Table: TEAMS
CREATE TABLE IF NOT EXISTS public."TEAMS"
(
id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
deleted boolean NOT NULL DEFAULT false,
name text COLLATE pg_catalog."default" NOT NULL,
owner text COLLATE pg_catalog."default" NOT NULL,
CONSTRAINT "TEAMS_pkey" PRIMARY KEY (id)
)
TABLESPACE pg_default;
ALTER TABLE public."TEAMS"
OWNER to admin;
ALTER TABLE public."TEAMS"
ENABLE ROW LEVEL SECURITY;
GRANT ALL ON TABLE public."TEAMS" TO admin;
GRANT ALL ON TABLE public."TEAMS" TO app_users;
CREATE POLICY teams_deleted
ON public."TEAMS"
AS RESTRICTIVE
FOR SELECT
TO app_users
USING (deleted = false);
CREATE POLICY teams_owner
ON public."TEAMS"
AS PERMISSIVE
FOR ALL
TO app_users
USING (owner = CURRENT_USER);
Table: PLAYERS
CREATE TABLE IF NOT EXISTS public."PLAYERS"
(
id text COLLATE pg_catalog."default" NOT NULL,
deleted boolean NOT NULL DEFAULT false,
first_name text COLLATE pg_catalog."default" NOT NULL,
last_name text COLLATE pg_catalog."default" NOT NULL,
team_id integer NOT NULL,
jersey_number integer NOT NULL,
CONSTRAINT "PLAYERS_pkey" PRIMARY KEY (id),
CONSTRAINT fkey_team_id FOREIGN KEY (team_id)
REFERENCES public."TEAMS" (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT check_player_number CHECK (jersey_number > 0 AND jersey_number < 100)
)
TABLESPACE pg_default;
ALTER TABLE public."PLAYERS"
OWNER to admin;
ALTER TABLE public."PLAYERS"
ENABLE ROW LEVEL SECURITY;
GRANT ALL ON TABLE public."PLAYERS" TO admin;
GRANT ALL ON TABLE public."PLAYERS" TO app_users;
CREATE POLICY players_deleted
ON public."PLAYERS"
AS RESTRICTIVE
FOR SELECT
TO app_users
USING (deleted = false);
CREATE POLICY players_owner
ON public."PLAYERS"
AS PERMISSIVE
FOR ALL
TO app_users
USING ((( SELECT "TEAMS".owner
FROM "TEAMS"
WHERE ("TEAMS".id = "PLAYERS".team_id)) = CURRENT_USER));
Test Case (Edited for better understanding)
Run this code using user bob:
INSERT INTO "TEAMS" (name, owner)
VALUES ('Jerusalem Lions', 'bob')
RETURNING id; -- We'll save this id for the next command
INSERT INTO "PLAYERS" (id, first_name, last_name, jersey_number, role, team_id)
VALUES ('99999', 'Eric', 'Cohen', 29, 'linebacker', 888) -- Replace 888 with the returned id from the previous command
RETURNING *;
-- These commands will work
SELECT * FROM "PLAYERS";
UPDATE "PLAYERS"
SET last_name = 'Levi'
WHERE id = '99999'
RETURNING *;
-- This is the command that won't work. I can't change the deleted.
UPDATE "PLAYERS"
SET deleted = true
WHERE id = '99999'
RETURNING *;
EDIT: Now, it's important to understand that The policies as defined above works when I do any query, as long as:
INSERT INTO doesn't include deleted = true (that's ok).
UPDATE that includes SET deleted = true. (This is the main issue).
I want to:
Allow bob to delete a record using deleted = true on an UPDATE command.
Hide in SELECT commands all records that deleted = true.
What should I do? 🤷♂️
From the documentation:
When a mix of permissive and restrictive policies are present, a record is only accessible if at least one of the permissive policies passes, in addition to all the restrictive policies.
It means that you cannot permit in one policy something restricted in another policy. For this reason, restrictive policies should be used with extreme caution. When there is no policy defined, everything is restricted, so you should focus on permitting what should be allowed.
Simplified example:
create table my_table(
id int primary key,
user_name text,
deleted bool);
alter table my_table enable row level security;
create policy rule_for_select
on my_table
as permissive
for select
to app_users
using (not deleted);
create policy rule_for_all
on my_table
as permissive
for all
to app_users
using (user_name = current_user and not deleted);
insert into my_table(id, user_name, deleted) values
(1, 'alice', false),
(2, 'bob', true),
(3, 'celine', true)
The user bob will see row 1. He would be able to update row 2 if deleted is false.
What I can gather is, that
UPDATE "PLAYERS" SET deleted=true
will not fail for "Bob". But:
UPDATE "PLAYERS" SET deleted=true RETURNING *
will. Postgraphile will usually "return" something when doing mutations. After updating the row - you basically forbid the select statement.
The usual hack (feature) in Postgraphile land is usually creating a custom mutation and define it as security definer.
Maybe there are other ways around this - but this is the hammer->nail approach in Postgraphile, in my experience.
Similar problem can be seen here: Unable to update row of table with Row Level Security where I proposed a workaround by implementing a grace period.
I have a table like:
CREATE TABLE public.pagos
(
condominio character varying(12) NOT NULL,
id_unidad character varying(10) NOT NULL,
fechapago date NOT NULL,
montopago integer NOT NULL
)
A user:
CREATE USER user1 WITH PASSWORD 'user1';
I give permissions to it user:
GRANT SELECT ON pagos TO user1;
How I can Revoke all permissions (in this case only select) of user1 in a specific field of the table (like the montopago field) ?
Thanks !
Permissions in SQL are additive, so you can never make exceptions of the kind "grant select on the whole table, but revoke it from one specific column". What you need to do is to revoke the permission you granted on the whole table and grant column permissions instead:
REVOKE SELECT ON pagos FROM user1;
GRANT SELECT (condominio, id_unidad, fechapago) ON pagos TO user1;
Is it possible to add a column with alter table if the column that i'm adding has a type created by me?
This is the code :
CREATE OR REPLACE TYPE istoric IS table OF number(6,2);
ALTER TABLE studenti ADD (history istoric) NESTED TABLE history STORE AS lista_tab;
I get the error :
ORA-01031: insufficient privileges.
I already tried to grant priviliges, but it doesn't change anything.
Your userid does not have the permission to alter the table. The DBA should GRANT your userid with the required rights via:
grant alter on tablename to youruserid;
Reference: Oracle alter table insufficient privileges
To learn more about the privileges in Oracle: https://docs.oracle.com/database/121/DBSEG/authorization.htm#DBSEG99869
There's nothing special you should do; I tested it on my Oracle 11g XE database. Apparently, all you need are CREATE TABLE and CREATE TYPE privileges. Have a look at this demonstration: first, as a privileged user, I'm creating a brand new user, grant privileges to it, and then do what you're doing in your question. Works just fine.
SQL> create user vlad identified by sima
2 default tablespace users
3 quota unlimited on users;
User created.
SQL> grant create session, create table, create type to vlad;
Grant succeeded.
SQL> connect vlad/sima#xe
Connected.
Session altered.
SQL> create table studenti (id number);
Table created.
SQL> create type istoric is table of number(6, 2);
2 /
Type created.
SQL> alter table studenti add (history istoric) nested table history store as lista_tab;
Table altered.
SQL>
So, who does table STUDENTI belong to? You, or someone else? If the latter, then yes - you might be lacking certain privileges.
This is my init.sql file :
DROP DATABASE IF EXISTS my_data;
CREATE DATABASE my_data;
DROP USER IF EXISTS u;
CREATE USER u WITH PASSWORD 'secret';
GRANT ALL ON ALL TABLES IN SCHEMA public TO u;
GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO u;
GRANT ALL ON ALL FUNCTIONS IN SCHEMA public TO u;
\c my_data;
CREATE TABLE users (
ID SERIAL PRIMARY KEY NOT NULL,
email VARCHAR NOT NULL DEFAULT '',
password VARCHAR NOT NULL DEFAULT '',
active SMALLINT NOT NULL DEFAULT '1',
created TIMESTAMP NOT NULL,
modified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
salt VARCHAR NOT NULL DEFAULT ''
);
Then if I :
psql -f init.sql
And..
psql -d my_data -U u
my_data=> select * from users;
ERROR: permission denied for relation users
Why would this permission be denied if I just granted it?
More info
my_data=> \z users
Access privileges
Schema | Name | Type | Access privileges | Column access privileges
--------+-------+-------+-------------------+--------------------------
public | users | table | |
(1 row)
You only gave permission on the schema, which is separate from the tables. If you want to give permissions on the tables also you can use
GRANT ALL ON ALL TABLES IN SCHEMA public TO u;
Note that if you have sequences or other objects they also need separate permissions.
This has to be set after the tables have been created since permissions are set for existing objects.
If you want to set default permissions for new objects you can use
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO u;