Prevent updates to generated primary key column - postgresql

In PostgreSQL I have created a table and with an id column defined as serial. I have inserted two rows, but I can still update the value of the id column.
But I need prevent updates to the generated value of the id column.
create table aas.apa_testtable
(
id serial primary key,
name text
)
insert into aas.apa_testtable(name) select ('test')
insert into aas.apa_testtable(name) select ('test2')
-- I want this to be impossible / result in an error:
update aas.apa_testtable set id=3 where id=2

You can revoke update on table and grant it on column(s):
REVOKE UPDATE ON TABLE aas.apa_testtable FROM some_role;
GRANT UPDATE (name) ON TABLE aas.apa_testtable TO some_role;
Remember about role public, superusers and other inheritance issues you might have in your setup.
--Do not try this, it will not work without revoking table level privileges:
REVOKE UPDATE (id) ON TABLE aas.apa_testtable FROM some_role;
Alternative is to create trigger that will check if old != new, but with details provided I don't see need for it.

Related

Is there any way in sql to resolve this scenario?

I have a table in database like this
CREATE TABLE assignments
(
id uuid,
owner_id uuid NOT NULL,
);
Now I want to check in records , If IDs I am getting from request already exist or Not. If exist I will update owner_id and If In request I Not getting a ID which already exist in table I have to delete that record.
(In short it's update mechanism In which I am getting multiple Id's to update in table and If there is already a record in database and In request aswell I will update in database , and If there is record in table but not in request I will delete that from database)
This can be done with a single Insert statement with the ON Conflict clause. Your first task will be creating a PK (or UNIQUE) constraint on the table. Presumably that would be id.
alter table assignments
add constraint primary key (id);
Then insert your data, the 'on constraint' clause will update the owner_id for any existing id.
insert assignments(id, owner_id)
values ( <id>,<owner>)
on conflict (id)
do update
set owner_id = excluded.owner_id;

Postgres row level policy issue

I am trying out the Postgres row level security feature and not being able to see it working. Not sure what I am missing.
CREATE TABLE tenants (id uuid PRIMARY KEY, name TEXT);
INSERT INTO tenants (id, name) values ('ec5e9a6b-ed71-4e41-bc1e-11dac6808e41', 'Tenant1'), ('a684edc2-19b2-40d6-b679-519a6f736981', 'Tenant2');
ALTER TABLE tenants ENABLE ROW LEVEL SECURITY ;
ALTER TABLE tenants FORCE ROW LEVEL SECURITY;
SET app.tenant_id = 'ec5e9a6b-ed71-4e41-bc1e-11dac6808e41';
CREATE POLICY tenants_policy ON tenants FOR ALL USING ( current_setting('app.tenant_id')::uuid = id );
SELECT * FROM tenants;
For the last select, I expected it to return only one row with id 'ec5e9a6b-ed71-4e41-bc1e-11dac6808e41' but it is returning both rows. What am I missing? Thank you!
Your example works for me. There are a few possibilities:
The current user is a superuser.
The current user is defined with BYPASSRLS.
The configuration parameter row_security is off.

Postgresql Row Level Security Checking new data in WITH CHECK in Policy for INSERT

I've a Database with several tables.
A user has several objects and an object has several parts.
I want to write a policy that only the creator of the object is allowed to add parts to the object. Therefore I need to get the object a to be inserted part belongs to, but I've no idea how to check the data.
Is there a way to get the data to be inserted in the policy?
Thanks for your effort.
Here is an example how to implement something like that with row level security. Adapt it to your need!
CREATE TABLE object(
id integer PRIMARY KEY,
name text NOT NULL,
owner name NOT NULL DEFAULT current_user
);
CREATE TABLE part(
id integer PRIMARY KEY,
parent_id integer NOT NULL REFERENCES object(id),
name text NOT NULL
);
We have to give people some permissions:
GRANT SELECT, INSERT ON object TO PUBLIC;
GRANT SELECT, INSERT ON part TO PUBLIC;
Now we enable row level security and allow only INSERTs in part when the owner in object matches:
ALTER TABLE part ENABLE ROW LEVEL SECURITY;
CREATE POLICY insert_only_owned ON part
FOR INSERT TO PUBLIC
WITH CHECK (EXISTS(
SELECT 1
FROM object o
WHERE o.id = parent_id
AND owner = current_user
));

Primary Key for Users per Module? PostgreSQL

I'm making a DB in PostgreSQL and I need a little help.
The user control work with LDAP, and I have a table called modules where I put all the information about the system modules,
Then I created a table called user_module where I put the username and the integer that references a module (in modules table), in this table, you can add/drop rows and I guess I don't need a primary Key for that or isn't it?
I'm using PgAdmin III and it said "I only can View data in this table, I need create a Primary for editing"
Table Code
CREATE TABLE public.adm_mod_usu
(
cusuario text NOT NULL,
cmodulo_det integer NOT NULL,
CONSTRAINT fk_adm_mod_usu_cmodulo_det FOREIGN KEY (cmodulo_det)
REFERENCES public.adm_mod_det (cmodulo_det) MATCH SIMPLE
ON UPDATE RESTRICT ON DELETE RESTRICT,
CONSTRAINT fk_adm_mod_usu_unique_cpermiso UNIQUE (cusuario, cpermiso)
USING INDEX TABLESPACE sistema_index
)
WITH (
OIDS=FALSE
);
ALTER TABLE public.adm_mod_usu
OWNER TO postgres;
GRANT ALL ON TABLE public.adm_mod_usu TO public;
GRANT ALL ON TABLE public.adm_mod_usu TO postgres;
By the help from a_horse_with_no_name:
ALTER TABLE public.adm_mod_usu DROP CONSTRAINT fk_adm_mod_usu_unique_cpermiso;
ALTER TABLE public.adm_mod_usu
ADD CONSTRAINT fk_adm_mod_usu PRIMARY KEY (cusuario, cpermiso)
USING INDEX TABLESPACE sistema_index;
I change the unique constraint to primary.

Prevent update of column except by trigger

Is it possible to configure a Postgres database such that a specific column may only be updated by a trigger, while still allowing the trigger itself to be executed in response to an update by a role without permission to update that column? If so, how?
For example, given tables and a trigger like this:
CREATE TABLE a(
id serial PRIMARY KEY,
flag boolean NOT NULL DEFAULT TRUE,
data text NOT NULL
);
CREATE TABLE b(
id serial PRIMARY KEY,
updated_on DATE NOT NULL DEFAULT CURRENT_DATE,
a_id INTEGER NOT NULL,
FOREIGN KEY (a_id) references a(id)
);
CREATE FUNCTION update_aflag() RETURNS trigger AS $update_aflag$
BEGIN
UPDATE a
SET flag = FALSE
WHERE id = NEW.a_id;
RETURN NEW;
END;
$update_aflag$ LANGUAGE plpgsql;
CREATE TRIGGER update_aflag_trigger
AFTER INSERT ON b
FOR EACH ROW
EXECUTE PROCEDURE update_aflag()
;
I'd like to define a role which does not have permission to update a.flag directly using an UPDATE statement, but which may update flag indirectly via the trigger.
Yes, this is possible using a SECURITY DEFINER trigger function. The trigger function runs as a role that has the right to modify the flag column, but you don't GRANT that right to normal users. You should create the trigger function as the role that you'll grant the required rights to.
This requires that the application not run as the user that owns the tables, and of course not as a superuser.
You can GRANT column update rights to other columns to the user, just leave out the flag column. Note that GRANTing UPDATE on all columns then REVOKEing it on flag will not work, it's not the same thing.