feat: usr_session
Some checks failed
migrate-stage / migrate-stage (push) Failing after 25s
migrate-devel / migrate-devel (push) Failing after 9s

This commit is contained in:
Orion Kindel 2023-07-02 15:57:28 -05:00
parent 0b9c597ca4
commit 0502154f4d
Signed by untrusted user who does not match committer: orion
GPG Key ID: 6D4165AE4C928719
5 changed files with 148 additions and 4 deletions

0
client_min_messages Normal file
View File

View File

@ -7,7 +7,7 @@ services:
ports: ports:
- "5433:5432" - "5433:5432"
volumes: volumes:
- "./data/base:/var/lib/postgres/data" - "./data/base:/var/lib/postgresql/data"
env_file: env_file:
- "./.env.schema" - "./.env.schema"
head: head:
@ -17,6 +17,6 @@ services:
ports: ports:
- "5432:5432" - "5432:5432"
volumes: volumes:
- "./data/head:/var/lib/postgres/data" - "./data/head:/var/lib/postgresql/data"
env_file: env_file:
- "./.env.schema" - "./.env.schema"

View File

@ -1,5 +1,33 @@
select create_newtype_text('public.usr_tag'); select create_newtype_text('public.usr_tag');
select create_newtype_text('public.usr_tag_or_email');
create function usr_tag_or_email_to_email(toe public.usr_tag_or_email)
returns public.email
language plpgsql
immutable as $$
begin
if position('@' in usr_tag_or_email_to_string(toe)) > 0 then
return email_of_string(usr_tag_or_email_to_string(toe));
else
return null;
end if;
end;
$$;
create function usr_tag_or_email_to_tag(toe public.usr_tag_or_email)
returns public.usr_tag
language plpgsql
immutable as $$
begin
if usr_tag_or_email_to_email(toe) is null then
return usr_tag_of_string(usr_tag_or_email_to_string(toe));
else
return null;
end if;
end;
$$;
create table public.usr create table public.usr
( id int not null primary key generated always as identity ( id int not null primary key generated always as identity
, uid uuid not null default gen_random_uuid() , uid uuid not null default gen_random_uuid()
@ -10,7 +38,6 @@ create table public.usr
); );
insert into public.usr (tag, password, email) insert into public.usr (tag, password, email)
overriding system value
values (usr_tag_of_string('system'), hashed_text_of_string(''), email_of_string('')); values (usr_tag_of_string('system'), hashed_text_of_string(''), email_of_string(''));
select audit( 'public' select audit( 'public'

View File

@ -118,4 +118,3 @@ select immutable( 'public'
, 'usr' , 'usr'
] ]
); );

118
schema/021_user.sql Normal file
View File

@ -0,0 +1,118 @@
select create_newtype_text('public.usr_session_key');
create function usr_session_key_gen()
returns public.usr_session_key
volatile
language sql as $$
select usr_session_key_of_string(
md5(extract(epoch from now()) || gen_random_bytes(32) :: text)
);
$$;
create type usr_session_device as enum
( 'linux'
, 'macos'
, 'win'
, 'android'
, 'ios'
, 'other'
);
create table public.usr_session
( id int not null primary key generated always as identity
, key public.usr_session_key not null unique default usr_session_key_gen()
, expired boolean not null default false
, expires_at timestamp not null
, usr int not null references public.usr (id)
, location text null
, device usr_session_device null
, ip inet null
);
select immutable( 'public'
, 'usr_session'
, array[ 'id'
, 'key'
, 'expires_at'
, 'usr'
, 'location'
, 'device'
, 'ip'
]
);
create function public.usr_session_login_validate
( tag_or_email public.usr_tag_or_email
, password text
)
returns public.usr
language plpgsql
stable
as $$
declare
usr_email public.email := public.usr_tag_or_email_to_email(tag_or_email);
usr_tag public.usr_tag := public.usr_tag_or_email_to_tag(tag_or_email);
usr public.usr;
begin
select *
from public.usr as u
where u.email = usr_email
or u.tag = usr_tag
into usr;
if usr.id = 1 or usr.tag = usr_tag_of_string('system') then
raise notice 'system user may not be logged into';
raise exception 'incorrect password for user %', usr_tag_or_email_to_string(tag_or_email);
end if;
if usr is null then
-- prevent email guess bruteforces by raising the same exception
-- for invalid password and user not found
raise notice 'user % not found', usr_tag_or_email_to_string(tag_or_email);
raise exception 'incorrect password for user %', usr_tag_or_email_to_string(tag_or_email);
end if;
if not hashed_text_matches(password, usr.password) then
raise notice 'password does not match for user %', usr_tag_or_email_to_string(tag_or_email);
raise exception 'incorrect password for user %', usr_tag_or_email_to_string(tag_or_email);
end if;
return usr;
end;
$$;
create function public.usr_session_login
( tag_or_email public.usr_tag_or_email
, password text
, remember boolean default false
, location text default null
, device public.usr_session_device default null
, ip inet default null
)
returns public.usr_session_key
language plpgsql
volatile
as $$
declare
usr public.usr;
key public.usr_session_key;
expires_at timestamp;
begin
usr := public.usr_session_login_validate(tag_or_email, password);
if remember then
expires_at := now() + interval '1 week';
else
expires_at := now() + interval '1 hour';
end if;
insert into public.usr_session
(expires_at, usr, location, device, ip)
values
(expires_at, usr.id, location, device, ip)
returning usr_session.key
into key;
return key;
end;
$$;