PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Passer l'ID utilisateur aux déclencheurs PostgreSQL

Les options incluent :

  • Lorsque vous ouvrez une connexion, CREATE TEMPORARY TABLE current_app_user(username text); INSERT INTO current_app_user(username) VALUES ('the_user'); . Ensuite, dans votre déclencheur, SELECT username FROM current_app_user pour obtenir le nom d'utilisateur actuel, éventuellement sous forme de sous-requête.

  • Dans postgresql.conf créez une entrée pour un GUC personnalisé comme my_app.username = 'unknown'; . Chaque fois que vous créez une connexion, exécutez SET my_app.username = 'the_user'; . Ensuite, dans les déclencheurs, utilisez le current_setting('my_app.username') fonction pour obtenir la valeur. En effet, vous abusez de la machinerie GUC pour fournir des variables de session. Lisez la documentation appropriée à la version de votre serveur, car les GUC personnalisés ont changé dans la version 9.2 .

  • Ajustez votre application afin qu'elle ait des rôles de base de données pour chaque utilisateur de l'application. SET ROLE à cet utilisateur avant de travailler. Cela vous permet non seulement d'utiliser le current_user intégré fonction de type variable pour SELECT current_user; , cela vous permet également d'appliquer la sécurité dans la base de données . Voir cette question. Vous pouvez vous connecter directement en tant qu'utilisateur au lieu d'utiliser SET ROLE , mais cela a tendance à compliquer la mise en commun des connexions.

Dans les trois cas, vous regroupez les connexions, vous devez faire attention à DISCARD ALL; lorsque vous rétablissez une connexion au pool. (Bien que cela ne soit pas documenté comme cela, DISCARD ALL fait un RESET ROLE ).

Configuration commune pour les démos :

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

Utilisation d'un GUC :

  • Dans les CUSTOMIZED OPTIONS section de postgresql.conf , ajoutez une ligne comme myapp.username = 'unknown_user' . Sur les versions de PostgreSQL antérieures à 9.2, vous devez également définir custom_variable_classes = 'myapp' .
  • Redémarrez PostgreSQL. Vous pourrez désormais SHOW myapp.username et obtenez la valeur unknown_user .

Vous pouvez maintenant utiliser SET myapp.username = 'the_user'; lorsque vous établissez une connexion, ou alternativement SET LOCAL myapp.username = 'the_user'; après BEGIN définir une transaction si vous voulez qu'elle soit transaction-locale, ce qui est pratique pour les connexions groupées.

Le get_app_user définition de la fonction :

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

Démo utilisant SET LOCAL pour le nom d'utilisateur actuel de la transaction locale :

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)

Si vous utilisez SET au lieu de SET LOCAL le paramètre ne sera pas annulé au moment de la validation/annulation, il est donc persistant tout au long de la session. Il est toujours réinitialisé par 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)

Notez également que vous ne pouvez pas utiliser SET ou SET LOCAL avec les paramètres de liaison côté serveur. Si vous souhaitez utiliser des paramètres de liaison ("instructions préparées"), pensez à utiliser la forme de fonction set_config(...) . Voir les fonctions d'administration système

Utiliser une table temporaire

Cette approche nécessite l'utilisation d'un déclencheur (ou d'une fonction d'assistance appelée par un déclencheur, de préférence) qui essaie de lire une valeur dans une table temporaire que chaque session devrait avoir. Si la table temporaire est introuvable, une valeur par défaut est fournie. Cela risque d'être quelque peu lent . Testez attentivement.

Le get_app_user() définition :

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;

Démo :

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

Variables de session sécurisées

Il y a aussi une proposition d'ajouter des "variables de session sécurisées" à PostgreSQL. Ce sont un peu comme des variables de package. Depuis PostgreSQL 12, la fonctionnalité n'a pas été incluse, mais gardez un œil sur la liste des hackers si vous en avez besoin.

Avancé :votre propre extension avec zone de mémoire partagée

Pour des utilisations avancées, vous pouvez même faire en sorte que votre propre extension C enregistre une zone de mémoire partagée et communique entre les backends à l'aide d'appels de fonction C qui lisent/écrivent des valeurs dans un segment DSA. Voir les exemples de programmation PostgreSQL pour plus de détails. Vous aurez besoin de connaissances en C, de temps et de patience.