-- test_sessions.sql -- -- Unit tests for Veil2 session handling. -- -- Copyright (c) 2020 Marc Munro -- Author: Marc Munro -- License: GPL V3 -- -- Usage: Called from test_veil2.sql -- -- TODO: -- test privileges after close create view apc as select * from veil2.accessor_privileges_cache; grant select on veil2.accessor_privileges_cache to public; grant select on apc to public; begin; select '...test veil2 session handling...'; create view session_context as select * from veil2.session_context() where accessor_id is not null; grant select on session_context to public; select plan(107); -- Perform a reset session without returning a row. This ensures the -- temporary table is created. with reset_session as ( select 1 as result from veil2.reset_session() ) select null from reset_session where result != 1; select is((select count(*) from session_context)::integer, 0, 'Expecting empty session_context'); --insert into veil2_session_context(accessor_id) values (1); select * from veil2.session_context(1, 2, 3, 4, 5, 6, 7, 8) where accessor_id != 1; -- Ensure that we can see the inserted session record. select is((select count(*) from session_context)::integer, 1, 'Expecting session_context record'); -- Test that resetting session causes veil2_session_context record to be -- removed. with reset_session as ( select 1 as result from veil2.reset_session() ) select null from reset_session where result != 1; -- Create_session select is((select count(*) from session_context)::integer, 0, 'Expecting empty session_context table(2)'); with session as (select * from veil2.create_session('gerry', 'wibble')) select is ((session_id is not null), true, 'create_session() returns session id') from session union all select is((session_token is not null), true, 'create_session() returns session token') from session union all select is(cnt, 0, 'No session context created for invalid session') from (select count(*)::integer cnt from session_context) x union all select is(cnt, 0, 'No session record created for invalid session') from (select count(*)::integer cnt from session s inner join veil2.sessions vs on vs.session_id = s.session_id) x; -- Invalid authentication type with valid accessor yields -- a session that will subsequently not open with session as (select * from veil2.create_session('eve', 'wibble')) select is((session.session_id is not null), true, 'Session id should have been returned(2)') from session union all select is((session.session_token is not null), true, 'Session token should have been returned(2)') from session; with sessions as ( select sp.session_id as reported_session_id, s.session_id from session_context sp left outer join veil2.sessions s on s.session_id = sp.session_id ) select is((reported_session_id is null), false, 'There should be a reported session_id(2)') from sessions union all select is((session_id is null), false, 'There should not be an actual session_id(2)') from sessions; -- We have a created session from the last tests above. Now we will try -- opening that session. Given that the authentication method was -- invalid, we expect appropriate failures. create temporary table prev_context as select * from session_context; with session as ( select os.* from prev_context pc cross join veil2.open_connection(pc.session_id, 1, 'wibble') os ) select is(success, false, 'Authentication should have failed(1)') from session union all select is(errmsg, 'AUTHFAIL', 'Authentication message should be AUTHFAIL(1)') from session; -- Try creating and opening a session with valid credentials but no -- connect privilege. with session as ( select o.* from veil2.create_session('fred', 'plaintext') c cross join veil2.open_connection(c.session_id, 1, 'password') o ) select is(success, false, 'Authentication should have failed(2)') from session union all select is(errmsg, 'AUTHFAIL', 'Authentication message should be AUTHFAIL(2)') from session; -- Allow plaintext authentication. update veil2.authentication_types set enabled = true where shortname = 'plaintext'; -- Try creating and opening a session with valid credentials and -- connect privilege - to establish that this works before the next -- test. with session as ( select o.* from veil2.create_session('eve', 'plaintext') c cross join veil2.open_connection(c.session_id, 1, 'password2') o ) select is(success, true, 'Authentication should have succeeded') from session union all select is(errmsg is null, true, 'There should be no error message') from session; -- Record the first session_id in a temp table. create temporary table mytest_session ( session_id1 integer, session_id2 integer); insert into mytest_session (session_id1) select session_id from session_context; -- Disconnect and reconnect the above session. Since this is a -- continuation of an existing session, we use the continuation -- authentication mechanism. with reset_session as ( select 1 as result from veil2.reset_session() ) select null from reset_session where result != 1; with session as ( select o.*, ms.session_id1 from mytest_session ms inner join veil2.sessions s on s.session_id = ms.session_id1 cross join veil2.open_connection(ms.session_id1, 2, encode(digest(s.token || to_hex(2), 'sha1'), 'base64')) o ) select is(success, true, 'Authentication should have succeeded (2)') from session union all select is(errmsg is null, true, 'There should be no error message (2)') from session; -- Create another valid session - this one for accessor -6 with session as ( select o.* from veil2.create_session('alice', 'plaintext') c cross join veil2.open_connection(c.session_id, 1, 'password6') o ) select is(success, true, 'Authentication should have succeeded(2)') from session union all select is(errmsg is null, true, 'There should be no error message(2)') from session; -- Record the second session_id. update mytest_session set session_id2 = (select session_id from session_context); -- Switch to the original session with session as ( select o.*, ms.session_id1 as session_id from mytest_session ms inner join veil2.sessions s on s.session_id = ms.session_id1 cross join veil2.open_connection(ms.session_id1, 3, encode(digest(s.token || to_hex(3), 'sha1'), 'base64')) o ) select is(success, true, 'Authentication should have succeeded (3)') from session union all select is(errmsg is null, true, 'There should be no error message (3)') from session; -- Switch to the second session with session as ( select o.*, ms.session_id2 as session_id from mytest_session ms inner join veil2.sessions s on s.session_id = ms.session_id2 cross join veil2.open_connection(ms.session_id2, 2, encode(digest(s.token || to_hex(2), 'sha1'), 'base64')) o ) select is(success, true, 'Authentication should have succeeded (4)') from session union all select is(errmsg is null, true, 'There should be no error message (4)') from session; -- Attempt to switch to the original session with a reused nonce with session as ( select o.*, ms.session_id1 as session_id from mytest_session ms cross join veil2.open_connection(ms.session_id1, 3, 'password2') o ) select is(success, false, 'Authentication should have failed (5)') from session union all select is(errmsg, 'NONCEFAIL', 'There should be a NONCEFAIL message (5)') from session; -- Again with a valid nonce with session as ( select o.*, ms.session_id1 as session_id from mytest_session ms inner join veil2.sessions s on s.session_id = ms.session_id1 cross join veil2.open_connection(ms.session_id1, 5, encode(digest(s.token || to_hex(5), 'sha1'), 'base64')) o ) select is(success, true, 'Authentication should have succeeded (6)') from session union all select is(errmsg is null, true, 'There should be no error message (6)') from session; -- Again with a valid nonce lower than the last with session as ( select o.*, ms.session_id1 as session_id from mytest_session ms inner join veil2.sessions s on s.session_id = ms.session_id1 cross join veil2.open_connection(ms.session_id1, 4, encode(digest(s.token || to_hex(4), 'sha1'), 'base64')) o ) select is(success, true, 'Authentication should have succeeded (7)') from session union all select is(errmsg is null, true, 'There should be no error message (7)') from session; -- Again with a valid nonce but significantly larger with session as ( select o.*, ms.session_id1 as session_id from mytest_session ms cross join veil2.open_connection(ms.session_id1, 300, 'password2') o ) select is(success, false, 'Authentication should have failed (8)') from session union all select is(errmsg, 'NONCEFAIL', 'There should be a NONCEFAIL message (8)') from session; -- Again with a valid nonce but slightly larger with session as ( select o.*, ms.session_id1 as session_id from mytest_session ms inner join veil2.sessions s on s.session_id = ms.session_id1 cross join veil2.open_connection(ms.session_id1, 364, encode(digest(s.token || to_hex(364), 'sha1'), 'base64')) o ) select is(success, true, 'Authentication should have succeeded (9)') from session union all select is(errmsg is null, true, 'There should be no error message (9)') from session; -- Ditto with session as ( select o.*, ms.session_id1 as session_id from mytest_session ms inner join veil2.sessions s on s.session_id = ms.session_id1 cross join veil2.open_connection(ms.session_id1, 427, encode(digest(s.token || to_hex(427), 'sha1'), 'base64')) o ) select is(success, true, 'Authentication should have succeeded (10)') from session union all select is(errmsg is null, true, 'There should be no error message (10)') from session; -- Again with session as ( select o.*, ms.session_id1 as session_id from mytest_session ms inner join veil2.sessions s on s.session_id = ms.session_id1 cross join veil2.open_connection(ms.session_id1, 480, encode(digest(s.token || to_hex(480), 'sha1'), 'base64')) o ) select is(success, true, 'Authentication should have succeeded (11)') from session union all select is(errmsg is null, true, 'There should be no error message (11)') from session; -- ...while we are here, let's ensure that we have some privileges. select is(veil2.i_have_global_priv(0), true, 'Session should have connect privilege(1)'); select is(veil2.i_have_priv_in_scope_or_global(0, 9, 9), true, 'Session should have connect privilege(2)'); select is(veil2.i_have_priv_in_scope_or_superior_or_global(0, 9, 9), true, 'Session should have connect privilege(3)'); -- Last time - should be forgetting those early nonces by now with session as ( select o.*, ms.session_id1 as session_id from mytest_session ms inner join veil2.sessions s on s.session_id = ms.session_id1 cross join veil2.open_connection(ms.session_id1, 540, encode(digest(s.token || to_hex(540), 'sha1'), 'base64')) o ) select is(success, true, 'Authentication should have succeeded (12)') from session union all select is(errmsg is null, true, 'There should be no error message (12)') from session; -- Now with an unused nonce that is too low with session as ( select o.*, ms.session_id1 as session_id from mytest_session ms cross join veil2.open_connection(ms.session_id1, 17, 'password2') o ) select is(success, false, 'Authentication should have failed (13)') from session union all select is(errmsg, 'NONCEFAIL', 'There should be a NONCEFAIL message (13)') from session; -- ...while we are here, let's ensure that we no longer have privileges. select is(veil2.i_have_global_priv(0), false, 'Session should not have connect privilege'); -- Check close_session() select null from veil2.create_session('alice', 'plaintext') c cross join veil2.open_connection(c.session_id, 1, 'password6') o where not o.success; -- Ensure no rows returned to make output untidy select is(veil2.i_have_global_priv(0), true, 'Open session should have connect privilege'); select null from veil2.close_connection() where close_connection is null; -- Ensure no rows returned select is(veil2.i_have_global_priv(0), false, 'Closed session should not have connect privilege'); -- Check dbuser-based session handling -- Get host and port info so we can connect as alice set session authorization veil2_alice; select null where not veil2.hello(); -- Establish our session select is(cnt > 0, true, 'Expect to see rows from scope_types') from (select count(*)::integer as cnt from veil2.scope_types) x; select is(cnt > 0, true, 'Expect to see rows from scopes') from (select count(*)::integer as cnt from veil2.scopes) x; select is(cnt > 0, true, 'Expect to see rows from privileges') from (select count(*)::integer as cnt from veil2.privileges) x; select is(cnt > 0, true, 'Expect to see rows from role_types') from (select count(*)::integer as cnt from veil2.role_types) x; select is(cnt > 0, true, 'Expect to see rows from roles') from (select count(*)::integer as cnt from veil2.roles) x; select is(cnt > 0, true, 'Expect to see rows from context_roles') from (select count(*)::integer as cnt from veil2.context_roles) x; select is(cnt > 0, true, 'Expect to see rows from role_privileges') from (select count(*)::integer as cnt from veil2.role_privileges) x; select * -- call reload_xxx without returning a row. from veil2.reload_connection_privs() where reload_connection_privs is null; select is(cnt > 0, true, 'Expect to see rows from role_roles') from (select count(*)::integer as cnt from veil2.role_roles) x; select is(cnt > 0, true, 'Expect to see rows from accessors') from (select count(*)::integer as cnt from veil2.accessors) x; select is(cnt > 0, true, 'Expect to see rows from authentication_types') from (select count(*)::integer as cnt from veil2.authentication_types) x; select is(cnt > 0, true, 'Expect to see rows from authentication_details') from (select count(*)::integer as cnt from veil2.authentication_details) x; select * -- call reload_xxx without returning a row. from veil2.reload_connection_privs() where reload_connection_privs is null; select is(cnt > 0, true, 'Expect to see rows from accessor_roles') from (select count(*)::integer as cnt from veil2.accessor_roles) x; select is(cnt > 0, true, 'Expect to see rows from sessions') from (select count(*)::integer as cnt from veil2.sessions) x; select is(cnt > 0, true, 'Expect to see rows from system_parameters') from (select count(*)::integer as cnt from veil2.system_parameters) x; -- Return to previous session connection reset session authorization; -- check handling of context in session handling... -- Invalid context. Authentication will fail. with session as ( select o.* from veil2.create_session('eve', 'plaintext', -3, -31) c cross join veil2.open_connection(c.session_id, 1, 'password2') o ) select is(success, false, 'Authentication should have failed (invalid context)') from session union all select is(errmsg, 'AUTHFAIL', 'Authentication message should be AUTHFAIL(3)') from session; -- Make the context a valid one create or replace view veil2.my_accessor_contexts ( accessor_id, context_type_id, context_id ) as select accessor_id, 1, 0 from veil2.accessors union all select -2, -3, -31 -- Give Eve two authentication contexts union all select -2, -3, -3; select * from veil2.init() where init is null; -- Now we should have connect privilege. Authentication should succeed. with session as ( select o.* from veil2.create_session('eve', 'plaintext', -3, -31) c cross join veil2.open_connection(c.session_id, 1, 'password2') o ) select is(success, true, 'Authentication should not have failed') from session; -- become_user... -- Modify user -2 (eve) to have role 7 (almost superuser) and connect delete from veil2.accessor_roles where accessor_id = -2 and role_id = 1; insert into veil2.accessor_roles (accessor_id, role_id, context_type_id, context_id) values (-2, 7, 1, 0), (-5, 0, 1, 0); -- Bob also needs connect -- Check that Bob (-5) authenticates and has some expected privileges with session as ( select o.* from veil2.create_session('bob', 'plaintext') c cross join veil2.open_connection(c.session_id, 1, 'password5') o ) select is(success, true, 'Bob should be authenticated (global login)') from session; select * -- call reload_xxx without returning a row. from veil2.reload_connection_privs() where reload_connection_privs is null; select is(veil2.i_have_priv_in_scope(20, -5, -51), true, 'Bob should have priv 20 in context -5,-51') union all select is(veil2.i_have_priv_in_scope_or_global(21, -5, -51), true, 'Bob should have priv 21 in context -5,-51 (2)') union all select is(veil2.i_have_priv_in_scope_or_superior_or_global(21, -5, -51), true, 'Bob should have priv 21 in context -5,-51 (3)') union all select is(veil2.i_have_priv_in_scope(23, -5, -51), true, 'Bob should have priv 23 in context -5,-51') union all select is(veil2.i_have_priv_in_scope(24, -5, -51), true, 'Bob should have priv 24 in context -5,-51') union all select is(veil2.i_have_priv_in_scope(25, -4, -41), true, 'Bob should have priv 25 in context -4,-41'); -- connect as eve with session as ( select o.* from veil2.create_session('eve', 'plaintext') c cross join veil2.open_connection(c.session_id, 1, 'password2') o ) select is(success, true, 'Eve should be authenticated (global)') from session; select is(accessor_id, -2, 'Eve''s accessor_id is Eve') from session_context; -- eve becomes bob -- Need to record the session_token for later use. create temporary table session_tt ( session_id integer, session_token text, success boolean, errmsg text); with session as ( select * from veil2.become_user('bob', 1, 0) ) insert into session_tt select * from session; select is(success, true, 'Eve should have successully become Bob') from session_tt; select is(accessor_id, -9, 'Bob''s accessor_id is Eve') from session_context where login_context_type_id is null; -- Check Eve-as-Bob's privs: should be mostly the same but without -- priv 20 which Eve did not have. select is(veil2.i_have_priv_in_scope(20, -5, -51), false, 'Bob should not have priv 20 in context -5,-51') union all select is(veil2.i_have_priv_in_scope(21, -5, -51), true, 'Bob should have priv 21 in context -5,-51 (2)') union all select is(veil2.i_have_priv_in_scope(23, -5, -51), true, 'Bob should have priv 23 in context -5,-51 (2)'); select is(veil2.i_have_priv_in_scope(24, -5, -51), true, 'Bob should have priv 24 in context -5,-51 (2)') union all select is(veil2.i_have_priv_in_scope(25, -4, -41), true, 'Bob should have priv 25 in context -4,-41 (2)'); select is(accessor_id, -5, 'Eve should now have Bob''s accessor_id') from session_context; -- ......continuation... select is(o.success, true, 'Bob''s session should have continued') from session_tt cross join veil2.open_connection(session_id, 2, encode(digest(session_token || to_hex(2), 'sha1'), 'base64')) o; -- Recheck Eve-as-Bob's privs: should be mostly the same but without -- priv 20 which Eve did not have. select is(veil2.i_have_priv_in_scope(20, -5, -51), false, 'Bob should not have priv 20 in context -5,-51 again') union all select is(veil2.i_have_priv_in_scope(21, -5, -51), true, 'Bob should have priv 21 in context -5,-51 (2) again') union all select is(veil2.i_have_priv_in_scope(23, -5, -51), true, 'Bob should have priv 23 in context -5,-51 (2) again') union all select is(veil2.i_have_priv_in_scope(24, -5, -51), true, 'Bob should have priv 24 in context -5,-51 (2) again') union all select is(veil2.i_have_priv_in_scope(25, -4, -41), true, 'Bob should have priv 25 in context -4,-41 (2) again'); select is(accessor_id, -5, 'Eve should now have Bob''s accessor_id again') from session_context; -- ...contextual role mapppings... -- We will use eve with some new role assignments delete from veil2.accessor_roles where accessor_id = -2; insert into veil2.accessor_roles (accessor_id, role_id, context_type_id, context_id) values (-2, 0, 1, 0), -- global connect (-2, 5, -3, -3), -- test_role_1 (-2, 6, -3, -3), -- test_role_2 (-2, 6, -3, -31), -- test_role_3 (-2, 7, -3, -31); -- test_role_3 -- Recreate role mappings for roles 5->9 in contexts delete from veil2.role_roles where primary_role_id in (5,6,7,8,9); insert into veil2.role_roles (primary_role_id, assigned_role_id, context_type_id, context_id) values (6, 8, -3, -3), (6, 9, -3, -31); -- Update target scope update veil2.system_parameters set parameter_value = '-3' where parameter_name = 'mapping context target scope type'; -- Odd query structure so that no rows are returned but function is -- called. with init as ( select 1 as result from veil2.init() ) select null from init where result != 1; -- connect as eve for scope -3,-3 with session as ( select o.* from veil2.create_session('eve', 'plaintext', -3, -3) c cross join veil2.open_connection(c.session_id, 1, 'password2') o ) select is(success, true, 'Eve should be authenticated (login -3, -3)') from session; -- Ensure Eve has roles for scope -3, -3 but not -3, -31 with sess as ( select * from veil2.session_privileges() where scope_type_id = -3 ) select is(1, (select count(*)::integer from sess), 'Eve should only have corp assignments in one scope') union all select is(scope_id, -3, 'Eve should only have corp assignments in scope -3') from sess union all select is(roles ? 5, true, 'Eve should have role 5') from sess union all select is(roles ? 6, true, 'Eve should have role 6') from sess union all select is(roles ? 8, true, 'Eve should have role 8') from sess union all select is(roles ? 9, false, 'Eve should not have role 9') from sess; -- connect as eve for scope -3,-31 with session as ( select o.* from veil2.create_session('eve', 'plaintext', -3, -31) c cross join veil2.open_connection(c.session_id, 1, 'password2') o ) select is(s.success, true, 'Eve should be authenticated (login -3, -31)') from session s; select is(veil2.i_have_priv_in_superior_scope(4, -6, -62), true, 'Eve should have priv 4 in a scope superior to -6, -62'); select is(veil2.i_have_priv_in_scope_or_superior(4, -6, -62), true, 'Eve should have priv 4 in a scope superior to -6, -62 (1)'); select is(veil2.i_have_priv_in_scope_or_superior_or_global(4, -6, -62), true, 'Eve should have priv 4 in a scope superior to -6, -62 (2)'); select is(veil2.i_have_priv_in_superior_scope(4, -6, -61), false, 'Eve should not have priv 4 in a scope superior to -6, -61'); /* \pset tuples_only false \pset format aligned select *, to_array(roles), to_array(privs) from veil2.session_privileges(); select * from veil2.superior_scopes; select veil2.i_have_priv_in_superior_scope(4, -6, -62); select veil2.i_have_priv_in_scope_or_superior(4, -6, -62); \pset format unaligned \pset tuples_only true */ with sess as ( select * from veil2.session_privileges() where scope_type_id = -3 ) select is(1, (select count(*)::integer from sess), 'Eve should only have corp assignments in one scope') union all select is(scope_id, -31, 'Eve should only have corp assignments in scope -31') from sess union all select is(roles ? 5, false, 'Eve should not have role 5') from sess union all select is(roles ? 6, true, 'Eve should have role 6') from sess union all select is(roles ? 7, true, 'Eve should have role 7') from sess union all select is(roles ? 9, true, 'Eve should have role 9') from sess; select * from finish(); /* \set QUIET 0 \pset format aligned \pset tuples_only false */ rollback;