diff --git a/internal/db/schema/migrations/oss/postgres/70/14_recording_static_credential.up.sql b/internal/db/schema/migrations/oss/postgres/70/14_recording_static_credential.up.sql new file mode 100644 index 000000000..185b7d05a --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/70/14_recording_static_credential.up.sql @@ -0,0 +1,82 @@ +-- Copyright (c) HashiCorp, Inc. +-- SPDX-License-Identifier: MPL-2.0 + +begin; + + create table recording_static_credential ( + recording_id wt_public_id not null + constraint recording_session_fk + references recording_session (public_id) + on delete cascade + on update cascade, + credential_static_store_hst_id wt_url_safe_id not null + constraint credential_static_store_hst_fk + references credential_static_store_hst (history_id) + on delete restrict -- History records with session recordings cannot be deleted + on update cascade, + credential_static_hst_id wt_url_safe_id not null + constraint credential_static_history_base_fk + references credential_static_history_base (history_id) + on delete restrict -- History records with session recordings cannot be deleted + on update cascade, + credential_purpose text not null + constraint credential_purpose_fkey + references credential_purpose_enm (name) + on delete restrict + on update cascade, + primary key(recording_id, credential_static_store_hst_id, credential_static_hst_id, credential_purpose) + ); + comment on table recording_static_credential is + 'recording_static_credential table is a join table that associates a session recording with ' + 'a static credential, the static credential''s static credential store, and the credential''s purpose ' + 'along with the values of those entities at the time of the recording. ' + 'These values are also stored in the BSR file.'; + + create function insert_recording_static_credentials() returns trigger + as $$ + begin + with + session_recording(session_id, recording_id) as ( + select session_id, public_id + from recording_session + where session_id = new.session_id + ), + session_static_creds(cred_id, purpose, recording_id) as ( + select credential_static_id, credential_purpose, recording_id + from session_credential_static + join session_recording using (session_id) + ), + static_cred_history(public_id, store_id, cred_hst_id, valid_range) as ( + select public_id, store_id, history_id, valid_range + from credential_static_json_credential_hst + union + select public_id, store_id, history_id, valid_range + from credential_static_ssh_private_key_credential_hst + union + select public_id, store_id, history_id, valid_range + from credential_static_username_password_credential_hst + ), + final(recording_id, cred_id, store_id, cred_hst_id, store_hst_id, cred_purpose) as ( + select ssc.recording_id, sc.public_id, sc.store_id, sc.cred_hst_id, store_hst.history_id, ssc.purpose + from static_cred_history as sc + join credential_static_store_hst as store_hst on sc.store_id = store_hst.public_id + and store_hst.valid_range @> current_timestamp + join session_static_creds as ssc on sc.public_id = ssc.cred_id + where sc.public_id in (select cred_id from session_static_creds) + and sc.valid_range @> current_timestamp + ) + insert into recording_static_credential + (recording_id, credential_static_store_hst_id, credential_static_hst_id, credential_purpose) + select recording_id, store_hst_id, cred_hst_id, cred_purpose + from final; + + return new; + end; + $$ language plpgsql; + comment on function insert_recording_static_credentials is + 'insert_recording_static_credentials is an after insert trigger for the recording_session table.'; + + create trigger insert_recording_static_credentials after insert on recording_session + for each row execute procedure insert_recording_static_credentials(); + +commit; diff --git a/internal/db/sqltest/initdb.d/01_colors_persona.sql b/internal/db/sqltest/initdb.d/01_colors_persona.sql index e0c303915..fe529f8e0 100644 --- a/internal/db/sqltest/initdb.d/01_colors_persona.sql +++ b/internal/db/sqltest/initdb.d/01_colors_persona.sql @@ -440,6 +440,7 @@ begin; ('p____bcolors', 't_________cb', 'csj__bcolors', 'brokered'), ('p____bcolors', 'tssh______cb', 'csj__bcolors', 'injected_application'), ('p____gcolors', 'tssh______cg', 'csj__gcolors', 'brokered'), + ('p____gcolors', 'tssh______cg', 'csu__gcolors', 'brokered'), ('p____gcolors', 'tssh______cg', 'cspk_gcolors', 'injected_application'); insert into target_credential_library @@ -449,16 +450,30 @@ begin; ('p____bcolors', 'tssh______cb', 'vl______cvl', 'brokered'); insert into session - (project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id) - values - ('p____bcolors', 'tssh______cb', 'u______clare', 'tok____clare', 'abc'::bytea, 'ep1', 's1_____clare'), - ('p____bcolors', 't_________cb', 'u______cindy', 'tok____cindy', 'abc'::bytea, 'ep1', 's1_____cindy'), - ('p____bcolors', 't_________cb', 'u______cindy', 'tok____cindy', 'abc'::bytea, 'ep1', 's1_____ciara'), - ('p____bcolors', 't_________cb', 'u______carly', 'tok____carly', 'abc'::bytea, 'ep1', 's1_____carly'), - ('p____gcolors', 'tssh______cg', 'u_______cora', 'tok_____cora', 'abc'::bytea, 'ep1', 's1______cora'), + (project_id, target_id, public_id, user_id, auth_token_id, certificate, endpoint) + values + ('p____bcolors', 'tssh______cb', 's1_____clare', 'u______clare', 'tok____clare', 'abc'::bytea, 'ep1'), + ('p____bcolors', 't_________cb', 's1_____cindy', 'u______cindy', 'tok____cindy', 'abc'::bytea, 'ep1'), + ('p____bcolors', 't_________cb', 's1_____ciara', 'u______cindy', 'tok____cindy', 'abc'::bytea, 'ep1'), + ('p____bcolors', 't_________cb', 's1_____carly', 'u______carly', 'tok____carly', 'abc'::bytea, 'ep1'), + ('p____gcolors', 'tssh______cg', 's1______cora', 'u_______cora', 'tok_____cora', 'abc'::bytea, 'ep1'), --- the next two are used in recording_session tests - ('p____bcolors', 'tssh______cb', 'u______clare', 'tok____clare', 'abc'::bytea, 'ep1', 's2_____clare'), - ('p____gcolors', 'tssh______cg', 'u_______cora', 'tok_____cora', 'abc'::bytea, 'ep1', 's2______cora'); + ('p____bcolors', 'tssh______cb', 's2_____clare', 'u______clare', 'tok____clare', 'abc'::bytea, 'ep1'), + ('p____gcolors', 'tssh______cg', 's2______cora', 'u_______cora', 'tok_____cora', 'abc'::bytea, 'ep1'); + + insert into session_credential_static + (session_id, credential_static_id, credential_purpose) + values + ('s1_____clare', 'csj__bcolors', 'injected_application'), -- tssh______cb + ('s2_____clare', 'csj__bcolors', 'injected_application'), -- tssh______cb + ('s1_____cindy', 'csj__bcolors', 'brokered'), -- t_________cb + ('s1_____ciara', 'csj__bcolors', 'brokered'), -- t_________cb + ('s1_____carly', 'csj__bcolors', 'brokered'), -- t_________cb + ('s1______cora', 'csj__gcolors', 'brokered'), -- tssh______cg + ('s1______cora', 'csu__gcolors', 'brokered'), -- tssh______cg + ('s1______cora', 'cspk_gcolors', 'injected_application'), -- tssh______cg + ('s2______cora', 'csj__gcolors', 'brokered'), -- tssh______cg + ('s2______cora', 'cspk_gcolors', 'injected_application'); -- tssh______cg insert into session_host_set_host (session_id, host_set_id, host_id) diff --git a/internal/db/sqltest/tests/recording/recording_static_credentials.sql b/internal/db/sqltest/tests/recording/recording_static_credentials.sql new file mode 100644 index 000000000..0aa71dc01 --- /dev/null +++ b/internal/db/sqltest/tests/recording/recording_static_credentials.sql @@ -0,0 +1,73 @@ +-- Copyright (c) HashiCorp, Inc. +-- SPDX-License-Identifier: MPL-2.0 + +begin; + select plan(27); + + select has_table('recording_static_credential'); + + -- tests a fk column referencing a history table + -- add 5 to the plan for each time this function is called + create function hst_fk_column(column_name name, pk_table name) returns text + as $$ + select * from collect_tap( + has_column('recording_static_credential', column_name), + col_not_null('recording_static_credential', column_name), + col_type_is('recording_static_credential', column_name, 'wt_url_safe_id'), -- should be the same type as the operational table + col_hasnt_default('recording_static_credential', column_name), + fk_ok('recording_static_credential', column_name, pk_table, 'history_id') + ); + $$ language sql; + + select hst_fk_column('credential_static_store_hst_id', 'credential_static_store_hst'); + select hst_fk_column('credential_static_hst_id', 'credential_static_history_base'); + + select has_column('recording_static_credential', 'recording_id'); + select col_not_null('recording_static_credential', 'recording_id'); + select col_type_is('recording_static_credential', 'recording_id', 'wt_public_id'); -- should be the same type as the operational table + select col_hasnt_default('recording_static_credential', 'recording_id'); + select fk_ok('recording_static_credential', 'recording_id', 'recording_session', 'public_id'); + + select has_column('recording_static_credential', 'credential_purpose'); + select col_not_null('recording_static_credential', 'credential_purpose'); + select col_type_is('recording_static_credential', 'credential_purpose', 'text'); -- should be the same type as the operational table + select col_hasnt_default('recording_static_credential', 'credential_purpose'); + select fk_ok('recording_static_credential', 'credential_purpose', 'credential_purpose_enm', 'name'); + + select col_is_pk('recording_static_credential', + array['recording_id', 'credential_static_store_hst_id', 'credential_static_hst_id', 'credential_purpose']); + -- 22 + + prepare get_target_creds as + select credential_static_id, credential_purpose + from target_static_credential + where target_id = 'tssh______cg'; + + prepare get_session_creds as + select credential_static_id, credential_purpose + from session_credential_static + join session on session_id = public_id + where target_id = 'tssh______cg' + and session_id = 's1______cora'; + + select results_eq('get_target_creds', 'get_session_creds'); + + select is(count(*), 3::bigint) + from target_static_credential + where target_id = 'tssh______cg'; + + select is(count(*), 3::bigint) + from session_credential_static + where session_id = 's1______cora'; + + select is(count(*), 1::bigint) + from recording_session + where session_id = 's1______cora' + and public_id = 'sr1_____cora'; + + select is(count(*), 3::bigint) + from recording_static_credential + where recording_id = 'sr1_____cora'; + + select * from finish(); +rollback;