How to grant permission using FUNCTION in PostgreSQL 9.4.5 - postgresql

I need to grant permissions to database users. i need to use FUNCTION to perform this.
grant all on schema schema1 to user1;
grant all on schema schema1 to user2;
grant all on schema schema1 to user3;
schema names and user names should be picked up from these sql query output:
select nspname from pg_namespace;
schema1
schema2
schema3
select usename from pg_user;
user1
user2
user3
could someone help me how to write a function to achive this ? i am using Postgresql 9.4.5.
Many Thanks,

It could be something like this:
CREATE OR REPLACE FUNCTION grant_all()
RETURNS VOID AS
$$
DECLARE
user TEXT;
schema TEXT;
BEGIN
FOR user IN (SELECT usename FROM pg_user)
LOOP
FOR schema IN (SELECT nspname FROM pg_namespace)
LOOP
EXECUTE format('GRANT ALL ON SCHEMA %s to %s', schema, user);
RAISE NOTICE 'Granted all on % to %', schema, user;
END LOOP;
END LOOP;
END
$$
LANGUAGE plpgsql;

Related

PostgreSQL: trigger function to grant privileges in a loop

I have a table users:
CREATE TABLE users (
id serial primary key,
name text,
status text,
tier text,
project text[]);
Column project is an array of schema names. I'd like to create a trigger function that grants a set of privileges to a user when a user is added to the table. A single user can have access to multiple schemas, so my idea is to loop through an array of schema names.
CREATE OR REPLACE FUNCTION common.add_user()
RETURNS trigger
LANGUAGE 'plpgsql'
COST 100
VOLATILE NOT LEAKPROOF
AS $BODY$
DECLARE
u_name text = NEW.name;
m text;
project text[] = NEW.project;
begin
FOREACH m IN ARRAY project LOOP
EXECUTE 'GRANT USAGE ON SCHEMA ' || m || ' TO ' || u_name;
END LOOP;
RETURN NEW;
end;
$BODY$;
And the trigger:
CREATE TRIGGER add_user
BEFORE INSERT
ON common.users
FOR EACH ROW
EXECUTE PROCEDURE common.add_user();
When I add an entry to common.users table:
INSERT INTO common.users (name, project) VALUES ('user01', '{"schema01", "schema02"}');
It executes without errors, an entry is added to the table but no privileges are granted. Of course I made sure that the insert query is executed by a user that can actually grant those privileges.
Actually, I was pretty close, maybe an issue with execute syntax inside loop? Anyway, this does exactly what I want:
CREATE OR REPLACE FUNCTION common.add_user()
RETURNS trigger
LANGUAGE 'plpgsql'
COST 100
VOLATILE NOT LEAKPROOF
AS $BODY$
DECLARE
u_name text = NEW.name;
m text;
project text[] = NEW.project;
begin
EXECUTE 'GRANT CONNECT ON DATABASE x TO ' ||u_name;
EXECUTE 'GRANT USAGE ON SCHEMA public TO ' ||u_name;
EXECUTE 'GRANT USAGE ON SCHEMA common TO ' ||u_name;
EXECUTE 'GRANT SELECT ON ALL TABLES IN SCHEMA common TO ' ||u_name;
FOREACH m IN ARRAY project LOOP
EXECUTE format('GRANT USAGE ON SCHEMA %1$s to %2$s', m, u_name);
EXECUTE format('GRANT SELECT, UPDATE, DELETE, INSERT ON ALL TABLES IN SCHEMA %1$s to %2$s', m, u_name);
EXECUTE format('GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA %1$s to %2$s', m, u_name);
EXECUTE format('GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA %1$s to %2$s', m, u_name);
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %1$s GRANT SELECT, UPDATE, INSERT, DELETE ON TABLES to %2$s', m, u_name);
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %1$s GRANT EXECUTE ON FUNCTIONS to %2$s', m, u_name);
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %1$s GRANT USAGE, SELECT ON SEQUENCES to %2$s', m, u_name);
END LOOP;
RETURN NEW;
end;
$BODY$;
I also wanted to be able to easily manage access to projects (schemas) by modifying value in column project. Together with RLS on tables managing privileges is now pretty simple.
CREATE OR REPLACE FUNCTION common.update_user()
RETURNS trigger
LANGUAGE 'plpgsql'
COST 100
VOLATILE NOT LEAKPROOF
AS $BODY$
DECLARE
u_name text = OLD.name;
m text;
n text;
deleted text[];
added text[];
begin
deleted := ARRAY(select * from unnest(OLD.project) except select * from unnest(NEW.project));
added := ARRAY(select * from unnest(NEW.project) except select * from unnest(OLD.project));
FOREACH m IN ARRAY deleted LOOP
Raise warning 'REVOKED ACCESS ON PROJECT % FROM %', m, u_name;
EXECUTE format('REVOKE USAGE ON SCHEMA %1$s FROM %2$s', m, u_name);
EXECUTE format('REVOKE ALL ON ALL TABLES IN SCHEMA %1$s FROM %2$s', m, u_name);
EXECUTE format('REVOKE USAGE, SELECT ON ALL SEQUENCES IN SCHEMA %1$s FROM %2$s', m, u_name);
EXECUTE format('REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA %1$s FROM %2$s', m, u_name);
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %1$s REVOKE ALL ON TABLES FROM %2$s', m, u_name);
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %1$s REVOKE EXECUTE ON FUNCTIONS FROM %2$s', m, u_name);
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %1$s REVOKE USAGE, SELECT ON SEQUENCES FROM %2$s', m, u_name);
END LOOP;
FOREACH n IN ARRAY added LOOP
Raise warning 'GRANTED ACCESS ON PROJECT % TO %', n, u_name;
EXECUTE format('GRANT USAGE ON SCHEMA %1$s to %2$s', n, u_name);
EXECUTE format('GRANT SELECT, UPDATE, DELETE, INSERT ON ALL TABLES IN SCHEMA %1$s to %2$s', n, u_name);
EXECUTE format('GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA %1$s to %2$s', n, u_name);
EXECUTE format('GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA %1$s to %2$s', n, u_name);
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %1$s GRANT SELECT, UPDATE, INSERT, DELETE ON TABLES to %2$s', n, u_name);
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %1$s GRANT EXECUTE ON FUNCTIONS to %2$s', n, u_name);
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %1$s GRANT USAGE, SELECT ON SEQUENCES to %2$s', n, u_name);
END LOOP;
RETURN NEW;
end;
$BODY$;

