db: add channel summary details

This commit is contained in:
Johan Brandhorst-Satzkorn authored and Timothy Messier committed 2023-05-31 12:18:17 +00:00
1 parent ee6a5f05b5
commit af8e4d63be
3 files changed
+387 -16

No files matched your search

@@ -273,11 +273,7 @@ begin;
'delete_recording_channel_subtype deletes a row from the base table when a row is deleted in a subtype table.';
-- TODO: We currently only include the channel type here,
-- but it's not enough on its own to determine what mime types
-- are supported by a channel. We'll need extra information in
-- the schema for this, but it's not clear what yet.
-- Channel types reference: https://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-11
-- Channel types reference: https://www.rfc-editor.org/rfc/rfc4250.html#section-4.9.1
create table recording_channel_ssh_channel_type_enm (
name text primary key
constraint only_predefined_channel_types_allowed
@@ -285,7 +281,7 @@ begin;
);
comment on table recording_channel_ssh_channel_type_enm is
'recording_channel_ssh_channel_type_enm holds valid values for the channel_type of a recording_channel_ssh row. '
'Some known channel types are defined in https://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-11';
'Some known channel types are defined in https://www.rfc-editor.org/rfc/rfc4250.html#section-4.9.1';
insert into recording_channel_ssh_channel_type_enm (name)
values
@@ -344,4 +340,214 @@ begin;
create trigger delete_recording_channel_subtype after delete on recording_channel_ssh
for each row execute procedure delete_recording_channel_subtype();
create function validate_recording_channel_ssh_insert(new_channel_type text, new_public_id text) returns void
as $$
begin
-- If the channel type is session, the program table must have exactly 1 entry matching this row
if new_channel_type = 'session' then
if count(*) != 1 from recording_channel_ssh_session_channel where recording_channel_id = new_public_id then
raise exception 'Channel recording of type ''session'' must populate recording_channel_ssh_session_channel';
end if;
end if;
end;
$$ language plpgsql;
comment on function validate_recording_channel_ssh_insert is
'function used to ensure each recording_channel_ssh entry with channel_type session has an associated '
'entry in recording_channel_ssh_session_channel.';
create function insert_recording_channel_ssh() returns trigger
as $$
begin
perform validate_recording_channel_ssh_insert(new.channel_type, new.public_id);
return null;
end;
$$ language plpgsql;
comment on function insert_recording_channel_ssh() is
'function used on recording_channel_ssh after insert initially deferred to ensure each '
'entry with a channel_type of ''session'' has an associated entry in recording_channel_ssh_session_channel';
create constraint trigger insert_recording_channel_ssh
after insert on recording_channel_ssh deferrable initially deferred
for each row execute procedure insert_recording_channel_ssh();
create table recording_channel_ssh_session_channel_program_enm (
name text primary key
constraint only_predefined_programs_allowed
check(name in ('none', 'shell', 'exec', 'subsystem'))
);
comment on table recording_channel_ssh_session_channel_program_enm is
'recording_channel_ssh_session_channel_program_enm holds valid values for the program column in recording_channel_ssh_session_channel';
insert into recording_channel_ssh_session_channel_program_enm (name)
values
('none'),
('shell'),
('exec'),
('subsystem');
create trigger immutable_columns before update on recording_channel_ssh_session_channel_program_enm
for each row execute procedure immutable_columns('name');
create table recording_channel_ssh_session_channel (
recording_channel_id wt_public_id primary key
constraint recording_channel_ssh_fkey
references recording_channel_ssh (public_id)
on delete cascade
on update cascade,
program text not null
constraint recording_channel_ssh_session_channel_program_enm_fkey
references recording_channel_ssh_session_channel_program_enm (name)
on delete restrict
on update cascade
);
create trigger immutable_columns before update on recording_channel_ssh_session_channel
for each row execute procedure immutable_columns('recording_channel_id', 'program');
create function validate_recording_channel_ssh_session_channel_insert(new_recording_channel_id text, new_program text) returns void
as $$
begin
-- First check that the channel_type is session in the channel recording table
if channel_type != 'session' from recording_channel_ssh where public_id = new_recording_channel_id then
raise exception 'Session channel must have channel_type ''session''';
end if;
-- If the program is subsystem, the subsystem table must have exactly 1 entry matching this row
if new_program = 'subsystem' then
if count(*) != 1 from recording_channel_ssh_session_channel_program_subsystem where recording_channel_id = new_recording_channel_id then
raise exception 'Session channel with program ''subsystem'' must populate recording_channel_ssh_session_channel_program_subsystem';
end if;
end if;
-- If the program is exec, the exec table must have exactly 1 entry matching this row
if new_program = 'exec' then
if count(*) != 1 from recording_channel_ssh_session_channel_program_exec where recording_channel_id = new_recording_channel_id then
raise exception 'Session channel with program ''exec'' must populate recording_channel_ssh_session_channel_program_exec';
end if;
end if;
end;
$$ language plpgsql;
comment on function validate_recording_channel_ssh_session_channel_insert is
'function used to ensure each recording_channel_ssh_session_channel entry has an associated '
'entry in recording_channel_ssh with a channel_type of ''session''.';
create function insert_recording_channel_ssh_session_channel() returns trigger
as $$
begin
perform validate_recording_channel_ssh_session_channel_insert(new.recording_channel_id, new.program);
return null;
end;
$$ language plpgsql;
comment on function insert_recording_channel_ssh_session_channel() is
'function used on recording_channel_ssh_session_channel after insert initially deferred to '
'validate inserts.';
create constraint trigger insert_recording_channel_ssh_session_channel
after insert on recording_channel_ssh_session_channel deferrable initially deferred
for each row execute procedure insert_recording_channel_ssh_session_channel();
create table recording_channel_ssh_session_channel_program_subsystem (
recording_channel_id wt_public_id primary key
constraint recording_channel_ssh_session_channel_fkey
references recording_channel_ssh_session_channel (recording_channel_id)
on delete cascade
on update cascade,
-- https://www.rfc-editor.org/rfc/rfc4250.html#section-4.6.1 defines
-- this as <= 64 bytes. Lets use a larger number just in case.
subsystem_name text not null
constraint subsystem_lte_1024_bytes
check(length(subsystem_name) <= 1024)
);
create trigger immutable_columns before update on recording_channel_ssh_session_channel_program_subsystem
for each row execute procedure immutable_columns('recording_channel_id', 'subsystem_name');
create function validate_recording_channel_ssh_subsystem_insert(new_recording_channel_id text) returns void
as $$
begin
if program != 'subsystem' from recording_channel_ssh_session_channel where recording_channel_id = new_recording_channel_id then
raise exception 'Session channel subsystem program must have program ''subsystem''';
end if;
end;
$$ language plpgsql;
comment on function validate_recording_channel_ssh_subsystem_insert is
'function used to ensure each recording_channel_ssh_session_channel_program_subsystem entry has an associated '
'entry in recording_channel_ssh_session_channel with a program of ''subsystem''.';
create function insert_recording_channel_ssh_session_channel_program_subsystem() returns trigger
as $$
begin
perform validate_recording_channel_ssh_subsystem_insert(new.recording_channel_id);
return null;
end;
$$ language plpgsql;
comment on function insert_recording_channel_ssh_session_channel_program_subsystem() is
'function used on recording_channel_ssh_session_channel_program_subsystem after insert initially deferred to ensure each '
'entry has an associated entry in recording_channel_ssh_session_channel with a program of ''subsystem''';
create constraint trigger insert_recording_channel_ssh_session_channel_program_subsystem
after insert on recording_channel_ssh_session_channel_program_subsystem deferrable initially deferred
for each row execute procedure insert_recording_channel_ssh_session_channel_program_subsystem();
create table recording_channel_ssh_session_channel_program_exec_enm (
name text primary key
constraint only_predefined_execs_allowed
check(name in ('unknown', 'scp', 'rsync'))
);
comment on table recording_channel_ssh_session_channel_program_exec_enm is
'recording_channel_ssh_session_channel_program_exec_enm holds valid values for the exec_program column in recording_channel_ssh_session_channel_program_exec';
insert into recording_channel_ssh_session_channel_program_exec_enm (name)
values
('unknown'),
('scp'),
('rsync');
create trigger immutable_columns before update on recording_channel_ssh_session_channel_program_exec_enm
for each row execute procedure immutable_columns('name');
create table recording_channel_ssh_session_channel_program_exec (
recording_channel_id wt_public_id primary key
constraint recording_channel_ssh_session_channel_fkey
references recording_channel_ssh_session_channel (recording_channel_id)
on delete cascade
on update cascade,
exec_program text not null
constraint recording_channel_ssh_session_channel_program_exec_enm_fkey
references recording_channel_ssh_session_channel_program_exec_enm (name)
on delete restrict
on update cascade
);
create trigger immutable_columns before update on recording_channel_ssh_session_channel_program_exec
for each row execute procedure immutable_columns('recording_channel_id');
create function validate_recording_channel_ssh_exec_insert(new_recording_channel_id text) returns void
as $$
begin
if program != 'exec' from recording_channel_ssh_session_channel where recording_channel_id = new_recording_channel_id then
raise exception 'Session channel exec program must have program ''exec''';
end if;
end;
$$ language plpgsql;
comment on function validate_recording_channel_ssh_exec_insert is
'function used to ensure each recording_channel_ssh_session_channel_program_exec entry has an associated '
'entry in recording_channel_ssh_session_channel with a program of ''exec''.';
create function insert_recording_channel_ssh_session_channel_program_exec() returns trigger
as $$
begin
perform validate_recording_channel_ssh_exec_insert(new.recording_channel_id);
return null;
end;
$$ language plpgsql;
comment on function insert_recording_channel_ssh_session_channel_program_exec() is
'function used on recording_channel_ssh_session_channel_program_exec after insert initially deferred to ensure each '
'entry has an associated entry in recording_channel_ssh_session_channel with a program of ''exec''';
create constraint trigger insert_recording_channel_ssh_session_channel_program_exec
after insert on recording_channel_ssh_session_channel_program_exec deferrable initially deferred
for each row execute procedure insert_recording_channel_ssh_session_channel_program_exec();
commit;
@@ -8,9 +8,10 @@
-- end_time_null_or_after_start_time
-- bytes_up_null_zero_or_positive
-- bytes_down_null_zero_or_positive
-- subsystem_shorter_than_1024_bytes
begin;
select plan(9);
select plan(12);
select wtt_load('widgets', 'iam', 'kms', 'auth', 'hosts', 'targets', 'sessions');
insert into recording_session
@@ -31,7 +32,7 @@ begin;
insert into recording_channel_ssh
(public_id, recording_connection_id, start_time, end_time, bytes_up, bytes_down, channel_type)
values
('chr_123456789', 'cr_123456789', clock_timestamp()::timestamptz, clock_timestamp()::timestamptz - '1s'::interval, 10, 10, 'session');
('chr_123456789', 'cr_123456789', clock_timestamp()::timestamptz, clock_timestamp()::timestamptz - '1s'::interval, 10, 10, 'x11');
select throws_ok('end_time_before_start_time', '23514', null, 'inserting a row with end_time before star_time succeeded');
-- Try to set bytes_up to a negative number
@@ -39,7 +40,7 @@ begin;
insert into recording_channel_ssh
(public_id, recording_connection_id, start_time, end_time, bytes_up, bytes_down, channel_type)
values
('chr_123456789', 'cr_123456789', clock_timestamp()::timestamptz, clock_timestamp()::timestamptz + '1s'::interval, -1, 10, 'session');
('chr_123456789', 'cr_123456789', clock_timestamp()::timestamptz, clock_timestamp()::timestamptz + '1s'::interval, -1, 10, 'x11');
select throws_ok('negative_bytes_up', '23514', null, 'inserting a row with a negative bytes_up value succeeded');
-- Try to set bytes_down to a negative number
@@ -47,34 +48,67 @@ begin;
insert into recording_channel_ssh
(public_id, recording_connection_id, start_time, end_time, bytes_up, bytes_down, channel_type)
values
('chr_123456789', 'cr_123456789', clock_timestamp()::timestamptz, clock_timestamp()::timestamptz + '1s'::interval, 10, -1, 'session');
('chr_123456789', 'cr_123456789', clock_timestamp()::timestamptz, clock_timestamp()::timestamptz + '1s'::interval, 10, -1, 'x11');
select throws_ok('negative_bytes_down', '23514', null, 'inserting a row with a negative bytes_down value succeeded');
-- Check that there are no rows
select is(count(*), 0::bigint) from recording_channel_ssh;
select is(count(*), 0::bigint) from recording_channel;
insert into recording_channel_ssh
(public_id, recording_connection_id, start_time, end_time, bytes_up, bytes_down, channel_type)
values
('chr_123456789', 'cr_123456789', clock_timestamp()::timestamptz, clock_timestamp()::timestamptz + '1s'::interval, 10, 10, 'session');
('chr_123456789', 'cr_123456789', clock_timestamp()::timestamptz, clock_timestamp()::timestamptz + '1s'::interval, 10, 10, 'session'),-- 'none', null, null),
('chr_234567891', 'cr_123456789', clock_timestamp()::timestamptz, clock_timestamp()::timestamptz + '1s'::interval, 10, 10, 'session'),-- 'shell', null, null),
('chr_345678912', 'cr_123456789', clock_timestamp()::timestamptz, clock_timestamp()::timestamptz + '1s'::interval, 10, 10, 'session'),-- 'exec', 'scp', null),
('chr_456789123', 'cr_123456789', clock_timestamp()::timestamptz, clock_timestamp()::timestamptz + '1s'::interval, 10, 10, 'session'),-- 'subsystem', null, 'sftp'),
('chr_567891234', 'cr_123456789', clock_timestamp()::timestamptz, clock_timestamp()::timestamptz + '1s'::interval, 10, 10, 'x11');--, null, null, null);
-- Check that a row was inserted
select is(count(*), 1::bigint) from recording_channel where public_id = 'chr_123456789' and recording_connection_id = 'cr_123456789';
insert into recording_channel_ssh_session_channel
(recording_channel_id, program)
values
('chr_123456789', 'none'),
('chr_234567891', 'shell'),
('chr_345678912', 'exec'),
('chr_456789123', 'subsystem');
insert into recording_channel_ssh_session_channel_program_exec
(recording_channel_id, exec_program)
values
('chr_345678912', 'scp');
-- Try to insert a large subsystem row
prepare insert_large_subsystem_name as
insert into recording_channel_ssh_session_channel_program_subsystem
(recording_channel_id, subsystem_name)
values
('chr_456789123', repeat('sftp', 1000));
select throws_ok('insert_large_subsystem_name', '23514', null, 'inserting a large subsystem_name succeeded');
insert into recording_channel_ssh_session_channel_program_subsystem
(recording_channel_id, subsystem_name)
values
('chr_456789123', 'sftp');
-- Check that the rows were inserted
select is(count(*), 5::bigint) from recording_channel_ssh where recording_connection_id = 'cr_123456789';
select is(count(*), 5::bigint) from recording_channel where recording_connection_id = 'cr_123456789';
-- Deleting the session connection should leave the recording in place
delete from session_connection where public_id = 'sc_123456789';
-- Row should still be present
select is(count(*), 1::bigint) from recording_channel where public_id = 'chr_123456789';
select is(count(*), 5::bigint) from recording_channel_ssh where recording_connection_id = 'cr_123456789';
-- Deleting the session should leave the recording in place
delete from session where public_id = 's2_____clare';
-- Row should still be present
select is(count(*), 1::bigint) from recording_channel where public_id = 'chr_123456789';
select is(count(*), 5::bigint) from recording_channel_ssh where recording_connection_id = 'cr_123456789';
-- Deleting the session recording should cascade to the channel recording
delete from recording_session where public_id = 'sr_123456789';
-- Row should be deleted
select is(count(*), 0::bigint) from recording_channel where public_id = 'chr_123456789';
select is(count(*), 0::bigint) from recording_channel_ssh where recording_connection_id = 'cr_123456789';
-- Check that it was also deleted from recording_channel
select is(count(*), 0::bigint) from recording_channel;
@@ -0,0 +1,131 @@
-- Copyright (c) HashiCorp, Inc.
-- SPDX-License-Identifier: MPL-2.0
-- recording_channel_ssh_validation_functions tests the following functions:
-- validate_recording_channel_ssh_insert
-- validate_recording_channel_ssh_session_channel_insert
-- validate_recording_channel_ssh_subsystem_insert
-- validate_recording_channel_ssh_exec_insert
begin;
select plan(14);
select wtt_load('widgets', 'iam', 'kms', 'auth', 'hosts', 'targets', 'sessions');
insert into recording_connection
(public_id, session_id, session_connection_id, recording_session_id)
values
('cr_123456789', 's1_____clare', 'sc1_____clare', 'sr1____clare');
insert into recording_channel_ssh
(public_id, recording_connection_id, start_time, end_time, bytes_up, bytes_down, channel_type)
values
('chr_123456789', 'cr_123456789', clock_timestamp()::timestamptz, clock_timestamp()::timestamptz + '1s'::interval, 10, 10, 'session'),
('chr_234567891', 'cr_123456789', clock_timestamp()::timestamptz, clock_timestamp()::timestamptz + '1s'::interval, 10, 10, 'session'),
('chr_345678912', 'cr_123456789', clock_timestamp()::timestamptz, clock_timestamp()::timestamptz + '1s'::interval, 10, 10, 'session'),
('chr_456789123', 'cr_123456789', clock_timestamp()::timestamptz, clock_timestamp()::timestamptz + '1s'::interval, 10, 10, 'session'),
('chr_567891234', 'cr_123456789', clock_timestamp()::timestamptz, clock_timestamp()::timestamptz + '1s'::interval, 10, 10, 'x11');
insert into recording_channel_ssh_session_channel
(recording_channel_id, program)
values
('chr_123456789', 'none'),
('chr_234567891', 'shell'),
('chr_345678912', 'exec'),
('chr_456789123', 'subsystem');
insert into recording_channel_ssh_session_channel_program_exec
(recording_channel_id, exec_program)
values
('chr_345678912', 'scp');
insert into recording_channel_ssh_session_channel_program_subsystem
(recording_channel_id, subsystem_name)
values
('chr_456789123', 'sftp');
---
-- validate_recording_channel_ssh_insert tests
----
-- Validate the valid row with id chr_123456789
prepare valid_session_channel_passes as
select validate_recording_channel_ssh_insert('session', 'chr_123456789');
select lives_ok('valid_session_channel_passes');
-- Validate the valid non-session row with id chr_567891234
prepare non_session_channel_passes as
select validate_recording_channel_ssh_insert('x11', 'chr_567891234');
select lives_ok('non_session_channel_passes');
-- Should error when pretending chr_567891234 is a session channel,
-- since there is no corresponding row in recording_channel_ssh_session_channel
prepare invalid_session_channel_fails as
select validate_recording_channel_ssh_insert('session', 'chr_567891234');
select throws_ok('invalid_session_channel_fails', null, 'Channel recording of type ''session'' must populate recording_channel_ssh_session_channel');
---
-- validate_recording_channel_ssh_session_channel_insert tests
----
-- Validate the valid row with id chr_123456789
prepare valid_session_channel_insert_none_passes as
select validate_recording_channel_ssh_session_channel_insert('chr_123456789', 'none');
select lives_ok('valid_session_channel_insert_none_passes');
-- Validate the valid row with id chr_234567891
prepare valid_session_channel_insert_shell_passes as
select validate_recording_channel_ssh_session_channel_insert('chr_234567891', 'shell');
select lives_ok('valid_session_channel_insert_shell_passes');
-- Validate the valid row with id chr_345678912
prepare valid_session_channel_insert_exec_passes as
select validate_recording_channel_ssh_session_channel_insert('chr_345678912', 'exec');
select lives_ok('valid_session_channel_insert_exec_passes');
-- Validate the valid row with id chr_456789123
prepare valid_session_channel_insert_subsystem_passes as
select validate_recording_channel_ssh_session_channel_insert('chr_456789123', 'subsystem');
select lives_ok('valid_session_channel_insert_subsystem_passes');
-- Should error when pretending chr_123456789 is a subsystem program,
-- since there is no corresponding row in recording_channel_ssh_session_channel_program_subsystem
prepare invalid_session_channel_subsystem_fails as
select validate_recording_channel_ssh_session_channel_insert('chr_123456789', 'subsystem');
select throws_ok('invalid_session_channel_subsystem_fails', null, 'Session channel with program ''exec'' must populate recording_channel_ssh_session_channel_program_exec');
-- Should error when pretending chr_123456789 is an exec program,
-- since there is no corresponding row in recording_channel_ssh_session_channel_program_exec
prepare invalid_session_channel_exec_fails as
select validate_recording_channel_ssh_session_channel_insert('chr_123456789', 'exec');
select throws_ok('invalid_session_channel_exec_fails', null, 'Session channel with program ''subsystem'' must populate recording_channel_ssh_session_channel_program_subsystem');
-- Should error when using the non-session channel chr_567891234,
-- as it doesn't have the channel_type session.
prepare non_session_channel_id_fails as
select validate_recording_channel_ssh_session_channel_insert('chr_567891234', 'none');
select throws_ok('non_session_channel_id_fails', null, 'Session channel must have channel_type ''session''');
---
-- validate_recording_channel_ssh_subsystem_insert tests
----
-- Validate the valid row with id chr_456789123
prepare valid_session_channel_subsystem_insert_passes as
select validate_recording_channel_ssh_subsystem_insert('chr_456789123');
select lives_ok('valid_session_channel_subsystem_insert_passes');
-- Should error when using the non-subsystem session channel chr_123456789,
-- as it doesn't have the program subsystem.
prepare non_subsystem_session_channel_id_fails as
select validate_recording_channel_ssh_subsystem_insert('chr_123456789');
select throws_ok('non_subsystem_session_channel_id_fails', null, 'Session channel subsystem program must have program ''subsystem''');
---
-- validate_recording_channel_ssh_exec_insert tests
----
-- Validate the valid row with id chr_345678912
prepare valid_session_channel_exec_insert_passes as
select validate_recording_channel_ssh_exec_insert('chr_345678912');
select lives_ok('valid_session_channel_exec_insert_passes');
-- Should error when using the non-exec session channel chr_123456789,
-- as it doesn't have the program exec.
prepare non_exec_session_channel_id_fails as
select validate_recording_channel_ssh_exec_insert('chr_123456789');
select throws_ok('non_exec_session_channel_id_fails', null, 'Session channel exec program must have program ''exec''');
select * from finish();
rollback;