diff --git a/demo.sql b/demo.sql index ef480ea..d39aba7 100644 --- a/demo.sql +++ b/demo.sql @@ -3,9 +3,9 @@ begin; set search_path to camper, auth, public; alter sequence user_user_id_seq restart with 42; -insert into auth."user" (email, name, password, role) -values ('demo@camper', 'Demo User', 'demo', 'employee') - , ('admin@camper', 'Demo Admin', 'admin', 'admin') +insert into auth."user" (email, name, password) +values ('demo@camper', 'Demo User', 'demo') + , ('admin@camper', 'Demo Admin', 'admin') ; alter sequence company_company_id_seq restart with 52; @@ -17,9 +17,9 @@ values (52, 'localhost:8080') , (52, 'camper.tandem.ws') ; -insert into company_user (company_id, user_id) -values (52, 42) - , (52, 43) +insert into company_user (company_id, user_id, role) +values (52, 42, 'employee') + , (52, 43, 'admin') ; commit; diff --git a/deploy/campsite_type.sql b/deploy/campsite_type.sql index 53a69c4..9ce481f 100644 --- a/deploy/campsite_type.sql +++ b/deploy/campsite_type.sql @@ -2,6 +2,7 @@ -- requires: roles -- requires: schema_camper -- requires: company +-- requires: user_profile begin; @@ -16,6 +17,7 @@ create table campsite_type ( active boolean not null default true ); +grant select on table campsite_type to guest; grant select on table campsite_type to employee; grant select, insert, update, delete on table campsite_type to admin; @@ -23,16 +25,35 @@ grant usage on sequence campsite_type_campsite_type_id_seq to admin; alter table campsite_type enable row level security; -create policy company_policy +create policy guest_ok on campsite_type +for select +using (true) +; + +create policy insert_to_company +on campsite_type +for insert +with check ( + company_id in (select company_id from user_profile) +) +; + +create policy update_company +on campsite_type +for update using ( - exists( - select 1 - from company_user - join user_profile using (user_id) - where company_user.company_id = campsite_type.company_id - ) -); + company_id in (select company_id from user_profile) +) +; + +create policy delete_from_company +on campsite_type +for delete +using ( + company_id in (select company_id from user_profile) +) +; commit; diff --git a/deploy/check_cookie.sql b/deploy/check_cookie.sql index 522ba45..b74a7f0 100644 --- a/deploy/check_cookie.sql +++ b/deploy/check_cookie.sql @@ -3,46 +3,53 @@ -- requires: schema_public -- requires: schema_auth -- requires: user +-- requires: company_host +-- requires: company_user begin; set search_path to public, auth; -create or replace function check_cookie(input_cookie text) returns name as +create or replace function check_cookie(input_cookie text, host text) returns name as $$ declare - uid text; - user_email text; - user_role name; + cid text; + user_email text; + user_role name; user_cookie text; begin - select user_id::text, email::text, role, cookie - into uid, user_email, user_role, user_cookie + select company_id::text, email::text, role, cookie + into cid, user_email, user_role, user_cookie from "user" + join company_user using (user_id) + join public.company_host using (company_id) where email = split_part(input_cookie, '/', 2) and cookie_expires_at > current_timestamp and length(password) > 0 - and cookie = split_part(input_cookie, '/', 1); + and cookie = split_part(input_cookie, '/', 1) + and company_host.host = check_cookie.host + ; if user_role is null then - uid := '0'; + cid := '0'; user_email := ''; user_cookie := ''; user_role := 'guest'::name; end if; perform set_config('request.user.email', user_email, false); perform set_config('request.user.cookie', user_cookie, false); + perform set_config('request.company.id', cid, false); return user_role; end; $$ language plpgsql security definer stable -set search_path = auth, pg_temp; +set search_path = auth, camper, pg_temp; -comment on function check_cookie(text) is +comment on function check_cookie(text, text) is '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; -grant execute on function check_cookie(text) to authenticator; +revoke execute on function check_cookie(text, text) from public; +grant execute on function check_cookie(text, text) to authenticator; commit; diff --git a/deploy/company.sql b/deploy/company.sql index fa3560d..ededd1e 100644 --- a/deploy/company.sql +++ b/deploy/company.sql @@ -36,7 +36,8 @@ create table company ( created_at timestamptz not null default current_timestamp ); -grant select, update on table company to employee; +grant select on table company to guest; +grant select on table company to employee; grant select, update on table company to admin; commit; diff --git a/deploy/company_user.sql b/deploy/company_user.sql index b08b27a..130dde1 100644 --- a/deploy/company_user.sql +++ b/deploy/company_user.sql @@ -11,29 +11,16 @@ set search_path to camper, auth, public; create table company_user ( company_id integer not null references company, user_id integer not null references "user", + role name not null check (length(role) < 512), primary key (company_id, user_id) ); grant select on table company_user to employee; grant select on table company_user to admin; - -alter table company enable row level security; - -create policy company_policy -on company -using ( - exists( - select 1 - from company_user - join user_profile using (user_id) - where company_user.company_id = company.company_id - ) -); - -- TODO: --- I think we can not do the same for company_user because it would be --- an infinite loop, but in this case i think it is fine because we can --- only see ids, nothing more. +-- I think we can enable row-level security for company_user because it would +-- be an infinite loop with user_profile, but in this case i think it is fine +-- because we can only see ids, nothing more. commit; diff --git a/deploy/current_company_id.sql b/deploy/current_company_id.sql new file mode 100644 index 0000000..70c09cd --- /dev/null +++ b/deploy/current_company_id.sql @@ -0,0 +1,24 @@ +-- Deploy camper:current_company_id to pg +-- requires: roles +-- requires: schema_camper + +begin; + +set search_path to camper; + +create or replace function current_company_id() returns integer as +$$ +select current_setting('request.company.id', true)::integer; +$$ +language sql +stable; + +comment on function current_company_id() is +'Returns the ID of the current company'; + +revoke execute on function current_company_id() from public; +grant execute on function current_company_id() to guest; +grant execute on function current_company_id() to employee; +grant execute on function current_company_id() to admin; + +commit; diff --git a/deploy/ensure_role_exists.sql b/deploy/ensure_role_exists.sql index b8bbe1e..498b298 100644 --- a/deploy/ensure_role_exists.sql +++ b/deploy/ensure_role_exists.sql @@ -1,10 +1,11 @@ -- Deploy camper:ensure_role_exists to pg --- requires: schema_auth --- requires: user +-- requires: roles +-- requires: schema_camper +-- requires: company_user begin; -set search_path to auth, public; +set search_path to camper, public; create or replace function ensure_role_exists() returns trigger as $$ @@ -24,7 +25,7 @@ revoke execute on function ensure_role_exists() from public; create trigger ensure_role_exists after insert or update -on "user" +on company_user for each row execute procedure ensure_role_exists(); diff --git a/deploy/policies_company.sql b/deploy/policies_company.sql new file mode 100644 index 0000000..ad071ff --- /dev/null +++ b/deploy/policies_company.sql @@ -0,0 +1,25 @@ +-- Deploy camper:policies_company to pg +-- requires: company +-- requires: user_profile + +begin; + +set search_path to camper, public; + +alter table company enable row level security; + +create policy guest_ok +on company +for select +using (true) +; + +create policy update_company +on company +for update +using ( + company_id in (select company_id from user_profile) +) +; + +commit; diff --git a/deploy/set_cookie.sql b/deploy/set_cookie.sql index 70a25d9..67b5908 100644 --- a/deploy/set_cookie.sql +++ b/deploy/set_cookie.sql @@ -7,17 +7,17 @@ begin; set search_path to public; -create or replace function set_cookie(input_cookie text) returns void as +create or replace function set_cookie(input_cookie text, host text) returns void as $$ -select set_config('role', check_cookie(input_cookie), false); +select set_config('role', check_cookie(input_cookie, host), false); $$ language sql stable; -comment on function set_cookie(text) is +comment on function set_cookie(text, text) is 'Sets the user information for the cookie and switches to its role'; -revoke execute on function set_cookie(text) from public; -grant execute on function set_cookie(text) to authenticator; +revoke execute on function set_cookie(text, text) from public; +grant execute on function set_cookie(text, text) to authenticator; commit; diff --git a/deploy/user.sql b/deploy/user.sql index a04b811..cb31b28 100644 --- a/deploy/user.sql +++ b/deploy/user.sql @@ -13,7 +13,6 @@ create table "user" ( email email not null unique, name text not null, password text not null check (length(password) < 512), - role name not null check (length(role) < 512), lang_tag text not null default 'und' references language, cookie text not null default '', cookie_expires_at timestamptz not null default '-infinity'::timestamp, diff --git a/deploy/user_profile.sql b/deploy/user_profile.sql index ea2b3c4..8be249e 100644 --- a/deploy/user_profile.sql +++ b/deploy/user_profile.sql @@ -2,8 +2,10 @@ -- requires: roles -- requires: schema_camper -- requires: user +-- requires: company_user -- requires: current_user_email -- requires: current_user_cookie +-- requires: current_company_id begin; @@ -11,18 +13,22 @@ set search_path to camper, public; create or replace view user_profile with (security_barrier) as select user_id + , company_id , email , name , role , lang_tag , left(cookie, 10) as csrf_token from auth."user" + join company_user using (user_id) where email = current_user_email() and cookie = current_user_cookie() and cookie_expires_at > current_timestamp and length(cookie) > 30 + and company_id = current_company_id() union all select 0 + , 0 , null::email , '' , 'guest'::name diff --git a/pkg/app/user.go b/pkg/app/user.go index c903706..7229113 100644 --- a/pkg/app/user.go +++ b/pkg/app/user.go @@ -26,7 +26,8 @@ import ( func (h *App) getUser(r *http.Request, conn *database.Conn) (*auth.User, error) { cookie := auth.GetSessionCookie(r) - if _, err := conn.Exec(r.Context(), "select set_cookie($1)", cookie); err != nil { + host := httplib.Host(r) + if _, err := conn.Exec(r.Context(), "select set_cookie($1, $2)", cookie, host); err != nil { return nil, err } diff --git a/revert/check_cookie.sql b/revert/check_cookie.sql index e7e946c..9cbcf04 100644 --- a/revert/check_cookie.sql +++ b/revert/check_cookie.sql @@ -2,6 +2,6 @@ begin; -drop function if exists public.check_cookie(text); +drop function if exists public.check_cookie(text, text); commit; diff --git a/revert/company_user.sql b/revert/company_user.sql index 0476377..4179f6d 100644 --- a/revert/company_user.sql +++ b/revert/company_user.sql @@ -2,7 +2,6 @@ begin; -drop policy if exists company_policy on camper.company; drop table if exists camper.company_user; commit; diff --git a/revert/current_company_id.sql b/revert/current_company_id.sql new file mode 100644 index 0000000..ef73d19 --- /dev/null +++ b/revert/current_company_id.sql @@ -0,0 +1,7 @@ +-- Revert camper:current_company_id from pg + +begin; + +drop function if exists camper.current_company_id(); + +commit; diff --git a/revert/ensure_role_exists.sql b/revert/ensure_role_exists.sql index 0409459..5cb2b70 100644 --- a/revert/ensure_role_exists.sql +++ b/revert/ensure_role_exists.sql @@ -2,7 +2,9 @@ begin; -drop trigger if exists ensure_role_exists on auth."user"; -drop function if exists auth.ensure_role_exists(); +set search_path to camper; + +drop trigger if exists ensure_role_exists on company_user; +drop function if exists ensure_role_exists(); commit; diff --git a/revert/policies_company.sql b/revert/policies_company.sql new file mode 100644 index 0000000..368e784 --- /dev/null +++ b/revert/policies_company.sql @@ -0,0 +1,9 @@ +-- Revert camper:policies_company from pg + +begin; + +set search_path to camper, public; +drop policy if exists guest_ok on company; +drop policy if exists update_company on company; + +commit; diff --git a/revert/set_cookie.sql b/revert/set_cookie.sql index a71ccb3..5f13363 100644 --- a/revert/set_cookie.sql +++ b/revert/set_cookie.sql @@ -2,6 +2,6 @@ begin; -drop function if exists public.set_cookie(text); +drop function if exists public.set_cookie(text, text); commit; diff --git a/sqitch.plan b/sqitch.plan index 57babf5..3ac9433 100644 --- a/sqitch.plan +++ b/sqitch.plan @@ -11,19 +11,14 @@ schema_camper [roles] 2023-07-21T22:08:39Z jordi fita mas # email [schema_camper extension_citext] 2023-07-21T22:11:13Z jordi fita mas # Add email domain schema_auth [roles] 2023-07-21T22:13:23Z jordi fita mas # Add authentication schema user [roles schema_auth email language] 2023-07-21T22:37:20Z jordi fita mas # Add user relation -ensure_role_exists [schema_auth user] 2023-07-21T22:48:56Z jordi fita mas # Add trigger to ensure users’ role exists extension_pgcrypto [schema_auth] 2023-07-21T22:53:25Z jordi fita mas # Add pgcrypto extension encrypt_password [schema_auth user extension_pgcrypto] 2023-07-21T22:56:40Z jordi fita mas # Add function to encrypt users’ password current_user_cookie [roles schema_camper] 2023-07-21T23:05:26Z jordi fita mas # Add function to get the cookie of the current user -current_user_email [roles schema_camper] 2023-07-21T23:09:34Z jordi fita mas # Add function to ge the email of the current user +current_user_email [roles schema_camper] 2023-07-21T23:09:34Z jordi fita mas # Add function to get the email of the current user build_cookie [roles schema_camper current_user_email current_user_cookie] 2023-07-21T23:14:35Z jordi fita mas # Add function to build the cookie for the current user login_attempt [schema_auth] 2023-07-21T23:22:17Z jordi fita mas # Add relation of log in attempts login [roles schema_auth schema_camper extension_pgcrypto email user login_attempt build_cookie] 2023-07-21T23:29:18Z jordi fita mas # Add function to login logout [roles schema_auth schema_camper current_user_email current_user_cookie user] 2023-07-21T23:36:12Z jordi fita mas # Add function to logout -check_cookie [roles schema_public schema_auth user] 2023-07-21T23:40:55Z jordi fita mas # Add function to check if a user cookie is valid -set_cookie [roles schema_public check_cookie] 2023-07-21T23:44:30Z jordi fita mas # Add function to set the role base don the cookie -user_profile [roles schema_camper user current_user_email current_user_cookie] 2023-07-21T23:47:36Z jordi fita mas # Add view for user profile -change_password [roles schema_auth schema_camper user] 2023-07-21T23:54:52Z jordi fita mas # Add function to change the current user’s password extension_vat [schema_public] 2023-07-29T01:18:45Z jordi fita mas # Add vat extension extension_pg_libphonenumber [schema_public] 2023-07-29T01:21:12Z jordi fita mas # Add phone numbers extension extension_uri [schema_public] 2023-07-29T01:23:46Z jordi fita mas # Add URI extension @@ -36,6 +31,13 @@ country_i18n [roles schema_camper country_code language country] 2023-07-29T01:4 available_countries [schema_camper country country_i18n] 2023-07-29T01:48:40Z jordi fita mas # Add the list of available countries company [roles schema_camper extension_vat email extension_pg_libphonenumber extension_uri currency_code currency country_code country language] 2023-07-29T01:56:41Z jordi fita mas # Add relation for company company_user [roles schema_camper user company] 2023-07-29T02:08:07Z jordi fita mas # Add relation of company user +ensure_role_exists [roles schema_camper company_user] 2023-07-21T22:48:56Z jordi fita mas # Add trigger to ensure users’ role exists company_host [roles schema_public] 2023-08-03T17:46:45Z jordi fita mas # Add relation of DNS domain and company -campsite_type [roles schema_camper company] 2023-07-31T11:20:29Z jordi fita mas # Add relation of campsite type +check_cookie [roles schema_public schema_auth user company_host company_user] 2023-07-21T23:40:55Z jordi fita mas # Add function to check if a user cookie is valid +set_cookie [roles schema_public check_cookie] 2023-07-21T23:44:30Z jordi fita mas # Add function to set the role base don the cookie +current_company_id [roles schema_camper] 2023-08-07T10:44:36Z jordi fita mas # Add function to get the ID of the current company +user_profile [roles schema_camper user company_user current_user_email current_user_cookie current_company_id] 2023-07-21T23:47:36Z jordi fita mas # Add view for user profile +policies_company [company user_profile] 2023-08-07T20:04:26Z jordi fita mas # Add row-level security profiles to company +change_password [roles schema_auth schema_camper user] 2023-07-21T23:54:52Z jordi fita mas # Add function to change the current user’s password +campsite_type [roles schema_camper company user_profile] 2023-07-31T11:20:29Z jordi fita mas # Add relation of campsite type add_campsite_type [roles schema_camper campsite_type company] 2023-08-04T16:14:48Z jordi fita mas # Add function to create campsite types diff --git a/test/build_cookie.sql b/test/build_cookie.sql index a51c157..a4923f6 100644 --- a/test/build_cookie.sql +++ b/test/build_cookie.sql @@ -20,14 +20,28 @@ select function_privs_are('camper', 'build_cookie', array ['email', 'text'], 'ad select function_privs_are('camper', 'build_cookie', array ['email', 'text'], 'authenticator', array []::text[]); set client_min_messages to warning; +truncate company_host cascade; +truncate company_user cascade; +truncate company cascade; 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', 'employee', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', - current_timestamp + interval '1 month') - , (9, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', - current_timestamp + interval '1 month') +insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at) +values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') + , (9, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') +; + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_lang_tag) +values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca') +; + +insert into company_user (company_id, user_id, role) +values (2, 1, 'employee') + , (2, 9, 'admin') +; + +insert into company_host (company_id, host) +values (2, 'localhost') ; select is( @@ -36,7 +50,7 @@ select is( 'Should build the cookie with the given user and cookie value' ); -select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog'); +select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'localhost'); reset role; select is( @@ -46,7 +60,7 @@ select is( ); -select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog'); +select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog', 'localhost'); reset role; select is( @@ -56,7 +70,7 @@ select is( ); -select set_cookie('ashtasth'); +select set_cookie('ashtasth', 'localhost'); reset role; select is( diff --git a/test/campsite_type.sql b/test/campsite_type.sql index 1a71dea..2fcfb66 100644 --- a/test/campsite_type.sql +++ b/test/campsite_type.sql @@ -5,13 +5,13 @@ reset client_min_messages; begin; -select plan(47); +select plan(55); set search_path to camper, public; select has_table('campsite_type'); select has_pk('campsite_type' ); -select table_privs_are('campsite_type', 'guest', array[]::text[]); +select table_privs_are('campsite_type', 'guest', array['SELECT']::text[]); select table_privs_are('campsite_type', 'employee', array['SELECT']); select table_privs_are('campsite_type', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']); select table_privs_are('campsite_type', 'authenticator', array[]::text[]); @@ -63,14 +63,15 @@ select col_default_is('campsite_type', 'active', 'true'); set client_min_messages to warning; truncate campsite_type cascade; +truncate company_host cascade; truncate company_user cascade; truncate company cascade; 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', 'employee', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') - , (5, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') +insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at) +values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') + , (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') ; insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_lang_tag) @@ -78,9 +79,14 @@ values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', ' , (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 'ca') ; -insert into company_user (company_id, user_id) -values (2, 1) - , (4, 5) +insert into company_user (company_id, user_id, role) +values (2, 1, 'admin') + , (4, 5, 'admin') +; + +insert into company_host (company_id, host) +values (2, 'co2') + , (4, 'co4') ; insert into campsite_type (company_id, name) @@ -93,44 +99,99 @@ select company_id, name from campsite_type order by company_id, name; -set role employee; -select is_empty('campsite_type_data', 'Should show no data when cookie is not set yet'); -reset role; - -select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog'); +set role guest; select bag_eq( 'campsite_type_data', $$ values (2, 'Wooden lodge') + , (4, 'Bungalow') $$, - 'Should only list campsite types of the companies where demo@tandem.blog is user of' + 'Everyone should be able to list all campsite types across all companies' ); reset role; -select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog'); +select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2'); + +select lives_ok( + $$ insert into campsite_type(company_id, name) values (2, 'Another type' ) $$, + 'Admin from company 2 should be able to insert a new campsite type to that company.' +); + select bag_eq( 'campsite_type_data', - $$ values (4, 'Bungalow') + $$ values (2, 'Wooden lodge') + , (2, 'Another type') + , (4, 'Bungalow') $$, - 'Should only list campsite type of the companies where admin@tandem.blog is user of' + 'The new row should have been added' ); -reset role; -select set_cookie('not-a-cookie'); -select throws_ok( +select lives_ok( + $$ update campsite_type set name = 'Another' where company_id = 2 and name = 'Another type' $$, + 'Admin from company 2 should be able to update campsite type of that company.' +); + +select bag_eq( 'campsite_type_data', - '42501', 'permission denied for table campsite_type', - 'Should not allow select campsite types to guest users' -); -reset role; - -select throws_ok( $$ - insert into campsite_type (company_id, name) - values (2, ' ') + $$ values (2, 'Wooden lodge') + , (2, 'Another') + , (4, 'Bungalow') $$, - '23514', 'new row for relation "campsite_type" violates check constraint "name_not_empty"', - 'Should not allow campsite types with blank name' + 'The row should have been updated.' ); +select lives_ok( + $$ delete from campsite_type where company_id = 2 and name = 'Another' $$, + 'Admin from company 2 should be able to delete campsite type from that company.' +); + +select bag_eq( + 'campsite_type_data', + $$ values (2, 'Wooden lodge') + , (4, 'Bungalow') + $$, + 'The row should have been deleted.' +); + +select throws_ok( + $$ insert into campsite_type (company_id, name) values (4, 'Another type' ) $$, + '42501', 'new row violates row-level security policy for table "campsite_type"', + 'Admin from company 2 should NOT be able to insert new campsite types to company 4.' +); + +select lives_ok( + $$ update campsite_type set name = 'Nope' where company_id = 4 $$, + 'Admin from company 2 should not be able to update new campsite types of company 4, but no error if company_id is not changed.' +); + +select bag_eq( + 'campsite_type_data', + $$ values (2, 'Wooden lodge') + , (4, 'Bungalow') + $$, + 'No row should have been changed.' +); + +select throws_ok( + $$ update campsite_type set company_id = 4 where company_id = 2 $$, + '42501', 'new row violates row-level security policy for table "campsite_type"', + 'Admin from company 2 should NOT be able to move campsite types to company 4' +); + +select lives_ok( + $$ delete from campsite_type where company_id = 4 $$, + 'Admin from company 2 should NOT be able to delete campsite types from company 4, but not error is thrown' +); + +select bag_eq( + 'campsite_type_data', + $$ values (2, 'Wooden lodge') + , (4, 'Bungalow') + $$, + 'No row should have been changed' +); + +reset role; + select * from finish(); diff --git a/test/change_password.sql b/test/change_password.sql index 1ee82e3..6fa2216 100644 --- a/test/change_password.sql +++ b/test/change_password.sql @@ -20,12 +20,28 @@ select function_privs_are('camper', 'change_password', array ['text'], 'admin', select function_privs_are('camper', 'change_password', array ['text'], 'authenticator', array []::text[]); set client_min_messages to warning; +truncate company_host cascade; +truncate company_user cascade; +truncate company cascade; 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', 'employee', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') - , (9, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') +insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at) +values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') + , (9, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') +; + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_lang_tag) +values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca') +; + +insert into company_user (company_id, user_id, role) +values (2, 1, 'employee') + , (2, 9, 'admin') +; + +insert into company_host (company_id, host) +values (2, 'localhost') ; select lives_ok($$ select change_password('another') $$, 'Should run even without current user'); @@ -35,7 +51,7 @@ select isnt_empty( 'Should not have changed any password' ); -select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog'); +select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'localhost'); select lives_ok($$ select change_password('another') $$, 'Should run with the correct user'); diff --git a/test/check_cookie.sql b/test/check_cookie.sql index 4d456da..f53f622 100644 --- a/test/check_cookie.sql +++ b/test/check_cookie.sql @@ -5,77 +5,107 @@ reset client_min_messages; begin; -select plan(21); +select plan(23); set search_path to auth, camper, public; -select has_function('public', 'check_cookie', array ['text']); -select function_lang_is('public', 'check_cookie', array ['text'], 'plpgsql'); -select function_returns('public', 'check_cookie', array ['text'], 'name'); -select is_definer('public', 'check_cookie', array ['text']); -select volatility_is('public', 'check_cookie', array ['text'], 'stable'); -select function_privs_are('public', 'check_cookie', array ['text'], 'guest', array []::text[]); -select function_privs_are('public', 'check_cookie', array ['text'], 'employee', array []::text[]); -select function_privs_are('public', 'check_cookie', array ['text'], 'admin', array []::text[]); -select function_privs_are('public', 'check_cookie', array ['text'], 'authenticator', array ['EXECUTE']); +select has_function('public', 'check_cookie', array ['text', 'text']); +select function_lang_is('public', 'check_cookie', array ['text', 'text'], 'plpgsql'); +select function_returns('public', 'check_cookie', array ['text', 'text'], 'name'); +select is_definer('public', 'check_cookie', array ['text', 'text']); +select volatility_is('public', 'check_cookie', array ['text', 'text'], 'stable'); +select function_privs_are('public', 'check_cookie', array ['text', 'text'], 'guest', array []::text[]); +select function_privs_are('public', 'check_cookie', array ['text', 'text'], 'employee', array []::text[]); +select function_privs_are('public', 'check_cookie', array ['text', 'text'], 'admin', array []::text[]); +select function_privs_are('public', 'check_cookie', array ['text', 'text'], 'authenticator', array ['EXECUTE']); set client_min_messages to warning; +truncate company_host cascade; +truncate company_user cascade; +truncate company cascade; 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', 'employee', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') - , (9, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') +insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at) +values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') + , (9, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') +; + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_lang_tag) +values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca') + , (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 'es') +; + +insert into company_user (company_id, user_id, role) +values (2, 1, 'employee') + , (4, 9, 'admin') +; + +insert into company_host (company_id, host) +values (2, 'co2') + , (4, 'co4') ; prepare user_info as -select current_user_email(), current_user_cookie(); +select current_user_email(), current_user_cookie(), current_company_id(); select is( - check_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog'), + check_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2'), 'employee'::name, 'Should validate the cookie for the first user' ); select results_eq( 'user_info', - $$ values ('demo@tandem.blog', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e') $$, + $$ values ('demo@tandem.blog', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', 2) $$, 'Should have updated the settings with the user info' ); select is( - check_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog'), + check_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog', 'co4'), 'admin'::name, 'Should validate the cookie for the second user' ); select results_eq( 'user_info', - $$ values ('admin@tandem.blog', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524') $$, + $$ values ('admin@tandem.blog', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', 4) $$, 'Should have updated the settings with the other user info' ); select is( - check_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/admin@tandem.blog'), + check_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/admin@tandem.blog', 'co2'), 'guest'::name, 'Should only match with the correct email' ); select results_eq( 'user_info', - $$ values ('', '') $$, + $$ values ('', '', 0) $$, 'Should have updated the settings with a guest user' ); select is( - check_cookie('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/admin@tandem.blog'), + check_cookie('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/admin@tandem.blog', 'co2'), 'guest'::name, 'Should only match with the correct cookie value' ); select results_eq( 'user_info', - $$ values ('', '') $$, + $$ values ('', '', 0) $$, + 'Should have left the settings with a guest user' +); + +select is( + check_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co4'), + 'guest'::name, + 'Should not allow cookies for a different company' +); + +select results_eq( + 'user_info', + $$ values ('', '', 0) $$, 'Should have left the settings with a guest user' ); @@ -83,26 +113,26 @@ update "user" set cookie_expires_at = current_timestamp - interval '1 minute'; select is( - check_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog'), + check_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2'), 'guest'::name, 'Should not allow expired cookies' ); select results_eq( 'user_info', - $$ values ('', '') $$, + $$ values ('', '', 0) $$, 'Should have left the settings with a guest user' ); select is( - check_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog'), + check_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog', 'co4'), 'guest'::name, 'Should not allow expired cookied for the other user as well' ); select results_eq( 'user_info', - $$ values ('', '') $$, + $$ values ('', '', 0) $$, 'Should have left the settings with a guest user' ); diff --git a/test/company.sql b/test/company.sql index 36eed64..f7dfe2a 100644 --- a/test/company.sql +++ b/test/company.sql @@ -5,14 +5,14 @@ reset client_min_messages; begin; -select plan(101); +select plan(102); set search_path to camper, public; select has_table('company'); select has_pk('company' ); -select table_privs_are('company', 'guest', array []::text[]); -select table_privs_are('company', 'employee', array ['SELECT', 'UPDATE']); +select table_privs_are('company', 'guest', array ['SELECT']); +select table_privs_are('company', 'employee', array ['SELECT']); select table_privs_are('company', 'admin', array ['SELECT', 'UPDATE']); select table_privs_are('company', 'authenticator', array []::text[]); @@ -127,27 +127,30 @@ select col_default_is('company', 'created_at', 'CURRENT_TIMESTAMP'); set client_min_messages to warning; +truncate company_host cascade; truncate company_user cascade; truncate company cascade; 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', 'employee', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') - , (5, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') +insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at) +values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') + , (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') ; insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_lang_tag) values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca') , (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 'es') - , (6, 'Company 6', 'XX345', '', '777-777-777', 'c@c', '', '', '', '', '', 'DE', 'USD', 'ca') ; -insert into company_user (company_id, user_id) -values (2, 1) - , (2, 5) - , (4, 1) - , (6, 5) +insert into company_user (company_id, user_id, role) +values (2, 1, 'admin') + , (4, 5, 'admin') +; + +insert into company_host (company_id, host) +values (2, 'co2') + , (4, 'co4') ; prepare company_data as @@ -155,36 +158,44 @@ select company_id, business_name from company order by company_id; -set role employee; -select is_empty('company_data', 'Should show no data when cookie is not set yet'); -reset role; - -select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog'); +set role guest; select results_eq( 'company_data', $$ values ( 2, 'Company 2' ) , ( 4, 'Company 4' ) $$, - 'Should only list companies where demo@tandem.blog is user of' + 'Everyone should be able to list all companies' ); reset role; -select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog'); +select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2'); + +select lives_ok( + $$ update company set business_name = 'Another Company 2' where company_id = 2 $$, + 'Admin from company 2 should be able to update that company.' +); + select results_eq( 'company_data', - $$ values ( 2, 'Company 2' ) - , ( 6, 'Company 6' ) + $$ values ( 2, 'Another Company 2' ) + , ( 4, 'Company 4' ) $$, - 'Should only list companies where admin@tandem.blog is user of' + 'Should have updated the row.' ); -reset role; -select set_cookie('not-a-cookie'); -select throws_ok( - 'company_data', - '42501', 'permission denied for table company', - 'Should not allow select to guest users' +select lives_ok( + $$ update company set business_name = 'Another Company 4' where company_id = 4 $$, + 'Admin from company 2 should NOT be able to update that company, but no error is raised.' ); + +select results_eq( + 'company_data', + $$ values ( 2, 'Another Company 2' ) + , ( 4, 'Company 4' ) + $$, + 'Should NOT have updated anything.' +); + reset role; select throws_ok( $$ diff --git a/test/company_user.sql b/test/company_user.sql index 2954745..1c55029 100644 --- a/test/company_user.sql +++ b/test/company_user.sql @@ -5,7 +5,7 @@ reset client_min_messages; begin; -select plan(19); +select plan(23); set search_path to camper, public; @@ -31,6 +31,12 @@ select col_type_is('company_user', 'user_id', 'integer'); select col_not_null('company_user', 'user_id'); select col_hasnt_default('company_user', 'user_id'); +select has_column('company_user', 'role'); +select col_type_is('company_user', 'role', 'name'); +select col_not_null('company_user', 'role'); +select col_hasnt_default('company_user', 'role'); + + select * from finish(); diff --git a/test/current_company_id.sql b/test/current_company_id.sql new file mode 100644 index 0000000..a937287 --- /dev/null +++ b/test/current_company_id.sql @@ -0,0 +1,88 @@ +-- Test current_company_id +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +set search_path to camper, auth, public; + +select plan(15); + +select has_function('camper', 'current_company_id', array []::name[]); +select function_lang_is('camper', 'current_company_id', array []::name[], 'sql'); +select function_returns('camper', 'current_company_id', array []::name[], 'integer'); +select isnt_definer('camper', 'current_company_id', array []::name[]); +select volatility_is('camper', 'current_company_id', array []::name[], 'stable'); +select function_privs_are('camper', 'current_company_id', array []::name[], 'guest', array ['EXECUTE']); +select function_privs_are('camper', 'current_company_id', array []::name[], 'employee', array ['EXECUTE']); +select function_privs_are('camper', 'current_company_id', array []::name[], 'admin', array ['EXECUTE']); +select function_privs_are('camper', 'current_company_id', array []::name[], 'authenticator', array []::text[]); + +set client_min_messages to warning; +truncate company_host cascade; +truncate company_user cascade; +truncate company cascade; +truncate auth."user" cascade; +reset client_min_messages; + +insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at) +values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') + , (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') +; + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_lang_tag) +values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca') + , (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 'es') +; + +insert into company_user (company_id, user_id, role) +values (2, 1, 'employee') + , (4, 5, 'admin') +; + +insert into company_host (company_id, host) +values (2, 'co2') + , (4, 'co4') +; + +select lives_ok( + $$ select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2') $$, + 'Should change ok for the first user' +); + +select is( + current_company_id(), + 2, + 'Should return the company ID of the first user' +); + +reset role; + + +select lives_ok( + $$ select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog', 'co4') $$, + 'Should change ok for the second user' +); + +select is( + current_company_id(), + 4, + 'Should return the cookie of the second user' +); + +reset role; + +select lives_ok( + $$ select set_cookie('', 'co2') $$, + 'Should change ok for a guest user' +); + +select is(current_company_id(), 0, 'Should return zero'); + +reset role; + +select * +from finish(); + +rollback; diff --git a/test/current_user_cookie.sql b/test/current_user_cookie.sql index 6955b01..972ed45 100644 --- a/test/current_user_cookie.sql +++ b/test/current_user_cookie.sql @@ -20,16 +20,34 @@ select function_privs_are('camper', 'current_user_cookie', array []::name[], 'ad select function_privs_are('camper', 'current_user_cookie', array []::name[], 'authenticator', array []::text[]); set client_min_messages to warning; +truncate company_host cascade; +truncate company_user cascade; +truncate company cascade; 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', 'employee', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') - , (5, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') +insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at) +values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') + , (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') +; + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_lang_tag) +values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca') + , (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 'es') +; + +insert into company_user (company_id, user_id, role) +values (2, 1, 'employee') + , (4, 5, 'admin') +; + +insert into company_host (company_id, host) +values (2, 'co2') + , (4, 'co4') ; select lives_ok( - $$ select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog') $$, + $$ select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2') $$, 'Should change ok for the first user' ); @@ -43,7 +61,7 @@ reset role; select lives_ok( - $$ select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog') $$, + $$ select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog', 'co4') $$, 'Should change ok for the second user' ); @@ -56,7 +74,7 @@ select is( reset role; select lives_ok( - $$ select set_cookie('') $$, + $$ select set_cookie('', 'co2') $$, 'Should change ok for a guest user' ); diff --git a/test/current_user_email.sql b/test/current_user_email.sql index 6718796..4aa058e 100644 --- a/test/current_user_email.sql +++ b/test/current_user_email.sql @@ -20,16 +20,34 @@ select function_privs_are('camper', 'current_user_email', array []::name[], 'adm select function_privs_are('camper', 'current_user_email', array []::name[], 'authenticator', array []::text[]); set client_min_messages to warning; +truncate company_host cascade; +truncate company_user cascade; +truncate company cascade; 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', 'employee', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') - , (5, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') +insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at) +values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') + , (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') +; + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_lang_tag) +values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca') + , (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 'es') +; + +insert into company_user (company_id, user_id, role) +values (2, 1, 'employee') + , (4, 5, 'admin') +; + +insert into company_host (company_id, host) +values (2, 'co2') + , (4, 'co4') ; select lives_ok( - $$ select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog') $$, + $$ select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2') $$, 'Should change ok for the first user' ); @@ -39,7 +57,7 @@ reset role; select lives_ok( - $$ select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog') $$, + $$ select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog', 'co4') $$, 'Should change ok for the second user' ); @@ -48,7 +66,7 @@ select is(current_user_email(), 'admin@tandem.blog', 'Should return the email of reset role; select lives_ok( - $$ select set_cookie('') $$, + $$ select set_cookie('', '') $$, 'Should change ok for a guest user' ); diff --git a/test/encrypt_password.sql b/test/encrypt_password.sql index e3a29d8..38dbfb4 100644 --- a/test/encrypt_password.sql +++ b/test/encrypt_password.sql @@ -25,8 +25,8 @@ set client_min_messages to warning; truncate "user" cascade; reset client_min_messages; -insert into "user" (email, name, password, role) -values ('info@tandem.blog', 'Perita', 'test', 'guest'); +insert into "user" (email, name, password) +values ('info@tandem.blog', 'Perita', 'test'); select row_eq( $$ select email from "user" where password = crypt('test', password) $$, diff --git a/test/ensure_role_exists.sql b/test/ensure_role_exists.sql index 2f5f401..efcbed1 100644 --- a/test/ensure_role_exists.sql +++ b/test/ensure_role_exists.sql @@ -7,41 +7,52 @@ begin; select plan(14); -set search_path to auth, public; +set search_path to auth, camper, public; -select has_function('auth', 'ensure_role_exists', array []::name[]); -select function_lang_is('auth', 'ensure_role_exists', array []::name[], 'plpgsql'); -select function_returns('auth', 'ensure_role_exists', array []::name[], 'trigger'); -select isnt_definer('auth', 'ensure_role_exists', array []::name[]); -select volatility_is('auth', 'ensure_role_exists', array []::name[], 'volatile'); -select function_privs_are('auth', 'ensure_role_exists', array []::name[], 'guest', array []::text[]); -select function_privs_are('auth', 'ensure_role_exists', array []::name[], 'employee', array []::text[]); -select function_privs_are('auth', 'ensure_role_exists', array []::name[], 'admin', array []::text[]); -select function_privs_are('auth', 'ensure_role_exists', array []::name[], 'authenticator', array []::text[]); +select has_function('camper', 'ensure_role_exists', array []::name[]); +select function_lang_is('camper', 'ensure_role_exists', array []::name[], 'plpgsql'); +select function_returns('camper', 'ensure_role_exists', array []::name[], 'trigger'); +select isnt_definer('camper', 'ensure_role_exists', array []::name[]); +select volatility_is('camper', 'ensure_role_exists', array []::name[], 'volatile'); +select function_privs_are('camper', 'ensure_role_exists', array []::name[], 'guest', array []::text[]); +select function_privs_are('camper', 'ensure_role_exists', array []::name[], 'employee', array []::text[]); +select function_privs_are('camper', 'ensure_role_exists', array []::name[], 'admin', array []::text[]); +select function_privs_are('camper', 'ensure_role_exists', array []::name[], 'authenticator', array []::text[]); -select trigger_is('user', 'ensure_role_exists', 'ensure_role_exists'); +select trigger_is('company_user', 'ensure_role_exists', 'ensure_role_exists'); set client_min_messages to warning; +truncate company_user cascade; +truncate company cascade; truncate "user" cascade; reset client_min_messages; +insert into auth."user" (user_id, email, name, password) +values (1, 'demo@tandem.blog', 'Demo', 'test') + , (9, 'admin@tandem.blog', 'Demo', 'test') +; + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_lang_tag) +values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca') +; + select lives_ok( - $$ insert into "user" (email, name, password, role) values ('info@tandem.blog', 'Factura', 'test', 'guest') $$, + $$ insert into company_user (company_id, user_id, role) values (2, 1, 'guest') $$, 'Should be able to insert a user with a valid role' ); select throws_ok( - $$ insert into "user" (email, name, password, role) values ('nope@tandem.blog', 'Factura', 'test', 'non-existing-role') $$, + $$ insert into company_user (companY_id, user_id, role) values (2, 9, 'non-existing-role') $$, '23503', 'role not found: non-existing-role', 'Should not allow insert users with invalid roles' ); -select lives_ok($$ update "user" set role = 'employee' where email = 'info@tandem.blog' $$, +select lives_ok($$ update company_user set role = 'employee' where company_id = 2 and user_id = 1 $$, 'Should be able to change the role of a user to another valid role' ); -select throws_ok($$ update "user" set role = 'usurer' where email = 'info@tandem.blog' $$, +select throws_ok($$ update company_user set role = 'usurer' where company_id = 2 and user_id = 1 $$, '23503', 'role not found: usurer', 'Should not allow update users to invalid roles' diff --git a/test/login.sql b/test/login.sql index 286311c..3c4098e 100644 --- a/test/login.sql +++ b/test/login.sql @@ -24,8 +24,8 @@ truncate auth."user" cascade; truncate auth.login_attempt cascade; reset client_min_messages; -insert into auth."user" (email, name, password, role) -values ('info@tandem.blog', 'Tandem', 'test', 'employee'); +insert into auth."user" (email, name, password) +values ('info@tandem.blog', 'Tandem', 'test'); create temp table _login_test ( diff --git a/test/logout.sql b/test/logout.sql index 4611955..d7bcf57 100644 --- a/test/logout.sql +++ b/test/logout.sql @@ -23,9 +23,9 @@ 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, 'info@tandem.blog', 'Tandem', 'test', 'employee', '8c23d4a8d777775f8fc507676a0d99d3dfa54b03b1b257c838', current_timestamp + interval '1 day') - , (12, 'admin@tandem.blog', 'Admin', 'test', 'admin', '0169e5f668eec1e6749fd25388b057997358efa8dfd697961a', current_timestamp + interval '2 day') +insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at) +values (1, 'info@tandem.blog', 'Tandem', 'test', '8c23d4a8d777775f8fc507676a0d99d3dfa54b03b1b257c838', current_timestamp + interval '1 day') + , (12, 'admin@tandem.blog', 'Admin', 'test', '0169e5f668eec1e6749fd25388b057997358efa8dfd697961a', current_timestamp + interval '2 day') ; prepare user_cookies as diff --git a/test/set_cookie.sql b/test/set_cookie.sql index a701d31..3eb9faa 100644 --- a/test/set_cookie.sql +++ b/test/set_cookie.sql @@ -5,66 +5,97 @@ reset client_min_messages; begin; -select plan(15); +select plan(17); set search_path to auth, camper, public; -select has_function('public', 'set_cookie', array ['text']); -select function_lang_is('public', 'set_cookie', array ['text'], 'sql'); -select function_returns('public', 'set_cookie', array ['text'], 'void'); -select isnt_definer('public', 'set_cookie', array ['text']); -select volatility_is('public', 'set_cookie', array ['text'], 'stable'); -select function_privs_are('public', 'set_cookie', array ['text'], 'guest', array []::text[]); -select function_privs_are('public', 'set_cookie', array ['text'], 'employee', array []::text[]); -select function_privs_are('public', 'set_cookie', array ['text'], 'admin', array []::text[]); -select function_privs_are('public', 'set_cookie', array ['text'], 'authenticator', array ['EXECUTE']); +select has_function('public', 'set_cookie', array ['text', 'text']); +select function_lang_is('public', 'set_cookie', array ['text', 'text'], 'sql'); +select function_returns('public', 'set_cookie', array ['text', 'text'], 'void'); +select isnt_definer('public', 'set_cookie', array ['text', 'text']); +select volatility_is('public', 'set_cookie', array ['text', 'text'], 'stable'); +select function_privs_are('public', 'set_cookie', array ['text', 'text'], 'guest', array []::text[]); +select function_privs_are('public', 'set_cookie', array ['text', 'text'], 'employee', array []::text[]); +select function_privs_are('public', 'set_cookie', array ['text', 'text'], 'admin', array []::text[]); +select function_privs_are('public', 'set_cookie', array ['text', 'text'], 'authenticator', array ['EXECUTE']); set client_min_messages to warning; +truncate company_host cascade; +truncate company_user cascade; +truncate company cascade; 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', 'employee', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') - , (5, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') +insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at) +values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') + , (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') +; + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_lang_tag) +values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca') + , (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 'es') +; + +insert into company_user (company_id, user_id, role) +values (2, 1, 'employee') + , (4, 5, 'admin') +; + +insert into company_host (company_id, host) +values (2, 'co2') + , (4, 'co4') ; prepare user_info as -select current_user_email(), current_user_cookie(), current_user; +select current_user_email(), current_user_cookie(), current_company_id(), current_user; select lives_ok( - $$ select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog') $$, + $$ select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2') $$, 'Should run ok for a valid cookie' ); select results_eq( 'user_info', - $$ values ('demo@tandem.blog', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', 'employee'::name) $$, + $$ values ('demo@tandem.blog', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', 2, 'employee'::name) $$, 'Should have updated the info with the correct user' ); reset role; select lives_ok( - $$ select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog') $$, + $$ select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co4') $$, + 'Should run ok for a valid cookie, even for the wrong company' +); + +select results_eq( + 'user_info', + $$ values ('', '', 0, 'guest'::name) $$, + 'Should have updated the info as a guest user for this company' +); + +reset role; + +select lives_ok( + $$ select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog', 'co4') $$, 'Should run ok for a different cookie' ); select results_eq( 'user_info', - $$ values ('admin@tandem.blog', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', 'admin'::name) $$, + $$ values ('admin@tandem.blog', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', 4, 'admin'::name) $$, 'Should have updated the info with the other user' ); reset role; select lives_ok( - $$ select set_cookie('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/admin@tandem.blog') $$, + $$ select set_cookie('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/admin@tandem.blog', 'asth') $$, 'Should run ok even for an invalid cookie' ); select results_eq( 'user_info', - $$ values ('', '', 'guest'::name) $$, + $$ values ('', '', 0, 'guest'::name) $$, 'Should have updated the info as a guest user' ); diff --git a/test/user.sql b/test/user.sql index e830dac..f95472c 100644 --- a/test/user.sql +++ b/test/user.sql @@ -5,7 +5,7 @@ reset client_min_messages; begin; -select plan(56); +select plan(52); set search_path to auth, public; @@ -45,11 +45,6 @@ select col_type_is('user', 'password', 'text'); select col_not_null('user', 'password'); select col_hasnt_default('user', 'password'); -select has_column('user', 'role'); -select col_type_is('user', 'role', 'name'); -select col_not_null('user', 'role'); -select col_hasnt_default('user', 'role'); - select has_column('user', 'lang_tag'); select col_is_fk('user', 'lang_tag'); select fk_ok('user', 'lang_tag', 'language', 'lang_tag'); diff --git a/test/user_profile.sql b/test/user_profile.sql index 425d909..3c5cdd1 100644 --- a/test/user_profile.sql +++ b/test/user_profile.sql @@ -59,34 +59,51 @@ select column_privs_are('user_profile', 'csrf_token', 'authenticator', array []: set client_min_messages to warning; +truncate company_host cascade; +truncate company_user cascade; +truncate company cascade; truncate auth."user" cascade; reset client_min_messages; -insert into auth."user" (user_id, email, name, password, role, cookie, cookie_expires_at, lang_tag) -values (1, 'demo@tandem.blog', 'Demo', 'test', 'employee', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', - current_timestamp + interval '1 month', 'ca') -, (5, 'admin@tandem.blog', 'Admin', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', - current_timestamp + interval '1 month', 'es') -, (7, 'another@tandem.blog', 'Another Admin', 'test', 'admin', default, default, default) +insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at, lang_tag) +values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month', 'ca') + , (5, 'admin@tandem.blog', 'Admin', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month', 'es') + , (7, 'another@tandem.blog', 'Another Admin', 'test', default, default, default) +; + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_lang_tag) +values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca') + , (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 'es') +; + +insert into company_user (company_id, user_id, role) +values (2, 1, 'employee') + , (4, 5, 'admin') + , (4, 7, 'admin') +; + +insert into company_host (company_id, host) +values (2, 'co2') + , (4, 'co4') ; prepare profile as -select user_id, email, name, role, lang_tag, csrf_token +select user_id, company_id, email, name, role, lang_tag, csrf_token from user_profile; select set_config('request.user.cookie', '', false); select results_eq( 'profile', - $$ values (0, null::email, '', 'guest'::name, 'und', '') $$, + $$ values (0, 0, null::email, '', 'guest'::name, 'und', '') $$, 'Should be set up with the guest user when no user logged in yet.' ); -select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog'); +select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2'); select results_eq( 'profile', - $$ values (1, 'demo@tandem.blog'::email, 'Demo', 'employee'::name, 'ca', '44facbb30d') $$, + $$ values (1, 2, 'demo@tandem.blog'::email, 'Demo', 'employee'::name, 'ca', '44facbb30d') $$, 'Should only see the profile of the first user' ); @@ -113,17 +130,17 @@ select throws_ok( select results_eq( 'profile', - $$ values (1, 'demo+update@tandem.blog'::email, 'Demo Update', 'employee'::name, 'es', '44facbb30d') $$, + $$ values (1, 2, 'demo+update@tandem.blog'::email, 'Demo Update', 'employee'::name, 'es', '44facbb30d') $$, 'Should see the changed profile of the first user' ); reset role; -select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog'); +select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog', 'co4'); select results_eq( 'profile', - $$ values (5, 'admin@tandem.blog'::email, 'Admin', 'admin'::name, 'es', '12af4c88b5') $$, + $$ values (5, 4, 'admin@tandem.blog'::email, 'Admin', 'admin'::name, 'es', '12af4c88b5') $$, 'Should only see the profile of the second user' ); @@ -150,7 +167,7 @@ select throws_ok( select results_eq( 'profile', - $$ values (5, 'admin+update@tandem.blog'::email, 'Admin Update', 'admin'::name, 'ca', '12af4c88b5') $$, + $$ values (5, 4, 'admin+update@tandem.blog'::email, 'Admin Update', 'admin'::name, 'ca', '12af4c88b5') $$, 'Should see the changed profile of the first user' ); diff --git a/verify/campsite_type.sql b/verify/campsite_type.sql index 6eaba0d..db74927 100644 --- a/verify/campsite_type.sql +++ b/verify/campsite_type.sql @@ -12,6 +12,9 @@ from camper.campsite_type where false; select 1 / count(*) from pg_class where oid = 'camper.campsite_type'::regclass and relrowsecurity; -select 1 / count(*) from pg_policy where polname = 'company_policy' and polrelid = 'camper.campsite_type'::regclass; +select 1 / count(*) from pg_policy where polname = 'guest_ok' and polrelid = 'camper.campsite_type'::regclass; +select 1 / count(*) from pg_policy where polname = 'insert_to_company' and polrelid = 'camper.campsite_type'::regclass; +select 1 / count(*) from pg_policy where polname = 'update_company' and polrelid = 'camper.campsite_type'::regclass; +select 1 / count(*) from pg_policy where polname = 'delete_from_company' and polrelid = 'camper.campsite_type'::regclass; rollback; diff --git a/verify/check_cookie.sql b/verify/check_cookie.sql index 116a7bd..059686e 100644 --- a/verify/check_cookie.sql +++ b/verify/check_cookie.sql @@ -2,6 +2,6 @@ begin; -select has_function_privilege('public.check_cookie(text)', 'execute'); +select has_function_privilege('public.check_cookie(text, text)', 'execute'); rollback; diff --git a/verify/company_user.sql b/verify/company_user.sql index 1b5a048..55c9e05 100644 --- a/verify/company_user.sql +++ b/verify/company_user.sql @@ -4,10 +4,8 @@ begin; select company_id , user_id + , role from camper.company_user where false; -select 1 / count(*) from pg_class where oid = 'camper.company'::regclass and relrowsecurity; -select 1 / count(*) from pg_policy where polname = 'company_policy' and polrelid = 'camper.company'::regclass; - rollback; diff --git a/verify/current_company_id.sql b/verify/current_company_id.sql new file mode 100644 index 0000000..c45e20c --- /dev/null +++ b/verify/current_company_id.sql @@ -0,0 +1,7 @@ +-- Verify camper:current_company_id on pg + +begin; + +select has_function_privilege('camper.current_company_id()', 'execute'); + +rollback; diff --git a/verify/ensure_role_exists.sql b/verify/ensure_role_exists.sql index 404f27b..e14f26b 100644 --- a/verify/ensure_role_exists.sql +++ b/verify/ensure_role_exists.sql @@ -2,13 +2,13 @@ begin; -select has_function_privilege('auth.ensure_role_exists()', 'execute'); +select has_function_privilege('camper.ensure_role_exists()', 'execute'); select 1 / count(*) from pg_trigger where not tgisinternal and tgname = 'ensure_role_exists' - and tgrelid = 'auth.user'::regclass + and tgrelid = 'camper.company_user'::regclass and tgtype = b'00010101'::int; -- │││││││ -- ││││││└─> row diff --git a/verify/policies_company.sql b/verify/policies_company.sql new file mode 100644 index 0000000..acbef32 --- /dev/null +++ b/verify/policies_company.sql @@ -0,0 +1,10 @@ +-- Verify camper:policies_company on pg + +begin; + +set search_path to camper, public; +select 1 / count(*) from pg_class where oid = 'company'::regclass and relrowsecurity; +select 1 / count(*) from pg_policy where polname = 'guest_ok' and polrelid = 'company'::regclass; +select 1 / count(*) from pg_policy where polname = 'update_company' and polrelid = 'company'::regclass; + +rollback; diff --git a/verify/set_cookie.sql b/verify/set_cookie.sql index 8e8b140..607ef15 100644 --- a/verify/set_cookie.sql +++ b/verify/set_cookie.sql @@ -2,6 +2,6 @@ begin; -select has_function_privilege('public.set_cookie(text)', 'execute'); +select has_function_privilege('public.set_cookie(text, text)', 'execute'); rollback; diff --git a/verify/user.sql b/verify/user.sql index c2606f6..33deb17 100644 --- a/verify/user.sql +++ b/verify/user.sql @@ -6,7 +6,6 @@ select user_id , email , name , password - , role , lang_tag , cookie , cookie_expires_at