Compare commits
5 Commits
c7e34cc488
...
d9c93b8797
Author | SHA1 | Date |
---|---|---|
jordi fita mas | d9c93b8797 | |
jordi fita mas | 56d149e211 | |
jordi fita mas | 5eeaab2013 | |
jordi fita mas | f9e22c0789 | |
jordi fita mas | 22509dd683 |
|
@ -0,0 +1,24 @@
|
||||||
|
-- Deploy numerus:build_cookie to pg
|
||||||
|
-- requires: schema_numerus
|
||||||
|
-- requires: current_user_email
|
||||||
|
-- requires: current_user_cookie
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
set search_path to numerus, public;
|
||||||
|
|
||||||
|
create or replace function build_cookie(user_email email default null, user_cookie text default null) returns text as
|
||||||
|
$$
|
||||||
|
select coalesce(user_cookie, current_user_cookie()) || '/' || coalesce(user_email, current_user_email());
|
||||||
|
$$
|
||||||
|
language sql
|
||||||
|
stable;
|
||||||
|
|
||||||
|
revoke execute on function build_cookie(email, text) from public;
|
||||||
|
grant execute on function build_cookie(email, text) to invoicer;
|
||||||
|
grant execute on function build_cookie(email, text) to admin;
|
||||||
|
|
||||||
|
comment on function build_cookie(email, text) is
|
||||||
|
'Build the cookie to send to the user’s browser, either for the given values or for the current user.';
|
||||||
|
|
||||||
|
commit;
|
|
@ -0,0 +1,28 @@
|
||||||
|
-- Deploy numerus:change_password to pg
|
||||||
|
-- requires: schema_numerus
|
||||||
|
-- requires: user
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
set search_path to numerus, auth, public;
|
||||||
|
|
||||||
|
create or replace function change_password(new_password text) returns void as
|
||||||
|
$$
|
||||||
|
update "user"
|
||||||
|
set password = new_password
|
||||||
|
where email = current_user_email()
|
||||||
|
and cookie = current_user_cookie()
|
||||||
|
and cookie_expires_at > current_timestamp
|
||||||
|
and length(cookie) > 30
|
||||||
|
$$ language sql
|
||||||
|
security definer
|
||||||
|
set search_path to auth, numerus, pg_temp;
|
||||||
|
|
||||||
|
revoke execute on function change_password(text) from public;
|
||||||
|
grant execute on function change_password(text) to invoicer;
|
||||||
|
grant execute on function change_password(text) to admin;
|
||||||
|
|
||||||
|
comment on function change_password(text) is
|
||||||
|
'Changes the password for the current app user';
|
||||||
|
|
||||||
|
commit;
|
|
@ -28,7 +28,6 @@ begin
|
||||||
user_cookie := '';
|
user_cookie := '';
|
||||||
user_role := 'guest'::name;
|
user_role := 'guest'::name;
|
||||||
end if;
|
end if;
|
||||||
perform set_config('request.user.id', uid, false);
|
|
||||||
perform set_config('request.user.email', user_email, false);
|
perform set_config('request.user.email', user_email, false);
|
||||||
perform set_config('request.user.cookie', user_cookie, false);
|
perform set_config('request.user.cookie', user_cookie, false);
|
||||||
return user_role;
|
return user_role;
|
||||||
|
@ -40,7 +39,7 @@ stable
|
||||||
set search_path = auth, numerus, pg_temp;
|
set search_path = auth, numerus, pg_temp;
|
||||||
|
|
||||||
comment on function check_cookie(text) is
|
comment on function check_cookie(text) is
|
||||||
'Checks whether a given cookie is for a valid users, returning its email and role';
|
'Checks whether a given cookie is for a valid users, returning their role, and setting current_user_email and current_user_cookie';
|
||||||
|
|
||||||
revoke execute on function check_cookie(text) from public;
|
revoke execute on function check_cookie(text) from public;
|
||||||
grant execute on function check_cookie(text) to authenticator;
|
grant execute on function check_cookie(text) to authenticator;
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
-- Deploy numerus:current_app_user to pg
|
|
||||||
-- requires: schema_numerus
|
|
||||||
|
|
||||||
begin;
|
|
||||||
|
|
||||||
set search_path to numerus;
|
|
||||||
|
|
||||||
create or replace function current_app_user() returns text as
|
|
||||||
$$
|
|
||||||
select current_setting('request.user.cookie', true);
|
|
||||||
$$
|
|
||||||
language sql
|
|
||||||
stable;
|
|
||||||
|
|
||||||
comment on function current_app_user() is
|
|
||||||
'Returns the ID of the current Numerus user';
|
|
||||||
|
|
||||||
revoke execute on function current_app_user() from public;
|
|
||||||
grant execute on function current_app_user() to guest;
|
|
||||||
grant execute on function current_app_user() to invoicer;
|
|
||||||
grant execute on function current_app_user() to admin;
|
|
||||||
|
|
||||||
commit;
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
-- Deploy numerus:current_user_cookie to pg
|
||||||
|
-- requires: schema_numerus
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
set search_path to numerus;
|
||||||
|
|
||||||
|
create or replace function current_user_cookie() returns text as
|
||||||
|
$$
|
||||||
|
select current_setting('request.user.cookie', true);
|
||||||
|
$$
|
||||||
|
language sql
|
||||||
|
stable;
|
||||||
|
|
||||||
|
comment on function current_user_cookie() is
|
||||||
|
'Returns the cookie of the current Numerus user';
|
||||||
|
|
||||||
|
revoke execute on function current_user_cookie() from public;
|
||||||
|
grant execute on function current_user_cookie() to guest;
|
||||||
|
grant execute on function current_user_cookie() to invoicer;
|
||||||
|
grant execute on function current_user_cookie() to admin;
|
||||||
|
|
||||||
|
commit;
|
|
@ -0,0 +1,23 @@
|
||||||
|
-- Deploy numerus:current_user_email to pg
|
||||||
|
-- requires: schema_numerus
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
set search_path to numerus;
|
||||||
|
|
||||||
|
create or replace function current_user_email() returns text as
|
||||||
|
$$
|
||||||
|
select current_setting('request.user.email', true);
|
||||||
|
$$
|
||||||
|
language sql
|
||||||
|
stable;
|
||||||
|
|
||||||
|
comment on function current_user_email() is
|
||||||
|
'Returns the email of the current Numerus user';
|
||||||
|
|
||||||
|
revoke execute on function current_user_email() from public;
|
||||||
|
grant execute on function current_user_email() to guest;
|
||||||
|
grant execute on function current_user_email() to invoicer;
|
||||||
|
grant execute on function current_user_email() to admin;
|
||||||
|
|
||||||
|
commit;
|
|
@ -6,6 +6,7 @@
|
||||||
-- requires: email
|
-- requires: email
|
||||||
-- requires: user
|
-- requires: user
|
||||||
-- requires: login_attempt
|
-- requires: login_attempt
|
||||||
|
-- requires: build_cookie
|
||||||
|
|
||||||
begin;
|
begin;
|
||||||
|
|
||||||
|
@ -48,7 +49,7 @@ begin
|
||||||
(user_name, ip_address, success)
|
(user_name, ip_address, success)
|
||||||
values (login.email, login.ip_address, true);
|
values (login.email, login.ip_address, true);
|
||||||
|
|
||||||
return user_cookie || '/' || email;
|
return build_cookie(email, user_cookie);
|
||||||
end;
|
end;
|
||||||
$$
|
$$
|
||||||
language plpgsql
|
language plpgsql
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
-- Deploy numerus:logout to pg
|
-- Deploy numerus:logout to pg
|
||||||
-- requires: schema_auth
|
-- requires: schema_auth
|
||||||
-- requires: user
|
-- requires: user
|
||||||
-- requires: current_app_user
|
-- requires: current_user_cookie
|
||||||
|
-- requires: current_user_email
|
||||||
|
|
||||||
begin;
|
begin;
|
||||||
|
|
||||||
|
@ -12,7 +13,8 @@ $$
|
||||||
update "user"
|
update "user"
|
||||||
set cookie = default
|
set cookie = default
|
||||||
, cookie_expires_at = default
|
, cookie_expires_at = default
|
||||||
where cookie = current_app_user()
|
where email = current_user_email()
|
||||||
|
and cookie = current_user_cookie()
|
||||||
and cookie_expires_at > current_timestamp
|
and cookie_expires_at > current_timestamp
|
||||||
and length(cookie) > 30
|
and length(cookie) > 30
|
||||||
$$
|
$$
|
||||||
|
@ -21,7 +23,7 @@ security definer
|
||||||
set search_path to auth, numerus, pg_temp;
|
set search_path to auth, numerus, pg_temp;
|
||||||
|
|
||||||
comment on function logout() is
|
comment on function logout() is
|
||||||
'Removes the cookie and its expiry data from the current user, set as request.user setting';
|
'Removes the cookie and its expiry data from the current user, as returned by current_user_email and current_user_cookie';
|
||||||
|
|
||||||
revoke execute on function logout() from public;
|
revoke execute on function logout() from public;
|
||||||
grant execute on function logout() to invoicer;
|
grant execute on function logout() to invoicer;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
-- Deploy numerus:user_profile to pg
|
-- Deploy numerus:user_profile to pg
|
||||||
-- requires: schema_numerus
|
-- requires: schema_numerus
|
||||||
-- requires: user
|
-- requires: user
|
||||||
-- requires: current_app_user
|
-- requires: current_user_cookie
|
||||||
|
-- requires: current_user_email
|
||||||
|
|
||||||
begin;
|
begin;
|
||||||
|
|
||||||
|
@ -16,7 +17,8 @@ select user_id
|
||||||
, role
|
, role
|
||||||
, lang_tag
|
, lang_tag
|
||||||
from auth."user"
|
from auth."user"
|
||||||
where cookie = current_app_user()
|
where email = current_user_email()
|
||||||
|
and cookie = current_user_cookie()
|
||||||
and cookie_expires_at > current_timestamp
|
and cookie_expires_at > current_timestamp
|
||||||
and length(cookie) > 30
|
and length(cookie) > 30
|
||||||
union all
|
union all
|
||||||
|
@ -28,23 +30,40 @@ select 0
|
||||||
where not exists (
|
where not exists (
|
||||||
select 1
|
select 1
|
||||||
from auth."user"
|
from auth."user"
|
||||||
where cookie = current_app_user()
|
where email = current_user_email()
|
||||||
|
and cookie = current_user_cookie()
|
||||||
and cookie_expires_at > current_timestamp
|
and cookie_expires_at > current_timestamp
|
||||||
and length(cookie) > 30
|
and length(cookie) > 30
|
||||||
);
|
);
|
||||||
|
|
||||||
create rule update_user_profile as on update to user_profile
|
|
||||||
do instead update auth."user"
|
|
||||||
set email = new.email
|
|
||||||
, name = new.name
|
|
||||||
, lang_tag = new.lang_tag
|
|
||||||
where cookie = current_app_user()
|
|
||||||
and cookie_expires_at > current_timestamp
|
|
||||||
and length(cookie) > 30
|
|
||||||
;
|
|
||||||
|
|
||||||
grant select on table user_profile to guest;
|
grant select on table user_profile to guest;
|
||||||
grant select, update(email, name, lang_tag) on table user_profile to invoicer;
|
grant select, update(email, name, lang_tag) on table user_profile to invoicer;
|
||||||
grant select, update(email, name, lang_tag) on table user_profile to admin;
|
grant select, update(email, name, lang_tag) on table user_profile to admin;
|
||||||
|
|
||||||
|
create or replace function update_user_profile() returns trigger as
|
||||||
|
$$
|
||||||
|
begin
|
||||||
|
update auth."user"
|
||||||
|
set email = new.email
|
||||||
|
, name = new.name
|
||||||
|
, lang_tag = new.lang_tag
|
||||||
|
where email = current_user_email()
|
||||||
|
and cookie = current_user_cookie()
|
||||||
|
and cookie_expires_at > current_timestamp
|
||||||
|
and length(cookie) > 30
|
||||||
|
;
|
||||||
|
|
||||||
|
perform set_config('request.user.email', new.email, false);
|
||||||
|
|
||||||
|
return new;
|
||||||
|
end;
|
||||||
|
$$
|
||||||
|
language plpgsql
|
||||||
|
security definer
|
||||||
|
set search_path to auth, numerus, pg_temp;
|
||||||
|
|
||||||
|
create trigger update_user_profile
|
||||||
|
instead of update on user_profile
|
||||||
|
for each row execute procedure update_user_profile();
|
||||||
|
|
||||||
commit;
|
commit;
|
||||||
|
|
|
@ -46,7 +46,7 @@ func LoginHandler() http.Handler {
|
||||||
conn := getConn(r)
|
conn := getConn(r)
|
||||||
cookie := conn.MustGetText(r.Context(), "", "select login($1, $2, $3)", page.Email, page.Password, remoteAddr(r))
|
cookie := conn.MustGetText(r.Context(), "", "select login($1, $2, $3)", page.Email, page.Password, remoteAddr(r))
|
||||||
if cookie != "" {
|
if cookie != "" {
|
||||||
http.SetCookie(w, createSessionCookie(cookie, 8766*24*time.Hour))
|
setSessionCookie(w, cookie)
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,10 @@ func remoteAddr(r *http.Request) string {
|
||||||
return address
|
return address
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setSessionCookie(w http.ResponseWriter, cookie string) {
|
||||||
|
http.SetCookie(w, createSessionCookie(cookie, 8766*24*time.Hour))
|
||||||
|
}
|
||||||
|
|
||||||
func createSessionCookie(value string, duration time.Duration) *http.Cookie {
|
func createSessionCookie(value string, duration time.Duration) *http.Cookie {
|
||||||
return &http.Cookie{
|
return &http.Cookie{
|
||||||
Name: sessionCookie,
|
Name: sessionCookie,
|
||||||
|
|
|
@ -30,9 +30,9 @@ func ProfileHandler() http.Handler {
|
||||||
conn := getConn(r)
|
conn := getConn(r)
|
||||||
locale := getLocale(r)
|
locale := getLocale(r)
|
||||||
page := ProfilePage{
|
page := ProfilePage{
|
||||||
Title: pgettext("title", "User Settings", locale),
|
Title: pgettext("title", "User Settings", locale),
|
||||||
Email: user.Email,
|
Email: user.Email,
|
||||||
Languages: mustGetLanguageOptions(r.Context(), conn),
|
Language: user.Language.String(),
|
||||||
}
|
}
|
||||||
if r.Method == "POST" {
|
if r.Method == "POST" {
|
||||||
r.ParseForm()
|
r.ParseForm()
|
||||||
|
@ -41,11 +41,16 @@ func ProfileHandler() http.Handler {
|
||||||
page.Password = r.FormValue("password")
|
page.Password = r.FormValue("password")
|
||||||
page.PasswordConfirm = r.FormValue("password_confirm")
|
page.PasswordConfirm = r.FormValue("password_confirm")
|
||||||
page.Language = r.FormValue("language")
|
page.Language = r.FormValue("language")
|
||||||
conn.MustExec(r.Context(), "update user_profile set name = $1, email = $2, lang_tag = $3", page.Name, page.Email, page.Language)
|
cookie := conn.MustGetText(r.Context(), "", "update user_profile set name = $1, email = $2, lang_tag = $3 returning build_cookie()", page.Name, page.Email, page.Language)
|
||||||
http.Redirect(w, r, "/profile", http.StatusSeeOther);
|
setSessionCookie(w, cookie)
|
||||||
return;
|
if page.Password != "" && page.Password == page.PasswordConfirm {
|
||||||
|
conn.MustExec(r.Context(), "select change_password($1)", page.Password)
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, "/profile", http.StatusSeeOther)
|
||||||
|
return
|
||||||
} else {
|
} else {
|
||||||
if err := conn.QueryRow(r.Context(), "select name, lang_tag from user_profile").Scan(&page.Name, &page.Language); err != nil {
|
page.Languages = mustGetLanguageOptions(r.Context(), conn)
|
||||||
|
if err := conn.QueryRow(r.Context(), "select name from user_profile").Scan(&page.Name); err != nil {
|
||||||
panic(nil)
|
panic(nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
6
po/ca.po
6
po/ca.po
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: numerus\n"
|
"Project-Id-Version: numerus\n"
|
||||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||||
"POT-Creation-Date: 2023-01-23 00:41+0100\n"
|
"POT-Creation-Date: 2023-01-23 18:50+0100\n"
|
||||||
"PO-Revision-Date: 2023-01-18 17:08+0100\n"
|
"PO-Revision-Date: 2023-01-18 17:08+0100\n"
|
||||||
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
||||||
"Language-Team: Catalan <ca@dodds.net>\n"
|
"Language-Team: Catalan <ca@dodds.net>\n"
|
||||||
|
@ -81,11 +81,11 @@ msgctxt "action"
|
||||||
msgid "Save changes"
|
msgid "Save changes"
|
||||||
msgstr "Desa canvis"
|
msgstr "Desa canvis"
|
||||||
|
|
||||||
#: web/template/app.html:16
|
#: web/template/app.html:20
|
||||||
msgid "Account"
|
msgid "Account"
|
||||||
msgstr "Compte"
|
msgstr "Compte"
|
||||||
|
|
||||||
#: web/template/app.html:19
|
#: web/template/app.html:27
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Logout"
|
msgid "Logout"
|
||||||
msgstr "Surt"
|
msgstr "Surt"
|
||||||
|
|
6
po/es.po
6
po/es.po
|
@ -7,7 +7,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: numerus\n"
|
"Project-Id-Version: numerus\n"
|
||||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||||
"POT-Creation-Date: 2023-01-23 00:41+0100\n"
|
"POT-Creation-Date: 2023-01-23 18:50+0100\n"
|
||||||
"PO-Revision-Date: 2023-01-18 17:45+0100\n"
|
"PO-Revision-Date: 2023-01-18 17:45+0100\n"
|
||||||
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
||||||
"Language-Team: Spanish <es@tp.org.es>\n"
|
"Language-Team: Spanish <es@tp.org.es>\n"
|
||||||
|
@ -81,11 +81,11 @@ msgctxt "action"
|
||||||
msgid "Save changes"
|
msgid "Save changes"
|
||||||
msgstr "Guardar cambios"
|
msgstr "Guardar cambios"
|
||||||
|
|
||||||
#: web/template/app.html:16
|
#: web/template/app.html:20
|
||||||
msgid "Account"
|
msgid "Account"
|
||||||
msgstr "Cuenta"
|
msgstr "Cuenta"
|
||||||
|
|
||||||
#: web/template/app.html:19
|
#: web/template/app.html:27
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Logout"
|
msgid "Logout"
|
||||||
msgstr "Salir"
|
msgstr "Salir"
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- Revert numerus:build_cookie from pg
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
drop function if exists numerus.build_cookie(numerus.email, text);
|
||||||
|
|
||||||
|
commit;
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- Revert numerus:change_password from pg
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
drop function if exists numerus.change_password(text);
|
||||||
|
|
||||||
|
commit;
|
|
@ -1,7 +0,0 @@
|
||||||
-- Revert numerus:current_app_user from pg
|
|
||||||
|
|
||||||
begin;
|
|
||||||
|
|
||||||
drop function if exists numerus.current_app_user();
|
|
||||||
|
|
||||||
commit;
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- Revert numerus:current_user_cookie from pg
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
drop function if exists numerus.current_user_cookie();
|
||||||
|
|
||||||
|
commit;
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- Revert numerus:current_user_email from pg
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
drop function if exists numerus.current_user_email();
|
||||||
|
|
||||||
|
commit;
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
begin;
|
begin;
|
||||||
|
|
||||||
|
drop trigger if exists update_user_profile on numerus.user_profile;
|
||||||
|
drop function if exists numerus.update_user_profile();
|
||||||
drop view if exists numerus.user_profile;
|
drop view if exists numerus.user_profile;
|
||||||
|
|
||||||
commit;
|
commit;
|
||||||
|
|
11
sqitch.plan
11
sqitch.plan
|
@ -15,9 +15,12 @@ extension_pgcrypto [schema_auth] 2023-01-13T00:11:50Z jordi fita mas <jordi@tand
|
||||||
encrypt_password [schema_auth user extension_pgcrypto] 2023-01-13T00:14:30Z jordi fita mas <jordi@tandem.blog> # Add trigger to encrypt user’s password
|
encrypt_password [schema_auth user extension_pgcrypto] 2023-01-13T00:14:30Z jordi fita mas <jordi@tandem.blog> # Add trigger to encrypt user’s password
|
||||||
login_attempt [schema_auth] 2023-01-17T14:05:49Z jordi fita mas <jordi@tandem.blog> # Add table to log login attempts
|
login_attempt [schema_auth] 2023-01-17T14:05:49Z jordi fita mas <jordi@tandem.blog> # Add table to log login attempts
|
||||||
login [roles schema_numerus schema_auth extension_pgcrypto email user login_attempt] 2023-01-13T00:32:32Z jordi fita mas <jordi@tandem.blog> # Add function to login
|
login [roles schema_numerus schema_auth extension_pgcrypto email user login_attempt] 2023-01-13T00:32:32Z jordi fita mas <jordi@tandem.blog> # Add function to login
|
||||||
check_cookie [schema_public user] 2023-01-17T17:48:49Z jordi fita mas <jordi@tandem.blog> # Add function to check if a user cookie is valid
|
current_user_cookie [schema_numerus] 2023-01-21T20:16:28Z jordi fita mas <jordi@tandem.blog> # Add function to get the cookie of the current Numerus’ user
|
||||||
current_app_user [schema_numerus] 2023-01-21T20:16:28Z jordi fita mas <jordi@tandem.blog> # Add function to get the ID of the current Numerus’ user
|
current_user_email [schema_numerus] 2023-01-23T19:11:53Z jordi fita mas <jordi@tandem.blog> # Add function to get the email of the current Numerus’ user
|
||||||
logout [schema_auth current_app_user user] 2023-01-17T19:10:21Z jordi fita mas <jordi@tandem.blog> # Add function to logout
|
build_cookie [schema_numerus current_user_email current_user_cookie] 2023-01-23T19:46:13Z jordi fita mas <jordi@tandem.blog> # Add function to build the cookie for the current user
|
||||||
|
check_cookie [schema_public user build_cookie] 2023-01-17T17:48:49Z jordi fita mas <jordi@tandem.blog> # Add function to check if a user cookie is valid
|
||||||
|
logout [schema_auth current_user_email current_user_cookie user] 2023-01-17T19:10:21Z jordi fita mas <jordi@tandem.blog> # Add function to logout
|
||||||
set_cookie [schema_public check_cookie] 2023-01-19T11:00:22Z jordi fita mas <jordi@tandem.blog> # Add function to set the role based on the cookie
|
set_cookie [schema_public check_cookie] 2023-01-19T11:00:22Z jordi fita mas <jordi@tandem.blog> # Add function to set the role based on the cookie
|
||||||
available_languages [schema_numerus language] 2023-01-21T21:11:08Z jordi fita mas <jordi@tandem.blog> # Add the initial available languages
|
available_languages [schema_numerus language] 2023-01-21T21:11:08Z jordi fita mas <jordi@tandem.blog> # Add the initial available languages
|
||||||
user_profile [schema_numerus user current_app_user] 2023-01-21T23:18:20Z jordi fita mas <jordi@tandem.blog> # Add view for user profile
|
user_profile [schema_numerus user current_user_email current_user_cookie] 2023-01-21T23:18:20Z jordi fita mas <jordi@tandem.blog> # Add view for user profile
|
||||||
|
change_password [schema_numerus user] 2023-01-23T20:22:45Z jordi fita mas <jordi@tandem.blog> # Add function to change the current user’s password
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
-- Test build_cookie
|
||||||
|
set client_min_messages to warning;
|
||||||
|
create extension if not exists pgtap;
|
||||||
|
reset client_min_messages;
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
select plan(13);
|
||||||
|
|
||||||
|
set search_path to numerus, auth, public;
|
||||||
|
|
||||||
|
select has_function('numerus', 'build_cookie', array ['email', 'text']);
|
||||||
|
select function_lang_is('numerus', 'build_cookie', array ['email', 'text'], 'sql');
|
||||||
|
select function_returns('numerus', 'build_cookie', array ['email', 'text'], 'text');
|
||||||
|
select isnt_definer('numerus', 'build_cookie', array ['email', 'text']);
|
||||||
|
select volatility_is('numerus', 'build_cookie', array ['email', 'text'], 'stable');
|
||||||
|
select function_privs_are('numerus', 'build_cookie', array ['email', 'text'], 'guest', array []::text[]);
|
||||||
|
select function_privs_are('numerus', 'build_cookie', array ['email', 'text'], 'invoicer', array ['EXECUTE']);
|
||||||
|
select function_privs_are('numerus', 'build_cookie', array ['email', 'text'], 'admin', array ['EXECUTE']);
|
||||||
|
select function_privs_are('numerus', 'build_cookie', array ['email', 'text'], 'authenticator', array []::text[]);
|
||||||
|
|
||||||
|
set client_min_messages to warning;
|
||||||
|
truncate auth."user" cascade;
|
||||||
|
reset client_min_messages;
|
||||||
|
|
||||||
|
insert into auth."user" (user_id, email, name, password, role, cookie, cookie_expires_at)
|
||||||
|
values (1, 'demo@tandem.blog', 'Demo', 'test', 'invoicer', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
||||||
|
, (9, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
||||||
|
;
|
||||||
|
|
||||||
|
select is(
|
||||||
|
build_cookie('test@example.com'::email, '123abc'),
|
||||||
|
'123abc/test@example.com',
|
||||||
|
'Should build the cookie with the given user and cookie value'
|
||||||
|
);
|
||||||
|
|
||||||
|
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog');
|
||||||
|
reset role;
|
||||||
|
|
||||||
|
select is(
|
||||||
|
build_cookie(),
|
||||||
|
'44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog',
|
||||||
|
'Should build the cookie for the logged in user'
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog');
|
||||||
|
reset role;
|
||||||
|
|
||||||
|
select is(
|
||||||
|
build_cookie(),
|
||||||
|
'12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog',
|
||||||
|
'Should build the cookie for the other logged in user'
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
select set_cookie('ashtasth');
|
||||||
|
reset role;
|
||||||
|
|
||||||
|
select is(
|
||||||
|
build_cookie(),
|
||||||
|
'/',
|
||||||
|
'Should build the cookie for the guest user'
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
select *
|
||||||
|
from finish();
|
||||||
|
|
||||||
|
rollback;
|
|
@ -0,0 +1,57 @@
|
||||||
|
-- Test change_password
|
||||||
|
set client_min_messages to warning;
|
||||||
|
create extension if not exists pgtap;
|
||||||
|
reset client_min_messages;
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
select plan(14);
|
||||||
|
|
||||||
|
set search_path to numerus, auth, public;
|
||||||
|
|
||||||
|
select has_function('numerus', 'change_password', array ['text']);
|
||||||
|
select function_lang_is('numerus', 'change_password', array ['text'], 'sql');
|
||||||
|
select function_returns('numerus', 'change_password', array ['text'], 'void');
|
||||||
|
select is_definer('numerus', 'change_password', array ['text']);
|
||||||
|
select volatility_is('numerus', 'change_password', array ['text'], 'volatile');
|
||||||
|
select function_privs_are('numerus', 'change_password', array ['text'], 'guest', array []::text[]);
|
||||||
|
select function_privs_are('numerus', 'change_password', array ['text'], 'invoicer', array ['EXECUTE']);
|
||||||
|
select function_privs_are('numerus', 'change_password', array ['text'], 'admin', array ['EXECUTE']);
|
||||||
|
select function_privs_are('numerus', 'change_password', array ['text'], 'authenticator', array []::text[]);
|
||||||
|
|
||||||
|
set client_min_messages to warning;
|
||||||
|
truncate auth."user" cascade;
|
||||||
|
reset client_min_messages;
|
||||||
|
|
||||||
|
insert into auth."user" (user_id, email, name, password, role, cookie, cookie_expires_at)
|
||||||
|
values (1, 'demo@tandem.blog', 'Demo', 'test', 'invoicer', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
||||||
|
, (9, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
||||||
|
;
|
||||||
|
|
||||||
|
select lives_ok( $$ select change_password('another') $$, 'Should run even without current user' );
|
||||||
|
|
||||||
|
select isnt_empty (
|
||||||
|
$$ select * from auth."user" where password = crypt('test', password) $$,
|
||||||
|
'Should not have changed any password'
|
||||||
|
);
|
||||||
|
|
||||||
|
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog');
|
||||||
|
|
||||||
|
select lives_ok( $$ select change_password('another') $$, 'Should run with the correct user' );
|
||||||
|
|
||||||
|
reset role;
|
||||||
|
|
||||||
|
select isnt_empty (
|
||||||
|
$$ select * from auth."user" where email = 'demo@tandem.blog' and password = crypt('another', password) $$,
|
||||||
|
'Should have changed the password of the current user'
|
||||||
|
);
|
||||||
|
|
||||||
|
select isnt_empty (
|
||||||
|
$$ select * from auth."user" where email = 'admin@tandem.blog' and password = crypt('test', password) $$,
|
||||||
|
'Should not have changed any other password'
|
||||||
|
);
|
||||||
|
|
||||||
|
select *
|
||||||
|
from finish();
|
||||||
|
|
||||||
|
rollback;
|
|
@ -29,10 +29,7 @@ values (1, 'demo@tandem.blog', 'Demo', 'test', 'invoicer', '44facbb30d8a419dfd4b
|
||||||
;
|
;
|
||||||
|
|
||||||
prepare user_info as
|
prepare user_info as
|
||||||
select current_setting('request.user.id', true)::integer
|
select current_user_email(), current_user_cookie();
|
||||||
, current_setting('request.user.email', true)
|
|
||||||
, current_setting('request.user.cookie', true)
|
|
||||||
;
|
|
||||||
|
|
||||||
select is (
|
select is (
|
||||||
check_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog'),
|
check_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog'),
|
||||||
|
@ -42,7 +39,7 @@ select is (
|
||||||
|
|
||||||
select results_eq (
|
select results_eq (
|
||||||
'user_info',
|
'user_info',
|
||||||
$$ values (1, 'demo@tandem.blog', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e') $$,
|
$$ values ('demo@tandem.blog', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e') $$,
|
||||||
'Should have updated the settings with the user info'
|
'Should have updated the settings with the user info'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -54,7 +51,7 @@ select is (
|
||||||
|
|
||||||
select results_eq (
|
select results_eq (
|
||||||
'user_info',
|
'user_info',
|
||||||
$$ values (9, 'admin@tandem.blog', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524') $$,
|
$$ values ('admin@tandem.blog', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524') $$,
|
||||||
'Should have updated the settings with the other user info'
|
'Should have updated the settings with the other user info'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -66,7 +63,7 @@ select is (
|
||||||
|
|
||||||
select results_eq (
|
select results_eq (
|
||||||
'user_info',
|
'user_info',
|
||||||
$$ values (0, '', '') $$,
|
$$ values ('', '') $$,
|
||||||
'Should have updated the settings with a guest user'
|
'Should have updated the settings with a guest user'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -78,7 +75,7 @@ select is (
|
||||||
|
|
||||||
select results_eq (
|
select results_eq (
|
||||||
'user_info',
|
'user_info',
|
||||||
$$ values (0, '', '') $$,
|
$$ values ('', '') $$,
|
||||||
'Should have left the settings with a guest user'
|
'Should have left the settings with a guest user'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -92,7 +89,7 @@ select is (
|
||||||
|
|
||||||
select results_eq (
|
select results_eq (
|
||||||
'user_info',
|
'user_info',
|
||||||
$$ values (0, '', '') $$,
|
$$ values ('', '') $$,
|
||||||
'Should have left the settings with a guest user'
|
'Should have left the settings with a guest user'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -104,7 +101,7 @@ select is (
|
||||||
|
|
||||||
select results_eq (
|
select results_eq (
|
||||||
'user_info',
|
'user_info',
|
||||||
$$ values (0, '', '') $$,
|
$$ values ('', '') $$,
|
||||||
'Should have left the settings with a guest user'
|
'Should have left the settings with a guest user'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
-- Test current_app_user
|
|
||||||
set client_min_messages to warning;
|
|
||||||
create extension if not exists pgtap;
|
|
||||||
reset client_min_messages;
|
|
||||||
|
|
||||||
begin;
|
|
||||||
|
|
||||||
set search_path to numerus, auth, public;
|
|
||||||
|
|
||||||
select plan(15);
|
|
||||||
|
|
||||||
select has_function('numerus', 'current_app_user', array []::name[]);
|
|
||||||
select function_lang_is('numerus', 'current_app_user', array []::name[], 'sql');
|
|
||||||
select function_returns('numerus', 'current_app_user', array []::name[], 'text');
|
|
||||||
select isnt_definer('numerus', 'current_app_user', array []::name[]);
|
|
||||||
select volatility_is('numerus', 'current_app_user', array []::name[], 'stable');
|
|
||||||
select function_privs_are('numerus', 'current_app_user', array []::name[], 'guest', array ['EXECUTE']);
|
|
||||||
select function_privs_are('numerus', 'current_app_user', array []::name[], 'invoicer', array ['EXECUTE']);
|
|
||||||
select function_privs_are('numerus', 'current_app_user', array []::name[], 'admin', array ['EXECUTE']);
|
|
||||||
select function_privs_are('numerus', 'current_app_user', array []::name[], 'authenticator', array []::text []);
|
|
||||||
|
|
||||||
set client_min_messages to warning;
|
|
||||||
truncate auth."user" cascade;
|
|
||||||
reset client_min_messages;
|
|
||||||
|
|
||||||
insert into auth."user" (user_id, email, name, password, role, cookie, cookie_expires_at)
|
|
||||||
values (1, 'demo@tandem.blog', 'Demo', 'test', 'invoicer', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
|
||||||
, (5, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
|
||||||
;
|
|
||||||
|
|
||||||
select lives_ok(
|
|
||||||
$$ select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog') $$,
|
|
||||||
'Should change ok for the first user'
|
|
||||||
);
|
|
||||||
|
|
||||||
select is(current_app_user(), '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', 'Should be running as the first user');
|
|
||||||
|
|
||||||
reset role;
|
|
||||||
|
|
||||||
|
|
||||||
select lives_ok(
|
|
||||||
$$ select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog') $$,
|
|
||||||
'Should change ok for the second user'
|
|
||||||
);
|
|
||||||
|
|
||||||
select is(current_app_user(), '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', 'Should be running as the second user');
|
|
||||||
|
|
||||||
reset role;
|
|
||||||
|
|
||||||
select lives_ok(
|
|
||||||
$$ select set_cookie('') $$,
|
|
||||||
'Should change ok for a guest user'
|
|
||||||
);
|
|
||||||
|
|
||||||
select is(current_app_user(), '', 'Should be running as the first user');
|
|
||||||
|
|
||||||
reset role;
|
|
||||||
|
|
||||||
select *
|
|
||||||
from finish();
|
|
||||||
|
|
||||||
rollback;
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
-- Test current_user_cookie
|
||||||
|
set client_min_messages to warning;
|
||||||
|
create extension if not exists pgtap;
|
||||||
|
reset client_min_messages;
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
set search_path to numerus, auth, public;
|
||||||
|
|
||||||
|
select plan(15);
|
||||||
|
|
||||||
|
select has_function('numerus', 'current_user_cookie', array []::name[]);
|
||||||
|
select function_lang_is('numerus', 'current_user_cookie', array []::name[], 'sql');
|
||||||
|
select function_returns('numerus', 'current_user_cookie', array []::name[], 'text');
|
||||||
|
select isnt_definer('numerus', 'current_user_cookie', array []::name[]);
|
||||||
|
select volatility_is('numerus', 'current_user_cookie', array []::name[], 'stable');
|
||||||
|
select function_privs_are('numerus', 'current_user_cookie', array []::name[], 'guest', array ['EXECUTE']);
|
||||||
|
select function_privs_are('numerus', 'current_user_cookie', array []::name[], 'invoicer', array ['EXECUTE']);
|
||||||
|
select function_privs_are('numerus', 'current_user_cookie', array []::name[], 'admin', array ['EXECUTE']);
|
||||||
|
select function_privs_are('numerus', 'current_user_cookie', array []::name[], 'authenticator', array []::text []);
|
||||||
|
|
||||||
|
set client_min_messages to warning;
|
||||||
|
truncate auth."user" cascade;
|
||||||
|
reset client_min_messages;
|
||||||
|
|
||||||
|
insert into auth."user" (user_id, email, name, password, role, cookie, cookie_expires_at)
|
||||||
|
values (1, 'demo@tandem.blog', 'Demo', 'test', 'invoicer', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
||||||
|
, (5, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
||||||
|
;
|
||||||
|
|
||||||
|
select lives_ok(
|
||||||
|
$$ select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog') $$,
|
||||||
|
'Should change ok for the first user'
|
||||||
|
);
|
||||||
|
|
||||||
|
select is(current_user_cookie(), '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', 'Should return the cookie of the first user');
|
||||||
|
|
||||||
|
reset role;
|
||||||
|
|
||||||
|
|
||||||
|
select lives_ok(
|
||||||
|
$$ select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog') $$,
|
||||||
|
'Should change ok for the second user'
|
||||||
|
);
|
||||||
|
|
||||||
|
select is(current_user_cookie(), '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', 'Should return the cookie of the second user');
|
||||||
|
|
||||||
|
reset role;
|
||||||
|
|
||||||
|
select lives_ok(
|
||||||
|
$$ select set_cookie('') $$,
|
||||||
|
'Should change ok for a guest user'
|
||||||
|
);
|
||||||
|
|
||||||
|
select is(current_user_cookie(), '', 'Should return an empty string');
|
||||||
|
|
||||||
|
reset role;
|
||||||
|
|
||||||
|
select *
|
||||||
|
from finish();
|
||||||
|
|
||||||
|
rollback;
|
|
@ -0,0 +1,63 @@
|
||||||
|
-- Test current_user_email
|
||||||
|
set client_min_messages to warning;
|
||||||
|
create extension if not exists pgtap;
|
||||||
|
reset client_min_messages;
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
set search_path to numerus, auth, public;
|
||||||
|
|
||||||
|
select plan(15);
|
||||||
|
|
||||||
|
select has_function('numerus', 'current_user_email', array []::name[]);
|
||||||
|
select function_lang_is('numerus', 'current_user_email', array []::name[], 'sql');
|
||||||
|
select function_returns('numerus', 'current_user_email', array []::name[], 'text');
|
||||||
|
select isnt_definer('numerus', 'current_user_email', array []::name[]);
|
||||||
|
select volatility_is('numerus', 'current_user_email', array []::name[], 'stable');
|
||||||
|
select function_privs_are('numerus', 'current_user_email', array []::name[], 'guest', array ['EXECUTE']);
|
||||||
|
select function_privs_are('numerus', 'current_user_email', array []::name[], 'invoicer', array ['EXECUTE']);
|
||||||
|
select function_privs_are('numerus', 'current_user_email', array []::name[], 'admin', array ['EXECUTE']);
|
||||||
|
select function_privs_are('numerus', 'current_user_email', array []::name[], 'authenticator', array []::text []);
|
||||||
|
|
||||||
|
set client_min_messages to warning;
|
||||||
|
truncate auth."user" cascade;
|
||||||
|
reset client_min_messages;
|
||||||
|
|
||||||
|
insert into auth."user" (user_id, email, name, password, role, cookie, cookie_expires_at)
|
||||||
|
values (1, 'demo@tandem.blog', 'Demo', 'test', 'invoicer', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
||||||
|
, (5, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
||||||
|
;
|
||||||
|
|
||||||
|
select lives_ok(
|
||||||
|
$$ select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog') $$,
|
||||||
|
'Should change ok for the first user'
|
||||||
|
);
|
||||||
|
|
||||||
|
select is(current_user_email(), 'demo@tandem.blog', 'Should return the email of the first user');
|
||||||
|
|
||||||
|
reset role;
|
||||||
|
|
||||||
|
|
||||||
|
select lives_ok(
|
||||||
|
$$ select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog') $$,
|
||||||
|
'Should change ok for the second user'
|
||||||
|
);
|
||||||
|
|
||||||
|
select is(current_user_email(), 'admin@tandem.blog', 'Should return the email of the second user');
|
||||||
|
|
||||||
|
reset role;
|
||||||
|
|
||||||
|
select lives_ok(
|
||||||
|
$$ select set_cookie('') $$,
|
||||||
|
'Should change ok for a guest user'
|
||||||
|
);
|
||||||
|
|
||||||
|
select is(current_user_email(), '', 'Should return an empty string');
|
||||||
|
|
||||||
|
reset role;
|
||||||
|
|
||||||
|
|
||||||
|
select *
|
||||||
|
from finish();
|
||||||
|
|
||||||
|
rollback;
|
|
@ -5,7 +5,7 @@ reset client_min_messages;
|
||||||
|
|
||||||
begin;
|
begin;
|
||||||
|
|
||||||
select plan(15);
|
select plan(17);
|
||||||
|
|
||||||
set search_path to auth, numerus, public;
|
set search_path to auth, numerus, public;
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ select cookie, cookie_expires_at from "user" order by user_id
|
||||||
;
|
;
|
||||||
|
|
||||||
select set_config('request.user.cookie', '', false);
|
select set_config('request.user.cookie', '', false);
|
||||||
|
select set_config('request.user.email', '', false);
|
||||||
select lives_ok( $$ select * from logout() $$, 'Can logout “nobody”' );
|
select lives_ok( $$ select * from logout() $$, 'Can logout “nobody”' );
|
||||||
|
|
||||||
select results_eq(
|
select results_eq(
|
||||||
|
@ -40,10 +41,23 @@ select results_eq(
|
||||||
$$ values ('8c23d4a8d777775f8fc507676a0d99d3dfa54b03b1b257c838', current_timestamp + interval '1 day')
|
$$ values ('8c23d4a8d777775f8fc507676a0d99d3dfa54b03b1b257c838', current_timestamp + interval '1 day')
|
||||||
, ('0169e5f668eec1e6749fd25388b057997358efa8dfd697961a', current_timestamp + interval '2 day')
|
, ('0169e5f668eec1e6749fd25388b057997358efa8dfd697961a', current_timestamp + interval '2 day')
|
||||||
$$,
|
$$,
|
||||||
'Nothing changed'
|
'Should have changed nothing'
|
||||||
|
);
|
||||||
|
|
||||||
|
select set_config('request.user.cookie', '0169e5f668eec1e6749fd25388b057997358efa8dfd697961a', false);
|
||||||
|
select set_config('request.user.email', 'info@tandem.blog', false);
|
||||||
|
select lives_ok( $$ select * from logout() $$, 'Can logout even if the email and cookie does not match' );
|
||||||
|
|
||||||
|
select results_eq(
|
||||||
|
'user_cookies',
|
||||||
|
$$ values ('8c23d4a8d777775f8fc507676a0d99d3dfa54b03b1b257c838', current_timestamp + interval '1 day')
|
||||||
|
, ('0169e5f668eec1e6749fd25388b057997358efa8dfd697961a', current_timestamp + interval '2 day')
|
||||||
|
$$,
|
||||||
|
'Should have changed nothing'
|
||||||
);
|
);
|
||||||
|
|
||||||
select set_config('request.user.cookie', '8c23d4a8d777775f8fc507676a0d99d3dfa54b03b1b257c838', false);
|
select set_config('request.user.cookie', '8c23d4a8d777775f8fc507676a0d99d3dfa54b03b1b257c838', false);
|
||||||
|
select set_config('request.user.email', 'info@tandem.blog', false);
|
||||||
select lives_ok( $$ select * from logout() $$, 'Can logout the first user' );
|
select lives_ok( $$ select * from logout() $$, 'Can logout the first user' );
|
||||||
|
|
||||||
select results_eq(
|
select results_eq(
|
||||||
|
@ -55,6 +69,7 @@ select results_eq(
|
||||||
);
|
);
|
||||||
|
|
||||||
select set_config('request.user.cookie', '0169e5f668eec1e6749fd25388b057997358efa8dfd697961a', false);
|
select set_config('request.user.cookie', '0169e5f668eec1e6749fd25388b057997358efa8dfd697961a', false);
|
||||||
|
select set_config('request.user.email', 'admin@tandem.blog', false);
|
||||||
select lives_ok( $$ select * from logout() $$, 'Can logout the second user' );
|
select lives_ok( $$ select * from logout() $$, 'Can logout the second user' );
|
||||||
|
|
||||||
select results_eq(
|
select results_eq(
|
||||||
|
|
|
@ -29,7 +29,7 @@ values (1, 'demo@tandem.blog', 'Demo', 'test', 'invoicer', '44facbb30d8a419dfd4b
|
||||||
;
|
;
|
||||||
|
|
||||||
prepare user_info as
|
prepare user_info as
|
||||||
select current_setting('request.user.id', true)::integer, current_setting('request.user.email', true), current_user;
|
select current_user_email(), current_user_cookie(), current_user;
|
||||||
|
|
||||||
select lives_ok(
|
select lives_ok(
|
||||||
$$ select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog') $$,
|
$$ select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog') $$,
|
||||||
|
@ -38,7 +38,7 @@ select lives_ok(
|
||||||
|
|
||||||
select results_eq(
|
select results_eq(
|
||||||
'user_info',
|
'user_info',
|
||||||
$$ values (1, 'demo@tandem.blog', 'invoicer'::name) $$,
|
$$ values ('demo@tandem.blog', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', 'invoicer'::name) $$,
|
||||||
'Should have updated the info with the correct user'
|
'Should have updated the info with the correct user'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ select lives_ok(
|
||||||
|
|
||||||
select results_eq(
|
select results_eq(
|
||||||
'user_info',
|
'user_info',
|
||||||
$$ values (5, 'admin@tandem.blog', 'admin'::name) $$,
|
$$ values ('admin@tandem.blog', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', 'admin'::name) $$,
|
||||||
'Should have updated the info with the other user'
|
'Should have updated the info with the other user'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ select lives_ok(
|
||||||
|
|
||||||
select results_eq(
|
select results_eq(
|
||||||
'user_info',
|
'user_info',
|
||||||
$$ values (0, '', 'guest'::name) $$,
|
$$ values ('', '', 'guest'::name) $$,
|
||||||
'Should have updated the info as a guest user'
|
'Should have updated the info as a guest user'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- Verify numerus:build_cookie on pg
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
select has_function_privilege('numerus.build_cookie(numerus.email, text)', 'execute');
|
||||||
|
|
||||||
|
rollback;
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- Verify numerus:change_password on pg
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
select has_function_privilege('numerus.change_password(text)', 'execute');
|
||||||
|
|
||||||
|
rollback;
|
|
@ -1,7 +0,0 @@
|
||||||
-- Verify numerus:current_app_user on pg
|
|
||||||
|
|
||||||
begin;
|
|
||||||
|
|
||||||
select has_function_privilege('numerus.current_app_user()', 'execute');
|
|
||||||
|
|
||||||
rollback;
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- Verify numerus:current_user_cookie on pg
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
select has_function_privilege('numerus.current_user_cookie()', 'execute');
|
||||||
|
|
||||||
|
rollback;
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- Verify numerus:current_user_email on pg
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
select has_function_privilege('numerus.current_user_email()', 'execute');
|
||||||
|
|
||||||
|
rollback;
|
|
@ -11,4 +11,35 @@ select
|
||||||
from numerus.user_profile
|
from numerus.user_profile
|
||||||
where false;
|
where false;
|
||||||
|
|
||||||
|
select has_function_privilege('numerus.update_user_profile()', 'execute');
|
||||||
|
|
||||||
|
select 1/count(*)
|
||||||
|
from pg_trigger
|
||||||
|
where not tgisinternal
|
||||||
|
and tgname = 'update_user_profile'
|
||||||
|
and tgrelid = 'numerus.user_profile'::regclass
|
||||||
|
and tgtype = b'01010001'::int;
|
||||||
|
-- │││││││
|
||||||
|
-- ││││││└─> row
|
||||||
|
-- │││││└──> before
|
||||||
|
-- ││││└───> insert
|
||||||
|
-- │││└────> delete
|
||||||
|
-- ││└─────> update
|
||||||
|
-- │└──────> truncate
|
||||||
|
-- └───────> instead
|
||||||
|
select 1/count(*)
|
||||||
|
from pg_trigger
|
||||||
|
where not tgisinternal
|
||||||
|
and tgname = 'encrypt_password'
|
||||||
|
and tgrelid = 'auth.user'::regclass
|
||||||
|
and tgtype = b'00010111'::int;
|
||||||
|
-- │││││││
|
||||||
|
-- ││││││└─> row
|
||||||
|
-- │││││└──> before
|
||||||
|
-- ││││└───> insert
|
||||||
|
-- │││└────> delete
|
||||||
|
-- ││└─────> update
|
||||||
|
-- │└──────> truncate
|
||||||
|
-- └───────> instead
|
||||||
|
|
||||||
rollback;
|
rollback;
|
||||||
|
|
|
@ -189,6 +189,7 @@ p, h1, h2, h3, h4, h5, h6 {
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="submit"], button {
|
input[type="submit"], button {
|
||||||
|
min-width: 34rem;
|
||||||
background-color: var(--numerus--color--white);
|
background-color: var(--numerus--color--white);
|
||||||
border: 2px solid var(--numerus--color--black);
|
border: 2px solid var(--numerus--color--black);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
@ -279,11 +280,59 @@ input[type="text"], input[type="password"], input[type="email"], select {
|
||||||
transition: 0.2s;
|
transition: 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.relative {
|
fieldset {
|
||||||
|
border: none;
|
||||||
|
padding: 2rem 0 0;
|
||||||
|
margin-top: 3rem;
|
||||||
|
border-top: 1px solid var(--numerus--color--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
float: left;
|
||||||
|
font-style: italic;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend + * {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-width {
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-width legend {
|
||||||
|
margin-bottom: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.full-width .input {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-width input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-content {
|
||||||
|
max-width: 120rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Profile Menu */
|
||||||
|
|
||||||
|
#profilemenu {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profilebutton {
|
#profilemenu summary {
|
||||||
width: 7rem;
|
width: 7rem;
|
||||||
height: 7rem;
|
height: 7rem;
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
|
@ -292,45 +341,41 @@ input[type="text"], input[type="password"], input[type="email"], select {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: none;
|
border: none;
|
||||||
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profilebutton, #profilemenu button {
|
#profilemenu summary::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#profilemenu summary, #profilemenu button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profilemenu {
|
#profilemenu summary, #profilemenu ul {
|
||||||
|
background-color: var(--numerus--background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#profilemenu[open] summary::before {
|
||||||
|
background-color: var(--numerus--header--background-color);
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
content: "";
|
||||||
|
cursor: default;
|
||||||
|
z-index: 10;
|
||||||
|
mix-blend-mode: multiply;
|
||||||
|
}
|
||||||
|
|
||||||
|
#profilemenu ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -1.875em;
|
right: -1.875em;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
padding: 1rem 2rem;
|
padding: 1rem 2rem;
|
||||||
background-color: var(--numerus--color--white);
|
z-index: 20;
|
||||||
display: none;
|
|
||||||
opacity: 0;
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
header div:hover #profilemenu {
|
|
||||||
opacity: 1;
|
|
||||||
display: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .overlay {
|
|
||||||
background-color: var(--numerus--header--background-color);
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
opacity: 0;
|
|
||||||
display: none;
|
|
||||||
pointer-events: none;
|
|
||||||
mix-blend-mode: multiply;
|
|
||||||
}
|
|
||||||
|
|
||||||
header div:hover + .overlay {
|
|
||||||
display: block;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#profilemenu li + li {
|
#profilemenu li + li {
|
||||||
|
@ -351,12 +396,12 @@ header div:hover + .overlay {
|
||||||
text-transform: initial;
|
text-transform: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profilemenu i[class^='ri-'] {
|
#profilemenu li i[class^='ri-'] {
|
||||||
margin-right: 2rem;
|
margin-right: 2rem;
|
||||||
color: var(--numerus--color--dark-gray);
|
color: var(--numerus--color--dark-gray);
|
||||||
}
|
}
|
||||||
|
|
||||||
#profilemenu button:hover, #profilemenu a:hover {
|
#profilemenu summary:hover, #profilemenu summary:focus, #profilemenu button:hover, #profilemenu a:hover {
|
||||||
background-color: var(--numerus--color--light-gray);
|
background-color: var(--numerus--color--light-gray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,18 +9,27 @@
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<h1><img src="/static/numerus.svg" alt="Numerus" width="261" height="33"></h1>
|
<h1><img src="/static/numerus.svg" alt="Numerus" width="261" height="33"></h1>
|
||||||
<div class="relative">
|
<details id="profilemenu">
|
||||||
<button id="profilebutton" aria-controls="profilemenu" aria-haspopup="true"><i class="ri-eye-close-line ri-3x"></i></button>
|
<summary>
|
||||||
<ul id="profilemenu" role="menu" aria-labelledby="profilebutton">
|
<i class="ri-eye-close-line ri-3x"></i>
|
||||||
|
</summary>
|
||||||
|
<ul role="menu">
|
||||||
<li role="presentation">
|
<li role="presentation">
|
||||||
<a role="menuitem" href="/profile"><i class="ri-account-circle-line"></i> {{( gettext "Account" )}}</a>
|
<a role="menuitem" href="/profile">
|
||||||
|
<i class="ri-account-circle-line"></i>
|
||||||
|
{{( gettext "Account" )}}
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li role="presentation">
|
<li role="presentation">
|
||||||
<form method="POST" action="/logout"><button type="submit" role="menuitem"><i class="ri-logout-circle-line"></i> {{( pgettext "Logout" "action" )}}</button></form>
|
<form method="POST" action="/logout">
|
||||||
|
<button type="submit" role="menuitem">
|
||||||
|
<i class="ri-logout-circle-line"></i>
|
||||||
|
{{( pgettext "Logout" "action" )}}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</details>
|
||||||
<div class="overlay"></div>
|
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
{{- template "content" . }}
|
{{- template "content" . }}
|
||||||
|
|
|
@ -1,40 +1,46 @@
|
||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
<h2>{{(pgettext "User Settings" "title")}}</h2>
|
<section class="dialog-content">
|
||||||
<form method="POST" action="/profile">
|
<h2>{{(pgettext "User Settings" "title")}}</h2>
|
||||||
<fieldset>
|
<form method="POST" action="/profile">
|
||||||
<legend>{{( pgettext "User Access Data" "title" )}}</legend>
|
<fieldset class="full-width">
|
||||||
|
<legend>{{( pgettext "User Access Data" "title" )}}</legend>
|
||||||
|
|
||||||
<div class="input">
|
<div class="input">
|
||||||
<input type="text" name="name" id="name" required="required" value="{{ .Name }}" placeholder="{{( pgettext "User name" "input" )}}">
|
<input type="text" name="name" id="name" required="required" value="{{ .Name }}" placeholder="{{( pgettext "User name" "input" )}}">
|
||||||
<label for="name">{{( pgettext "User name" "input" )}}</label>
|
<label for="name">{{( pgettext "User name" "input" )}}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input">
|
<div class="input">
|
||||||
<input type="email" name="email" id="email" required="required" value="{{ .Email }}" placeholder="{{( pgettext "Email" "input" )}}">
|
<input type="email" name="email" id="email" required="required" value="{{ .Email }}" placeholder="{{( pgettext "Email" "input" )}}">
|
||||||
<label for="email">{{( pgettext "Email" "input" )}}</label>
|
<label for="email">{{( pgettext "Email" "input" )}}</label>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset class="full-width">
|
||||||
<legend>{{( pgettext "Password Change" "title" )}}</legend>
|
<legend>{{( pgettext "Password Change" "title" )}}</legend>
|
||||||
|
|
||||||
<div class="input">
|
<div class="input">
|
||||||
<input type="password" name="password" id="password" value="{{ .Password }}" placeholder="{{( pgettext "Password" "input" )}}">
|
<input type="password" name="password" id="password" value="{{ .Password }}" placeholder="{{( pgettext "Password" "input" )}}">
|
||||||
<label for="password">{{( pgettext "Password" "input" )}}</label>
|
<label for="password">{{( pgettext "Password" "input" )}}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input">
|
<div class="input">
|
||||||
<input type="password" name="password_confirm" id="password_confirm" value="{{ .PasswordConfirm }}" placeholder="{{( pgettext "Password Confirmation" "input" )}}">
|
<input type="password" name="password_confirm" id="password_confirm" value="{{ .PasswordConfirm }}" placeholder="{{( pgettext "Password Confirmation" "input" )}}">
|
||||||
<label for="password_confirm">{{( pgettext "Password Confirmation" "input" )}}</label>
|
<label for="password_confirm">{{( pgettext "Password Confirmation" "input" )}}</label>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<label for="language">{{( pgettext "Language" "input" )}}</label>
|
<fieldset>
|
||||||
<select id="language" name="language">
|
<legend id="language-legend">{{( pgettext "Language" "input" )}}</legend>
|
||||||
<option value="und">{{( pgettext "Automatic" "language option" )}}</option>
|
|
||||||
{{- range $language := .Languages }}
|
<select id="language" name="language" aria-labelledby="language-legend">
|
||||||
<option value="{{ .Tag }}" {{ if eq .Tag $.Language }}selected="selected"{{ end }}>{{ .Name }}</option>
|
<option value="und">{{( pgettext "Automatic" "language option" )}}</option>
|
||||||
{{- end }}
|
{{- range $language := .Languages }}
|
||||||
</select>
|
<option value="{{ .Tag }}" {{ if eq .Tag $.Language }}selected="selected"{{ end }}>{{ .Name }}</option>
|
||||||
<button type="submit">{{( pgettext "Save changes" "action" )}}</button>
|
{{- end }}
|
||||||
</form>
|
</select>
|
||||||
|
|
||||||
|
<button type="submit">{{( pgettext "Save changes" "action" )}}</button>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
Loading…
Reference in New Issue