diff --git a/schema/schema.definition.sql b/schema/schema.definition.sql index 31087be..3e72a2f 100644 --- a/schema/schema.definition.sql +++ b/schema/schema.definition.sql @@ -994,32 +994,40 @@ CREATE FUNCTION maevsi.invitation_claim_array() RETURNS uuid[] LANGUAGE plpgsql STABLE STRICT AS $$ DECLARE - _arr UUID[]; - _result_arr UUID[] := ARRAY[]::UUID[]; - _id UUID; + _invitation_ids UUID[]; + _invitation_ids_unblocked UUID[] := ARRAY[]::UUID[]; + _invitation_id UUID; BEGIN - _arr := string_to_array(replace(btrim(current_setting('jwt.claims.invitations', true), '[]'), '"', ''), ',')::UUID[]; - FOREACH _id IN ARRAY arr - LOOP - -- omit invitations authored by a blocked account - IF NOT EXISTS( - SELECT 1 - FROM maevsi.invitation i - JOIN maevsi.contact c ON i.contact_id = c.contact_id - JOIN maevsi.account_block b ON c.author_account_id = b.blocked_account_id - WHERE i.id = _id and b.author_account_id = current_setting('jwt.claims.account_id', true) - ) THEN - _result_arr := append_array(result_arr, _id); - END IF; - END LOOP; - - RETURN _result_arr; + _invitation_ids := string_to_array(replace(btrim(current_setting('jwt.claims.invitations', true), '[]'), '"', ''), ',')::UUID[]; + IF _invitation_ids IS NOT NULL THEN + FOREACH _invitation_id IN ARRAY _invitation_ids + LOOP + -- omit invitations authored by a blocked account + IF NOT EXISTS( + SELECT 1 + FROM maevsi.invitation i + JOIN maevsi.contact c ON i.contact_id = c.contact_id + JOIN maevsi.account_block b ON c.author_account_id = b.blocked_account_id + WHERE i.id = _invitation_id and b.author_account_id = current_setting('jwt.claims.account_id', true) + ) THEN + _invitation_ids_unblocked := append_invitation_array(result_invitation_ids, _invitation_id); + END IF; + END LOOP; + END IF; + RETURN _invitation_ids_unblocked; END $$; ALTER FUNCTION maevsi.invitation_claim_array() OWNER TO postgres; +-- +-- Name: FUNCTION invitation_claim_array(); Type: COMMENT; Schema: maevsi; Owner: postgres +-- + +COMMENT ON FUNCTION maevsi.invitation_claim_array() IS 'Returns the current invitation claims as UUID array.'; + + -- -- Name: invitation_contact_ids(); Type: FUNCTION; Schema: maevsi; Owner: postgres -- @@ -1029,17 +1037,25 @@ CREATE FUNCTION maevsi.invitation_contact_ids() RETURNS TABLE(contact_id uuid) AS $$ BEGIN RETURN QUERY - SELECT invitation.contact_id FROM maevsi.invitation - WHERE id = ANY (maevsi.invitation_claim_array()) + SELECT i.contact_id FROM maevsi.invitation i + WHERE i.id = ANY (maevsi.invitation_claim_array()) OR ( - event_id IN (SELECT maevsi.events_organized()) - AND - -- omit contacts authored by a blocked account or referring to a blocked account - contact_id NOT IN ( - SELECT c.id - FROM maevsi.contact c - JOIN maevsi.account_block b ON c.author_account_id = b.blocked_account_id OR c.account_id = b.blocked_account_id - WHERE b.author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + i.event_id IN (SELECT maevsi.events_organized()) + AND + -- omit contacts authored by a blocked account or referring to a blocked account + i.contact_id NOT IN ( + SELECT c.id + FROM maevsi.contact c + WHERE c.account_id IS NULL + OR c.account_id NOT IN ( + SELECT blocked_account_id + FROM maevsi.account_block + WHERE author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + UNION ALL + SELECT author_account_id + FROM maevsi.account_block + WHERE blocked_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + ) ) ); END; @@ -1543,25 +1559,29 @@ BEGIN jwt_account_id := NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID; RETURN QUERY - SELECT event_id FROM maevsi.invitation + SELECT i.event_id FROM maevsi.invitation i WHERE ( - contact_id IN ( + i.contact_id IN ( SELECT id FROM maevsi.contact WHERE account_id = jwt_account_id - -- The contact selection does not return rows where account_id "IS" null due to the equality comparison. - AND - -- contact not created by a blocked account - author_account_id NOT IN ( - SELECT account_block_id - FROM maevsi.account_block - WHERE b.author_account_id = jwt_account_id - ) - ) + -- The contact selection does not return rows where account_id "IS" null due to the equality comparison. + AND + -- contact is not a blocked user and is not authored by a user who blocked jwt_account_id + author_account_id NOT IN ( + SELECT blocked_account_id + FROM maevsi.account_block + WHERE author_account_id = jwt_account_id + UNION ALL + SELECT author_account_id + FROM maevsi.account_block + WHERE blocked_account_id = jwt_account_id + ) + ) ) - OR id = ANY (maevsi.invitation_claim_array()); + OR i.id = ANY (maevsi.invitation_claim_array()); END $$; @@ -1657,7 +1677,7 @@ COMMENT ON COLUMN maevsi.account_block.blocked_account_id IS 'The id of the acco -- Name: COLUMN account_block.created_at; Type: COMMENT; Schema: maevsi; Owner: postgres -- -COMMENT ON COLUMN maevsi.account_block.created_at IS '@omit update,delete +COMMENT ON COLUMN maevsi.account_block.created_at IS '@omit create,update,delete The timestamp when the blocking was created.'; @@ -3659,7 +3679,7 @@ CREATE POLICY account_block_insert ON maevsi.account_block FOR INSERT WITH CHECK -- Name: account_block account_block_select; Type: POLICY; Schema: maevsi; Owner: postgres -- -CREATE POLICY account_block_select ON maevsi.account_block FOR SELECT USING ((((NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid IS NOT NULL) AND (author_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid))); +CREATE POLICY account_block_select ON maevsi.account_block FOR SELECT USING (((author_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid) OR (blocked_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid))); -- @@ -3710,9 +3730,17 @@ CREATE POLICY contact_insert ON maevsi.contact FOR INSERT WITH CHECK (((author_a CREATE POLICY contact_select ON maevsi.contact FOR SELECT USING ((((account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid) AND (NOT (author_account_id IN ( SELECT account_block.blocked_account_id FROM maevsi.account_block - WHERE (account_block.author_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid))))) OR ((author_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid) AND (NOT (account_id IN ( SELECT account_block.blocked_account_id + WHERE (account_block.author_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid) +UNION ALL + SELECT account_block.author_account_id + FROM maevsi.account_block + WHERE (account_block.blocked_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid))))) OR ((author_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid) AND ((account_id IS NULL) OR (NOT (account_id IN ( SELECT account_block.blocked_account_id + FROM maevsi.account_block + WHERE (account_block.author_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid) +UNION ALL + SELECT account_block.author_account_id FROM maevsi.account_block - WHERE (account_block.author_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid))))) OR (id IN ( SELECT maevsi.invitation_contact_ids() AS invitation_contact_ids)))); + WHERE (account_block.blocked_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid)))))) OR (id IN ( SELECT maevsi.invitation_contact_ids() AS invitation_contact_ids)))); -- @@ -3755,7 +3783,11 @@ CREATE POLICY event_insert ON maevsi.event FOR INSERT WITH CHECK ((((NULLIF(curr CREATE POLICY event_select ON maevsi.event FOR SELECT USING ((((visibility = 'public'::maevsi.event_visibility) AND ((invitee_count_maximum IS NULL) OR (invitee_count_maximum > maevsi.invitee_count(id))) AND (NOT (author_account_id IN ( SELECT account_block.blocked_account_id FROM maevsi.account_block - WHERE (account_block.author_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid))))) OR (author_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid) OR (id IN ( SELECT maevsi_private.events_invited() AS events_invited)))); + WHERE (account_block.author_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid) +UNION ALL + SELECT account_block.author_account_id + FROM maevsi.account_block + WHERE (account_block.blocked_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid))))) OR (author_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid) OR (id IN ( SELECT maevsi_private.events_invited() AS events_invited)))); -- @@ -3803,10 +3835,15 @@ EXCEPT SELECT c.id FROM (maevsi.contact c JOIN maevsi.account_block b ON (((c.account_id = b.author_account_id) AND (c.author_account_id = b.blocked_account_id)))) - WHERE (c.account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid))) OR ((event_id IN ( SELECT maevsi.events_organized() AS events_organized)) AND (NOT (contact_id IN ( SELECT c.id - FROM (maevsi.contact c - JOIN maevsi.account_block b ON (((c.author_account_id = b.blocked_account_id) OR (c.account_id = b.blocked_account_id)))) - WHERE (b.author_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid))))))); + WHERE (c.account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid))) OR ((event_id IN ( SELECT maevsi.events_organized() AS events_organized)) AND (contact_id IN ( SELECT c.id + FROM maevsi.contact c + WHERE ((c.account_id IS NULL) OR (NOT (c.account_id IN ( SELECT account_block.blocked_account_id + FROM maevsi.account_block + WHERE (account_block.author_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid) + UNION ALL + SELECT account_block.author_account_id + FROM maevsi.account_block + WHERE (account_block.blocked_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid)))))))))); -- @@ -3820,10 +3857,15 @@ EXCEPT SELECT c.id FROM (maevsi.contact c JOIN maevsi.account_block b ON (((c.account_id = b.author_account_id) AND (c.author_account_id = b.blocked_account_id)))) - WHERE (c.account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid))) OR ((event_id IN ( SELECT maevsi.events_organized() AS events_organized)) AND (NOT (contact_id IN ( SELECT c.id - FROM (maevsi.contact c - JOIN maevsi.account_block b ON (((c.author_account_id = b.blocked_account_id) OR (c.account_id = b.blocked_account_id)))) - WHERE (b.author_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid))))))); + WHERE (c.account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid))) OR ((event_id IN ( SELECT maevsi.events_organized() AS events_organized)) AND (contact_id IN ( SELECT c.id + FROM maevsi.contact c + WHERE ((c.account_id IS NULL) OR (NOT (c.account_id IN ( SELECT account_block.blocked_account_id + FROM maevsi.account_block + WHERE (account_block.author_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid) + UNION ALL + SELECT account_block.author_account_id + FROM maevsi.account_block + WHERE (account_block.blocked_account_id = (NULLIF(current_setting('jwt.claims.account_id'::text, true), ''::text))::uuid)))))))))); -- @@ -4491,6 +4533,7 @@ GRANT SELECT ON TABLE maevsi.account TO maevsi_anonymous; -- GRANT SELECT,INSERT ON TABLE maevsi.account_block TO maevsi_account; +GRANT SELECT ON TABLE maevsi.account_block TO maevsi_anonymous; -- diff --git a/src/deploy/function_events_invited.sql b/src/deploy/function_events_invited.sql index b07fc5c..d84396e 100644 --- a/src/deploy/function_events_invited.sql +++ b/src/deploy/function_events_invited.sql @@ -10,7 +10,7 @@ BEGIN; -CREATE OR REPLACE FUNCTION maevsi_private.events_invited() +CREATE FUNCTION maevsi_private.events_invited() RETURNS TABLE(event_id uuid) AS $$ DECLARE @@ -19,25 +19,29 @@ BEGIN jwt_account_id := NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID; RETURN QUERY - SELECT event_id FROM maevsi.invitation + SELECT i.event_id FROM maevsi.invitation i WHERE ( - contact_id IN ( + i.contact_id IN ( SELECT id FROM maevsi.contact WHERE account_id = jwt_account_id - -- The contact selection does not return rows where account_id "IS" null due to the equality comparison. - AND - -- contact not created by a blocked account - author_account_id NOT IN ( - SELECT account_block_id - FROM maevsi.account_block - WHERE b.author_account_id = jwt_account_id - ) - ) + -- The contact selection does not return rows where account_id "IS" null due to the equality comparison. + AND + -- contact is not a blocked user and is not authored by a user who blocked jwt_account_id + author_account_id NOT IN ( + SELECT blocked_account_id + FROM maevsi.account_block + WHERE author_account_id = jwt_account_id + UNION ALL + SELECT author_account_id + FROM maevsi.account_block + WHERE blocked_account_id = jwt_account_id + ) + ) ) - OR id = ANY (maevsi.invitation_claim_array()); + OR i.id = ANY (maevsi.invitation_claim_array()); END $$ LANGUAGE plpgsql STABLE STRICT SECURITY DEFINER ; diff --git a/src/deploy/function_invitation_claim_array.sql b/src/deploy/function_invitation_claim_array.sql index 365833a..7d67781 100644 --- a/src/deploy/function_invitation_claim_array.sql +++ b/src/deploy/function_invitation_claim_array.sql @@ -9,31 +9,35 @@ BEGIN; -CREATE OR REPLACE FUNCTION maevsi.invitation_claim_array() +CREATE FUNCTION maevsi.invitation_claim_array() RETURNS UUID[] AS $$ DECLARE - _arr UUID[]; - _result_arr UUID[] := ARRAY[]::UUID[]; - _id UUID; + _invitation_ids UUID[]; + _invitation_ids_unblocked UUID[] := ARRAY[]::UUID[]; + _invitation_id UUID; BEGIN - _arr := string_to_array(replace(btrim(current_setting('jwt.claims.invitations', true), '[]'), '"', ''), ',')::UUID[]; - FOREACH _id IN ARRAY arr - LOOP - -- omit invitations authored by a blocked account - IF NOT EXISTS( - SELECT 1 - FROM maevsi.invitation i - JOIN maevsi.contact c ON i.contact_id = c.contact_id - JOIN maevsi.account_block b ON c.author_account_id = b.blocked_account_id - WHERE i.id = _id and b.author_account_id = current_setting('jwt.claims.account_id', true) - ) THEN - _result_arr := append_array(result_arr, _id); - END IF; - END LOOP; - - RETURN _result_arr; + _invitation_ids := string_to_array(replace(btrim(current_setting('jwt.claims.invitations', true), '[]'), '"', ''), ',')::UUID[]; + IF _invitation_ids IS NOT NULL THEN + FOREACH _invitation_id IN ARRAY _invitation_ids + LOOP + -- omit invitations authored by a blocked account + IF NOT EXISTS( + SELECT 1 + FROM maevsi.invitation i + JOIN maevsi.contact c ON i.contact_id = c.contact_id + JOIN maevsi.account_block b ON c.author_account_id = b.blocked_account_id + WHERE i.id = _invitation_id and b.author_account_id = current_setting('jwt.claims.account_id', true) + ) THEN + _invitation_ids_unblocked := append_invitation_array(result_invitation_ids, _invitation_id); + END IF; + END LOOP; + END IF; + RETURN _invitation_ids_unblocked; END -$$ LANGUAGE PLPGSQL STRICT STABLE SECURITY INVOKER; +$$ LANGUAGE PLPGSQL STRICT STABLE SECURITY INVOKER +; + +COMMENT ON FUNCTION maevsi.invitation_claim_array() IS 'Returns the current invitation claims as UUID array.'; GRANT EXECUTE ON FUNCTION maevsi.invitation_claim_array() TO maevsi_account, maevsi_anonymous; diff --git a/src/deploy/function_invitation_contact_ids.sql b/src/deploy/function_invitation_contact_ids.sql index e3f0f9b..aa84939 100644 --- a/src/deploy/function_invitation_contact_ids.sql +++ b/src/deploy/function_invitation_contact_ids.sql @@ -11,21 +11,29 @@ BEGIN; -CREATE OR REPLACE FUNCTION maevsi.invitation_contact_ids() +CREATE FUNCTION maevsi.invitation_contact_ids() RETURNS TABLE (contact_id UUID) AS $$ BEGIN RETURN QUERY - SELECT invitation.contact_id FROM maevsi.invitation - WHERE id = ANY (maevsi.invitation_claim_array()) + SELECT i.contact_id FROM maevsi.invitation i + WHERE i.id = ANY (maevsi.invitation_claim_array()) OR ( - event_id IN (SELECT maevsi.events_organized()) - AND - -- omit contacts authored by a blocked account or referring to a blocked account - contact_id NOT IN ( - SELECT c.id - FROM maevsi.contact c - JOIN maevsi.account_block b ON c.author_account_id = b.blocked_account_id OR c.account_id = b.blocked_account_id - WHERE b.author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + i.event_id IN (SELECT maevsi.events_organized()) + AND + -- omit contacts authored by a blocked account or referring to a blocked account + i.contact_id NOT IN ( + SELECT c.id + FROM maevsi.contact c + WHERE c.account_id IS NULL + OR c.account_id NOT IN ( + SELECT blocked_account_id + FROM maevsi.account_block + WHERE author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + UNION ALL + SELECT author_account_id + FROM maevsi.account_block + WHERE blocked_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + ) ) ); END; diff --git a/src/deploy/table_account_block.sql b/src/deploy/table_account_block.sql index 8182dbe..2bba97f 100644 --- a/src/deploy/table_account_block.sql +++ b/src/deploy/table_account_block.sql @@ -17,6 +17,6 @@ COMMENT ON TABLE maevsi.account_block IS E'@omit update,delete\nBlocking of an a COMMENT ON COLUMN maevsi.account_block.id IS '@omit create\nThe blocking''s internal id.'; COMMENT ON COLUMN maevsi.account_block.author_account_id IS 'The id of the user who created the blocking.'; COMMENT ON COLUMN maevsi.account_block.blocked_account_id IS 'The id of the account to be blocked.'; -COMMENT ON COLUMN maevsi.account_block.created_at IS E'@omit update,delete\nThe timestamp when the blocking was created.'; +COMMENT ON COLUMN maevsi.account_block.created_at IS E'@omit create,update,delete\nThe timestamp when the blocking was created.'; COMMIT; diff --git a/src/deploy/table_account_block_policy.sql b/src/deploy/table_account_block_policy.sql index c2d45c4..19f7212 100644 --- a/src/deploy/table_account_block_policy.sql +++ b/src/deploy/table_account_block_policy.sql @@ -6,6 +6,7 @@ BEGIN; GRANT INSERT, SELECT ON TABLE maevsi.account_block TO maevsi_account; +GRANT SELECT ON TABLE maevsi.account_block TO maevsi_anonymous; ALTER TABLE maevsi.account_block ENABLE ROW LEVEL SECURITY; @@ -16,11 +17,11 @@ CREATE POLICY account_block_insert ON maevsi.account_block FOR INSERT WITH CHECK author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID ); --- Only allow selects for blocked accounts authored by the current user. +-- Only show rows where the current account is involved. CREATE POLICY account_block_select ON maevsi.account_block FOR SELECT USING ( - NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID IS NOT NULL - AND author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + OR + blocked_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID ); COMMIT; diff --git a/src/deploy/table_contact_policy.sql b/src/deploy/table_contact_policy.sql index 8652d14..e5fba06 100644 --- a/src/deploy/table_contact_policy.sql +++ b/src/deploy/table_contact_policy.sql @@ -15,49 +15,61 @@ ALTER TABLE maevsi.contact ENABLE ROW LEVEL SECURITY; -- Only display contacts referencing the invoker's account, omit contacts authored by a blocked account. -- Only display contacts authored by the invoker's account, omit contacts referring to a blocked account. --- Only display contacts for which an accessible invitation exists, omit contacts auhored by a blocked account or referring to a blocked account. +-- Only display contacts for which an accessible invitation exists. CREATE POLICY contact_select ON maevsi.contact FOR SELECT USING ( ( - account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID - AND - author_account_id NOT IN ( - SELECT blocked_account_id - FROM maevsi.account_block - WHERE author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID - ) - ) - OR - ( - author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID - AND - account_id NOT IN ( - SELECT blocked_account_id - FROM maevsi.account_block - WHERE author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID - ) - ) - OR id IN (SELECT maevsi.invitation_contact_ids()) + account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + AND + author_account_id NOT IN ( + SELECT blocked_account_id + FROM maevsi.account_block + WHERE author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + UNION ALL + SELECT author_account_id + FROM maevsi.account_block + WHERE blocked_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + ) + ) + OR + ( + author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + AND + ( + account_id IS NULL + OR + account_id NOT IN ( + SELECT blocked_account_id + FROM maevsi.account_block + WHERE author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + UNION ALL + SELECT author_account_id + FROM maevsi.account_block + WHERE blocked_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + ) + ) + ) + OR id IN (SELECT maevsi.invitation_contact_ids()) ); --- Only allow inserts for contacts authored by the invoker's account --- No blocked account can be invited +-- Only allow inserts for contacts authored by the invoker's account. +-- Disallow inserts for contacts that refer to a blocked account. CREATE POLICY contact_insert ON maevsi.contact FOR INSERT WITH CHECK ( author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID AND account_id NOT IN ( - SELECT blocked_account_id - FROM maevsi.account_block - WHERE author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + SELECT blocked_account_id + FROM maevsi.account_block + WHERE author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID ) ); -- Only allow updates for contacts authored by the invoker's account. --- No contact referring to a blocked account can be updated +-- No contact referring to a blocked account can be updated. CREATE POLICY contact_update ON maevsi.contact FOR UPDATE USING ( author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID AND account_id NOT IN ( - SELECT blocked_account_id - FROM maevsi.account_block - WHERE author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + SELECT blocked_account_id + FROM maevsi.account_block + WHERE author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID ) ); diff --git a/src/deploy/table_event_policy.sql b/src/deploy/table_event_policy.sql index 63f1f0f..7a1493f 100644 --- a/src/deploy/table_event_policy.sql +++ b/src/deploy/table_event_policy.sql @@ -14,11 +14,11 @@ GRANT INSERT, UPDATE, DELETE ON TABLE maevsi.event TO maevsi_account; ALTER TABLE maevsi.event ENABLE ROW LEVEL SECURITY; --- Only display events that are public and not full and not organized by a blocked account +-- Only display events that are public and not full and not organized by a blocked account. -- Only display events that are organized by oneself. +-- Only display events to which oneself is invited, but not by an invitation authored by a blocked account. -- Only display events to which oneself is invited, but not by an invitation authored by a blocked account CREATE POLICY event_select ON maevsi.event FOR SELECT USING ( --- Below copied to function `maevsi.event_invitee_count_maximum`. ( visibility = 'public' AND @@ -27,18 +27,22 @@ CREATE POLICY event_select ON maevsi.event FOR SELECT USING ( OR invitee_count_maximum > (maevsi.invitee_count(id)) -- Using the function here is required as there would otherwise be infinite recursion. ) - AND author_account_id NOT IN ( - SELECT blocked_account_id - FROM maevsi.account_block - WHERE author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID - ) + AND author_account_id NOT IN ( + SELECT blocked_account_id + FROM maevsi.account_block + WHERE author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + UNION ALL + SELECT author_account_id + FROM maevsi.account_block + WHERE blocked_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + ) + ) + OR ( + author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID ) - OR ( - author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID - ) OR ( - id IN (SELECT maevsi_private.events_invited()) - ) + id IN (SELECT maevsi_private.events_invited()) + ) ); -- Only allow inserts for events authored by the current user. diff --git a/src/deploy/table_invitation_policy.sql b/src/deploy/table_invitation_policy.sql index 4f61ea6..d957f1d 100644 --- a/src/deploy/table_invitation_policy.sql +++ b/src/deploy/table_invitation_policy.sql @@ -28,33 +28,39 @@ CREATE POLICY invitation_select ON maevsi.invitation FOR SELECT USING ( FROM maevsi.contact WHERE account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID - EXCEPT + EXCEPT - -- contacts to oneself authored by a blocked account - SELECT c.id - FROM maevsi.contact c - JOIN maevsi.account_block b ON c.account_id = b.author_account_id AND c.author_account_id = b.blocked_account_id - WHERE c.account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + -- contacts to oneself authored by a blocked account + SELECT c.id + FROM maevsi.contact c + JOIN maevsi.account_block b ON c.account_id = b.author_account_id AND c.author_account_id = b.blocked_account_id + WHERE c.account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID ) ) OR ( - event_id IN (SELECT maevsi.events_organized()) - AND - contact_id NOT IN ( - SELECT c.id - FROM maevsi.contact c - JOIN maevsi.account_block b - -- contact authored by a blocked account OR referring to a blocked account - ON c.author_account_id = b.blocked_account_id OR c.account_id = b.blocked_account_id - WHERE b.author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID - ) + event_id IN (SELECT maevsi.events_organized()) + AND + contact_id IN ( + SELECT c.id + FROM maevsi.contact c + WHERE c.account_id IS NULL + OR c.account_id NOT IN ( + SELECT blocked_account_id + FROM maevsi.account_block + WHERE author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + UNION ALL + SELECT author_account_id + FROM maevsi.account_block + WHERE blocked_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + ) + ) ) ); -- Only allow inserts for invitations to events organized by oneself. -- Only allow inserts for invitations to events for which the maximum invitee count is not yet reached. --- Only allow inserts for invitations issued to a contact that was created by oneself --- Do not allow inserts for invitations issued to a contact referring a blocked account +-- Only allow inserts for invitations issued to a contact that was created by oneself. +-- Do not allow inserts for invitations issued to a contact referring a blocked account. CREATE POLICY invitation_insert ON maevsi.invitation FOR INSERT WITH CHECK ( event_id IN (SELECT maevsi.events_organized()) AND ( @@ -68,18 +74,18 @@ CREATE POLICY invitation_insert ON maevsi.invitation FOR INSERT WITH CHECK ( FROM maevsi.contact WHERE author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID - EXCEPT + EXCEPT - SELECT c.id - FROM maevsi.contact c - JOIN maevsi.account_block b ON c.account_id = b.blocked_account_id and c.author_account_id = b.author_account_id - WHERE c.author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID - ) + SELECT c.id + FROM maevsi.contact c + JOIN maevsi.account_block b ON c.account_id = b.blocked_account_id and c.author_account_id = b.author_account_id + WHERE c.author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + ) ); -- Only allow updates to invitations issued to oneself through invitation claims. --- Only allow updates to invitations issued to oneself through the account, but not invitations auhored by a blocked account --- Only allow updates to invitations to events organized by oneself, but not invitations issued to a blocked account or issued by a blocked account +-- Only allow updates to invitations issued to oneself through the account, but not invitations auhored by a blocked account. +-- Only allow updates to invitations to events organized by oneself, but not invitations issued to a blocked account or issued by a blocked account. CREATE POLICY invitation_update ON maevsi.invitation FOR UPDATE USING ( id = ANY (maevsi.invitation_claim_array()) OR @@ -89,24 +95,32 @@ CREATE POLICY invitation_update ON maevsi.invitation FOR UPDATE USING ( FROM maevsi.contact WHERE account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID - EXCEPT + EXCEPT - SELECT c.id - FROM maevsi.contact c - JOIN maevsi.account_block b ON c.account_id = b.author_account_id and c.author_account_id = b.blocked_account_id - WHERE c.account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + SELECT c.id + FROM maevsi.contact c + JOIN maevsi.account_block b ON c.account_id = b.author_account_id and c.author_account_id = b.blocked_account_id + WHERE c.account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID ) ) OR ( event_id IN (SELECT maevsi.events_organized()) - AND - -- omit contacts authored by a blocked account or referring to a blocked account - contact_id NOT IN ( - SELECT c.id - FROM maevsi.contact c - JOIN maevsi.account_block b ON c.author_account_id = b.blocked_account_id OR c.account_id = b.blocked_account_id - WHERE b.author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + AND + -- omit contacts authored by a blocked account or referring to a blocked account + contact_id IN ( + SELECT c.id + FROM maevsi.contact c + WHERE c.account_id IS NULL + OR c.account_id NOT IN ( + SELECT blocked_account_id + FROM maevsi.account_block + WHERE author_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + UNION ALL + SELECT author_account_id + FROM maevsi.account_block + WHERE blocked_account_id = NULLIF(current_setting('jwt.claims.account_id', true), '')::UUID + ) ) ) ); @@ -116,7 +130,7 @@ CREATE POLICY invitation_delete ON maevsi.invitation FOR DELETE USING ( event_id IN (SELECT maevsi.events_organized()) ); -CREATE OR REPLACE FUNCTION maevsi.trigger_invitation_update() RETURNS TRIGGER AS $$ +CREATE FUNCTION maevsi.trigger_invitation_update() RETURNS TRIGGER AS $$ DECLARE whitelisted_cols TEXT[] := ARRAY['feedback', 'feedback_paper']; BEGIN diff --git a/src/revert/function_invitation_claim_array.sql b/src/revert/function_invitation_claim_array.sql index 2e1283c..35671f4 100644 --- a/src/revert/function_invitation_claim_array.sql +++ b/src/revert/function_invitation_claim_array.sql @@ -2,6 +2,6 @@ BEGIN; -DROP FUNCTION maevsi.invitation_claim_array; +DROP FUNCTION maevsi.invitation_claim_array(); COMMIT; diff --git a/src/sqitch.plan b/src/sqitch.plan index 7f3d006..f4e039a 100644 --- a/src/sqitch.plan +++ b/src/sqitch.plan @@ -21,12 +21,12 @@ table_event_group [schema_public role_account role_anonymous table_account_publi index_event_group_author_username [table_event_group] 1970-01-01T00:00:00Z Jonas Thelemann # Add an index to the event group table's author_username field. table_event [schema_public role_account role_anonymous table_account_public] 1970-01-01T00:00:00Z Jonas Thelemann # Add table event. function_events_organized [privilege_execute_revoke schema_public table_event role_account role_anonymous] 1970-01-01T00:00:00Z Jonas Thelemann # Add a function that returns all event ids for which the invoker is the author. -function_invitation_claim_array [privilege_execute_revoke schema_public role_account role_anonymous] 1970-01-01T00:00:00Z Jonas Thelemann # Add a function that returns the current invitation claims as UUID array. index_event_author_username [table_event] 1970-01-01T00:00:00Z Jonas Thelemann # Add an index to the event table's username field. enum_invitation_feedback_paper 1970-01-01T00:00:00Z Jonas Thelemann # Possible choices on how to receive a paper invitation: paper, digital. table_contact [schema_public role_account role_anonymous table_account_public] 1970-01-01T00:00:00Z Jonas Thelemann # Add table contact. table_invitation [schema_public table_event table_contact] 1970-01-01T00:00:00Z Jonas Thelemann # Add table invitation. function_events_invited [privilege_execute_revoke schema_private schema_public table_invitation table_contact role_account role_anonymous] 1970-01-01T00:00:00Z Jonas Thelemann # Add a function that returns all event ids for which the invoker is invited. +function_invitation_claim_array [privilege_execute_revoke schema_public table_invitation table_contact table_account_block role_account role_anonymous] 1970-01-01T00:00:00Z Jonas Thelemann # Add a function that returns the current invitation claims as UUID array. function_invitation_contact_ids [privilege_execute_revoke schema_public table_invitation function_invitation_claim_array function_events_organized role_account role_anonymous] 1970-01-01T00:00:00Z Jonas Thelemann # Add a function that returns all event ids for which the invoker is invited. table_contact_policy [schema_public table_contact role_account role_anonymous function_invitation_contact_ids] 1970-01-01T00:00:00Z Jonas Thelemann # Add policy for table contact. function_invitee_count [privilege_execute_revoke schema_public table_invitation role_account role_anonymous] 1970-01-01T00:00:00Z Jonas Thelemann # Add a function that returns the invitee count for an event. diff --git a/src/verify/table_account_block_policy.sql b/src/verify/table_account_block_policy.sql index 6d0d9e1..b21bded 100644 --- a/src/verify/table_account_block_policy.sql +++ b/src/verify/table_account_block_policy.sql @@ -8,7 +8,7 @@ BEGIN ASSERT (SELECT pg_catalog.has_table_privilege('maevsi_account', 'maevsi.account_block', 'SELECT')); ASSERT NOT (SELECT pg_catalog.has_table_privilege('maevsi_account', 'maevsi.account_block', 'UPDATE')); ASSERT NOT (SELECT pg_catalog.has_table_privilege('maevsi_account', 'maevsi.account_block', 'DELETE')); - ASSERT NOT (SELECT pg_catalog.has_table_privilege('maevsi_anonymous', 'maevsi.account_block', 'SELECT')); + ASSERT (SELECT pg_catalog.has_table_privilege('maevsi_anonymous', 'maevsi.account_block', 'SELECT')); ASSERT NOT (SELECT pg_catalog.has_table_privilege('maevsi_anonymous', 'maevsi.account_block', 'INSERT')); ASSERT NOT (SELECT pg_catalog.has_table_privilege('maevsi_anonymous', 'maevsi.account_block', 'UPDATE')); ASSERT NOT (SELECT pg_catalog.has_table_privilege('maevsi_anonymous', 'maevsi.account_block', 'DELETE'));