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 (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.
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>')%
I'm currently not able to create a postgresql database user within a function.
Background:
I have a Java Swing application and my goal is to develop a menu to create, alter and delete database users. To make it a bit more secure I created a role "usermanagement" and only members of this role are allowed to use the function to create users. The role also contains the right "createuser"
The query runs without any problems, but it does not create a new user... So i don't know what's wrong with it.
This is how i try to use my function:
SELECT create_databaseuser(v_username := 'thisname' ,v_password := 'pwpwpw');
Can anyone help?
Here is my code:
-- Function: public.create_databaseuser(text, text)
-- DROP FUNCTION public.create_databaseuser(text, text);
CREATE OR REPLACE FUNCTION public.create_databaseuser(
v_username text,
v_password text)
RETURNS numeric AS
$BODY$
DECLARE
r_id numeric;
BEGIN
--CREATE ROLE v_username LOGIN
--PASSWORD 'v_password' NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE NOREPLICATION;
EXECUTE 'CREATE USER ' || v_username || ' WITH PASSWORD ' || v_password;
-- Alternative:CREATE ROLE v_username LOGIN PASSWORD v_password NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE NOREPLICATION;
return 1;
-- Simple Exception
EXCEPTION
WHEN others THEN
RETURN 0;
END;
$BODY$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER
COST 100;
ALTER FUNCTION public.create_databaseuser(text, text)
OWNER TO postgres;
Note the STRICT (returns NULL on NULL input), uses FORMAT() to help against SQL injection issues, and quotes the inputs properly. The input argument "v_username" was revised to be of type NAME, to match the type in pg_catalog.pg_roles.
DROP FUNCTION IF EXISTS public.create_databaseuser(NAME, TEXT);
CREATE OR REPLACE FUNCTION public.create_databaseuser(
v_username NAME,
v_password TEXT)
RETURNS smallint AS
$BODY$
DECLARE
BEGIN
EXECUTE FORMAT('CREATE ROLE "%I" LOGIN PASSWORD ''%L''', v_username, v_password);
RETURN 1;
-- Simple Exception
EXCEPTION
WHEN others THEN
RETURN 0;
END;
$BODY$
LANGUAGE plpgsql STRICT VOLATILE SECURITY DEFINER
COST 100;
ALTER FUNCTION public.create_databaseuser(NAME, TEXT) OWNER TO postgres;
select rolname from pg_catalog.pg_roles order by 1;
SELECT create_databaseuser(v_username := 'thisname' ,v_password := 'pwpwpw');
select rolname from pg_catalog.pg_roles order by 1;
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;