feat: usr_session
This commit is contained in:
parent
0b9c597ca4
commit
0502154f4d
0
client_min_messages
Normal file
0
client_min_messages
Normal file
@ -7,7 +7,7 @@ services:
|
||||
ports:
|
||||
- "5433:5432"
|
||||
volumes:
|
||||
- "./data/base:/var/lib/postgres/data"
|
||||
- "./data/base:/var/lib/postgresql/data"
|
||||
env_file:
|
||||
- "./.env.schema"
|
||||
head:
|
||||
@ -17,6 +17,6 @@ services:
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- "./data/head:/var/lib/postgres/data"
|
||||
- "./data/head:/var/lib/postgresql/data"
|
||||
env_file:
|
||||
- "./.env.schema"
|
||||
|
@ -1,5 +1,33 @@
|
||||
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
|
||||
( id int not null primary key generated always as identity
|
||||
, uid uuid not null default gen_random_uuid()
|
||||
@ -10,7 +38,6 @@ create table public.usr
|
||||
);
|
||||
|
||||
insert into public.usr (tag, password, email)
|
||||
overriding system value
|
||||
values (usr_tag_of_string('system'), hashed_text_of_string(''), email_of_string(''));
|
||||
|
||||
select audit( 'public'
|
||||
|
@ -118,4 +118,3 @@ select immutable( 'public'
|
||||
, 'usr'
|
||||
]
|
||||
);
|
||||
|
||||
|
118
schema/021_user.sql
Normal file
118
schema/021_user.sql
Normal 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;
|
||||
$$;
|
Loading…
Reference in New Issue
Block a user