PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Passaggio dell'ID utente ai trigger di PostgreSQL

Le opzioni includono:

  • Quando si apre una connessione, CREATE TEMPORARY TABLE current_app_user(username text); INSERT INTO current_app_user(username) VALUES ('the_user'); . Quindi nel tuo trigger, SELECT username FROM current_app_user per ottenere il nome utente corrente, possibilmente come sottoquery.

  • In postgresql.conf crea una voce per un GUC personalizzato come my_app.username = 'unknown'; . Ogni volta che crei una connessione, esegui SET my_app.username = 'the_user'; . Quindi nei trigger, usa current_setting('my_app.username') funzione per ottenere il valore. In effetti, stai abusando del macchinario GUC per fornire variabili di sessione. Leggi la documentazione appropriata alla versione del tuo server, poiché le GUC personalizzate sono state modificate in 9.2 .

  • Regola la tua applicazione in modo che disponga dei ruoli del database per ogni utente dell'applicazione. SET ROLE a quell'utente prima di eseguire il lavoro. Questo non solo ti permette di usare il built-in current_user funzione di tipo variabile per SELECT current_user; , ti consente anche di imporre la sicurezza nel database . Vedi questa domanda. Puoi accedere direttamente come utente invece di usare SET ROLE , ma ciò tende a rendere difficile il pool di connessioni.

In entrambi i tre casi in cui stai eseguendo il pool di connessioni, devi fare attenzione a DISCARD ALL; quando si restituisce una connessione al pool. (Sebbene non sia documentato come farlo, DISCARD ALL esegue un RESET ROLE ).

Configurazione comune per le demo:

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();

Utilizzo di una GUC:

  • Nel CUSTOMIZED OPTIONS sezione di postgresql.conf , aggiungi una riga come myapp.username = 'unknown_user' . Nelle versioni di PostgreSQL precedenti alla 9.2 devi anche impostare custom_variable_classes = 'myapp' .
  • Riavvia PostgreSQL. Ora sarai in grado di SHOW myapp.username e ottieni il valore unknown_user .

Ora puoi usare SET myapp.username = 'the_user'; quando stabilisci una connessione, o in alternativa SET LOCAL myapp.username = 'the_user'; dopo BEGIN Creare una transazione se si desidera che sia locale alla transazione, il che è conveniente per le connessioni in pool.

Il get_app_user definizione della funzione:

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

Demo usando SET LOCAL per il nome utente corrente locale della transazione:

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)

Se usi SET invece di SET LOCAL l'impostazione non verrà ripristinata al momento del commit/rollback, quindi è persistente per tutta la sessione. Viene comunque ripristinato da 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)

Inoltre, tieni presente che non puoi utilizzare SET o SET LOCAL con parametri di binding lato server. Se desideri utilizzare i parametri di binding ("istruzioni preparate"), considera l'utilizzo del modulo della funzione set_config(...) . Vedi le funzioni di amministrazione del sistema

Utilizzo di una tabella temporanea

Questo approccio richiede l'uso di un trigger (o una funzione di supporto chiamata da un trigger, preferibilmente) che tenta di leggere un valore da una tabella temporanea che ogni sessione dovrebbe avere. Se non è possibile trovare la tabella temporanea, viene fornito un valore predefinito. È probabile che sia piuttosto lento . Testare attentamente.

Il get_app_user() definizione:

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

Variabili di sessione sicure

C'è anche una proposta per aggiungere "variabili di sessione sicure" a PostgreSQL. Queste sono un po' come le variabili del pacchetto. A partire da PostgreSQL 12 la funzionalità non è stata inclusa, ma tieni gli occhi aperti e parla dell'elenco degli hacker se è qualcosa di cui hai bisogno.

Avanzate:il tuo interno con area di memoria condivisa

Per usi avanzati puoi anche avere il tuo interno C che registra un'area di memoria condivisa e comunica tra i backend usando chiamate di funzione C che leggono/scrivono valori in un segmento DSA. Vedere gli esempi di programmazione PostgreSQL per i dettagli. Avrai bisogno di conoscenza, tempo e pazienza in C.