Thread: Re: Problem with delete trigger: how to allow only triggers to delete a row?
Re: Problem with delete trigger: how to allow only triggers to delete a row?
From
aklaver@comcast.net (Adrian Klaver)
Date:
-------------- Original message ---------------------- From: "Christopher Maier" <maier@med.unc.edu> > I have a table where some rows are originally entered, and others are > logically deduced from original rows by triggers. For instance, if > one row contains data indicating that "A < B" and another row > indicates "B < C", the triggers will generate a new row indicating > that "A < C". All deduced rows have a boolean attribute (named > "deduced") that is TRUE only if the row was generated by such a > deduction. A value of FALSE indicates that the row was original data, > entered by a user. > > When original data is modified, the triggers are responsible for > removing any deduced rows that are now invalid and generating new rows > that are now implied. I would like to make it so that the only way > that deduced rows can be deleted is through the actions of these > triggers; I don't want a user inadvertently deleting a deduction when > the underlying premises (the original rows that were used to generate > the deduced rows) still imply that deduction is valid. Users should > only be able to manipulate the original data. > > I can create a trigger that will prevent deletion of deduced rows > easily enough, but I'm not sure how to let rows targeted for deletion > by these deduction triggers through. Is there a way to pass some sort > of state indicator into a trigger? Is this at all possible? > > Thanks in advance, > Chris > > > -- From the manual http://www.postgresql.org/docs/8.2/interactive/plpgsql-trigger.html: Row-level triggers fired BEFORE may return null to signal the trigger manager to skip the rest of the operation for thisrow (i.e., subsequent triggers are not fired, and the INSERT/UPDATE/DELETE does not occur for this row). If a nonnullvalue is returned then the operation proceeds with that row value Could you have the the trigger examine the row to see if it meets the criteria for deletion. If it does RETURN a NON NULLvalue so the trigger completes, otherwise RETURN NULL to prevent the DELETE. -- Adrian Klaver aklaver@comcast.net
Re: Problem with delete trigger: how to allow only triggers to delete a row?
From
"Christopher Maier"
Date:
On Oct 10, 2008, at 1:23 PM, Adrian Klaver wrote: > Could you have the the trigger examine the row to see if it meets > the criteria for deletion. If it does RETURN a NON NULL value so the > trigger completes, otherwise RETURN NULL to prevent the DELETE. Thanks for your reply, Adrian. This is indeed part of the solution. My problem concerns the determination of the criteria for deletion. Each row has a boolean attribute that says whether it is deduced or not and that can be inspected readily enough. However, I need to restrict deletion based on, for lack of a better term, "where" the DELETE command comes from. I do not want someone sitting at a psql console to be able to type: DELETE FROM my_table WHERE deduced IS TRUE; This should fail because users should only be able to delete non- deduced rows. However, when a user deletes a non-deduced row, my triggers issue DELETE commands for all deduced rows that are logically derived from that non-deduced row. Back to the example from my original post, if I have two rows in the table that say, in effect: A < B B < C then the triggers will generate a row that says "A < C". If the user then deletes the "B < C" row, the triggers will delete the "A < C" row, because there is no longer any support for this. I need to figure out how to block DELETEs from the user, while allowing DELETEs that come from the triggers. If I could pass along some kind of flag or parameter with the DELETE commands issued from the triggers, then that might do it, but my understanding of trigger parameters is that they are the same for all invocations on a particular table, whereas I need them to be different for each call of the trigger. I'm currently using plpgsql, but I'm open to other PL languages or architectural restructurings if that's necessary. I'm just stumped as to how to achieve this effect. I hope that clarifies things. Thanks again, Chris
Re: Problem with delete trigger: how to allow only triggers to delete a row?
From
Alvaro Herrera
Date:
Christopher Maier wrote: > However, I need to restrict deletion based on, for lack of a better > term, "where" the DELETE command comes from. I do not want someone > sitting at a psql console to be able to type: > > DELETE FROM my_table WHERE deduced IS TRUE; Looks like you should revoke DELETE privilege from plain users, and have your delete trigger be a security definer function. There would be another security definer function to delete non-deduced rows which users can call directly. -- Alvaro Herrera http://www.CommandPrompt.com/ PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Re: Problem with delete trigger: how to allow only triggers to delete a row?
From
"Christopher Maier"
Date:
On Oct 10, 2008, at 2:05 PM, Alvaro Herrera wrote: > Looks like you should revoke DELETE privilege from plain users, and > have your delete trigger be a security definer function. There > would be > another security definer function to delete non-deduced rows which > users > can call directly. Thanks, Alvaro. So you're suggesting I create a function like this: CREATE FUNCTION user_delete(identifier my_table.id%TYPE) RETURNS VOID LANGUAGE plpgsql SECURITY DEFINER AS $$ BEGIN...-- do various checks...DELETE FROM my_table WHERE id = identifier;... END; $$; Correct? That sounds like it would work. If at all possible, I'd like to keep the "interface" the same for all my tables, though (i.e., users don't have to be concerned with whether they can do regular SQL deletes, or if they have to call a special function). I suppose that can ultimately be hidden, though. I will try this approach and see how it works out. If there is any other way to achieve this goal, however, I would be interested to hear. Thanks again. --Chris
Re: Problem with delete trigger: how to allow only triggers to delete a row?
From
Alvaro Herrera
Date:
Christopher Maier wrote: > Correct? That sounds like it would work. If at all possible, I'd like > to keep the "interface" the same for all my tables, though (i.e., users > don't have to be concerned with whether they can do regular SQL deletes, > or if they have to call a special function). Hmm, maybe you can turn a regular DELETE into a function call by using an INSTEAD rule, but I'm not sure. That way they would just do a plain DELETE and the sec-def function would be called instead. -- Alvaro Herrera http://www.CommandPrompt.com/ PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Alvaro Herrera <alvherre@commandprompt.com> writes: > Looks like you should revoke DELETE privilege from plain users, and > have your delete trigger be a security definer function. There would be > another security definer function to delete non-deduced rows which users > can call directly. That seems overly complicated to use. If the triggers that are privileged to delete deduced rows run as a special user, couldn't the validation triggers look at CURRENT_USER to see whether to allow the delete of a deduced row or not? regards, tom lane
Re: Problem with delete trigger: how to allow only triggers to delete a row?
From
Adrian Klaver
Date:
On Friday 10 October 2008 11:25:05 am Christopher Maier wrote: > On Oct 10, 2008, at 2:05 PM, Alvaro Herrera wrote: > > Looks like you should revoke DELETE privilege from plain users, and > > have your delete trigger be a security definer function. There > > would be > > another security definer function to delete non-deduced rows which > > users > > can call directly. > > Thanks, Alvaro. So you're suggesting I create a function like this: > > CREATE FUNCTION user_delete(identifier my_table.id%TYPE) RETURNS VOID > LANGUAGE plpgsql SECURITY DEFINER AS $$ > BEGIN > ... > -- do various checks > ... > DELETE FROM my_table WHERE id = identifier; > ... > END; > $$; > > Correct? That sounds like it would work. If at all possible, I'd > like to keep the "interface" the same for all my tables, though (i.e., > users don't have to be concerned with whether they can do regular SQL > deletes, or if they have to call a special function). I suppose that > can ultimately be hidden, though. > > I will try this approach and see how it works out. If there is any > other way to achieve this goal, however, I would be interested to hear. > > Thanks again. > > --Chris A possible approach, not fully tested. REVOKE DELETE from normal users as suggested above. GRANT DELETE to privileged_user Semi psuedo-code below. CREATE OR REPLACE FUNCTION check_delete RETURNS TRIGGER AS $Body$ BEGINIF current_user != 'privileged_user' AND old.deduced = 'f' THEN SET LOCAL ROLE 'privileged_user'; --Do your sanitychecks and create DELETE statements RETURN OLD;ELSIF current_user != 'privileged_user' AND old.deduced ='t' THEN RETURN NULL;ELSIF current_user = 'priviliged_user' THEN RETURN OLD END; $Body$ LANGUAGE plpgsql; -- Adrian Klaver aklaver@comcast.net
Re: Problem with delete trigger: how to allow only triggers to delete a row?
From
"Christopher Maier"
Date:
On Oct 10, 2008, at 4:53 PM, Tom Lane wrote: > Alvaro Herrera <alvherre@commandprompt.com> writes: >> Looks like you should revoke DELETE privilege from plain users, and >> have your delete trigger be a security definer function. There >> would be >> another security definer function to delete non-deduced rows which >> users >> can call directly. > > That seems overly complicated to use. > > If the triggers that are privileged to delete deduced rows run as a > special user, couldn't the validation triggers look at CURRENT_USER > to see whether to allow the delete of a deduced row or not? > > regards, tom lane That sounds like the best approach, Tom. I've already implemented Alvaro's suggestion, which works nicely. It should be a simple matter to add in the current_user check. I'll give that a whirl and see how it goes. Thanks for all the great suggestions, everyone. Chris
Re: Problem with delete trigger: how to allow only triggers to delete a row?
From
Adrian Klaver
Date:
On Friday 10 October 2008 1:57:28 pm Adrian Klaver wrote: > On Friday 10 October 2008 11:25:05 am Christopher Maier wrote: > > On Oct 10, 2008, at 2:05 PM, Alvaro Herrera wrote: > > > Looks like you should revoke DELETE privilege from plain users, and > > > have your delete trigger be a security definer function. There > > > would be > > > another security definer function to delete non-deduced rows which > > > users > > > can call directly. > > > > Thanks, Alvaro. So you're suggesting I create a function like this: > > > > CREATE FUNCTION user_delete(identifier my_table.id%TYPE) RETURNS VOID > > LANGUAGE plpgsql SECURITY DEFINER AS $$ > > BEGIN > > ... > > -- do various checks > > ... > > DELETE FROM my_table WHERE id = identifier; > > ... > > END; > > $$; > > > > Correct? That sounds like it would work. If at all possible, I'd > > like to keep the "interface" the same for all my tables, though (i.e., > > users don't have to be concerned with whether they can do regular SQL > > deletes, or if they have to call a special function). I suppose that > > can ultimately be hidden, though. > > > > I will try this approach and see how it works out. If there is any > > other way to achieve this goal, however, I would be interested to hear. > > > > Thanks again. > > > > --Chris > > A possible approach, not fully tested. > REVOKE DELETE from normal users as suggested above. > GRANT DELETE to privileged_user Oops the above is wrong. In testing I used a login role that automatically inherited the privileged role I was using below. Using a different login role showed me the error. > > Semi psuedo-code below. > > CREATE OR REPLACE FUNCTION check_delete RETURNS TRIGGER AS > $Body$ > BEGIN > IF current_user != 'privileged_user' AND old.deduced = 'f' THEN > SET LOCAL ROLE 'privileged_user'; > --Do your sanity checks and create DELETE statements > RETURN OLD; > ELSIF current_user != 'privileged_user' AND old.deduced ='t' THEN > RETURN NULL; > ELSIF current_user = 'priviliged_user' THEN > RETURN OLD > > > END; > > $Body$ > LANGUAGE plpgsql; The above would still work as long as the privileged role(user) was not assigned to normal users and the privileged role had DELETE rights to the table. Also the function would need to be created with the privileges necessary to do the SET ROLE. > -- > Adrian Klaver > aklaver@comcast.net -- Adrian Klaver aklaver@comcast.net
Re: Problem with delete trigger: how to allow only triggers to delete a row?
From
"Christopher Maier"
Date:
On Oct 10, 2008, at 5:16 PM, Christopher Maier wrote: > > On Oct 10, 2008, at 4:53 PM, Tom Lane wrote: > >> Alvaro Herrera <alvherre@commandprompt.com> writes: >>> Looks like you should revoke DELETE privilege from plain users, and >>> have your delete trigger be a security definer function. There >>> would be >>> another security definer function to delete non-deduced rows which >>> users >>> can call directly. >> >> That seems overly complicated to use. >> >> If the triggers that are privileged to delete deduced rows run as a >> special user, couldn't the validation triggers look at CURRENT_USER >> to see whether to allow the delete of a deduced row or not? >> >> regards, tom lane > > That sounds like the best approach, Tom. I've already implemented > Alvaro's suggestion, which works nicely. It should be a simple > matter to add in the current_user check. I'll give that a whirl and > see how it goes. > > Thanks for all the great suggestions, everyone. > > Chris Just for completeness, and for posterity, this solution (checking for CURRENT_USER) works great. I don't need to revoke DELETE privileges from anyone; simply define all my triggers that kick off a DELETE operation as SECURITY DEFINER (created by my privileged user role), and then have a BEFORE DELETE trigger that compares the value of CURRENT_USER to this privileged user's name. Works great, and is very easy to implement. Thanks for the help! --Chris