sql >> Database >  >> RDS >> PostgreSQL

Gebruikers-ID doorgeven aan PostgreSQL-triggers

Opties zijn onder meer:

  • Wanneer u een verbinding opent, CREATE TEMPORARY TABLE current_app_user(username text); INSERT INTO current_app_user(username) VALUES ('the_user'); . Dan in je trigger, SELECT username FROM current_app_user om de huidige gebruikersnaam te krijgen, mogelijk als een subquery.

  • In postgresql.conf maak een vermelding voor een aangepaste GUC zoals my_app.username = 'unknown'; . Telkens wanneer u een verbinding maakt, voert u SET my_app.username = 'the_user'; . uit . Gebruik vervolgens in triggers de current_setting('my_app.username') functie om de waarde te verkrijgen. In feite misbruik je de GUC-machinerie om sessievariabelen te leveren. Lees de documentatie die van toepassing is op uw serverversie, aangezien aangepaste GUC's zijn gewijzigd in 9.2 .

  • Pas uw applicatie zo aan dat deze databaserollen heeft voor elke applicatiegebruiker. SET ROLE aan die gebruiker voordat u aan het werk gaat. Hiermee kunt u niet alleen de ingebouwde current_user variabele-achtige functie voor SELECT current_user; , kunt u ook beveiliging in de database afdwingen . Zie deze vraag. U kunt direct inloggen als gebruiker in plaats van SET ROLE . te gebruiken , maar dat maakt het poolen van verbindingen vaak moeilijk.

In alle drie de gevallen ben je aan het poolen, je moet voorzichtig zijn om DISCARD ALL; wanneer u weer verbinding maakt met het zwembad. (Hoewel dit niet is gedocumenteerd, DISCARD ALL doet een RESET ROLE ).

Algemene instellingen voor demo's:

CREATE TABLE tg_demo(blah text);
INSERT INTO tg_demo(blah) VALUES ('spam'),('eggs');

-- Placeholder; will be replaced by demo functions
CREATE OR REPLACE FUNCTION get_app_user() RETURNS text AS $$
SELECT 'unknown';
$$ LANGUAGE sql;

CREATE OR REPLACE FUNCTION tg_demo_trigger() RETURNS trigger AS $$
BEGIN
    RAISE NOTICE 'Current user is: %',get_app_user();
    RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER tg_demo_tg
AFTER INSERT OR UPDATE OR DELETE ON tg_demo 
FOR EACH ROW EXECUTE PROCEDURE tg_demo_trigger();

Een GUC gebruiken:

  • In de CUSTOMIZED OPTIONS sectie van postgresql.conf , voeg een regel toe zoals myapp.username = 'unknown_user' . Op PostgreSQL-versies ouder dan 9.2 moet u ook custom_variable_classes = 'myapp' instellen .
  • Start PostgreSQL opnieuw. U kunt nu SHOW myapp.username en krijg de waarde unknown_user .

Nu kunt u SET myapp.username = 'the_user'; . gebruiken wanneer u een verbinding tot stand brengt, of afwisselend SET LOCAL myapp.username = 'the_user'; na BEGIN een transactie uitvoeren als u wilt dat deze transactie-lokaal is, wat handig is voor gepoolde verbindingen.

De get_app_user functiedefinitie:

CREATE OR REPLACE FUNCTION get_app_user() RETURNS text AS $$
    SELECT current_setting('myapp.username');
$$ LANGUAGE sql;

Demo met SET LOCAL voor transactie-lokale huidige gebruikersnaam:

regress=> BEGIN;
BEGIN
regress=> SET LOCAL myapp.username = 'test_user';
SET
regress=> INSERT INTO tg_demo(blah) VALUES ('42');
NOTICE:  Current user is: test_user
INSERT 0 1
regress=> COMMIT;
COMMIT
regress=> SHOW myapp.username;
 myapp.username 
----------------
 unknown_user
(1 row)

Als u SET . gebruikt in plaats van SET LOCAL de instelling wordt niet teruggedraaid tijdens de commit/rollback-tijd, dus het is persistent gedurende de hele sessie. Het wordt nog steeds gereset door DISCARD ALL :

regress=> SET myapp.username = 'test';
SET
regress=> SHOW myapp.username;
 myapp.username 
----------------
 test
(1 row)

regress=> DISCARD ALL;
DISCARD ALL
regress=> SHOW myapp.username;
 myapp.username 
----------------
 unknown_user
(1 row)

Houd er ook rekening mee dat u SET . niet kunt gebruiken of SET LOCAL met bindingsparameters aan de serverzijde. Als u bindparameters ("voorbereide instructies") wilt gebruiken, kunt u overwegen het functieformulier set_config(...) te gebruiken . Zie systeembeheerfuncties

Een tijdelijke tabel gebruiken

Deze benadering vereist het gebruik van een trigger (of hulpfunctie die bij voorkeur wordt aangeroepen door een trigger) die probeert een waarde te lezen uit een tijdelijke tabel die elke sessie zou moeten hebben. Als de tijdelijke tabel niet kan worden gevonden, wordt een standaardwaarde opgegeven. Dit is waarschijnlijk enigszins traag . Test zorgvuldig.

De get_app_user() definitie:

CREATE OR REPLACE FUNCTION get_app_user() RETURNS text AS $$
DECLARE
    cur_user text;
BEGIN
    BEGIN
        cur_user := (SELECT username FROM current_app_user);
    EXCEPTION WHEN undefined_table THEN
        cur_user := 'unknown_user';
    END;
    RETURN cur_user;
END;
$$ LANGUAGE plpgsql VOLATILE;

Demo:

regress=> CREATE TEMPORARY TABLE current_app_user(username text);
CREATE TABLE
regress=> INSERT INTO current_app_user(username) VALUES ('testuser');
INSERT 0 1
regress=> INSERT INTO tg_demo(blah) VALUES ('42');
NOTICE:  Current user is: testuser
INSERT 0 1
regress=> DISCARD ALL;
DISCARD ALL
regress=> INSERT INTO tg_demo(blah) VALUES ('42');
NOTICE:  Current user is: unknown_user
INSERT 0 1

Veilige sessievariabelen

Er is ook een voorstel om "veilige sessievariabelen" toe te voegen aan PostgreSQL. Dit zijn een beetje zoals pakketvariabelen. Vanaf PostgreSQL 12 is de functie niet inbegrepen, maar houd de lijst met hackers in de gaten als dit iets is dat u nodig heeft.

Geavanceerd:uw eigen extensie met gedeelde geheugenruimte

Voor geavanceerd gebruik kunt u zelfs uw eigen C-extensie een gedeeld geheugengebied laten registreren en communiceren tussen backends met behulp van C-functieaanroepen die waarden in een DSA-segment lezen/schrijven. Zie de PostgreSQL-programmeervoorbeelden voor details. Je hebt kennis, tijd en geduld van C nodig.



  1. Het Halloween-probleem - Deel 3

  2. Hoe FROM_UNIXTIME() werkt in MariaDB

  3. Waarom krijg ik deze SQLSyntaxErrorException:ORA-00933:SQL-opdracht is niet correct beëindigd wanneer ik deze JDBC-query probeer uit te voeren?

  4. Kunnen er meerdere primaire sleutels op een enkele tafel bestaan?