From c2457112b60084294762a787a89e5306e14b45b5 Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Mon, 8 Sep 2025 16:25:14 +1000 Subject: [PATCH] fix: add constraint on add search config --- src/config/config_test.sql | 22 ++++++++++- src/config/functions.sql | 2 +- src/encrypted/constraints_test.sql | 60 +++++++++++++++++++++++++++++ src/encrypted/functions.sql | 10 +++-- src/encryptindex/functions_test.sql | 10 ++--- 5 files changed, 93 insertions(+), 11 deletions(-) diff --git a/src/config/config_test.sql b/src/config/config_test.sql index 5453420..e67b484 100644 --- a/src/config/config_test.sql +++ b/src/config/config_test.sql @@ -1,6 +1,24 @@ \set ON_ERROR_STOP on +-- Create tables for adding configuration +DROP TABLE IF EXISTS users; +CREATE TABLE users +( + id bigint GENERATED ALWAYS AS IDENTITY, + name eql_v2_encrypted, + PRIMARY KEY(id) +); + +DROP TABLE IF EXISTS blah; +CREATE TABLE blah +( + id bigint GENERATED ALWAYS AS IDENTITY, + vtha eql_v2_encrypted, + PRIMARY KEY(id) +); + + -- -- Helper function for assertions -- @@ -90,7 +108,7 @@ DO $$ PERFORM eql_v2.remove_search_config('blah', 'vtha', 'unique', migrating => true); ASSERT NOT (SELECT _search_config_exists('users', 'vtha', 'unique')); - -- All indexes removed, but column config preserved + -- All indexes removed, but column config preserved ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'pending')); ASSERT (SELECT data #> array['tables', 'blah', 'vtha', 'indexes'] = '{}' FROM eql_v2_configuration c WHERE c.state = 'pending'); @@ -222,7 +240,7 @@ DO $$ 'Pending configuration exists but is empty', 'SELECT * FROM eql_v2_configuration c WHERE c.state = ''pending''', 1); - + -- Verify the config is empty ASSERT (SELECT data #> array['tables'] = '{}' FROM eql_v2_configuration c WHERE c.state = 'pending'); diff --git a/src/config/functions.sql b/src/config/functions.sql index b754d67..54abeff 100644 --- a/src/config/functions.sql +++ b/src/config/functions.sql @@ -61,7 +61,7 @@ AS $$ PERFORM eql_v2.activate_config(); END IF; - -- PERFORM eql_v2.add_encrypted_constraint(table_name, column_name); + PERFORM eql_v2.add_encrypted_constraint(table_name, column_name); -- exeunt RETURN _config; diff --git a/src/encrypted/constraints_test.sql b/src/encrypted/constraints_test.sql index 0dc88e5..df85ef1 100644 --- a/src/encrypted/constraints_test.sql +++ b/src/encrypted/constraints_test.sql @@ -43,6 +43,66 @@ DO $$ $$ LANGUAGE plpgsql; +-- ----------------------------------------------- +-- Adding search config adds the constraint +-- +-- ----------------------------------------------- +TRUNCATE TABLE eql_v2_configuration; + +DO $$ + BEGIN + -- reset the table + PERFORM create_table_with_encrypted(); + + PERFORM eql_v2.add_search_config('encrypted', 'e', 'match'); + + PERFORM assert_exception( + 'Constraint catches invalid eql_v2_encrypted', + 'INSERT INTO encrypted (e) VALUES (''{}''::jsonb::eql_v2_encrypted)'); + + -- add constraint without error + PERFORM eql_v2.add_encrypted_constraint('encrypted', 'e'); + + PERFORM eql_v2.remove_encrypted_constraint('encrypted', 'e'); + + PERFORM assert_result( + 'Insert invalid data without constraint', + 'INSERT INTO encrypted (e) VALUES (''{}''::jsonb::eql_v2_encrypted) RETURNING id'); + + END; +$$ LANGUAGE plpgsql; + + +-- ----------------------------------------------- +-- Adding column adds the constraint +-- +-- ----------------------------------------------- +TRUNCATE TABLE eql_v2_configuration; + +DO $$ + BEGIN + -- reset the table + PERFORM create_table_with_encrypted(); + + PERFORM eql_v2.add_column('encrypted', 'e'); + + PERFORM assert_exception( + 'Constraint catches invalid eql_v2_encrypted', + 'INSERT INTO encrypted (e) VALUES (''{}''::jsonb::eql_v2_encrypted)'); + + -- add constraint without error + PERFORM eql_v2.add_encrypted_constraint('encrypted', 'e'); + + PERFORM eql_v2.remove_encrypted_constraint('encrypted', 'e'); + + PERFORM assert_result( + 'Insert invalid data without constraint', + 'INSERT INTO encrypted (e) VALUES (''{}''::jsonb::eql_v2_encrypted) RETURNING id'); + + END; +$$ LANGUAGE plpgsql; + + -- EQL version is enforced DO $$ DECLARE diff --git a/src/encrypted/functions.sql b/src/encrypted/functions.sql index 0629754..454c01c 100644 --- a/src/encrypted/functions.sql +++ b/src/encrypted/functions.sql @@ -51,8 +51,12 @@ CREATE FUNCTION eql_v2.add_encrypted_constraint(table_name TEXT, column_name TEX RETURNS void AS $$ BEGIN - EXECUTE format('ALTER TABLE %I ADD CONSTRAINT eql_v2_encrypted_check_%I CHECK (eql_v2.check_encrypted(%I))', table_name, column_name, column_name); - END; + EXECUTE format('ALTER TABLE %I ADD CONSTRAINT eql_v2_encrypted_constraint_%I_%I CHECK (eql_v2.check_encrypted(%I))', table_name, table_name, column_name, column_name); + EXCEPTION + WHEN duplicate_table THEN + WHEN duplicate_object THEN + RAISE NOTICE 'Constraint `eql_v2_encrypted_constraint_%_%` already exists, skipping', table_name, column_name; + END; $$ LANGUAGE plpgsql; @@ -66,7 +70,7 @@ CREATE FUNCTION eql_v2.remove_encrypted_constraint(table_name TEXT, column_name RETURNS void AS $$ BEGIN - EXECUTE format('ALTER TABLE %I DROP CONSTRAINT IF EXISTS eql_v2_encrypted_check_%I', table_name, column_name); + EXECUTE format('ALTER TABLE %I DROP CONSTRAINT IF EXISTS eql_v2_encrypted_constraint_%I_%I', table_name, table_name, column_name); END; $$ LANGUAGE plpgsql; diff --git a/src/encryptindex/functions_test.sql b/src/encryptindex/functions_test.sql index 9945f72..1044e10 100644 --- a/src/encryptindex/functions_test.sql +++ b/src/encryptindex/functions_test.sql @@ -154,7 +154,7 @@ CREATE TABLE users -- An encrypting config should exist DO $$ BEGIN - PERFORM eql_v2.add_search_config('users', 'name', 'match', migrating => true); + PERFORM eql_v2.add_search_config('users', 'name_encrypted', 'match', migrating => true); PERFORM eql_v2.migrate_config(); ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'active')); ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'encrypting')); @@ -167,7 +167,7 @@ $$ LANGUAGE plpgsql; DO $$ BEGIN TRUNCATE TABLE eql_v2_configuration; - PERFORM eql_v2.add_search_config('users', 'name', 'match'); + PERFORM eql_v2.add_search_config('users', 'name_encrypted', 'match'); ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'active')); END; $$ LANGUAGE plpgsql; @@ -177,7 +177,7 @@ $$ LANGUAGE plpgsql; DO $$ BEGIN TRUNCATE TABLE eql_v2_configuration; - PERFORM eql_v2.add_search_config('users', 'name', 'match'); + PERFORM eql_v2.add_search_config('users', 'name_encrypted', 'match'); PERFORM assert_exception( 'eql_v2.migrate_config() should raise an exception when no pending configuration exists', @@ -226,7 +226,7 @@ CREATE TABLE users -- An encrypting config should exist DO $$ BEGIN - PERFORM eql_v2.add_search_config('users', 'name', 'match', migrating => true); + PERFORM eql_v2.add_search_config('users', 'name_encrypted', 'match', migrating => true); PERFORM eql_v2.migrate_config(); ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'active')); @@ -276,7 +276,7 @@ CREATE TABLE users -- An encrypting config should exist DO $$ BEGIN - PERFORM eql_v2.add_search_config('users', 'name', 'match', migrating => true); + PERFORM eql_v2.add_search_config('users', 'name_encrypted', 'match', migrating => true); PERFORM eql_v2.migrate_config(); -- need to encrypt first PERFORM eql_v2.activate_config();