From 95b8751496cc1883e714240899ff211ae96266d0 Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Tue, 20 Feb 2024 20:28:28 +0100 Subject: [PATCH] docs: Simplify SQL for user management how-tos All those DROP IF EXISTS and CREATE IF NOT EXISTS etc. just give a lot more text to read and understand. If in fact a user creates the same thing twice, they should be able to understand the error message from postgres. --- ...agement-using-postgres-users-and-passwords.rst | 8 ++++---- docs/how-tos/sql-user-management.rst | 15 ++++++--------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/docs/how-tos/sql-user-management-using-postgres-users-and-passwords.rst b/docs/how-tos/sql-user-management-using-postgres-users-and-passwords.rst index 6a55c3ff4..c0da33876 100644 --- a/docs/how-tos/sql-user-management-using-postgres-users-and-passwords.rst +++ b/docs/how-tos/sql-user-management-using-postgres-users-and-passwords.rst @@ -31,7 +31,7 @@ As in :ref:`sql_user_management`, we create a :code:`basic_auth` schema: -- We put things inside the basic_auth schema to hide -- them from public view. Certain public procs/views will -- refer to helpers and tables inside. - CREATE SCHEMA IF NOT EXISTS basic_auth; + CREATE SCHEMA basic_auth; As in :ref:`sql_user_management`, we create the :code:`pgcrypto` and :code:`pgjwt` extensions. Here we prefer to put the extensions in its own schemas: @@ -40,7 +40,7 @@ As in :ref:`sql_user_management`, we create the :code:`pgcrypto` and :code:`pgjw CREATE SCHEMA ext_pgcrypto; ALTER SCHEMA ext_pgcrypto OWNER TO postgres; - CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA ext_pgcrypto; + CREATE EXTENSION pgcrypto WITH SCHEMA ext_pgcrypto; Concerning the `pgjwt extension `_, please cf. to :ref:`client_auth`. @@ -49,7 +49,7 @@ Concerning the `pgjwt extension `_, please cf. CREATE SCHEMA ext_pgjwt; ALTER SCHEMA ext_pgjwt OWNER TO postgres; - CREATE EXTENSION IF NOT EXISTS pgjwt WITH SCHEMA ext_pgjwt; + CREATE EXTENSION pgjwt WITH SCHEMA ext_pgjwt; In order to be able to work with postgres' SCRAM-SHA-256 password hashes, we also need the PBKDF2 key derivation function. Luckily there is `a PL/pgSQL implementation on stackoverflow `_: @@ -117,7 +117,7 @@ In order to be able to work with postgres' SCRAM-SHA-256 password hashes, we als ALTER FUNCTION basic_auth.pbkdf2(salt bytea, pw text, count integer, desired_length integer, algorithm text) OWNER TO postgres; -Analogous to :ref:`sql_user_management` creates the function :code:`basic_auth.user_role`, we create a helper function to check the user's password, here with another name and signature (since we want the username, not an email address). +Analogous to how :ref:`sql_user_management` creates the function :code:`basic_auth.user_role`, we create a helper function to check the user's password, here with another name and signature (since we want the username, not an email address). But contrary to :ref:`sql_user_management`, this function does not use a dedicated :code:`users` table with passwords, but instead utilizes the built-in table `pg_catalog.pg_authid `_: .. code-block:: postgres diff --git a/docs/how-tos/sql-user-management.rst b/docs/how-tos/sql-user-management.rst index fd314622c..bde15921e 100644 --- a/docs/how-tos/sql-user-management.rst +++ b/docs/how-tos/sql-user-management.rst @@ -17,9 +17,8 @@ First we'll need a table to keep track of our users: -- We put things inside the basic_auth schema to hide -- them from public view. Certain public procs/views will -- refer to helpers and tables inside. - create schema if not exists basic_auth; - create table if not exists + create table basic_auth.users ( email text primary key check ( email ~* '^.+@.+\..+$' ), pass text not null check (length(pass) < 512), @@ -30,7 +29,7 @@ We would like the role to be a foreign key to actual database roles, however Pos .. code-block:: postgres - create or replace function + create function basic_auth.check_role_exists() returns trigger as $$ begin if not exists (select 1 from pg_roles as r where r.rolname = new.role) then @@ -42,7 +41,6 @@ We would like the role to be a foreign key to actual database roles, however Pos end $$ language plpgsql; - drop trigger if exists ensure_user_role_exists on basic_auth.users; create constraint trigger ensure_user_role_exists after insert or update on basic_auth.users for each row @@ -52,9 +50,9 @@ Next we'll use the pgcrypto extension and a trigger to keep passwords safe in th .. code-block:: postgres - create extension if not exists pgcrypto; + create extension pgcrypto; - create or replace function + create function basic_auth.encrypt_pass() returns trigger as $$ begin if tg_op = 'INSERT' or new.pass <> old.pass then @@ -64,7 +62,6 @@ Next we'll use the pgcrypto extension and a trigger to keep passwords safe in th end $$ language plpgsql; - drop trigger if exists encrypt_pass on basic_auth.users; create trigger encrypt_pass before insert or update on basic_auth.users for each row @@ -74,7 +71,7 @@ With the table in place we can make a helper to check a password against the enc .. code-block:: postgres - create or replace function + create function basic_auth.user_role(email text, pass text) returns name language plpgsql as $$ @@ -158,7 +155,7 @@ As described in `JWT from SQL`_, we'll create a JWT inside our login function. N .. code-block:: postgres -- login should be on your exposed schema - create or replace function + create function login(email text, pass text, out token text) as $$ declare _role name;