How to call DROP USER from pl/pgsql?

How to call DROP USER from Pl/PgSql (see example) ?
CREATE PROCEDURE myfunc()
LANGUAGE PLPGSQL AS
$$
DECLARE
super_users TEXT[];
ldap_users TEXT[];
u TEXT;
BEGIN
super_users := ARRAY(SELECT usename::TEXT FROM pg_catalog.pg_user WHERE usesuper);
ldap_users := ARRAY(SELECT uid::TEXT FROM ldap_users);
FOREACH u IN ARRAY ldap_users LOOP
IF (u <> 'postgres' AND u <> ALL(super_users)) THEN
DROP USER IF EXISTS u;
END IF;
END LOOP;
END;
$$;
It leads to error that "role u does not exist"...
IMHO PL/PGSQL does not treat u as a variable, but as a name. And DROP USER... is not SQL but some extension. How to do it? Maybe some system function? Or special syntax to substitute u?
EDIT:
My solution (just found):
DECLARE
stm TEXT;
...
BEGIN
...
stm := 'DROP USER IF EXISTS "' || u '"';
EXECUTE stm;
...
It seems to work. Maybe there is other solutions? More canonical?
You need dynamic SQL for this:
execute format('DROP USER IF EXISTS %I', u);
Try this:
DROP OWNED BY user;
DROP ROLE user;
DROP USER user;
It works for me.
P.d: After checking SOF, you can try:
REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM user;
REVOKE ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public FROM user;
REVOKE ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public FROM user;
DROP USER user;
Cheers mate.

How to give grant select on full database to user in DB2 z/OS

I want to grant select rights on all the tables in database to particular user in db2 z/OS or mainframe.
I know with following query, we can grant select on table to user by running with ibmuser.
GRANT SELECT ON <TableName> TO <UserName>
However I have tried the same by using Db2 z/OS stored procedure in ibmuser:
CREATE PROCEDURE GRANT_SELECT_ON_DB(
IN DatabaseName varchar(255),
IN UserName varchar(255))
LANGUAGE SQL
BEGIN
DECLARE v_NAME VARCHAR(64);
DECLARE v_CREATOR VARCHAR(64);
DECLARE v_GrantQuery VARCHAR(320);
DECLARE c1 CURSOR FOR
(SELECT NAME,CREATOR FROM SYSIBM.SYSTABLES tabs WHERE upper(dBname)=upper(DatabaseName));
OPEN c1;
fetch_loop:
LOOP
FETCH c1 INTO v_NAME,v_CREATOR ;
SET v_GrantQuery = 'grant select on '|| v_CREATOR ||'.'|| v_NAME ||' to '|| UserName;
EXECUTE IMMEDIATE v_GrantQuery;
END LOOP fetch_loop;
CLOSE c1;
END%
On calling of stored procedure, it get stuck in loading and not producing any results or error.
call IBMUSER.GRANT_SELECT_ON_DB('<DatabaseName>','<UserName>')%
Also GRANT SELECT ON <DatabaseName> TO <UserName> is not working as it is not supported in DB2 z/OS.
Please suggest. May be I am missing some basics.
You can try below procedure, it worked for me:
CREATE PROCEDURE GRANT_SELECT_ON_DB(IN DatabaseName varchar(255),
IN UserName varchar(255))
LANGUAGE SQL
MODIFIES SQL DATA
BEGIN
DECLARE v_NAME VARCHAR(400);
DECLARE v_CREATOR VARCHAR(400);
DECLARE v_GrantQuery VARCHAR(1000);
FOR v1 AS
c1 CURSOR FOR
SELECT NAME,CREATOR FROM SYSIBM.SYSTABLES tabs WHERE upper(dBname)=upper(DatabaseName)
DO
SET v_NAME = NAME;
SET v_CREATOR = CREATOR;
SET v_GrantQuery = 'grant select on '|| v_CREATOR ||'.'|| v_NAME ||' to '|| UserName;
EXECUTE IMMEDIATE v_GrantQuery;
END FOR;
END%
call IBMUSER.GRANT_SELECT_ON_DB('<DatabaseName>','<UserName>')%

