Remove usernames, just use email

This commit is contained in:
Joe Nelson
2015-11-10 21:00:50 -08:00
parent 1b28f77854
commit a508f8df37
+64 -59
View File
@@ -38,16 +38,15 @@ $$ LANGUAGE plpgsql;
create table if not exists create table if not exists
basic_auth.users ( basic_auth.users (
username text not null, email text not null,
pass text not null, pass text not null,
role name not null, role name not null,
email text not null unique,
active boolean not null default false, active boolean not null default false,
more JSON, -- If you like add more columns, or a json column
constraint user_pkey primary key (username), constraint user_pkey primary key (email),
constraint user_field_length_limits check ( constraint user_field_length_limits check (
length(username::text) < 512 AND length(pass) < 512 AND length(pass) < 512
length(email::text) < 512 AND length(more::text) < 1024) AND length(email::text) < 512)
); );
create or replace function create or replace function
@@ -96,12 +95,11 @@ declare
tok text; tok text;
begin begin
select uuid_generate_v4() into tok; select uuid_generate_v4() into tok;
insert into basic_auth.tokens (token, token_type, username) insert into basic_auth.tokens (token, token_type, email)
values (tok, 'validation', new.username); values (tok, 'validation', new.email);
perform pg_notify('validate', perform pg_notify('validate',
json_build_object( json_build_object(
'email', new.email, 'email', new.email,
'username', new.username,
'token', tok, 'token', tok,
'token_type', 'validation' 'token_type', 'validation'
)::text )::text
@@ -123,10 +121,10 @@ create table if not exists
basic_auth.tokens ( basic_auth.tokens (
token text unique, token text unique,
token_type text not null, token_type text not null,
username text not null, email text not null,
created_at timestamptz not null default current_date, created_at timestamptz not null default current_date,
constraint t_pk primary key (token), constraint t_pk primary key (token),
constraint t_user_fk foreign key (username) references basic_auth.users constraint t_user_fk foreign key (email) references basic_auth.users
on delete cascade on update cascade on delete cascade on update cascade
); );
@@ -134,23 +132,36 @@ basic_auth.tokens (
-- Login helper -- Login helper
create or replace function create or replace function
basic_auth.user_role(username text, pass text) returns text basic_auth.user_role(email text, pass text) returns text
language plpgsql language plpgsql
as $$ as $$
begin begin
return ( return (
select role from basic_auth.users select role from basic_auth.users
where users.username = user_role.username where users.email = user_role.email
and users.pass = crypt(user_role.pass, users.pass) and users.pass = crypt(user_role.pass, users.pass)
); );
end; end;
$$; $$;
create or replace function
basic_auth.current_email() returns text
language plpgsql
as $$
begin
return current_setting('postgrest.claims.email');
exception
-- handle unrecognized configuration parameter error
when undefined_object then return '';
end;
$$;
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Public functions (in current schema, not basic_auth) -- Public functions (in current schema, not basic_auth)
create or replace function create or replace function
request_password_reset(username text) returns void request_password_reset(email text) returns void
language plpgsql language plpgsql
as $$ as $$
declare declare
@@ -158,17 +169,14 @@ declare
begin begin
delete from basic_auth.tokens delete from basic_auth.tokens
where token_type = 'reset' where token_type = 'reset'
and tokens.username = request_password_reset.username; and tokens.email = request_password_reset.email;
select uuid_generate_v4() into tok; select uuid_generate_v4() into tok;
insert into basic_auth.tokens (token, token_type, username) insert into basic_auth.tokens (token, token_type, email)
values (tok, 'reset', request_password_reset.username); values (tok, 'reset', request_password_reset.email);
perform pg_notify('reset', perform pg_notify('reset',
json_build_object( json_build_object(
'email', (select email 'email', request_password_reset.email,
from basic_auth.users
where users.username = request_password_reset.username),
'username', request_password_reset.username,
'token', tok, 'token', tok,
'token_type', 'reset' 'token_type', 'reset'
)::text )::text
@@ -177,7 +185,7 @@ end;
$$; $$;
create or replace function create or replace function
reset_password(username text, token text, pass text) reset_password(email text, token text, pass text)
returns void returns void
language plpgsql language plpgsql
as $$ as $$
@@ -185,14 +193,14 @@ declare
tok text; tok text;
begin begin
if exists(select 1 from basic_auth.tokens if exists(select 1 from basic_auth.tokens
where tokens.username = reset_password.username where tokens.email = reset_password.email
and tokens.token = reset_password.token and tokens.token = reset_password.token
and token_type = 'reset') then and token_type = 'reset') then
update basic_auth.users set pass=reset_password.pass update basic_auth.users set pass=reset_password.pass
where users.username = reset_password.username; where users.email = reset_password.email;
delete from basic_auth.tokens delete from basic_auth.tokens
where tokens.username = reset_password.username where tokens.email = reset_password.email
and tokens.token = reset_password.token and tokens.token = reset_password.token
and token_type = 'reset'; and token_type = 'reset';
else else
@@ -201,17 +209,14 @@ begin
end if; end if;
delete from basic_auth.tokens delete from basic_auth.tokens
where token_type = 'reset' where token_type = 'reset'
and tokens.username = reset_password.username; and tokens.email = reset_password.email;
select uuid_generate_v4() into tok; select uuid_generate_v4() into tok;
insert into basic_auth.tokens (token, token_type, username) insert into basic_auth.tokens (token, token_type, email)
values (tok, 'reset', reset_password.username); values (tok, 'reset', reset_password.email);
perform pg_notify('reset', perform pg_notify('reset',
json_build_object( json_build_object(
'email', (select email 'email', reset_password.email,
from basic_auth.users
where users.username = reset_password.username),
'username', reset_password.username,
'token', tok 'token', tok
)::text )::text
); );
@@ -220,32 +225,34 @@ $$;
drop type if exists basic_auth.jwt_claims cascade; drop type if exists basic_auth.jwt_claims cascade;
create type create type
basic_auth.jwt_claims AS (role text, username text); basic_auth.jwt_claims AS (role text, email text);
create or replace function create or replace function
login(username text, pass text) returns basic_auth.jwt_claims login(email text, pass text) returns basic_auth.jwt_claims
language plpgsql language plpgsql
as $$ as $$
declare declare
_role text; _role text;
result basic_auth.jwt_claims; result basic_auth.jwt_claims;
begin begin
select basic_auth.user_role(username, pass) into _role; select basic_auth.user_role(email, pass) into _role;
if _role is null then if _role is null then
raise invalid_password using message = 'invalid user or password'; raise invalid_password using message = 'invalid user or password';
end if; end if;
select _role as role, login.username as username into result; -- TODO; check active flag if you care whether users
-- have validated their emails
select _role as role, login.email as email into result;
return result; return result;
end; end;
$$; $$;
create or replace function create or replace function
signup(username text, email text, pass text) returns void signup(email text, pass text) returns void
language plpgsql language plpgsql
as $$ as $$
begin begin
insert into basic_auth.users (username, email, pass, role) values insert into basic_auth.users (email, pass, role) values
(signup.username, signup.email, signup.pass, 'author'); (signup.email, signup.pass, 'author');
end; end;
$$; $$;
@@ -253,12 +260,10 @@ $$;
-- User management -- User management
create or replace view users as create or replace view users as
select actual.username as username, select actual.role as role,
actual.role as role,
'***'::text as pass, '***'::text as pass,
actual.email as email, actual.email as email,
actual.active as active, actual.active as active
actual.more as more
from basic_auth.users as actual, from basic_auth.users as actual,
(select rolname (select rolname
from pg_authid from pg_authid
@@ -267,7 +272,7 @@ from basic_auth.users as actual,
where actual.role = member_of.rolname where actual.role = member_of.rolname
and ( and (
actual.role <> 'author' actual.role <> 'author'
or username = current_setting('postgrest.claims.username') or email = basic_auth.current_email()
); );
create or replace function create or replace function
@@ -279,9 +284,9 @@ begin
perform basic_auth.clearance_for_role(new.role); perform basic_auth.clearance_for_role(new.role);
insert into basic_auth.users insert into basic_auth.users
(username, role, pass, email, active, more) values (role, pass, email, active) values
(new.username, coalesce(new.role, 'author'), new.pass, (coalesce(new.role, 'author'), new.pass,
new.email, coalesce(new.active, false), new.more); new.email, coalesce(new.active, false));
return new; return new;
elsif tg_op = 'UPDATE' then elsif tg_op = 'UPDATE' then
-- no need to check clearance for old.role because -- no need to check clearance for old.role because
@@ -289,17 +294,17 @@ begin
perform basic_auth.clearance_for_role(new.role); perform basic_auth.clearance_for_role(new.role);
update basic_auth.users set update basic_auth.users set
username = new.username, role = new.role, email = new.email,
pass = new.pass, email = new.email, role = new.role,
active = coalesce(new.active, old.active, false), pass = new.pass,
more = new.more active = coalesce(new.active, old.active, false)
where username = old.username; where email = old.email;
return new; return new;
elsif tg_op = 'DELETE' then elsif tg_op = 'DELETE' then
-- no need to check clearance for old.role (see previous case) -- no need to check clearance for old.role (see previous case)
delete from basic_auth.users delete from basic_auth.users
where basic_auth.username = old.username; where basic_auth.email = old.email;
return null; return null;
end if; end if;
end end
@@ -344,20 +349,20 @@ comments (
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Permissions -- Permissions
--create role anon noinherit;
grant insert on table basic_auth.users, basic_auth.tokens to anon; grant insert on table basic_auth.users, basic_auth.tokens to anon;
grant select on table pg_authid, basic_auth.users, posts, comments to anon; grant select on table pg_authid, basic_auth.users, posts, comments to anon;
grant execute on function grant execute on function
login(text,text), login(text,text),
request_password_reset(text), request_password_reset(text),
reset_password(text,text,text), reset_password(text,text,text),
signup(text, text, text) signup(text, text)
to anon; to anon;
--create role author;
grant author to anon; grant author to anon;
grant select, insert, update, delete grant select, insert, update, delete
on table basic_auth.users, users, posts, comments to author; on basic_auth.tokens, basic_auth.users to anon, author;
grant select, insert, update, delete
on table users, posts, comments to author;
grant usage, select on sequence posts_id_seq, comments_id_seq to author; grant usage, select on sequence posts_id_seq, comments_id_seq to author;
grant usage on schema public, basic_auth to anon, author; grant usage on schema public, basic_auth to anon, author;
@@ -367,7 +372,7 @@ drop policy if exists authors_eigenedit on posts;
create policy authors_eigenedit on posts create policy authors_eigenedit on posts
using (true) using (true)
with check ( with check (
author = current_setting('postgrest.claims.username') author = basic_auth.current_email()
); );
ALTER TABLE comments ENABLE ROW LEVEL SECURITY; ALTER TABLE comments ENABLE ROW LEVEL SECURITY;
@@ -375,7 +380,7 @@ drop policy if exists authors_eigenedit on comments;
create policy authors_eigenedit on comments create policy authors_eigenedit on comments
using (true) using (true)
with check ( with check (
author = current_setting('postgrest.claims.username') author = basic_auth.current_email()
); );
commit; commit;