sql >> Base de Datos >  >> RDS >> PostgreSQL

Pasar la identificación de usuario a los disparadores de PostgreSQL

Las opciones incluyen:

  • Cuando abre una conexión, CREATE TEMPORARY TABLE current_app_user(username text); INSERT INTO current_app_user(username) VALUES ('the_user'); . Luego, en su activador, SELECT username FROM current_app_user para obtener el nombre de usuario actual, posiblemente como una subconsulta.

  • En postgresql.conf cree una entrada para un GUC personalizado como my_app.username = 'unknown'; . Siempre que cree una conexión, ejecute SET my_app.username = 'the_user'; . Luego, en los activadores, use current_setting('my_app.username') función para obtener el valor. Efectivamente, está abusando de la maquinaria GUC para proporcionar variables de sesión. Lea la documentación correspondiente a la versión de su servidor, ya que los GUC personalizados cambiaron en 9.2 .

  • Ajuste su aplicación para que tenga roles de base de datos para cada usuario de la aplicación. SET ROLE a ese usuario antes de hacer el trabajo. Esto no solo le permite usar el current_user incorporado función de tipo variable para SELECT current_user; , también le permite hacer cumplir la seguridad en la base de datos . Ver esta pregunta. Puede iniciar sesión directamente como usuario en lugar de usar SET ROLE , pero eso tiende a dificultar la agrupación de conexiones.

En los tres casos en los que está agrupando conexiones, debe tener cuidado de DISCARD ALL; cuando devuelve una conexión al grupo. (Aunque no está documentado que lo haga, DISCARD ALL hace un RESET ROLE ).

Configuración común para demostraciones:

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

Usando un GUC:

  • En las CUSTOMIZED OPTIONS sección de postgresql.conf , agregue una línea como myapp.username = 'unknown_user' . En las versiones de PostgreSQL anteriores a la 9.2, también debe configurar custom_variable_classes = 'myapp' .
  • Reinicie PostgreSQL. Ahora podrá SHOW myapp.username y obtenga el valor unknown_user .

Ahora puede usar SET myapp.username = 'the_user'; cuando establece una conexión, o alternativamente SET LOCAL myapp.username = 'the_user'; después de BEGIN programar una transacción si desea que sea local de transacción, lo cual es conveniente para conexiones agrupadas.

El get_app_user definición de función:

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

Demostración usando SET LOCAL para el nombre de usuario actual de transacciones locales:

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 usa SET en lugar de SET LOCAL la configuración no se revertirá en el momento de la confirmación/reversión, por lo que es persistente durante toda la sesión. Todavía se restablece con 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)

Además, tenga en cuenta que no puede usar SET o SET LOCAL con parámetros de enlace del lado del servidor. Si desea usar parámetros de vinculación ("declaraciones preparadas"), considere usar el formulario de función set_config(...) . Ver funciones de administración del sistema

Usando una tabla temporal

Este enfoque requiere el uso de un disparador (o una función auxiliar llamada por un disparador, preferiblemente) que intenta leer un valor de una tabla temporal que debería tener cada sesión. Si no se puede encontrar la tabla temporal, se proporciona un valor predeterminado. Es probable que esto sea algo lento . Prueba cuidadosamente.

El get_app_user() definición:

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;

Demostración:

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 sesión segura

También hay una propuesta para agregar "variables de sesión seguras" a PostgreSQL. Estas son un poco como variables de paquete. A partir de PostgreSQL 12, la función no se ha incluido, pero esté atento y hable sobre la lista de piratas informáticos si esto es algo que necesita.

Avanzado:tu propia extensión con área de memoria compartida

Para usos avanzados, incluso puede hacer que su propia extensión C registre un área de memoria compartida y se comunique entre backends mediante llamadas a funciones C que leen/escriben valores en un segmento DSA. Consulte los ejemplos de programación de PostgreSQL para obtener más detalles. Necesitará conocimientos de C, tiempo y paciencia.