diff --git a/db_capabilities.sql b/db_capabilities.sql index 2c71b2a..91ff14a 100644 --- a/db_capabilities.sql +++ b/db_capabilities.sql @@ -25,7 +25,7 @@ create table if not exists capabilities_http( capability_default_claims jsonb, capability_required_groups text[], capability_required_attributes jsonb, - capability_group_match_method text check (capability_group_match_method in ('exact', 'wildcard')), + capability_group_match_method text default 'wildcard' check (capability_group_match_method in ('exact', 'wildcard')), capability_lifetime int not null check (capability_lifetime > 0), -- minutes capability_description text not null, capability_expiry_date date, @@ -149,7 +149,6 @@ $$ language plpgsql; create table if not exists capabilities_http_grants( row_id uuid unique not null default gen_random_uuid(), - capability_name text references capabilities_http (capability_name) on delete cascade, capability_grant_id uuid not null default gen_random_uuid() primary key, capability_grant_hostname text not null, capability_grant_namespace text not null, @@ -164,8 +163,9 @@ create table if not exists capabilities_http_grants( capability_grant_max_num_usages int, capability_grant_group_existence_check boolean default 't', capability_grant_metadata jsonb, - unique (capability_name, capability_grant_hostname, - capability_grant_namespace, capability_grant_http_method, + unique (capability_grant_hostname, + capability_grant_namespace, + capability_grant_http_method, capability_grant_rank) ); @@ -177,16 +177,14 @@ create or replace function generate_grant_rank() declare num int; declare new_rank int; begin - -- check if first grant for (capability_name, host, namespace, method) combination + -- check if first grant for (host, namespace, method) combination select count(*) from capabilities_http_grants - where capability_name = NEW.capability_name - and capability_grant_hostname = NEW.capability_grant_hostname + where capability_grant_hostname = NEW.capability_grant_hostname and capability_grant_namespace = NEW.capability_grant_namespace and capability_grant_http_method = NEW.capability_grant_http_method into num; select max(capability_grant_rank) from capabilities_http_grants - where capability_name = NEW.capability_name - and capability_grant_hostname = NEW.capability_grant_hostname + where capability_grant_hostname = NEW.capability_grant_hostname and capability_grant_namespace = NEW.capability_grant_namespace and capability_grant_http_method = NEW.capability_grant_http_method into current_max; @@ -254,7 +252,6 @@ create or replace function capability_grant_rank_set(grant_id text, new_grant_ra returns boolean as $$ declare target_id uuid; declare target_curr_rank int; - declare target_cap_name text; declare target_hostname text; declare target_namespace text; declare target_http_method text; @@ -271,12 +268,11 @@ create or replace function capability_grant_rank_set(grant_id text, new_grant_ra if new_grant_rank = target_curr_rank then return true; end if; - select capability_name, capability_grant_hostname, capability_grant_namespace, capability_grant_http_method + select capability_grant_hostname, capability_grant_namespace, capability_grant_http_method from capabilities_http_grants where capability_grant_id = target_id - into target_cap_name, target_hostname, target_namespace, target_http_method; + into target_hostname, target_namespace, target_http_method; select max(capability_grant_rank) from capabilities_http_grants - where capability_name = target_cap_name - and capability_grant_hostname = target_hostname + where capability_grant_hostname = target_hostname and capability_grant_namespace = target_namespace and capability_grant_http_method = target_http_method into current_max; @@ -289,7 +285,6 @@ create or replace function capability_grant_rank_set(grant_id text, new_grant_ra select capability_grant_id, capability_grant_rank from capabilities_http_grants where capability_grant_rank >= new_grant_rank and capability_grant_rank < target_curr_rank - and capability_name = target_cap_name and capability_grant_hostname = target_hostname and capability_grant_namespace = target_namespace and capability_grant_http_method = target_http_method @@ -304,7 +299,6 @@ create or replace function capability_grant_rank_set(grant_id text, new_grant_ra select capability_grant_id, capability_grant_rank from capabilities_http_grants where capability_grant_rank <= new_grant_rank and capability_grant_rank > target_curr_rank - and capability_name = target_cap_name and capability_grant_hostname = target_hostname and capability_grant_namespace = target_namespace and capability_grant_http_method = target_http_method @@ -327,19 +321,17 @@ create or replace function capability_grant_delete(grant_id text) returns boolean as $$ declare target_id uuid; declare target_rank int; - declare target_cap_name text; declare target_hostname text; declare target_namespace text; declare target_http_method text; declare ans boolean; begin target_id := grant_id::uuid; - select capability_name, capability_grant_hostname, capability_grant_namespace, capability_grant_http_method + select capability_grant_hostname, capability_grant_namespace, capability_grant_http_method from capabilities_http_grants where capability_grant_id = target_id - into target_cap_name, target_hostname, target_namespace, target_http_method; + into target_hostname, target_namespace, target_http_method; select max(capability_grant_rank) from capabilities_http_grants - where capability_name = target_cap_name - and capability_grant_hostname = target_hostname + where capability_grant_hostname = target_hostname and capability_grant_namespace = target_namespace and capability_grant_http_method = target_http_method into target_rank; @@ -350,23 +342,6 @@ create or replace function capability_grant_delete(grant_id text) $$ language plpgsql; -drop function if exists capability_grants(text) cascade; -create or replace function capability_grants(capability_name text) - returns json as $$ - declare data json; - begin - assert (select exists(select 1 from capabilities_http where capabilities_http.capability_name = $1)) = 't', - 'capability_name does not exist'; - select json_agg(json_build_object( - 'http_method', capability_grant_http_method, - 'uri_pattern', capability_grant_uri_pattern)) - from capabilities_http_grants - where capabilities_http_grants.capability_name = $1 into data; - return json_build_object('capability_name', capability_name, 'capability_grants', data); - end; -$$ language plpgsql; - - drop function if exists grp_cpbts(text, boolean) cascade; create or replace function grp_cpbts(grp text, grants boolean default 'f') returns json as $$ @@ -377,6 +352,9 @@ create or replace function grp_cpbts(grp text, grants boolean default 'f') declare matches boolean; declare grant_data json; declare data json; + declare grnt_grp text[]; + declare grnt_mthd text; + declare grnt_ptrn text; begin assert (select exists(select 1 from groups where group_name = grp)) = 't', 'group does not exist'; create temporary table if not exists cpb(ct text unique not null) on commit drop; @@ -405,8 +383,23 @@ create or replace function grp_cpbts(grp text, grants boolean default 'f') if grants = 'f' then return json_build_object('group_name', grp, 'group_capabilities_http', data); else - select json_agg(json_build_object(capability_name, capability_grants(capability_name))) - from capabilities_http where capability_name in (select * from cpb) into grant_data; + create temporary table if not exists grnts(method text, pattern text, + unique (method, pattern)) on commit drop; + for grnt_grp, grnt_mthd, grnt_ptrn in + select capability_grant_required_groups, capability_grant_http_method, capability_grant_uri_pattern + from capabilities_http_grants loop + for rgrp in select unnest(grnt_grp) loop + reg := '.*' || rgrp || '.*'; + if grp ~ reg then + begin + insert into grnts values (grnt_mthd, grnt_ptrn); + exception when unique_violation then + null; + end; + end if; + end loop; + end loop; + select json_agg(json_build_object('method', method, 'pattern', pattern)) from grnts into grant_data; return json_build_object('group_name', grp, 'group_capabilities_http', data, 'grants', grant_data); end if; end; diff --git a/db_identities_groups.sql b/db_identities_groups.sql index 068aeaa..d953188 100644 --- a/db_identities_groups.sql +++ b/db_identities_groups.sql @@ -117,7 +117,7 @@ create or replace function update_audit_log_relations() parent := NEW.group_name; child := NEW.group_moderator_name; elsif table_name = 'capabilities_http_grants' then - parent := NEW.capability_name; + parent := NEW.capability_grant_id; child := NEW.capability_grant_hostname || ',' || NEW.capability_grant_namespace || ',' || NEW.capability_grant_http_method || ',' @@ -133,7 +133,7 @@ create or replace function update_audit_log_relations() parent := OLD.group_name; child := OLD.group_moderator_name; elsif table_name = 'capabilities_http_grants' then - parent := OLD.capability_name; + parent := OLD.capability_grant_id; child := OLD.capability_grant_http_method || ',' || OLD.capability_grant_uri_pattern; end if; end if; diff --git a/tests.sql b/tests.sql index cf1d6c6..e996024 100644 --- a/tests.sql +++ b/tests.sql @@ -554,29 +554,21 @@ create or replace function test_capabilities_http() '{"admin2-group", "very-special-group"}', 'wildcard', '123', 'bla', current_date, 'f'); delete from capabilities_http where capability_name = 'admin2'; - insert into capabilities_http_grants (capability_name, - capability_grant_hostname, capability_grant_namespace, + insert into capabilities_http_grants (capability_grant_hostname, capability_grant_namespace, capability_grant_http_method, capability_grant_uri_pattern) - values ('p11import', - 'api.com', 'files', + values ('api.com', 'files', 'PUT', '/p11/files'); - insert into capabilities_http_grants (capability_name, - capability_grant_hostname, capability_grant_namespace, + insert into capabilities_http_grants (capability_grant_hostname, capability_grant_namespace, capability_grant_http_method, capability_grant_uri_pattern) - values ('export', - 'api.com', 'files', + values ('api.com', 'files', 'GET', '/(.*)/export'); - insert into capabilities_http_grants (capability_name, - capability_grant_hostname, capability_grant_namespace, + insert into capabilities_http_grants (capability_grant_hostname, capability_grant_namespace, capability_grant_http_method, capability_grant_uri_pattern) - values ('admin', - 'api.com', 'files', + values ('api.com', 'files', 'DELETE', '/(.*)/files'); - insert into capabilities_http_grants (capability_name, - capability_grant_hostname, capability_grant_namespace, + insert into capabilities_http_grants (capability_grant_hostname, capability_grant_namespace, capability_grant_http_method, capability_grant_uri_pattern) - values ('admin', - 'api.com', 'files', + values ('api.com', 'files', 'GET', '/(.*)/admin'); -- immutability begin @@ -592,92 +584,60 @@ create or replace function test_capabilities_http() raise notice 'capabilities_http_grants: capability_grant_id immutable'; end; -- referential constraints - begin - insert into capabilities_http_grants (capability_name, - capability_grant_hostname, capability_grant_namespace, - capability_grant_http_method, capability_grant_uri_pattern) - values ('35b77cf9-0a6f-49d7-83df-e388d75c4b0b', 'admin', - 'api.com', 'files', - 'GET', '/(.*)/admin'); - assert false; - exception when others then - raise notice 'capabilities_http_grants: capability_id should reference entry in capabilities_http'; - end; - begin - insert into capabilities_http_grants (capability_name, - capability_grant_hostname, capability_grant_namespace, - capability_grant_http_method, capability_grant_uri_pattern) - values ('admin2', - 'api.com', 'files', - 'GET', '/(.*)/admin'); - assert false; - exception when others then - raise notice 'capabilities_http_grants: capability_name should refernce entry in capabilities_http'; - end; begin select capability_id from capabilities_http where capability_name = 'export' into cid; - insert into capabilities_http_grants (capability_name, - capability_grant_hostname, capability_grant_namespace, + insert into capabilities_http_grants (capability_grant_hostname, capability_grant_namespace, capability_grant_http_method, capability_grant_uri_pattern, capability_grant_required_groups) - values ('export', - 'api.com', 'files', + values ('api.com', 'files', 'GET', '/(.*)/admin', '{"my-own-crazy-group"}'); exception when assert_failure then - raise notice 'capabilities_http_grants: required groups need to exist when referenced'; + raise notice 'capabilities_http_grants: required groups need to exist when referenced, by default'; end; -- ability to override group references - insert into capabilities_http_grants (capability_name, - capability_grant_hostname, capability_grant_namespace, + insert into capabilities_http_grants (capability_grant_hostname, capability_grant_namespace, capability_grant_http_method, capability_grant_uri_pattern, capability_grant_required_groups, capability_grant_group_existence_check) - values ('export', - 'api.com', 'files', + values ('api.com', 'files', 'GET', '/(.*)/admin', '{"my-own-crazy-group"}', 'f'); -- add some more test data - insert into capabilities_http_grants (capability_name, - capability_grant_hostname, capability_grant_namespace, + insert into capabilities_http_grants (capability_grant_hostname, capability_grant_namespace, capability_grant_http_method, capability_grant_uri_pattern, capability_grant_required_groups, capability_grant_group_existence_check) - values ('export', - 'api.com', 'files', + values ('api.com', 'files', 'GET', '/(.*)/export', '{"my-own-custom-export-group"}', 'f'); - insert into capabilities_http_grants (capability_name, - capability_grant_hostname, capability_grant_namespace, + insert into capabilities_http_grants (capability_grant_hostname, capability_grant_namespace, capability_grant_http_method, capability_grant_uri_pattern, capability_grant_required_groups, capability_grant_group_existence_check) - values ('export', - 'api.com', 'files', + values ('api.com', 'files', 'HEAD', '/(.*)/export', '{"my-own-custom-export-group"}', 'f'); - insert into capabilities_http_grants (capability_name, - capability_grant_hostname, capability_grant_namespace, + insert into capabilities_http_grants (capability_grant_hostname, capability_grant_namespace, capability_grant_http_method, capability_grant_uri_pattern, capability_grant_required_groups, capability_grant_group_existence_check) - values ('export', - 'api.com', 'files', + values ('api.com', 'files', 'GET', '/something', '{"my-own-custom-export-group"}', 'f'); -- grant ranking -- generation assert 1 in (select capability_grant_rank from capabilities_http_grants - where capability_name = 'export' and capability_grant_http_method = 'GET'), - 'rank generation issue'; + where capability_grant_hostname = 'api.com' and capability_grant_http_method = 'GET'), + 'rank generation issue: 1'; assert 2 in (select capability_grant_rank from capabilities_http_grants - where capability_name = 'export' and capability_grant_http_method = 'GET'), - 'rank generation issue'; + where capability_grant_hostname = 'api.com' and capability_grant_http_method = 'GET'), + 'rank generation issue: 2'; assert 3 in (select capability_grant_rank from capabilities_http_grants - where capability_name = 'export' and capability_grant_http_method = 'GET'), - 'rank generation issue'; - assert 5 not in (select capability_grant_rank from capabilities_http_grants - where capability_name = 'export' and capability_grant_http_method = 'GET'), - 'rank generation issue'; + where capability_grant_hostname = 'api.com' and capability_grant_http_method = 'GET'), + 'rank generation issue: 3'; + assert 6 not in (select capability_grant_rank from capabilities_http_grants + where capability_grant_hostname = 'api.com' and capability_grant_http_method = 'GET'), + 'rank generation issue: 6'; -- natural numbers select capability_grant_id from capabilities_http_grants - where capability_name = 'export' and capability_grant_http_method = 'GET' + where capability_grant_hostname = 'api.com' and capability_grant_http_method = 'GET' and capability_grant_rank = 1 into grid; begin select capability_grant_rank_set(grid::text, -9) into ans; @@ -694,13 +654,11 @@ create or replace function test_capabilities_http() end; -- uniqueness begin - insert into capabilities_http_grants (capability_name, - capability_grant_hostname, capability_grant_namespace, + insert into capabilities_http_grants (capability_grant_hostname, capability_grant_namespace, capability_grant_http_method, capability_grant_uri_pattern, capability_grant_required_groups, capability_grant_group_existence_check, capability_grant_rank) - values ('export', - 'api.com', 'files', + values ('api.com', 'files', 'HEAD', '/(.*)/export', '{"my-own-custom-export-group"}', 'f', 1); @@ -717,16 +675,16 @@ create or replace function test_capabilities_http() end; -- correct reorder select capability_grant_id from capabilities_http_grants - where capability_name = 'export' and capability_grant_http_method = 'GET' + where capability_grant_hostname = 'api.com' and capability_grant_http_method = 'GET' and capability_grant_rank = 1 into grid1; select capability_grant_id from capabilities_http_grants - where capability_name = 'export' and capability_grant_http_method = 'GET' + where capability_grant_hostname = 'api.com' and capability_grant_http_method = 'GET' and capability_grant_rank = 2 into grid2; select capability_grant_id from capabilities_http_grants - where capability_name = 'export' and capability_grant_http_method = 'GET' + where capability_grant_hostname = 'api.com' and capability_grant_http_method = 'GET' and capability_grant_rank = 3 into grid3; select capability_grant_id from capabilities_http_grants - where capability_name = 'export' and capability_grant_http_method = 'GET' + where capability_grant_hostname = 'api.com' and capability_grant_http_method = 'GET' and capability_grant_rank = 4 into grid4; /* id , rank_before, rank_after @@ -745,13 +703,9 @@ create or replace function test_capabilities_http() assert (select capability_grant_rank from capabilities_http_grants where capability_grant_id = grid4) = 4, 'rank set issue - id4'; raise notice 'capability_grant_rank_set works'; - -- per capability rankings - assert (select max(capability_grant_rank) from capabilities_http_grants - where capability_name = 'p11import') = 1, - 'per capability ranking broken'; -- irrelevant rankings not affected (within and between rank sets) assert (select max(capability_grant_rank) from capabilities_http_grants - where capability_name = 'export' + where capability_grant_hostname = 'api.com' and capability_grant_http_method = 'HEAD') = 1, 'per capability, per http_method ranking broken'; -- deletes (keep rank consistent) @@ -759,12 +713,10 @@ create or replace function test_capabilities_http() assert (select capability_grant_rank from capabilities_http_grants where capability_grant_id = grid4) = 3, 'rank delete issue - id4'; -- test self and moderator keywords - insert into capabilities_http_grants (capability_name, - capability_grant_hostname, capability_grant_namespace, + insert into capabilities_http_grants (capability_grant_hostname, capability_grant_namespace, capability_grant_http_method, capability_grant_uri_pattern, capability_grant_required_groups) - values ('export', - 'api.com', 'files', + values ('api.com', 'files', 'GET', '/(.*)/admin/profile/([a-zA-Z0-9])', '{"self","moderator"}'); return true; @@ -895,12 +847,12 @@ create or replace function test_funcs() values ('p11-art', '{"role": "p11_art_user"}', '{"p11-surrealist-group", "p11-admin-group"}', 'exact', '123', 'bla', current_date); - insert into capabilities_http_grants (capability_name, - capability_grant_hostname, capability_grant_namespace, - capability_grant_http_method, capability_grant_uri_pattern) - values ('p11-art', - 'api.com', 'files', - 'GET', '/(.*)/art'); + insert into capabilities_http_grants (capability_grant_hostname, capability_grant_namespace, + capability_grant_http_method, capability_grant_uri_pattern, + capability_grant_required_groups) + values ('api.com', 'files', + 'GET', '/(.*)/art', + '{surrealist-group}'); select person_capabilities(pid::text, 't') into data; err := 'person_capabilities issue'; assert data->>'person_id' = pid::text, err; @@ -929,7 +881,6 @@ create or replace function test_funcs() assert data->>'user_name' = 'p11-dali', err; assert data->'user_capabilities'->0->>'group_name' = 'p11-surrealist-group', err; assert data->'user_capabilities'->0->'group_capabilities_http'->>0 = 'p11-art', err; - assert data->'user_capabilities'->0->>'grants' is not null, err; -- group_members insert into persons (full_name, person_expiry_date) values ('Andre Breton', '2050-10-01'); @@ -977,12 +928,6 @@ create or replace function test_funcs() err := 'group_capabilities issue'; assert data->>'group_name' = 'p11-surrealist-group', err; assert data->'group_capabilities_http'->>0 = 'p11-art', err; - -- capability_grants - select capability_grants('p11-art') into data; - err := 'capability_grants issue'; - assert data->>'capability_name' = 'p11-art', err; - assert data->'capability_grants'->0->>'http_method' = 'GET', err; - assert data->'capability_grants'->0->>'uri_pattern' = '/(.*)/art', err; return true; end; $$ language plpgsql;