PostgreSQL adds the login type for event triggers

PostgreSQL 17 adds a new firing event for event triggers: login. Therefore it is now possible to catch a login attempt on a database. Caution: this is not the same as Oracle logon triggers, even if it resembles the same functionality to me. However, thanks to this, is is now possible to get some more information when a login attempt succeeds. In order to implement a poor-man auditing (don’t do this at home!) to breifly demonstrate this feature, you can:

postgres=# CREATE TABLE wrong_audit( pk int generated always as identity
          , who text
          , ts  timestamp default current_timestamp );
CREATE TABLE

postgres=# grant INSERT on table wrong_audit to public;
GRANT


postgres=# create or replace
	function f_etr_audit() returns event_trigger
	as $code$
	begin
     	insert into wrong_audit( who ) select current_role;
	end
	$code$
	language plpgsql;
CREATE FUNCTION

postgres=# create event trigger
	 poor_auditing on login
	 execute function f_etr_audit();
CREATE EVENT TRIGGER




Ano now, when you connect to the database you will see the table getting populated.

postgres=# table wrong_audit;
 pk |   who    |             ts
----+----------+----------------------------
  1 | postgres | 2024-10-03 11:39:27.659018
  2 | postgres | 2024-10-03 11:40:01.057011
  3 | postgres | 2024-10-03 11:46:06.38925
  4 | luca     | 2024-10-03 11:46:44.621835
  5 | luca     | 2024-10-03 11:46:46.389537
  6 | postgres | 2024-10-03 11:46:53.789339



There are a few things to note. First of all, there is the need to grant the INSERT permission to the users that are going to fire the event, i.e., the user that are going to connect, or the trigger will not be able to execute. Obviously, there are other ways to do this, like settings permissions on the function itself. Most important: if the trigger fails (due to an exception), the login attempt is aborted. For example, imagine that I remove the permissions on the tbale:

% psql -h localhost -U luca postgres
psql: error: connection to server at "localhost" (127.0.0.1), port 5432 failed: FATAL:  permission denied for table wrong_audit
CONTEXT:  SQL statement "insert into wrong_audit( who ) select current_role"
PL/pgSQL function f_etr_audit() line 2 at SQL statement



The connection is aborted due to the problem in completing the function. Last but not least, the trigger function should not be a long running one, or the user will be locked waiting for the trigger to complete. Now, for me to remember Oracle logon trigger, let’s complicate a little the above example (don’t try this at home):

postgres=# alter table wrong_audit add column db text;


ostgres=# create or replace function f_etr_audit()
returns event_trigger
as $code$
declare
        me text;
        db text;
begin
        SELECT current_role, current_database()
        INTO me, db;

        IF me = 'luca' AND db = 'postgres' THEN
           RAISE 'Get out of here!';
        END IF;

        insert into wrong_audit( who, db ) VALUES( me, db );
end
$code$
language plpgsql;



And now the poor bastard me when trying to connect to postgres gets:

% psql -h localhost -U luca postgres
psql: error: connection to server at "localhost" (127.0.0.1), port 5432 failed: FATAL:  Get out of here!
CONTEXT:  PL/pgSQL function f_etr_audit() line 10 at RAISE


while other users can still connect, and the table gets populated more and more.

The article PostgreSQL adds the login type for event triggers has been posted by Luca Ferrari on October 3, 2024