From d9c93b8797adb31e454652d5a2de87ff6bf06ec5 Mon Sep 17 00:00:00 2001 From: jordi fita mas Date: Mon, 23 Jan 2023 21:41:14 +0100 Subject: [PATCH] =?UTF-8?q?Add=20function=20to=20change=20the=20current=20?= =?UTF-8?q?user=E2=80=99s=20password?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function does not ask for the confirmation because this is an user-facing issue, not for the database. Still missing: validation and proper error messages. --- deploy/change_password.sql | 28 +++++++++++++++++++ pkg/profile.go | 3 ++ revert/change_password.sql | 7 +++++ sqitch.plan | 1 + test/change_password.sql | 57 ++++++++++++++++++++++++++++++++++++++ verify/change_password.sql | 7 +++++ 6 files changed, 103 insertions(+) create mode 100644 deploy/change_password.sql create mode 100644 revert/change_password.sql create mode 100644 test/change_password.sql create mode 100644 verify/change_password.sql diff --git a/deploy/change_password.sql b/deploy/change_password.sql new file mode 100644 index 0000000..3bb57fa --- /dev/null +++ b/deploy/change_password.sql @@ -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; diff --git a/pkg/profile.go b/pkg/profile.go index 285f9c1..7a2d990 100644 --- a/pkg/profile.go +++ b/pkg/profile.go @@ -43,6 +43,9 @@ func ProfileHandler() http.Handler { page.Language = r.FormValue("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) setSessionCookie(w, cookie) + 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 { diff --git a/revert/change_password.sql b/revert/change_password.sql new file mode 100644 index 0000000..5e6a09d --- /dev/null +++ b/revert/change_password.sql @@ -0,0 +1,7 @@ +-- Revert numerus:change_password from pg + +begin; + +drop function if exists numerus.change_password(text); + +commit; diff --git a/sqitch.plan b/sqitch.plan index 216a946..4560fc6 100644 --- a/sqitch.plan +++ b/sqitch.plan @@ -23,3 +23,4 @@ logout [schema_auth current_user_email current_user_cookie user] 2023-01-17T19:1 set_cookie [schema_public check_cookie] 2023-01-19T11:00:22Z jordi fita mas # Add function to set the role based on the cookie available_languages [schema_numerus language] 2023-01-21T21:11:08Z jordi fita mas # Add the initial available languages user_profile [schema_numerus user current_user_email current_user_cookie] 2023-01-21T23:18:20Z jordi fita mas # Add view for user profile +change_password [schema_numerus user] 2023-01-23T20:22:45Z jordi fita mas # Add function to change the current user’s password diff --git a/test/change_password.sql b/test/change_password.sql new file mode 100644 index 0000000..d2a1bcd --- /dev/null +++ b/test/change_password.sql @@ -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; diff --git a/verify/change_password.sql b/verify/change_password.sql new file mode 100644 index 0000000..f8277d4 --- /dev/null +++ b/verify/change_password.sql @@ -0,0 +1,7 @@ +-- Verify numerus:change_password on pg + +begin; + +select has_function_privilege('numerus.change_password(text)', 'execute'); + +rollback;