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 comemy_app.username = 'unknown';
. Ogni volta che crei una connessione, eseguiSET my_app.username = 'the_user';
. Quindi nei trigger, usacurrent_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-incurrent_user
funzione di tipo variabile perSELECT current_user;
, ti consente anche di imporre la sicurezza nel database . Vedi questa domanda. Puoi accedere direttamente come utente invece di usareSET 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 dipostgresql.conf
, aggiungi una riga comemyapp.username = 'unknown_user'
. Nelle versioni di PostgreSQL precedenti alla 9.2 devi anche impostarecustom_variable_classes = 'myapp'
. - Riavvia PostgreSQL. Ora sarai in grado di
SHOW myapp.username
e ottieni il valoreunknown_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.