Skip to content
112 changes: 93 additions & 19 deletions sql/010-core.sql
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,94 @@ BEGIN
END
$$;

DROP FUNCTION IF EXISTS _cs_encrypted_check_kind(jsonb);

CREATE FUNCTION _cs_encrypted_check_kind(val jsonb)
RETURNS BOOLEAN
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
BEGIN ATOMIC
RETURN (
(val->>'k' = 'ct' AND val ? 'c') OR
(val->>'k' = 'sv' AND val ? 'sv')
) AND NOT val ? 'p';
END;
--
-- CT payload should include a c field
--
DROP FUNCTION IF EXISTS _cs_encrypted_check_k_ct(jsonb);
CREATE FUNCTION _cs_encrypted_check_k_ct(val jsonb)
RETURNS boolean
AS $$
BEGIN
IF (val->>'k' = 'ct') THEN
IF (val ? 'c') THEN
RETURN true;
END IF;
RAISE 'Encrypted kind (k) of "ct" missing data field (c): %', val;
END IF;
RETURN true;
END;
$$ LANGUAGE plpgsql;

--
-- SV payload should include an sv field
--
DROP FUNCTION IF EXISTS _cs_encrypted_check_k_sv(jsonb);
CREATE FUNCTION _cs_encrypted_check_k_sv(val jsonb)
RETURNS boolean
AS $$
BEGIN
IF (val->>'k' = 'sv') THEN
IF (val ? 'sv') THEN
RETURN true;
END IF;
RAISE 'Encrypted kind (k) of "sv" missing data field (sv): %', val;
END IF;
RETURN true;
END;
$$ LANGUAGE plpgsql;


-- Plaintext field should never be present in an encrypted column
DROP FUNCTION IF EXISTS _cs_encrypted_check_p(jsonb);
CREATE FUNCTION _cs_encrypted_check_p(val jsonb)
RETURNS boolean
AS $$
BEGIN
IF NOT val ? 'p' THEN
RETURN true;
END IF;
RAISE 'Encrypted includes plaintext (p) field: %', val;
END;
$$ LANGUAGE plpgsql;

-- Should include an ident field
DROP FUNCTION IF EXISTS _cs_encrypted_check_i(jsonb);
CREATE FUNCTION _cs_encrypted_check_i(val jsonb)
RETURNS boolean
AS $$
BEGIN
IF val ? 'i' THEN
RETURN true;
END IF;
RAISE 'Encrypted missing ident (i) field: %', val;
END;
$$ LANGUAGE plpgsql;

-- Should include an ident field
DROP FUNCTION IF EXISTS _cs_encrypted_check_i_ct(jsonb);
CREATE FUNCTION _cs_encrypted_check_i_ct(val jsonb)
RETURNS boolean
AS $$
BEGIN
IF (val->'i' ?& array['t', 'c']) THEN
RETURN true;
END IF;
RAISE 'Encrypted ident (i) missing table (t) or column (c) fields: %', val;
END;
$$ LANGUAGE plpgsql;

-- Should include an ident field
DROP FUNCTION IF EXISTS _cs_encrypted_check_v(jsonb);
CREATE FUNCTION _cs_encrypted_check_v(val jsonb)
RETURNS boolean
AS $$
BEGIN
IF (val ? 'v') THEN
RETURN true;
END IF;
RAISE 'Encrypted missing version (v) field: %', val;
END;
$$ LANGUAGE plpgsql;


DROP FUNCTION IF EXISTS cs_check_encrypted_v1(val jsonb);
Expand All @@ -34,14 +111,11 @@ CREATE FUNCTION cs_check_encrypted_v1(val jsonb)
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
BEGIN ATOMIC
RETURN (
-- version and source are required
val ?& array['v'] AND

-- table and column
val->'i' ?& array['t', 'c'] AND

-- plaintext or ciphertext for kind
_cs_encrypted_check_kind(val)
_cs_encrypted_check_v(val) AND
_cs_encrypted_check_i(val) AND
_cs_encrypted_check_k_ct(val) AND
_cs_encrypted_check_k_sv(val) AND
_cs_encrypted_check_p(val)
);
END;

Expand Down
58 changes: 48 additions & 10 deletions sql/020-config-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -44,28 +44,66 @@ DROP FUNCTION IF EXISTS _cs_config_check_indexes(jsonb);

