PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Übergeben der Benutzer-ID an PostgreSQL-Trigger

Zu den Optionen gehören:

  • Wenn Sie eine Verbindung öffnen, CREATE TEMPORARY TABLE current_app_user(username text); INSERT INTO current_app_user(username) VALUES ('the_user'); . Dann in Ihrem Auslöser SELECT username FROM current_app_user um den aktuellen Benutzernamen zu erhalten, möglicherweise als Unterabfrage.

  • In postgresql.conf Erstellen Sie einen Eintrag für einen benutzerdefinierten GUC wie my_app.username = 'unknown'; . Immer wenn Sie eine Verbindung erstellen, führen Sie SET my_app.username = 'the_user'; aus . Verwenden Sie dann in Triggern die current_setting('my_app.username') Funktion, um den Wert zu erhalten. Tatsächlich missbrauchen Sie die GUC-Maschinerie, um Sitzungsvariablen bereitzustellen. Lesen Sie die Ihrer Serverversion entsprechende Dokumentation, da sich benutzerdefinierte GUCs in 9.2 geändert haben .

  • Passen Sie Ihre Anwendung so an, dass sie Datenbankrollen für jeden Anwendungsbenutzer hat. SET ROLE an diesen Benutzer, bevor Sie mit der Arbeit beginnen. Dadurch können Sie nicht nur den eingebauten current_user verwenden variablenähnliche Funktion zu SELECT current_user; , ermöglicht es Ihnen auch, Sicherheit in der Datenbank zu erzwingen . Siehe diese Frage. Sie könnten sich direkt als Benutzer anmelden, anstatt SET ROLE zu verwenden , aber das erschwert das Verbindungspooling.

In allen drei Fällen, in denen Sie Verbindungen zusammenfassen, müssen Sie darauf achten, DISCARD ALL; wenn Sie eine Verbindung zum Pool zurückgeben. (Obwohl dies nicht dokumentiert ist, DISCARD ALL führt eine RESET ROLE aus ).

Allgemeines Setup für Demos:

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

Verwenden eines GUC:

  • In den CUSTOMIZED OPTIONS Abschnitt von postgresql.conf , fügen Sie eine Zeile wie myapp.username = 'unknown_user' hinzu . Bei PostgreSQL-Versionen älter als 9.2 müssen Sie außerdem custom_variable_classes = 'myapp' setzen .
  • Starten Sie PostgreSQL neu. Sie können jetzt SHOW myapp.username und erhalten Sie den Wert unknown_user .

Jetzt können Sie SET myapp.username = 'the_user'; verwenden wenn Sie eine Verbindung herstellen, oder alternativ SET LOCAL myapp.username = 'the_user'; nach BEGIN ning einer Transaktion, wenn Sie möchten, dass sie transaktionslokal ist, was praktisch für gepoolte Verbindungen ist.

Der get_app_user Funktionsdefinition:

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

Demo mit SET LOCAL für transaktionslokalen aktuellen Benutzernamen:

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)

Wenn Sie SET verwenden statt SET LOCAL Die Einstellung wird beim Commit/Rollback nicht zurückgesetzt, sodass sie über die gesamte Sitzung hinweg bestehen bleibt. Es wird immer noch durch DISCARD ALL zurückgesetzt :

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)

Beachten Sie auch, dass Sie SET nicht verwenden können oder SET LOCAL mit serverseitigen Bindungsparametern. Wenn Sie Bindungsparameter ("vorbereitete Anweisungen") verwenden möchten, ziehen Sie die Verwendung der Funktionsform set_config(...) in Betracht . Siehe Systemverwaltungsfunktionen

Eine temporäre Tabelle verwenden

Dieser Ansatz erfordert die Verwendung eines Triggers (oder vorzugsweise einer Hilfsfunktion, die von einem Trigger aufgerufen wird), der versucht, einen Wert aus einer temporären Tabelle zu lesen, den jede Sitzung haben sollte. Wenn die temporäre Tabelle nicht gefunden werden kann, wird ein Standardwert bereitgestellt. Dies ist wahrscheinlich etwas langsam . Testen Sie sorgfältig.

Der get_app_user() Definition:

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

Sichere Sitzungsvariablen

Es gibt auch einen Vorschlag, PostgreSQL "sichere Sitzungsvariablen" hinzuzufügen. Diese sind ein bisschen wie Paketvariablen. Ab PostgreSQL 12 ist diese Funktion nicht mehr enthalten, aber halten Sie Ausschau und melden Sie sich auf der Hackerliste, wenn Sie dies benötigen.

Erweitert:Ihre eigene Erweiterung mit gemeinsamem Speicherbereich

Für fortgeschrittene Anwendungen können Sie sogar Ihre eigene C-Erweiterung einen gemeinsam genutzten Speicherbereich registrieren lassen und mithilfe von C-Funktionsaufrufen, die Werte in einem DSA-Segment lesen/schreiben, zwischen Backends kommunizieren. Einzelheiten finden Sie in den PostgreSQL-Programmierbeispielen. Sie benötigen C-Kenntnisse, Zeit und Geduld.