PGsql : Grant every single right to a user on a schema

I can't figure out how to give every single right to a specific user, I want a user to have every single right on a schema:
inserts, deletes, updates, selects, ... on existing tables
I have tried doing :
GRANT ALL PRIVILEGES ON SCHEMA schema to "user";
GRANT ALL ON SCHEMA schema to "local_518561";
GRANT ALL PRIVILEGES ON table schema.table to "user";
GRANT ALL ON table schema.table to "user";
The querys return succesfull, but every time I use the other user I get insuffiecent permissions error.
GRANT ALL PRIVILEGES ON SCHEMA schema_name TO role_name;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA schema_name TO role_name;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA schema_name TO role_name;
If you are using a version of PostgreSQL < 9, you can use the following store procedures to manage permissions of tables and sequences:
CREATE OR REPLACE FUNCTION grantTablesOfSchema (user VARCHAR,
permissions VARCHAR, schema VARCHAR) RETURNS VARCHAR AS
$body$
DECLARE
regActual RECORD;
numTables INTEGER;
BEGIN
numTables := 0;
FOR regActual IN
SELECT tablename FROM pg_tables WHERE schemaname = schema
LOOP
numTables := numTables + 1;
EXECUTE 'GRANT ' || permissions || ' ON ' || schema || '.' || regActual.tablename || ' TO ' || user;
END LOOP;
RETURN 'Tables: ' || numTables::VARCHAR;
END;
$body$
LANGUAGE 'plpgsql';
CREATE OR REPLACE FUNCTION grantSequencesOfSchema (user VARCHAR,
permissions VARCHAR, database VARCHAR, schema VARCHAR) RETURNS VARCHAR AS
$body$
DECLARE
regActual RECORD;
numSequences INTEGER;
BEGIN
numSequences := 0;
FOR regActual IN
SELECT sequence_catalog, sequence_schema, sequence_name
FROM information_schema.sequences
WHERE sequence_catalog = database AND sequence_schema = schema
LOOP
numSequences := numSequences + 1;
EXECUTE 'GRANT ' || permissions || ' ON ' || schema || '.' || regActual.sequence_name || ' TO ' || user;
END LOOP;
RETURN 'Sequences: ' || numSequences::VARCHAR;
END;
$body$
LANGUAGE 'plpgsql';
And a example of use:
CREATE USER user1 WITH PASSWORD 'user1#user1?user1';
GRANT CONNECT ON DATABASE database1 TO user1;
GRANT USAGE ON SCHEMA schema1 TO user1;
SELECT * FROM grantTablesOfSchema ('user1', 'SELECT, UPDATE, INSERT, DELETE', 'schema1');
SELECT * FROM grantSequencesOfSchema ('user1', 'ALL', 'database1', 'schema1');
If, on the contrary your version of PostgreSQL is >= 9:
GRANT ALL ON ALL SEQUENCES IN SCHEMA schema1 TO user1;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA schema1 TO user1;
The answer lies in the sequences, if you do not give rights to the table AND the sequence (if any) than you cannot insert.

postgresql stored procedure with query on several schema

After some answer on a previous question (request over several schema), I try to write a stored procedure to select tables for several schemas (Each user have a schema).
create or replace public.select_simulations() returns setof simulation as $$
declare
users pg_user%ROWTYPE;
simu simulation%ROWTYPE;
begin
for users in select usename from pg_user where usename <> 'postgres' loop
for simu in select id, name from (users.usename).simulation loop
return next simu;
end loop;
end loop;
end;
$$
but it doesn't accept the (users.usename).simulation, and without the parenthesis it produced an error (seems to search a sub field, not a schema)...
So what is the correct syntax to tell that users.usename is a schema name ?
Thank you for your help!
You could take a look at the for-in-execute control structure:
FOR record_or_row IN EXECUTE text_expression LOOP
statements
END LOOP [ label ];
http://www.postgresql.org/docs/8.1/static/plpgsql-control-structures.html#PLPGSQL-RECORDS-ITERATING
Something like:
...
for users in select usename from pg_user where usename <> 'postgres' loop
for simu in execute 'select id, name from '||quote_ident(users.usename)||'.simulation' loop
return next simu;
end loop;
end loop;
...