CREATE FUNCTION _cs_config_check_indexes(val jsonb)
RETURNS BOOLEAN
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
BEGIN ATOMIC
SELECT jsonb_object_keys(jsonb_path_query(val, '$.tables.*.*.indexes')) = ANY('{match, ore, unique, ste_vec}');
END;
AS $$
BEGIN
IF jsonb_path_query(val, '$.tables.*.*.indexes') <> '{}':jsonb THEN
IF EXISTS (SELECT jsonb_object_keys(jsonb_path_query(val, '$.tables.*.*.indexes')) = ANY('{match, ore, unique, ste_vec}')) THEN
RETURN true;
END IF;
RAISE 'Invalid index (%) in configuration. Index should be one of {match, ore, unique, ste_vec}', val;
END IF;
END;
$$ LANGUAGE plpgsql;


DROP FUNCTION IF EXISTS _cs_config_check_cast(jsonb);

CREATE FUNCTION _cs_config_check_cast(val jsonb)
RETURNS BOOLEAN
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
BEGIN ATOMIC
SELECT jsonb_array_elements_text(jsonb_path_query_array(val, '$.tables.*.*.cast_as')) = ANY('{text, int, small_int, big_int, real, double, boolean, date, jsonb}');
END;
AS $$
BEGIN
IF EXISTS (SELECT jsonb_array_elements_text(jsonb_path_query_array(val, '$.tables.*.*.cast_as')) = ANY('{text, int, small_int, big_int, real, double, boolean, date, jsonb}')) THEN
RETURN true;
END IF;
RAISE 'Invalid cast (%) in configuration. Cast should be one of {text, int, small_int, big_int, real, double, boolean, date, jsonb}', val;
END;
$$ LANGUAGE plpgsql;

--
-- Should include a tables field
-- Tables should not be empty
DROP FUNCTION IF EXISTS _cs_config_check_tables(jsonb);
CREATE FUNCTION _cs_config_check_tables(val jsonb)
RETURNS boolean
AS $$
BEGIN
IF (val ? 'tables') AND (val->'tables' <> '{"A":"a"}'::jsonb) THEN
RETURN true;
END IF;
RAISE 'Configuration missing tables (tables) field: %', val;
END;
$$ LANGUAGE plpgsql;

-- Should include a version field
DROP FUNCTION IF EXISTS _cs_config_check_v(jsonb);
CREATE FUNCTION _cs_config_check_v(val jsonb)
RETURNS boolean
AS $$
BEGIN
IF (val ? 'v') THEN
RETURN true;
END IF;
RAISE 'Configuration missing version (v) field: %', val;
END;
$$ LANGUAGE plpgsql;


ALTER DOMAIN cs_configuration_data_v1 DROP CONSTRAINT IF EXISTS cs_configuration_data_v1_check;

ALTER DOMAIN cs_configuration_data_v1
ADD CONSTRAINT cs_configuration_data_v1_check CHECK (
VALUE ?& array['v', 'tables'] AND
VALUE->'tables' <> '{}'::jsonb AND
_cs_config_check_v(VALUE) AND
_cs_config_check_tables(VALUE) AND
_cs_config_check_cast(VALUE) AND
_cs_config_check_indexes(VALUE)
);
Expand Down
4 changes: 3 additions & 1 deletion sql/021-config-functions.sql
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ $$ LANGUAGE plpgsql;

DROP FUNCTION IF EXISTS cs_add_column_v1(table_name text, column_name text);

CREATE FUNCTION cs_add_column_v1(table_name text, column_name text)
CREATE FUNCTION cs_add_column_v1(table_name text, column_name text, cast_as text DEFAULT 'text')
RETURNS jsonb
AS $$
DECLARE
Expand All @@ -323,6 +323,8 @@ AS $$

SELECT _cs_config_add_column(table_name, column_name, _config) INTO _config;

SELECT _cs_config_add_cast(table_name, column_name, cast_as, _config) INTO _config;

-- create a new pending record if we don't have one
INSERT INTO cs_configuration_v1 (state, data) VALUES ('pending', _config)
ON CONFLICT (state)
Expand Down
2 changes: 1 addition & 1 deletion sql/666-drop_types.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
DROP TYPE IF EXISTS ore_64_8_v1;
DROP TYPE IF EXISTS ore_64_8_v1_term;
DROP TYPE IF EXISTS cs_ste_vec_index_v1;
DROP TYPE IF EXISTS ste_vec_v1_entry;
DROP TYPE IF EXISTS cs_ste_vec_v1_entry;
DROP TYPE IF EXISTS ore_cllw_8_v1;
DROP TYPE IF EXISTS ore_cllw_8_variable_v1;
DROP TYPE IF EXISTS cs_ste_vec_encrypted_term_v1;
Loading