diff --git a/src/main.rs b/src/main.rs index 4334bd1..b94ecca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -184,6 +184,7 @@ pub enum HTTPError { NotFoundError(Option), ConflictError(Option), BadRequestError(Option), + NotImplementedError(Option), InternalServerError(Option), UnauthorizedError(Option) } @@ -197,6 +198,7 @@ impl fmt::Display for HTTPError { HTTPError::ForbiddenError(message) => write!(f, "{}", message.to_owned().unwrap_or("Forbidden.".to_string())), HTTPError::GoneError(message) => write!(f, "{}", message.to_owned().unwrap_or("Gone.".to_string())), HTTPError::BadRequestError(message) => write!(f, "{}", message.to_owned().unwrap_or("Bad request.".to_string())), + HTTPError::NotImplementedError(message) => write!(f, "{}", message.to_owned().unwrap_or("Not implemented.".to_string())), HTTPError::InternalServerError(message) => write!(f, "{}", message.to_owned().unwrap_or("Internal server error.".to_string())), HTTPError::UnauthorizedError(message) => write!(f, "{}", message.to_owned().unwrap_or("Unauthorized.".to_string())) } @@ -220,7 +222,9 @@ impl IntoResponse for HTTPError { HTTPError::UnauthorizedError(message) => (StatusCode::UNAUTHORIZED, message.unwrap_or("Unauthorized.".to_string())), - HTTPError::InternalServerError(_) => (StatusCode::INTERNAL_SERVER_ERROR, "Something bad happened on our side. Please try again later.".to_string()) + HTTPError::NotImplementedError(message) => (StatusCode::NOT_IMPLEMENTED, message.unwrap_or("Not implemented.".to_string())), + + HTTPError::InternalServerError(_) => (StatusCode::INTERNAL_SERVER_ERROR, "Something bad happened on our side. Please try again later.".to_string()), }; diff --git a/src/queries/access-policies/get-access-policy-row-by-id.sql b/src/queries/access-policies/get-access-policy-row-by-id.sql deleted file mode 100644 index 655fc30..0000000 --- a/src/queries/access-policies/get-access-policy-row-by-id.sql +++ /dev/null @@ -1 +0,0 @@ -select * from hydrated_access_policies where id = $1; \ No newline at end of file diff --git a/src/queries/access-policies/initialize-access-policies-table.sql b/src/queries/access-policies/initialize-access-policies-table.sql deleted file mode 100644 index a334327..0000000 --- a/src/queries/access-policies/initialize-access-policies-table.sql +++ /dev/null @@ -1,97 +0,0 @@ -do $$ -begin - if not exists (select 1 from pg_type where typname = 'permission_level') then - create type permission_level as enum ( - 'None', - 'User', - 'Editor', - 'Admin' - ); - end if; - - if not exists (select 1 from pg_type where typname = 'inheritance_level') then - create type inheritance_level as enum ( - 'Disabled', - 'Enabled', - 'Required' - ); - end if; - - if not exists (select 1 from pg_type where typname = 'scoped_resource_type') then - create type scoped_resource_type as enum ( - 'Instance', - 'Workspace', - 'Project', - 'Item', - 'Action', - 'User', - 'Role', - 'Group', - 'App', - 'AppCredential', - 'Milestone' - ); - end if; - - if not exists (select 1 from pg_type where typname = 'principal_type') then - create type principal_type as enum ( - 'User', - 'Group', - 'Role', - 'App' - ); - end if; - - create table if not exists access_policies ( - id UUID default uuidv7() primary key, - - /* Principals */ - principal_type principal_type not null, - principal_user_id UUID references users(id) on delete cascade, - principal_group_id UUID references groups(id) on delete cascade, - principal_role_id UUID references roles(id) on delete cascade, - principal_app_id UUID references apps(id) on delete cascade, - - /* Scopes */ - scoped_resource_type scoped_resource_type not null, - scoped_workspace_id UUID references workspaces(id) on delete cascade, - scoped_project_id UUID references projects(id) on delete cascade, - scoped_item_id UUID references items(id) on delete cascade, - scoped_action_id UUID references actions(id) on delete cascade, - scoped_user_id UUID references users(id) on delete cascade, - scoped_role_id UUID references roles(id) on delete cascade, - scoped_group_id UUID references groups(id) on delete cascade, - scoped_app_id UUID references apps(id) on delete cascade, - scoped_app_credential_id UUID references app_credentials(id) on delete cascade, - scoped_app_authorization_id UUID references app_authorizations(id) on delete cascade, - scoped_app_authorization_credential_id UUID references app_authorization_credentials(id) on delete cascade, - scoped_milestone_id UUID references milestones(id) on delete cascade, - - /* Permissions */ - action_id UUID not null references actions(id) on delete cascade, - permission_level permission_level not null, - inheritance_level inheritance_level not null, - - /* Constraints */ - constraint one_principal_type check ( - (principal_type = 'User' and principal_user_id is not null and principal_group_id is null and principal_role_id is null and principal_app_id is null) - or (principal_type = 'Group' and principal_user_id is null and principal_group_id is not null and principal_role_id is null and principal_app_id is null) - or (principal_type = 'Role' and principal_user_id is null and principal_group_id is null and principal_role_id is not null and principal_app_id is null) - or (principal_type = 'App' and principal_user_id is null and principal_group_id is null and principal_role_id is null and principal_app_id is not null) - ), - - constraint one_scoped_resource_type check ( - (scoped_resource_type = 'Instance' and scoped_workspace_id is null and scoped_project_id is null and scoped_item_id is null and scoped_action_id is null and scoped_user_id is null and scoped_role_id is null and scoped_group_id is null and scoped_app_id is null and scoped_milestone_id is null) - or (scoped_resource_type = 'Workspace' and scoped_workspace_id is not null and scoped_project_id is null and scoped_item_id is null and scoped_action_id is null and scoped_user_id is null and scoped_role_id is null and scoped_group_id is null and scoped_app_id is null and scoped_milestone_id is null) - or (scoped_resource_type = 'Project' and scoped_workspace_id is null and scoped_project_id is not null and scoped_item_id is null and scoped_action_id is null and scoped_user_id is null and scoped_role_id is null and scoped_group_id is null and scoped_app_id is null and scoped_milestone_id is null) - or (scoped_resource_type = 'Item' and scoped_workspace_id is null and scoped_project_id is null and scoped_item_id is not null and scoped_action_id is null and scoped_user_id is null and scoped_role_id is null and scoped_group_id is null and scoped_app_id is null and scoped_milestone_id is null) - or (scoped_resource_type = 'Action' and scoped_workspace_id is null and scoped_project_id is null and scoped_item_id is null and scoped_action_id is not null and scoped_user_id is null and scoped_role_id is null and scoped_group_id is null and scoped_app_id is null and scoped_milestone_id is null) - or (scoped_resource_type = 'User' and scoped_workspace_id is null and scoped_project_id is null and scoped_item_id is null and scoped_action_id is null and scoped_user_id is not null and scoped_role_id is null and scoped_group_id is null and scoped_app_id is null and scoped_milestone_id is null) - or (scoped_resource_type = 'Role' and scoped_workspace_id is null and scoped_project_id is null and scoped_item_id is null and scoped_action_id is null and scoped_user_id is null and scoped_role_id is not null and scoped_group_id is null and scoped_app_id is null and scoped_milestone_id is null) - or (scoped_resource_type = 'Group' and scoped_workspace_id is null and scoped_project_id is null and scoped_item_id is null and scoped_action_id is null and scoped_user_id is null and scoped_role_id is null and scoped_group_id is not null and scoped_app_id is null and scoped_milestone_id is null) - or (scoped_resource_type = 'App' and scoped_workspace_id is null and scoped_project_id is null and scoped_item_id is null and scoped_action_id is null and scoped_user_id is null and scoped_role_id is null and scoped_group_id is null and scoped_app_id is not null and scoped_milestone_id is null) - or (scoped_resource_type = 'Milestone' and scoped_workspace_id is null and scoped_project_id is null and scoped_item_id is null and scoped_action_id is null and scoped_user_id is null and scoped_role_id is null and scoped_group_id is null and scoped_app_id is null and scoped_milestone_id is not null) - ) - ); -end -$$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/src/queries/access-policies/initialize-hydrated-access-policies-view.sql b/src/queries/access-policies/initialize-hydrated-access-policies-view.sql deleted file mode 100644 index 4cc83b9..0000000 --- a/src/queries/access-policies/initialize-hydrated-access-policies-view.sql +++ /dev/null @@ -1,58 +0,0 @@ -create or replace view hydrated_access_policies as - select - access_policies.*, - row_to_json(actions.*) as action, - - row_to_json(principal_users.*) as principal_user, - row_to_json(principal_groups.*) as principal_group, - row_to_json(principal_roles.*) as principal_role, - row_to_json(principal_apps.*) as principal_app, - - row_to_json(scoped_actions.*) as scoped_action, - row_to_json(scoped_apps.*) as scoped_app, - row_to_json(scoped_groups.*) as scoped_group, - row_to_json(scoped_items.*) as scoped_item, - row_to_json(scoped_milestones.*) as scoped_milestone, - row_to_json(scoped_projects.*) as scoped_project, - row_to_json(scoped_roles.*) as scoped_role, - row_to_json(scoped_users.*) as scoped_user, - row_to_json(scoped_workspaces.*) as scoped_workspace, - row_to_json(scoped_app_authorizations.*) as scoped_app_authorization, - row_to_json(scoped_app_authorization_credentials.*) as scoped_app_authorization_credential, - row_to_json(scoped_app_credentials.*) as scoped_app_credential - from - access_policies - left join - users as principal_users on principal_users.id = access_policies.principal_user_id - left join - groups as principal_groups on principal_groups.id = access_policies.principal_group_id - left join - roles as principal_roles on principal_roles.id = access_policies.principal_role_id - left join - apps as principal_apps on principal_apps.id = access_policies.principal_app_id - left join - actions as actions on actions.id = access_policies.action_id - left join - actions as scoped_actions on scoped_actions.id = access_policies.scoped_action_id - left join - apps as scoped_apps on scoped_apps.id = access_policies.scoped_app_id - left join - groups as scoped_groups on scoped_groups.id = access_policies.scoped_group_id - left join - items as scoped_items on scoped_items.id = access_policies.scoped_item_id - left join - milestones as scoped_milestones on scoped_milestones.id = access_policies.scoped_milestone_id - left join - projects as scoped_projects on scoped_projects.id = access_policies.scoped_project_id - left join - roles as scoped_roles on scoped_roles.id = access_policies.scoped_role_id - left join - users as scoped_users on scoped_users.id = access_policies.scoped_user_id - left join - workspaces as scoped_workspaces on scoped_workspaces.id = access_policies.scoped_workspace_id - left join - app_authorizations as scoped_app_authorizations on scoped_app_authorizations.id = access_policies.scoped_app_authorization_id - left join - app_authorization_credentials as scoped_app_authorization_credentials on scoped_app_authorization_credentials.id = access_policies.scoped_app_authorization_credential_id - left join - app_credentials as scoped_app_credentials on scoped_app_credentials.id = access_policies.scoped_app_credential_id \ No newline at end of file diff --git a/src/queries/access_policies/create_function_can_principal_get_access_policy.sql b/src/queries/access_policies/create_function_can_principal_get_access_policy.sql new file mode 100644 index 0000000..873e5a1 --- /dev/null +++ b/src/queries/access_policies/create_function_can_principal_get_access_policy.sql @@ -0,0 +1,1012 @@ +-- This function returns true if a principal can get an access policy. +-- It's helpful for filtering access policies on the database level, making offsets more consistent. +CREATE OR REPLACE FUNCTION can_principal_get_access_policy(parameter_principal_type principal_type, parameter_principal_user_id UUID, parameter_principal_app_id UUID, access_policy_record access_policies) RETURNS BOOLEAN AS $$ + DECLARE + get_access_policy_action_id UUID; + current_permission_Level permission_level; + is_inheritance_enabled_on_selected_resource BOOLEAN; + selected_resource_type access_policy_resource_type; + selected_resource_id UUID; + selected_resource_parent_type access_policy_resource_type; + selected_resource_parent_id UUID; + needs_inheritance BOOLEAN := FALSE; + BEGIN + + -- Set the selected resource type and ID based on the principal type. + get_access_policy_action_id := ( + select + id + from + actions + where + name = 'slashstep.accessPolicies.get' + ); + + selected_resource_type := access_policy_record.scoped_resource_type; + selected_resource_id := CASE selected_resource_type + WHEN 'Action' THEN access_policy_record.scoped_action_id + WHEN 'ActionLogEntry' THEN access_policy_record.scoped_action_log_entry_id + WHEN 'App' THEN access_policy_record.scoped_app_id + WHEN 'AppAuthorization' THEN access_policy_record.scoped_app_authorization_id + WHEN 'AppAuthorizationCredential' THEN access_policy_record.scoped_app_authorization_credential_id + WHEN 'AppCredential' THEN access_policy_record.scoped_app_credential_id + WHEN 'Group' THEN access_policy_record.scoped_group_id + WHEN 'GroupMembership' THEN access_policy_record.scoped_group_membership_id + WHEN 'HTTPTransaction' THEN access_policy_record.scoped_http_transaction_id + WHEN 'HTTPTransactionLogEntry' THEN access_policy_record.scoped_http_transaction_log_entry_id + WHEN 'Instance' THEN NULL + WHEN 'Item' THEN access_policy_record.scoped_item_id + WHEN 'Milestone' THEN access_policy_record.scoped_milestone_id + WHEN 'Project' THEN access_policy_record.scoped_project_id + WHEN 'Role' THEN access_policy_record.scoped_role_id + WHEN 'RoleMembership' THEN access_policy_record.scoped_role_membership_id + WHEN 'Session' THEN access_policy_record.scoped_session_id + WHEN 'User' THEN access_policy_record.scoped_user_id + WHEN 'Workspace' THEN access_policy_record.scoped_workspace_id + END; + + LOOP + + IF selected_resource_type = 'Instance' THEN + + -- Instance + SELECT + permission_level, + is_inheritance_enabled + INTO + current_permission_Level, + is_inheritance_enabled_on_selected_resource + FROM + get_principal_access_policies(parameter_principal_type, parameter_principal_user_id, parameter_principal_app_id, get_access_policy_action_id) principal_access_policies + WHERE + principal_access_policies.scoped_resource_type = 'Instance' AND ( + NOT needs_inheritance OR + principal_access_policies.is_inheritance_enabled + ) + LIMIT 1; + + RETURN current_permission_Level IS NOT NULL AND current_permission_Level >= 'User'; + + ELSIF selected_resource_type = 'Action' THEN + + -- Action -> (App | Instance) + SELECT + permission_level, + is_inheritance_enabled + INTO + current_permission_Level, + is_inheritance_enabled_on_selected_resource + FROM + get_principal_access_policies(parameter_principal_type, parameter_principal_user_id, parameter_principal_app_id, get_access_policy_action_id) principal_access_policies + WHERE + principal_access_policies.scoped_resource_type = 'Action' AND + principal_access_policies.scoped_action_id = parameter_scoped_action_id AND ( + NOT needs_inheritance OR + principal_access_policies.is_inheritance_enabled + ) + LIMIT 1; + + IF needs_inheritance AND NOT is_inheritance_enabled_on_selected_resource THEN + + RETURN FALSE; + + ELSIF current_permission_Level IS NOT NULL THEN + + RETURN current_permission_Level >= 'User'; + + END IF; + + -- Look for the parent resource type. + needs_inheritance := TRUE; + + SELECT + parent_resource_type + INTO + selected_resource_parent_type + FROM + actions + WHERE + actions.id = parameter_scoped_action_id; + + IF selected_resource_parent_type = 'App' THEN + + SELECT + parent_app_id + INTO + selected_resource_parent_id + FROM + actions + WHERE + actions.id = parameter_scoped_action_id; + + IF selected_resource_parent_id IS NULL THEN + + RAISE EXCEPTION 'Couldn''t find a parent app for action %.', selected_resource_id; + + END IF; + + selected_resource_type := 'App'; + selected_resource_id := selected_resource_parent_id; + + ELSIF selected_resource_parent_type = 'Instance' THEN + + selected_resource_type := 'Instance'; + selected_resource_id := NULL; + + ELSE + + RAISE EXCEPTION 'Unknown parent resource type % for action %.', selected_resource_parent_type, parameter_scoped_action_id; + + END IF; + + ELSIF selected_resource_type = 'App' THEN + + -- App -> (Workspace | User | Instance) + -- Check if the app has an associated access policy. + SELECT + permission_level, + is_inheritance_enabled + INTO + current_permission_Level, + is_inheritance_enabled_on_selected_resource + FROM + get_principal_access_policies(parameter_principal_type, parameter_principal_user_id, parameter_principal_app_id, get_access_policy_action_id) principal_access_policies + WHERE + principal_access_policies.scoped_resource_type = 'App' AND + principal_access_policies.scoped_app_id = selected_resource_id AND ( + NOT needs_inheritance OR + principal_access_policies.is_inheritance_enabled + ) + LIMIT 1; + + IF needs_inheritance AND NOT is_inheritance_enabled_on_selected_resource THEN + + RETURN FALSE; + + ELSIF current_permission_Level IS NOT NULL THEN + + RETURN current_permission_Level >= 'User'; + + END IF; + + -- Look for the parent resource type. + needs_inheritance := TRUE; + + SELECT + workspace_id + INTO + selected_resource_parent_id + FROM + apps + WHERE + apps.id = selected_resource_id; + + IF selected_resource_parent_id IS NOT NULL THEN + + selected_resource_type := 'Workspace'; + selected_resource_id := selected_resource_parent_id; + CONTINUE; + + END IF; + + SELECT + user_id + INTO + selected_resource_parent_id + FROM + apps + WHERE + apps.id = selected_resource_id; + + IF selected_resource_parent_id IS NOT NULL THEN + + selected_resource_type := 'User'; + selected_resource_id := selected_resource_parent_id; + CONTINUE; + + END IF; + + selected_resource_type := 'Instance'; + selected_resource_id := NULL; + + ELSIF selected_resource_type = 'AppAuthorization' THEN + + -- AppAuthorization -> (User | Workspace | Instance) + -- Check if the app authorization has an associated access policy. + SELECT + permission_level, + is_inheritance_enabled + INTO + current_permission_Level, + is_inheritance_enabled_on_selected_resource + FROM + get_principal_access_policies(parameter_principal_type, parameter_principal_user_id, parameter_principal_app_id, get_access_policy_action_id) principal_access_policies + WHERE + principal_access_policies.scoped_resource_type = 'AppAuthorization' AND + principal_access_policies.scoped_app_authorization_id = selected_resource_id AND ( + NOT needs_inheritance OR + principal_access_policies.is_inheritance_enabled + ) + LIMIT 1; + + IF needs_inheritance AND NOT is_inheritance_enabled_on_selected_resource THEN + + RETURN FALSE; + + ELSIF current_permission_Level IS NOT NULL THEN + + RETURN current_permission_Level >= 'User'; + + END IF; + + -- Look for the parent resource type. + needs_inheritance := TRUE; + + SELECT + parent_resource_type + INTO + selected_resource_parent_type + FROM + app_authorizations + WHERE + app_authorizations.id = selected_resource_id; + + IF selected_resource_parent_type = 'User' THEN + + SELECT + parent_user_id + INTO + selected_resource_parent_id + FROM + app_authorizations + WHERE + app_authorizations.id = selected_resource_id; + + IF selected_resource_parent_id IS NULL THEN + + RAISE EXCEPTION 'Couldn''t find a parent user for app authorization %.', selected_resource_id; + + END IF; + + selected_resource_type := 'User'; + selected_resource_id := selected_resource_parent_id; + + ELSIF selected_resource_parent_type = 'Workspace' THEN + + SELECT + parent_workspace_id + INTO + selected_resource_parent_id + FROM + app_authorizations + WHERE + app_authorizations.id = selected_resource_id; + + IF selected_resource_parent_id IS NULL THEN + + RAISE EXCEPTION 'Couldn''t find a parent workspace for app authorization %.', selected_resource_id; + + END IF; + + selected_resource_type := 'Workspace'; + selected_resource_id := selected_resource_parent_id; + + ELSIF selected_resource_parent_type = 'Instance' THEN + + selected_resource_type := 'Instance'; + selected_resource_id := NULL; + + ELSE + + RAISE EXCEPTION 'Unknown parent resource type % for action %.', selected_resource_parent_type, parameter_scoped_action_id; + + END IF; + + ELSIF selected_resource_type = 'AppAuthorizationCredential' THEN + + -- AppAuthorizationCredential -> AppAuthorization + -- Check if the app authorization credential has an associated access policy. + SELECT + permission_level, + is_inheritance_enabled + INTO + current_permission_Level, + is_inheritance_enabled_on_selected_resource + FROM + get_principal_access_policies(parameter_principal_type, parameter_principal_user_id, parameter_principal_app_id, get_access_policy_action_id) principal_access_policies + WHERE + principal_access_policies.scoped_resource_type = 'AppAuthorizationCredential' AND + principal_access_policies.scoped_app_authorization_credential_id = selected_resource_id AND ( + NOT needs_inheritance OR + principal_access_policies.is_inheritance_enabled + ) + LIMIT 1; + + IF needs_inheritance AND NOT is_inheritance_enabled_on_selected_resource THEN + + RETURN FALSE; + + ELSIF current_permission_Level IS NOT NULL THEN + + RETURN current_permission_Level >= 'User'; + + END IF; + + -- Look for the parent resource type. + needs_inheritance := TRUE; + + SELECT + app_authorization_id + INTO + selected_resource_parent_id + FROM + app_authorization_credentials + WHERE + app_authorization_credentials.id = selected_resource_id; + + IF selected_resource_parent_id IS NULL THEN + + RAISE EXCEPTION 'Couldn''t find a parent app authorization for app authorization credential %.', selected_resource_id; + + END IF; + + selected_resource_type := 'AppAuthorization'; + selected_resource_id := selected_resource_parent_id; + + ELSIF selected_resource_type = 'AppCredential' THEN + + -- AppCredential -> App + -- Check if the app credential has an associated access policy. + SELECT + permission_level, + is_inheritance_enabled + INTO + current_permission_Level, + is_inheritance_enabled_on_selected_resource + FROM + get_principal_access_policies(parameter_principal_type, parameter_principal_user_id, parameter_principal_app_id, get_access_policy_action_id) principal_access_policies + WHERE + principal_access_policies.scoped_resource_type = 'AppCredential' AND + principal_access_policies.scoped_app_credential_id = selected_resource_id AND ( + NOT needs_inheritance OR + principal_access_policies.is_inheritance_enabled + ) + LIMIT 1; + + IF needs_inheritance AND NOT is_inheritance_enabled_on_selected_resource THEN + + RETURN FALSE; + + ELSIF current_permission_Level IS NOT NULL THEN + + RETURN current_permission_Level >= 'User'; + + END IF; + + -- Look for the parent resource type. + needs_inheritance := TRUE; + + SELECT + app_id + INTO + selected_resource_parent_id + FROM + app_credentials + WHERE + app_credentials.id = selected_resource_id; + + IF selected_resource_parent_id IS NULL THEN + + RAISE EXCEPTION 'Couldn''t find a parent app for app credential %.', selected_resource_id; + + END IF; + + selected_resource_type := 'App'; + selected_resource_id := selected_resource_parent_id; + + ELSIF selected_resource_type = 'Group' THEN + + -- Group -> Instance + -- Check if the group has an associated access policy. + SELECT + permission_level, + is_inheritance_enabled + INTO + current_permission_Level, + is_inheritance_enabled_on_selected_resource + FROM + get_principal_access_policies(parameter_principal_type, parameter_principal_user_id, parameter_principal_app_id, get_access_policy_action_id) principal_access_policies + WHERE + principal_access_policies.scoped_resource_type = 'Group' AND + principal_access_policies.scoped_group_id = selected_resource_id AND ( + NOT needs_inheritance OR + principal_access_policies.is_inheritance_enabled + ) + LIMIT 1; + + IF needs_inheritance AND NOT is_inheritance_enabled_on_selected_resource THEN + + RETURN FALSE; + + ELSIF current_permission_Level IS NOT NULL THEN + + RETURN current_permission_Level >= 'User'; + + END IF; + + -- Use the parent resource type. + needs_inheritance := TRUE; + selected_resource_type := 'Instance'; + selected_resource_id := NULL; + + ELSIF selected_resource_type = 'HTTPTransaction' THEN + + -- HTTPTransaction -> Instance + -- Check if the HTTP transaction has an associated access policy. + SELECT + permission_level, + is_inheritance_enabled + INTO + current_permission_Level, + is_inheritance_enabled_on_selected_resource + FROM + get_principal_access_policies(parameter_principal_type, parameter_principal_user_id, parameter_principal_app_id, get_access_policy_action_id) principal_access_policies + WHERE + principal_access_policies.scoped_resource_type = 'HTTPTransaction' AND + principal_access_policies.scoped_http_transaction_id = selected_resource_id AND ( + NOT needs_inheritance OR + principal_access_policies.is_inheritance_enabled + ) + LIMIT 1; + + IF needs_inheritance AND NOT is_inheritance_enabled_on_selected_resource THEN + + RETURN FALSE; + + ELSIF current_permission_Level IS NOT NULL THEN + + RETURN current_permission_Level >= 'User'; + + END IF; + + -- Use the parent resource type. + needs_inheritance := TRUE; + selected_resource_type := 'Instance'; + selected_resource_id := NULL; + + ELSIF selected_resource_type = 'HTTPTransactionLogEntry' THEN + + -- HTTPTransactionLogEntry -> HTTPTransaction + -- Check if the HTTP transaction log entry has an associated access policy. + SELECT + permission_level, + is_inheritance_enabled + INTO + current_permission_Level, + is_inheritance_enabled_on_selected_resource + FROM + get_principal_access_policies(parameter_principal_type, parameter_principal_user_id, parameter_principal_app_id, get_access_policy_action_id) principal_access_policies + WHERE + principal_access_policies.scoped_resource_type = 'HTTPTransactionLogEntry' AND + principal_access_policies.scoped_http_transaction_log_entry_id = selected_resource_id AND ( + NOT needs_inheritance OR + principal_access_policies.is_inheritance_enabled + ) + LIMIT 1; + + IF needs_inheritance AND NOT is_inheritance_enabled_on_selected_resource THEN + + RETURN FALSE; + + ELSIF current_permission_Level IS NOT NULL THEN + + RETURN current_permission_Level >= 'User'; + + END IF; + + -- Use the parent resource type. + needs_inheritance := TRUE; + + SELECT + http_transaction_id + INTO + selected_resource_parent_id + FROM + http_transaction_log_entries + WHERE + http_transaction_log_entries.id = selected_resource_id; + + IF selected_resource_parent_id IS NULL THEN + + RAISE EXCEPTION 'Couldn''t find a parent HTTP transaction for HTTP transaction log entry %.', selected_resource_id; + + END IF; + + selected_resource_type := 'HTTPTransaction'; + selected_resource_id := selected_resource_parent_id; + + ELSIF selected_resource_type = 'Item' THEN + + -- Item -> Project + -- Check if the item has an associated access policy. + SELECT + permission_level, + is_inheritance_enabled + INTO + current_permission_Level, + is_inheritance_enabled_on_selected_resource + FROM + get_principal_access_policies(parameter_principal_type, parameter_principal_user_id, parameter_principal_app_id, get_access_policy_action_id) principal_access_policies + WHERE + principal_access_policies.scoped_resource_type = 'Item' AND + principal_access_policies.scoped_item_id = selected_resource_id AND ( + NOT needs_inheritance OR + principal_access_policies.is_inheritance_enabled + ) + LIMIT 1; + + IF needs_inheritance AND NOT is_inheritance_enabled_on_selected_resource THEN + + RETURN FALSE; + + ELSIF current_permission_Level IS NOT NULL THEN + + RETURN current_permission_Level >= 'User'; + + END IF; + + -- Look for the parent resource type. + needs_inheritance := TRUE; + + SELECT + project_id + INTO + selected_resource_parent_id + FROM + items + WHERE + items.id = selected_resource_id; + + IF selected_resource_parent_id IS NULL THEN + + RAISE EXCEPTION 'Couldn''t find a parent project for item %.', selected_resource_id; + + END IF; + + selected_resource_type := 'Project'; + selected_resource_id := selected_resource_parent_id; + + ELSIF selected_resource_type = 'Milestone' THEN + + -- Milestone -> (Project | Workspace) + -- Check if the milestone has an associated access policy. + SELECT + permission_level, + is_inheritance_enabled + INTO + current_permission_Level, + is_inheritance_enabled_on_selected_resource + FROM + get_principal_access_policies(parameter_principal_type, parameter_principal_user_id, parameter_principal_app_id, get_access_policy_action_id) principal_access_policies + WHERE + principal_access_policies.scoped_resource_type = 'Milestone' AND + principal_access_policies.scoped_milestone_id = selected_resource_id AND ( + NOT needs_inheritance OR + principal_access_policies.is_inheritance_enabled + ) + LIMIT 1; + + IF needs_inheritance AND NOT is_inheritance_enabled_on_selected_resource THEN + + RETURN FALSE; + + ELSIF current_permission_Level IS NOT NULL THEN + + RETURN current_permission_Level >= 'User'; + + END IF; + + -- Look for the parent resource type. + needs_inheritance := TRUE; + + SELECT + parent_resource_type + INTO + selected_resource_parent_type + FROM + milestones + WHERE + milestones.id = selected_resource_id; + + IF selected_resource_parent_type = 'Project' THEN + + SELECT + project_id + INTO + selected_resource_parent_id + FROM + milestones + WHERE + milestones.id = selected_resource_id; + + IF selected_resource_parent_id IS NULL THEN + + RAISE EXCEPTION 'Couldn''t find a parent project for milestone %.', selected_resource_id; + + END IF; + + selected_resource_type := 'Project'; + selected_resource_id := selected_resource_parent_id; + + ELSIF selected_resource_parent_type = 'Workspace' THEN + + SELECT + workspace_id + INTO + selected_resource_parent_id + FROM + milestones + WHERE + milestones.id = selected_resource_id; + + IF selected_resource_parent_id IS NULL THEN + + RAISE EXCEPTION 'Couldn''t find a parent workspace for milestone %.', selected_resource_id; + + END IF; + + selected_resource_type := 'Workspace'; + selected_resource_id := selected_resource_parent_id; + + ELSE + + RAISE EXCEPTION 'Couldn''t find a parent resource for milestone %.', selected_resource_id; + + END IF; + + ELSIF selected_resource_type = 'Project' THEN + + -- Project -> Workspace + -- Check if the project has an associated access policy. + SELECT + permission_level, + is_inheritance_enabled + INTO + current_permission_Level, + is_inheritance_enabled_on_selected_resource + FROM + get_principal_access_policies(parameter_principal_type, parameter_principal_user_id, parameter_principal_app_id, get_access_policy_action_id) principal_access_policies + WHERE + principal_access_policies.scoped_resource_type = 'Project' AND + principal_access_policies.scoped_project_id = selected_resource_id AND ( + NOT needs_inheritance OR + principal_access_policies.is_inheritance_enabled + ) + LIMIT 1; + + IF needs_inheritance AND NOT is_inheritance_enabled_on_selected_resource THEN + + RETURN FALSE; + + ELSIF current_permission_Level IS NOT NULL THEN + + RETURN current_permission_Level >= 'User'; + + END IF; + + -- Use the parent resource type. + needs_inheritance := TRUE; + + SELECT + workspace_id + INTO + selected_resource_parent_id + FROM + projects + WHERE + projects.id = selected_resource_id; + + IF selected_resource_parent_id IS NULL THEN + + RAISE EXCEPTION 'Couldn''t find a parent workspace for project %.', selected_resource_id; + + END IF; + + selected_resource_type := 'Workspace'; + selected_resource_id := selected_resource_parent_id; + + ELSIF selected_resource_type = 'Role' THEN + + -- Role -> (Project | Workspace | Group | Instance) + -- Check if the role has an associated access policy. + SELECT + permission_level, + is_inheritance_enabled + INTO + current_permission_Level, + is_inheritance_enabled_on_selected_resource + FROM + get_principal_access_policies(parameter_principal_type, parameter_principal_user_id, parameter_principal_app_id, get_access_policy_action_id) principal_access_policies + WHERE + principal_access_policies.scoped_resource_type = 'Role' AND + principal_access_policies.scoped_role_id = selected_resource_id AND ( + NOT needs_inheritance OR + principal_access_policies.is_inheritance_enabled + ) + LIMIT 1; + + IF needs_inheritance AND NOT is_inheritance_enabled_on_selected_resource THEN + + RETURN FALSE; + + ELSIF current_permission_Level IS NOT NULL THEN + + RETURN current_permission_Level >= 'User'; + + END IF; + + -- Look for the parent resource type. + needs_inheritance := TRUE; + + SELECT + parent_resource_type + INTO + selected_resource_parent_type + FROM + roles + WHERE + roles.id = selected_resource_id; + + IF selected_resource_parent_type = 'Instance' THEN + + selected_resource_type := 'Instance'; + selected_resource_id := NULL; + + ELSIF selected_resource_parent_type = 'Workspace' THEN + + SELECT + workspace_id + INTO + selected_resource_parent_id + FROM + roles + WHERE + roles.id = selected_resource_id; + + IF selected_resource_parent_id IS NULL THEN + + RAISE EXCEPTION 'Couldn''t find a parent workspace for role %.', selected_resource_id; + + END IF; + + selected_resource_type := 'Workspace'; + selected_resource_id := selected_resource_parent_id; + + ELSIF selected_resource_parent_type = 'Project' THEN + + SELECT + project_id + INTO + selected_resource_parent_id + FROM + roles + WHERE + roles.id = selected_resource_id; + + IF selected_resource_parent_id IS NULL THEN + + RAISE EXCEPTION 'Couldn''t find a parent project for role %.', selected_resource_id; + + END IF; + + selected_resource_type := 'Project'; + selected_resource_id := selected_resource_parent_id; + + ELSIF selected_resource_parent_type = 'Group' THEN + + SELECT + group_id + INTO + selected_resource_parent_id + FROM + roles + WHERE + roles.id = selected_resource_id; + + IF selected_resource_parent_id IS NULL THEN + + RAISE EXCEPTION 'Couldn''t find a parent group for role %.', selected_resource_id; + + END IF; + + selected_resource_type := 'Group'; + selected_resource_id := selected_resource_parent_id; + + ELSE + + RAISE EXCEPTION 'Couldn''t find a parent resource for role %.', selected_resource_id; + + END IF; + + ELSIF selected_resource_type = 'RoleMembership' THEN + + -- RoleMembership -> Role + -- Check if the role membership has an associated access policy. + SELECT + permission_level, + is_inheritance_enabled + INTO + current_permission_Level, + is_inheritance_enabled_on_selected_resource + FROM + get_principal_access_policies(parameter_principal_type, parameter_principal_user_id, parameter_principal_app_id, get_access_policy_action_id) principal_access_policies + WHERE + principal_access_policies.scoped_resource_type = 'RoleMembership' AND + principal_access_policies.scoped_role_membership_id = selected_resource_id AND ( + NOT needs_inheritance OR + principal_access_policies.is_inheritance_enabled + ) + LIMIT 1; + + IF needs_inheritance AND NOT is_inheritance_enabled_on_selected_resource THEN + + RETURN FALSE; + + ELSIF current_permission_Level IS NOT NULL THEN + + RETURN current_permission_Level >= 'User'; + + END IF; + + -- Look for the parent resource type. + needs_inheritance := TRUE; + + SELECT + role_id + INTO + selected_resource_parent_id + FROM + role_memberships + WHERE + role_memberships.id = selected_resource_id; + + IF selected_resource_parent_id IS NULL THEN + + RAISE EXCEPTION 'Couldn''t find a parent role for role membership %.', selected_resource_id; + + END IF; + + selected_resource_type := 'Role'; + selected_resource_id := selected_resource_parent_id; + + ELSIF selected_resource_type = 'Session' THEN + + -- Session -> User + -- Check if the session has an associated access policy. + SELECT + permission_level, + is_inheritance_enabled + INTO + current_permission_Level, + is_inheritance_enabled_on_selected_resource + FROM + get_principal_access_policies(parameter_principal_type, parameter_principal_user_id, parameter_principal_app_id, get_access_policy_action_id) principal_access_policies + WHERE + principal_access_policies.scoped_resource_type = 'Session' AND + principal_access_policies.scoped_session_id = selected_resource_id AND ( + NOT needs_inheritance OR + principal_access_policies.is_inheritance_enabled + ) + LIMIT 1; + + IF needs_inheritance AND NOT is_inheritance_enabled_on_selected_resource THEN + + RETURN FALSE; + + ELSIF current_permission_Level IS NOT NULL THEN + + RETURN current_permission_Level >= 'User'; + + END IF; + + -- Look for the parent resource type. + needs_inheritance := TRUE; + + SELECT + user_id + INTO + selected_resource_parent_id + FROM + sessions + WHERE + sessions.id = selected_resource_id; + + IF selected_resource_parent_id IS NULL THEN + + RAISE EXCEPTION 'Couldn''t find a parent user for session %.', selected_resource_id; + + END IF; + + selected_resource_type := 'User'; + selected_resource_id := selected_resource_parent_id; + + ELSIF selected_resource_type = 'User' THEN + + -- User -> Instance + -- Check if the user has an associated access policy. + SELECT + permission_level, + is_inheritance_enabled + INTO + current_permission_Level, + is_inheritance_enabled_on_selected_resource + FROM + get_principal_access_policies(parameter_principal_type, parameter_principal_user_id, parameter_principal_app_id, get_access_policy_action_id) principal_access_policies + WHERE + principal_access_policies.scoped_resource_type = 'User' AND + principal_access_policies.scoped_user_id = selected_resource_id AND ( + NOT needs_inheritance OR + principal_access_policies.is_inheritance_enabled + ) + LIMIT 1; + + IF needs_inheritance AND NOT is_inheritance_enabled_on_selected_resource THEN + + RETURN FALSE; + + ELSIF current_permission_Level IS NOT NULL THEN + + RETURN current_permission_Level >= 'User'; + + END IF; + + -- Look for the parent resource type. + needs_inheritance := TRUE; + selected_resource_type := 'Instance'; + selected_resource_id := NULL; + + ELSIF selected_resource_type = 'Workspace' THEN + + -- Workspace -> Instance + -- Check if the workspace has an associated access policy. + SELECT + permission_level, + is_inheritance_enabled + INTO + current_permission_Level, + is_inheritance_enabled_on_selected_resource + FROM + get_principal_access_policies(parameter_principal_type, parameter_principal_user_id, parameter_principal_app_id, get_access_policy_action_id) principal_access_policies + WHERE + principal_access_policies.scoped_resource_type = 'Workspace' AND + principal_access_policies.scoped_workspace_id = selected_resource_id AND ( + NOT needs_inheritance OR + principal_access_policies.is_inheritance_enabled + ) + LIMIT 1; + + IF needs_inheritance AND NOT is_inheritance_enabled_on_selected_resource THEN + + RETURN FALSE; + + ELSIF current_permission_Level IS NOT NULL THEN + + RETURN current_permission_Level >= 'User'; + + END IF; + + -- Use the parent resource type. + needs_inheritance := TRUE; + selected_resource_type := 'Instance'; + selected_resource_id := NULL; + + ELSE + + RAISE EXCEPTION 'Unknown resource type: %', selected_resource_type; + + END IF; + + END LOOP; + + END; +$$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/src/queries/access_policies/create_function_get_principal_access_policies.sql b/src/queries/access_policies/create_function_get_principal_access_policies.sql new file mode 100644 index 0000000..8aef9a1 --- /dev/null +++ b/src/queries/access_policies/create_function_get_principal_access_policies.sql @@ -0,0 +1,86 @@ +CREATE OR REPLACE FUNCTION get_principal_access_policies(parameter_principal_type principal_type, parameter_principal_user_id UUID, parameter_principal_app_id UUID, get_access_policy_action_id UUID) RETURNS SETOF access_policies AS $$ + BEGIN + + RETURN QUERY + WITH RECURSIVE all_group_memberships AS ( + SELECT + parameter_principal_user_id as root_principal_user_id, + parameter_principal_app_id as root_principal_app_id, + group_memberships.group_id, + group_memberships.principal_group_id + FROM + group_memberships + WHERE + group_memberships.principal_type::TEXT = parameter_principal_type::TEXT AND ( + ( + parameter_principal_type = 'User' AND + group_memberships.principal_user_id = parameter_principal_user_id + ) OR ( + parameter_principal_type = 'App' AND + group_memberships.principal_app_id = parameter_principal_app_id + ) + ) + UNION + SELECT + all_group_memberships.root_principal_user_id, + all_group_memberships.root_principal_app_id, + inherited_group_memberships.group_id, + inherited_group_memberships.principal_group_id + FROM + group_memberships inherited_group_memberships + JOIN + all_group_memberships ON all_group_memberships.group_id = inherited_group_memberships.principal_group_id + ) + SELECT + access_policies.* + FROM + access_policies + LEFT JOIN + all_group_memberships ON ( + parameter_principal_type = 'User' AND + all_group_memberships.root_principal_user_id = parameter_principal_user_id + ) OR ( + parameter_principal_type = 'App' AND + all_group_memberships.root_principal_app_id = parameter_principal_app_id + ) + LEFT JOIN + role_memberships ON ( + role_memberships.principal_group_id = all_group_memberships.group_id + ) OR ( + parameter_principal_type = 'User' AND + role_memberships.principal_user_id = parameter_principal_user_id + ) OR ( + parameter_principal_type = 'App' AND + role_memberships.principal_app_id = parameter_principal_app_id + ) + WHERE + ( + ( + parameter_principal_type = 'User' AND + access_policies.principal_user_id = parameter_principal_user_id + ) OR ( + parameter_principal_type = 'App' AND + access_policies.principal_app_id = parameter_principal_app_id + ) OR + access_policies.principal_group_id = all_group_memberships.group_id OR + access_policies.principal_role_id = role_memberships.role_id + ) AND + access_policies.action_id = get_access_policy_action_id + ORDER BY + CASE access_policies.principal_type + WHEN 'User' THEN 1 + WHEN 'App' THEN 2 + WHEN 'Group' THEN 3 + WHEN 'Role' THEN 4 + ELSE 5 + END, + CASE access_policies.permission_level + WHEN 'Admin' THEN 1 + WHEN 'Editor' THEN 2 + WHEN 'User' THEN 3 + WHEN 'None' THEN 4 + ELSE 5 + END; + + END; +$$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/src/queries/access-policies/delete-access-policy-row.sql b/src/queries/access_policies/delete-access-policy-row.sql similarity index 100% rename from src/queries/access-policies/delete-access-policy-row.sql rename to src/queries/access_policies/delete-access-policy-row.sql diff --git a/src/queries/access_policies/get-access-policy-row-by-id.sql b/src/queries/access_policies/get-access-policy-row-by-id.sql new file mode 100644 index 0000000..6e1e3f3 --- /dev/null +++ b/src/queries/access_policies/get-access-policy-row-by-id.sql @@ -0,0 +1 @@ +select * from access_policies where id = $1; \ No newline at end of file diff --git a/src/queries/access-policies/grant-admin-permissions.sql b/src/queries/access_policies/grant-admin-permissions.sql similarity index 100% rename from src/queries/access-policies/grant-admin-permissions.sql rename to src/queries/access_policies/grant-admin-permissions.sql diff --git a/src/queries/access_policies/initialize_access_policies_table.sql b/src/queries/access_policies/initialize_access_policies_table.sql new file mode 100644 index 0000000..28590a8 --- /dev/null +++ b/src/queries/access_policies/initialize_access_policies_table.sql @@ -0,0 +1,97 @@ +DO $$ + BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'permission_level') THEN + CREATE TYPE permission_level AS ENUM ( + 'None', + 'User', + 'Editor', + 'Admin' + ); + END IF; + + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'inheritance_level') THEN + CREATE TYPE inheritance_level AS ENUM ( + 'Disabled', + 'Enabled', + 'Required' + ); + END IF; + + if not exists (SELECT 1 FROM pg_type WHERE typname = 'access_policy_resource_type') THEN + CREATE TYPE access_policy_resource_type AS ENUM ( + 'Instance', + 'Workspace', + 'Project', + 'Item', + 'Action', + 'User', + 'Role', + 'Group', + 'App', + 'AppCredential', + 'Milestone' + ); + END IF; + + if not exists (SELECT 1 FROM pg_type WHERE typname = 'principal_type') THEN + CREATE TYPE principal_type AS ENUM ( + 'User', + 'Group', + 'Role', + 'App' + ); + END IF; + + CREATE TABLE IF NOT EXISTS access_policies ( + id UUID default uuidv7() primary key, + + /* Principals */ + principal_type principal_type not null, + principal_user_id UUID references users(id) on delete cascade, + principal_group_id UUID references groups(id) on delete cascade, + principal_role_id UUID references roles(id) on delete cascade, + principal_app_id UUID references apps(id) on delete cascade, + + /* Scopes */ + scoped_resource_type access_policy_resource_type not null, + scoped_workspace_id UUID references workspaces(id) on delete cascade, + scoped_project_id UUID references projects(id) on delete cascade, + scoped_item_id UUID references items(id) on delete cascade, + scoped_action_id UUID references actions(id) on delete cascade, + scoped_user_id UUID references users(id) on delete cascade, + scoped_role_id UUID references roles(id) on delete cascade, + scoped_group_id UUID references groups(id) on delete cascade, + scoped_app_id UUID references apps(id) on delete cascade, + scoped_app_credential_id UUID references app_credentials(id) on delete cascade, + scoped_app_authorization_id UUID references app_authorizations(id) on delete cascade, + scoped_app_authorization_credential_id UUID references app_authorization_credentials(id) on delete cascade, + scoped_milestone_id UUID references milestones(id) on delete cascade, + + /* Permissions */ + action_id UUID not null references actions(id) on delete cascade, + permission_level permission_level not null, + inheritance_level inheritance_level not null, + + /* Constraints */ + constraint one_principal_type check ( + (principal_type = 'User' and principal_user_id is not null and principal_group_id is null and principal_role_id is null and principal_app_id is null) + or (principal_type = 'Group' and principal_user_id is null and principal_group_id is not null and principal_role_id is null and principal_app_id is null) + or (principal_type = 'Role' and principal_user_id is null and principal_group_id is null and principal_role_id is not null and principal_app_id is null) + or (principal_type = 'App' and principal_user_id is null and principal_group_id is null and principal_role_id is null and principal_app_id is not null) + ), + + constraint one_scoped_resource_type check ( + (scoped_resource_type = 'Instance' and scoped_workspace_id is null and scoped_project_id is null and scoped_item_id is null and scoped_action_id is null and scoped_user_id is null and scoped_role_id is null and scoped_group_id is null and scoped_app_id is null and scoped_milestone_id is null) + or (scoped_resource_type = 'Workspace' and scoped_workspace_id is not null and scoped_project_id is null and scoped_item_id is null and scoped_action_id is null and scoped_user_id is null and scoped_role_id is null and scoped_group_id is null and scoped_app_id is null and scoped_milestone_id is null) + or (scoped_resource_type = 'Project' and scoped_workspace_id is null and scoped_project_id is not null and scoped_item_id is null and scoped_action_id is null and scoped_user_id is null and scoped_role_id is null and scoped_group_id is null and scoped_app_id is null and scoped_milestone_id is null) + or (scoped_resource_type = 'Item' and scoped_workspace_id is null and scoped_project_id is null and scoped_item_id is not null and scoped_action_id is null and scoped_user_id is null and scoped_role_id is null and scoped_group_id is null and scoped_app_id is null and scoped_milestone_id is null) + or (scoped_resource_type = 'Action' and scoped_workspace_id is null and scoped_project_id is null and scoped_item_id is null and scoped_action_id is not null and scoped_user_id is null and scoped_role_id is null and scoped_group_id is null and scoped_app_id is null and scoped_milestone_id is null) + or (scoped_resource_type = 'User' and scoped_workspace_id is null and scoped_project_id is null and scoped_item_id is null and scoped_action_id is null and scoped_user_id is not null and scoped_role_id is null and scoped_group_id is null and scoped_app_id is null and scoped_milestone_id is null) + or (scoped_resource_type = 'Role' and scoped_workspace_id is null and scoped_project_id is null and scoped_item_id is null and scoped_action_id is null and scoped_user_id is null and scoped_role_id is not null and scoped_group_id is null and scoped_app_id is null and scoped_milestone_id is null) + or (scoped_resource_type = 'Group' and scoped_workspace_id is null and scoped_project_id is null and scoped_item_id is null and scoped_action_id is null and scoped_user_id is null and scoped_role_id is null and scoped_group_id is not null and scoped_app_id is null and scoped_milestone_id is null) + or (scoped_resource_type = 'App' and scoped_workspace_id is null and scoped_project_id is null and scoped_item_id is null and scoped_action_id is null and scoped_user_id is null and scoped_role_id is null and scoped_group_id is null and scoped_app_id is not null and scoped_milestone_id is null) + or (scoped_resource_type = 'Milestone' and scoped_workspace_id is null and scoped_project_id is null and scoped_item_id is null and scoped_action_id is null and scoped_user_id is null and scoped_role_id is null and scoped_group_id is null and scoped_app_id is null and scoped_milestone_id is not null) + ) + ); + END +$$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/src/queries/access-policies/insert-access-policy-row.sql b/src/queries/access_policies/insert-access-policy-row.sql similarity index 100% rename from src/queries/access-policies/insert-access-policy-row.sql rename to src/queries/access_policies/insert-access-policy-row.sql diff --git a/src/queries/access_policies/list_access_policies.sql b/src/queries/access_policies/list_access_policies.sql new file mode 100644 index 0000000..f43f24e --- /dev/null +++ b/src/queries/access_policies/list_access_policies.sql @@ -0,0 +1,9 @@ + + +-- List access policies that the user has access to. +select + * +from + access_policies +WHERE + can_principal_get_access_policy('User', '019afa5b-9b69-7cd5-8e8e-0b50a13f68f5', NULL, access_policies.*); \ No newline at end of file diff --git a/src/queries/http-requests/create-http-requests-table.sql b/src/queries/http-transactions/create-http-transactions-table.sql similarity index 100% rename from src/queries/http-requests/create-http-requests-table.sql rename to src/queries/http-transactions/create-http-transactions-table.sql diff --git a/src/queries/http-requests/create-hydrated-http-requests-view.sql b/src/queries/http-transactions/create-hydrated-http-transactions-view.sql similarity index 100% rename from src/queries/http-requests/create-hydrated-http-requests-view.sql rename to src/queries/http-transactions/create-hydrated-http-transactions-view.sql diff --git a/src/queries/http-requests/insert-http-request-row.sql b/src/queries/http-transactions/insert-http-transaction-row.sql similarity index 100% rename from src/queries/http-requests/insert-http-request-row.sql rename to src/queries/http-transactions/insert-http-transaction-row.sql diff --git a/src/queries/server-log-entries/create-hydrated-server-log-entries-view.sql b/src/queries/server_log_entries/create-hydrated-server-log-entries-view.sql similarity index 100% rename from src/queries/server-log-entries/create-hydrated-server-log-entries-view.sql rename to src/queries/server_log_entries/create-hydrated-server-log-entries-view.sql diff --git a/src/queries/server-log-entries/create-server-log-entries-table.sql b/src/queries/server_log_entries/create-server-log-entries-table.sql similarity index 100% rename from src/queries/server-log-entries/create-server-log-entries-table.sql rename to src/queries/server_log_entries/create-server-log-entries-table.sql diff --git a/src/queries/server-log-entries/insert-server-log-entry-row.sql b/src/queries/server_log_entries/insert-server-log-entry-row.sql similarity index 100% rename from src/queries/server-log-entries/insert-server-log-entry-row.sql rename to src/queries/server_log_entries/insert-server-log-entry-row.sql diff --git a/src/resources/access_policy/mod.rs b/src/resources/access_policy/mod.rs index a750aa8..c22337e 100644 --- a/src/resources/access_policy/mod.rs +++ b/src/resources/access_policy/mod.rs @@ -108,10 +108,10 @@ pub enum AccessPolicyError { InvalidPrincipalType(String), #[error("A scoped resource ID is required for the {0} resource type.")] - ScopedResourceIDMissingError(AccessPolicyScopedResourceType), + ScopedResourceIDMissingError(AccessPolicyResourceType), #[error("An ancestor resource of type {0} is required for this access policy.")] - OrphanedResourceError(AccessPolicyScopedResourceType), + OrphanedResourceError(AccessPolicyResourceType), #[error("An access policy for action {0} already exists.")] ConflictError(Uuid), @@ -205,8 +205,8 @@ impl FromStr for AccessPolicyInheritanceLevel { } #[derive(Debug, Clone, PartialEq, Eq, ToSql, FromSql, Serialize, Deserialize, Default)] -#[postgres(name = "scoped_resource_type")] -pub enum AccessPolicyScopedResourceType { +#[postgres(name = "access_policy_resource_type")] +pub enum AccessPolicyResourceType { #[default] Instance, Workspace, @@ -221,41 +221,41 @@ pub enum AccessPolicyScopedResourceType { Milestone, } -impl fmt::Display for AccessPolicyScopedResourceType { +impl fmt::Display for AccessPolicyResourceType { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match self { - AccessPolicyScopedResourceType::Workspace => write!(formatter, "Workspace"), - AccessPolicyScopedResourceType::Project => write!(formatter, "Project"), - AccessPolicyScopedResourceType::Milestone => write!(formatter, "Milestone"), - AccessPolicyScopedResourceType::Item => write!(formatter, "Item"), - AccessPolicyScopedResourceType::Action => write!(formatter, "Action"), - AccessPolicyScopedResourceType::Role => write!(formatter, "Role"), - AccessPolicyScopedResourceType::Group => write!(formatter, "Group"), - AccessPolicyScopedResourceType::User => write!(formatter, "User"), - AccessPolicyScopedResourceType::App => write!(formatter, "App"), - AccessPolicyScopedResourceType::AppCredential => write!(formatter, "AppCredential"), - AccessPolicyScopedResourceType::Instance => write!(formatter, "Instance") + AccessPolicyResourceType::Workspace => write!(formatter, "Workspace"), + AccessPolicyResourceType::Project => write!(formatter, "Project"), + AccessPolicyResourceType::Milestone => write!(formatter, "Milestone"), + AccessPolicyResourceType::Item => write!(formatter, "Item"), + AccessPolicyResourceType::Action => write!(formatter, "Action"), + AccessPolicyResourceType::Role => write!(formatter, "Role"), + AccessPolicyResourceType::Group => write!(formatter, "Group"), + AccessPolicyResourceType::User => write!(formatter, "User"), + AccessPolicyResourceType::App => write!(formatter, "App"), + AccessPolicyResourceType::AppCredential => write!(formatter, "AppCredential"), + AccessPolicyResourceType::Instance => write!(formatter, "Instance") } } } -impl FromStr for AccessPolicyScopedResourceType { +impl FromStr for AccessPolicyResourceType { type Err = AccessPolicyError; fn from_str(string: &str) -> Result { match string { - "Instance" => Ok(AccessPolicyScopedResourceType::Instance), - "Workspace" => Ok(AccessPolicyScopedResourceType::Workspace), - "Project" => Ok(AccessPolicyScopedResourceType::Project), - "Milestone" => Ok(AccessPolicyScopedResourceType::Milestone), - "Item" => Ok(AccessPolicyScopedResourceType::Item), - "Action" => Ok(AccessPolicyScopedResourceType::Action), - "Role" => Ok(AccessPolicyScopedResourceType::Role), - "Group" => Ok(AccessPolicyScopedResourceType::Group), - "User" => Ok(AccessPolicyScopedResourceType::User), - "App" => Ok(AccessPolicyScopedResourceType::App), + "Instance" => Ok(AccessPolicyResourceType::Instance), + "Workspace" => Ok(AccessPolicyResourceType::Workspace), + "Project" => Ok(AccessPolicyResourceType::Project), + "Milestone" => Ok(AccessPolicyResourceType::Milestone), + "Item" => Ok(AccessPolicyResourceType::Item), + "Action" => Ok(AccessPolicyResourceType::Action), + "Role" => Ok(AccessPolicyResourceType::Role), + "Group" => Ok(AccessPolicyResourceType::Group), + "User" => Ok(AccessPolicyResourceType::User), + "App" => Ok(AccessPolicyResourceType::App), _ => Err(AccessPolicyError::InvalidScopedResourceType(string.to_string())) } @@ -343,7 +343,7 @@ pub struct InitialAccessPolicyProperties { pub principal_app_id: Option, - pub scoped_resource_type: AccessPolicyScopedResourceType, + pub scoped_resource_type: AccessPolicyResourceType, pub scoped_action_id: Option, @@ -377,7 +377,13 @@ pub struct EditableAccessPolicyProperties { } -pub type ResourceHierarchy = Vec<(AccessPolicyScopedResourceType, Option)>; +pub type ResourceHierarchy = Vec<(AccessPolicyResourceType, Option)>; + +#[derive(Debug, Clone)] +pub enum IndividualPrincipal { + User(Uuid), + App(Uuid) +} /// A piece of information that defines the level of access and inheritance for a principal to perform an action. #[derive(Debug, Serialize, Deserialize)] @@ -403,7 +409,7 @@ pub struct AccessPolicy { pub principal_app_id: Option, - pub scoped_resource_type: AccessPolicyScopedResourceType, + pub scoped_resource_type: AccessPolicyResourceType, pub scoped_action_id: Option, @@ -431,7 +437,7 @@ impl AccessPolicy { /* Static methods */ /// Counts the number of access policies based on a query. - pub async fn count(query: &str, postgres_client: &mut deadpool_postgres::Client) -> Result { + pub async fn count(query: &str, postgres_client: &mut deadpool_postgres::Client, individual_principal: Option<&IndividualPrincipal>) -> Result { // Prepare the query. let sanitizer_options = SlashstepQLSanitizeFunctionOptions { @@ -443,8 +449,35 @@ impl AccessPolicy { should_ignore_offset: true }; let sanitized_filter = SlashstepQLFilterSanitizer::sanitize(&sanitizer_options)?; - let where_clause = sanitized_filter.where_clause.and_then(|string| Some(format!(" where {}", string))).unwrap_or("".to_string()); - let query = format!("select count(*) from hydrated_access_policies{}", where_clause); + let where_clause = sanitized_filter.where_clause.and_then(|string| Some(string)).unwrap_or("".to_string()); + let where_clause = match individual_principal { + + Some(individual_principal) => { + + let additional_condition = match individual_principal { + + IndividualPrincipal::User(user_id) => format!("can_principal_get_access_policy('User', {}, NULL, access_policies.*)", quote_literal(&user_id.to_string())), + IndividualPrincipal::App(app_id) => format!("can_principal_get_access_policy('App', NULL, {}, access_policies.*)", quote_literal(&app_id.to_string())) + + }; + + if where_clause == "" { + + additional_condition + + } else { + + format!("({}) AND {}", where_clause, additional_condition) + + } + + }, + + None => where_clause + + }; + let where_clause = if where_clause == "" { where_clause } else { format!(" where {}", where_clause) }; + let query = format!("select count(*) from access_policies{}", where_clause); // Execute the query and return the count. let rows = postgres_client.query_one(&query, &[]).await?; @@ -457,7 +490,7 @@ impl AccessPolicy { pub async fn create(initial_properties: &InitialAccessPolicyProperties, postgres_client: &mut deadpool_postgres::Client) -> Result { // Insert the access policy into the database. - let query = include_str!("../../queries/access-policies/insert-access-policy-row.sql"); + let query = include_str!("../../queries/access_policies/insert-access-policy-row.sql"); let parameters: &[&(dyn ToSql + Sync)] = &[ &initial_properties.principal_type, &initial_properties.principal_user_id, @@ -527,7 +560,7 @@ impl AccessPolicy { /// Gets an access policy by its ID. pub async fn get_by_id(id: &Uuid, postgres_client: &mut deadpool_postgres::Client) -> Result { - let query = include_str!("../../queries/access-policies/get-access-policy-row-by-id.sql"); + let query = include_str!("../../queries/access_policies/get-access-policy-row-by-id.sql"); let parameters: &[&(dyn ToSql + Sync)] = &[&id]; let row = match postgres_client.query_opt(query, parameters).await { @@ -579,11 +612,14 @@ impl AccessPolicy { /// Initializes the access policies table. pub async fn initialize_access_policies_table(postgres_client: &mut deadpool_postgres::Client) -> Result<(), AccessPolicyError> { - let table_query = include_str!("../../queries/access-policies/initialize-access-policies-table.sql"); + let table_query = include_str!("../../queries/access_policies/initialize_access_policies_table.sql"); postgres_client.execute(table_query, &[]).await?; - let view_query = include_str!("../../queries/access-policies/initialize-hydrated-access-policies-view.sql"); - postgres_client.execute(view_query, &[]).await?; + let get_prinicipal_access_policies_function = include_str!("../../queries/access_policies/create_function_get_principal_access_policies.sql"); + postgres_client.execute(get_prinicipal_access_policies_function, &[]).await?; + + let can_principal_get_access_policy_function = include_str!("../../queries/access_policies/create_function_can_principal_get_access_policy.sql"); + postgres_client.execute(can_principal_get_access_policy_function, &[]).await?; return Ok(()); } @@ -609,7 +645,7 @@ impl AccessPolicy { "scoped_resource_type" => { - let scoped_resource_type = AccessPolicyScopedResourceType::from_str(string_value)?; + let scoped_resource_type = AccessPolicyResourceType::from_str(string_value)?; parameters.push(Box::new(scoped_resource_type)); }, @@ -668,7 +704,7 @@ impl AccessPolicy { } /// Returns a list of access policies based on a query. - pub async fn list(query: &str, postgres_client: &mut deadpool_postgres::Client) -> Result, AccessPolicyError> { + pub async fn list(query: &str, postgres_client: &mut deadpool_postgres::Client, individual_principal: Option<&IndividualPrincipal>) -> Result, AccessPolicyError> { // Prepare the query. let sanitizer_options = SlashstepQLSanitizeFunctionOptions { @@ -680,10 +716,38 @@ impl AccessPolicy { should_ignore_offset: false }; let sanitized_filter = SlashstepQLFilterSanitizer::sanitize(&sanitizer_options)?; - let where_clause = sanitized_filter.where_clause.and_then(|string| Some(format!(" where {}", string))).unwrap_or("".to_string()); + let where_clause = sanitized_filter.where_clause.and_then(|string| Some(string)).unwrap_or("".to_string()); + let where_clause = match individual_principal { + + Some(individual_principal) => { + + let additional_condition = match individual_principal { + + IndividualPrincipal::User(user_id) => format!("can_principal_get_access_policy('User', {}, NULL, access_policies.*)", quote_literal(&user_id.to_string())), + IndividualPrincipal::App(app_id) => format!("can_principal_get_access_policy('App', NULL, {}, access_policies.*)", quote_literal(&app_id.to_string())) + + }; + + if where_clause == "" { + + additional_condition + + } else { + + format!("({}) AND {}", where_clause, additional_condition) + + } + + }, + + None => where_clause + + }; + let where_clause = if where_clause == "" { where_clause } else { format!(" where {}", where_clause) }; + let limit_clause = sanitized_filter.limit.and_then(|limit| Some(format!(" limit {}", limit))).unwrap_or("".to_string()); let offset_clause = sanitized_filter.offset.and_then(|offset| Some(format!(" offset {}", offset))).unwrap_or("".to_string()); - let query = format!("select * from hydrated_access_policies{}{}{}", where_clause, limit_clause, offset_clause); + let query = format!("select * from access_policies{}{}{}", where_clause, limit_clause, offset_clause); // Execute the query. let parsed_parameters = Self::parse_slashstepql_parameters(&sanitized_filter.parameters)?; @@ -703,17 +767,17 @@ impl AccessPolicy { match resource_type { - AccessPolicyScopedResourceType::Instance => query_clauses.push(format!("scoped_resource_type = 'Instance'")), - AccessPolicyScopedResourceType::Workspace => query_clauses.push(format!("scoped_workspace_id = {}", resource_id.expect("A workspace ID must be provided."))), - AccessPolicyScopedResourceType::Project => query_clauses.push(format!("scoped_project_id = {}", resource_id.expect("A project ID must be provided."))), - AccessPolicyScopedResourceType::Milestone => query_clauses.push(format!("scoped_milestone_id = {}", resource_id.expect("A milestone ID must be provided."))), - AccessPolicyScopedResourceType::Item => query_clauses.push(format!("scoped_item_id = {}", resource_id.expect("An item ID must be provided."))), - AccessPolicyScopedResourceType::Action => query_clauses.push(format!("scoped_action_id = {}", resource_id.expect("An action ID must be provided."))), - AccessPolicyScopedResourceType::User => query_clauses.push(format!("scoped_user_id = {}", resource_id.expect("A user ID must be provided."))), - AccessPolicyScopedResourceType::Role => query_clauses.push(format!("scoped_role_id = {}", resource_id.expect("A role ID must be provided."))), - AccessPolicyScopedResourceType::Group => query_clauses.push(format!("scoped_group_id = {}", resource_id.expect("A group ID must be provided."))), - AccessPolicyScopedResourceType::App => query_clauses.push(format!("scoped_app_id = {}", resource_id.expect("An app ID must be provided."))), - AccessPolicyScopedResourceType::AppCredential => query_clauses.push(format!("scoped_app_credential_id = {}", resource_id.expect("An app credential ID must be provided."))) + AccessPolicyResourceType::Instance => query_clauses.push(format!("scoped_resource_type = 'Instance'")), + AccessPolicyResourceType::Workspace => query_clauses.push(format!("scoped_workspace_id = {}", resource_id.expect("A workspace ID must be provided."))), + AccessPolicyResourceType::Project => query_clauses.push(format!("scoped_project_id = {}", resource_id.expect("A project ID must be provided."))), + AccessPolicyResourceType::Milestone => query_clauses.push(format!("scoped_milestone_id = {}", resource_id.expect("A milestone ID must be provided."))), + AccessPolicyResourceType::Item => query_clauses.push(format!("scoped_item_id = {}", resource_id.expect("An item ID must be provided."))), + AccessPolicyResourceType::Action => query_clauses.push(format!("scoped_action_id = {}", resource_id.expect("An action ID must be provided."))), + AccessPolicyResourceType::User => query_clauses.push(format!("scoped_user_id = {}", resource_id.expect("A user ID must be provided."))), + AccessPolicyResourceType::Role => query_clauses.push(format!("scoped_role_id = {}", resource_id.expect("A role ID must be provided."))), + AccessPolicyResourceType::Group => query_clauses.push(format!("scoped_group_id = {}", resource_id.expect("A group ID must be provided."))), + AccessPolicyResourceType::App => query_clauses.push(format!("scoped_app_id = {}", resource_id.expect("An app ID must be provided."))), + AccessPolicyResourceType::AppCredential => query_clauses.push(format!("scoped_app_credential_id = {}", resource_id.expect("An app credential ID must be provided."))) } @@ -744,7 +808,7 @@ impl AccessPolicy { } query_filter.push_str(")"); - let access_policies: Vec = AccessPolicy::list(&query_filter, postgres_client).await?; + let access_policies: Vec = AccessPolicy::list(&query_filter, postgres_client, None).await?; return Ok(access_policies); @@ -754,7 +818,7 @@ impl AccessPolicy { /// Deletes this access policy. pub async fn delete(&self, postgres_client: &mut deadpool_postgres::Client) -> Result<(), AccessPolicyError> { - let query = include_str!("../../queries/access-policies/delete-access-policy-row.sql"); + let query = include_str!("../../queries/access_policies/delete-access-policy-row.sql"); postgres_client.execute(query, &[&self.id]).await?; return Ok(()); @@ -797,30 +861,30 @@ impl AccessPolicy { pub async fn get_hierarchy(&self, postgres_client: &mut deadpool_postgres::Client) -> Result { let mut hierarchy: ResourceHierarchy = vec![]; - let mut selected_resource_type: AccessPolicyScopedResourceType = self.scoped_resource_type.clone(); + let mut selected_resource_type: AccessPolicyResourceType = self.scoped_resource_type.clone(); let mut selected_resource_id: Option = match self.scoped_resource_type { - AccessPolicyScopedResourceType::Instance => None, + AccessPolicyResourceType::Instance => None, - AccessPolicyScopedResourceType::Action => self.scoped_action_id, + AccessPolicyResourceType::Action => self.scoped_action_id, - AccessPolicyScopedResourceType::App => self.scoped_app_id, + AccessPolicyResourceType::App => self.scoped_app_id, - AccessPolicyScopedResourceType::AppCredential => self.scoped_app_credential_id, + AccessPolicyResourceType::AppCredential => self.scoped_app_credential_id, - AccessPolicyScopedResourceType::Group => self.scoped_group_id, + AccessPolicyResourceType::Group => self.scoped_group_id, - AccessPolicyScopedResourceType::Item => self.scoped_item_id, + AccessPolicyResourceType::Item => self.scoped_item_id, - AccessPolicyScopedResourceType::Milestone => self.scoped_milestone_id, + AccessPolicyResourceType::Milestone => self.scoped_milestone_id, - AccessPolicyScopedResourceType::Project => self.scoped_project_id, + AccessPolicyResourceType::Project => self.scoped_project_id, - AccessPolicyScopedResourceType::Role => self.scoped_role_id, + AccessPolicyResourceType::Role => self.scoped_role_id, - AccessPolicyScopedResourceType::User => self.scoped_user_id, + AccessPolicyResourceType::User => self.scoped_user_id, - AccessPolicyScopedResourceType::Workspace => self.scoped_workspace_id + AccessPolicyResourceType::Workspace => self.scoped_workspace_id }; @@ -829,18 +893,18 @@ impl AccessPolicy { match selected_resource_type { // Instance - AccessPolicyScopedResourceType::Instance => break, + AccessPolicyResourceType::Instance => break, // Action -> (App | Instance) - AccessPolicyScopedResourceType::Action => { + AccessPolicyResourceType::Action => { let Some(action_id) = selected_resource_id else { - return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyScopedResourceType::Action)); + return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyResourceType::Action)); }; - hierarchy.push((AccessPolicyScopedResourceType::Action, Some(action_id))); + hierarchy.push((AccessPolicyResourceType::Action, Some(action_id))); let action = match Action::get_by_id(&action_id, postgres_client).await { @@ -848,7 +912,7 @@ impl AccessPolicy { Err(error) => match error { - ActionError::NotFoundError(_) => return Err(AccessPolicyError::OrphanedResourceError(AccessPolicyScopedResourceType::Action)), + ActionError::NotFoundError(_) => return Err(AccessPolicyError::OrphanedResourceError(AccessPolicyResourceType::Action)), _ => return Err(AccessPolicyError::ActionError(error)) @@ -858,12 +922,12 @@ impl AccessPolicy { if let Some(app_id) = action.app_id { - selected_resource_type = AccessPolicyScopedResourceType::App; + selected_resource_type = AccessPolicyResourceType::App; selected_resource_id = Some(app_id); } else { - selected_resource_type = AccessPolicyScopedResourceType::Instance; + selected_resource_type = AccessPolicyResourceType::Instance; selected_resource_id = None; } @@ -871,28 +935,28 @@ impl AccessPolicy { } // Workspace -> Instance - AccessPolicyScopedResourceType::Workspace => { + AccessPolicyResourceType::Workspace => { let Some(scoped_workspace_id) = self.scoped_workspace_id else { - return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyScopedResourceType::Workspace)); + return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyResourceType::Workspace)); }; - hierarchy.push((AccessPolicyScopedResourceType::Workspace, Some(scoped_workspace_id))); + hierarchy.push((AccessPolicyResourceType::Workspace, Some(scoped_workspace_id))); }, // Project -> Workspace - AccessPolicyScopedResourceType::Project => { + AccessPolicyResourceType::Project => { let Some(scoped_project_id) = self.scoped_project_id else { - return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyScopedResourceType::Project)); + return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyResourceType::Project)); }; - hierarchy.push((AccessPolicyScopedResourceType::Project, Some(scoped_project_id))); + hierarchy.push((AccessPolicyResourceType::Project, Some(scoped_project_id))); let project = match Project::get_by_id(&scoped_project_id, postgres_client).await { @@ -900,7 +964,7 @@ impl AccessPolicy { Err(error) => match error { - ProjectError::NotFoundError(_) => return Err(AccessPolicyError::OrphanedResourceError(AccessPolicyScopedResourceType::Project)), + ProjectError::NotFoundError(_) => return Err(AccessPolicyError::OrphanedResourceError(AccessPolicyResourceType::Project)), _ => return Err(AccessPolicyError::ProjectError(error)) @@ -908,52 +972,52 @@ impl AccessPolicy { }; - selected_resource_type = AccessPolicyScopedResourceType::Workspace; + selected_resource_type = AccessPolicyResourceType::Workspace; selected_resource_id = Some(project.workspace_id); }, // Item -> Project - AccessPolicyScopedResourceType::Item => { + AccessPolicyResourceType::Item => { let Some(scoped_item_id) = self.scoped_item_id else { - return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyScopedResourceType::Item)); + return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyResourceType::Item)); }; - hierarchy.push((AccessPolicyScopedResourceType::Item, Some(scoped_item_id))); + hierarchy.push((AccessPolicyResourceType::Item, Some(scoped_item_id))); let item = Item::get_by_id(&scoped_item_id, postgres_client).await?; - selected_resource_type = AccessPolicyScopedResourceType::Project; + selected_resource_type = AccessPolicyResourceType::Project; selected_resource_id = Some(item.project_id); }, // User -> Instance - AccessPolicyScopedResourceType::User => { + AccessPolicyResourceType::User => { let Some(scoped_user_id) = self.scoped_user_id else { - return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyScopedResourceType::User)); + return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyResourceType::User)); }; - hierarchy.push((AccessPolicyScopedResourceType::User, Some(scoped_user_id))); + hierarchy.push((AccessPolicyResourceType::User, Some(scoped_user_id))); }, // Role -> (Project | Workspace | Group | Instance) - AccessPolicyScopedResourceType::Role => { + AccessPolicyResourceType::Role => { let Some(scoped_role_id) = self.scoped_role_id else { - return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyScopedResourceType::Role)); + return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyResourceType::Role)); }; - hierarchy.push((AccessPolicyScopedResourceType::Role, Some(scoped_role_id))); + hierarchy.push((AccessPolicyResourceType::Role, Some(scoped_role_id))); let role = match Role::get_by_id(&scoped_role_id, postgres_client).await { @@ -961,7 +1025,7 @@ impl AccessPolicy { Err(error) => match error { - RoleError::NotFoundError(_) => return Err(AccessPolicyError::OrphanedResourceError(AccessPolicyScopedResourceType::Role)), + RoleError::NotFoundError(_) => return Err(AccessPolicyError::OrphanedResourceError(AccessPolicyResourceType::Role)), _ => return Err(AccessPolicyError::RoleError(error)) @@ -973,7 +1037,7 @@ impl AccessPolicy { RoleParentResourceType::Instance => { - selected_resource_type = AccessPolicyScopedResourceType::Instance; + selected_resource_type = AccessPolicyResourceType::Instance; selected_resource_id = None; }, @@ -982,11 +1046,11 @@ impl AccessPolicy { let Some(workspace_id) = role.parent_workspace_id else { - return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyScopedResourceType::Workspace)); + return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyResourceType::Workspace)); }; - selected_resource_type = AccessPolicyScopedResourceType::Workspace; + selected_resource_type = AccessPolicyResourceType::Workspace; selected_resource_id = Some(workspace_id); }, @@ -995,11 +1059,11 @@ impl AccessPolicy { let Some(project_id) = role.parent_project_id else { - return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyScopedResourceType::Project)); + return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyResourceType::Project)); }; - selected_resource_type = AccessPolicyScopedResourceType::Project; + selected_resource_type = AccessPolicyResourceType::Project; selected_resource_id = Some(project_id); }, @@ -1008,11 +1072,11 @@ impl AccessPolicy { let Some(group_id) = role.parent_group_id else { - return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyScopedResourceType::Group)); + return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyResourceType::Group)); }; - selected_resource_type = AccessPolicyScopedResourceType::Group; + selected_resource_type = AccessPolicyResourceType::Group; selected_resource_id = Some(group_id); } @@ -1022,31 +1086,31 @@ impl AccessPolicy { }, // Group -> Instance - AccessPolicyScopedResourceType::Group => { + AccessPolicyResourceType::Group => { let Some(group_id) = selected_resource_id else { - return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyScopedResourceType::Group)); + return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyResourceType::Group)); }; - hierarchy.push((AccessPolicyScopedResourceType::Group, Some(group_id))); + hierarchy.push((AccessPolicyResourceType::Group, Some(group_id))); - selected_resource_type = AccessPolicyScopedResourceType::Instance; + selected_resource_type = AccessPolicyResourceType::Instance; selected_resource_id = None; } // App -> (Workspace | User | Instance) - AccessPolicyScopedResourceType::App => { + AccessPolicyResourceType::App => { let Some(app_id) = selected_resource_id else { - return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyScopedResourceType::App)); + return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyResourceType::App)); }; - hierarchy.push((AccessPolicyScopedResourceType::App, Some(app_id))); + hierarchy.push((AccessPolicyResourceType::App, Some(app_id))); let app = match App::get_by_id(&app_id, postgres_client).await { @@ -1054,7 +1118,7 @@ impl AccessPolicy { Err(error) => match error { - AppError::NotFoundError(_) => return Err(AccessPolicyError::OrphanedResourceError(AccessPolicyScopedResourceType::App)), + AppError::NotFoundError(_) => return Err(AccessPolicyError::OrphanedResourceError(AccessPolicyResourceType::App)), _ => return Err(AccessPolicyError::AppError(error)) @@ -1066,7 +1130,7 @@ impl AccessPolicy { AppParentResourceType::Instance => { - selected_resource_type = AccessPolicyScopedResourceType::Instance; + selected_resource_type = AccessPolicyResourceType::Instance; selected_resource_id = None; }, @@ -1075,11 +1139,11 @@ impl AccessPolicy { let Some(workspace_id) = app.parent_workspace_id else { - return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyScopedResourceType::Workspace)); + return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyResourceType::Workspace)); }; - selected_resource_type = AccessPolicyScopedResourceType::Workspace; + selected_resource_type = AccessPolicyResourceType::Workspace; selected_resource_id = Some(workspace_id); }, @@ -1088,11 +1152,11 @@ impl AccessPolicy { let Some(user_id) = app.parent_user_id else { - return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyScopedResourceType::User)); + return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyResourceType::User)); }; - selected_resource_type = AccessPolicyScopedResourceType::User; + selected_resource_type = AccessPolicyResourceType::User; selected_resource_id = Some(user_id); } @@ -1102,15 +1166,15 @@ impl AccessPolicy { } // AppCredential -> App - AccessPolicyScopedResourceType::AppCredential => { + AccessPolicyResourceType::AppCredential => { let Some(app_credential_id) = selected_resource_id else { - return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyScopedResourceType::AppCredential)); + return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyResourceType::AppCredential)); }; - hierarchy.push((AccessPolicyScopedResourceType::AppCredential, Some(app_credential_id))); + hierarchy.push((AccessPolicyResourceType::AppCredential, Some(app_credential_id))); let app_credential = match AppCredential::get_by_id(&app_credential_id, postgres_client).await { @@ -1118,7 +1182,7 @@ impl AccessPolicy { Err(error) => match error { - AppCredentialError::NotFoundError(_) => return Err(AccessPolicyError::OrphanedResourceError(AccessPolicyScopedResourceType::AppCredential)), + AppCredentialError::NotFoundError(_) => return Err(AccessPolicyError::OrphanedResourceError(AccessPolicyResourceType::AppCredential)), _ => return Err(AccessPolicyError::AppCredentialError(error)) @@ -1126,21 +1190,21 @@ impl AccessPolicy { }; - selected_resource_type = AccessPolicyScopedResourceType::App; + selected_resource_type = AccessPolicyResourceType::App; selected_resource_id = Some(app_credential.app_id); } // Milestone -> (Project | Workspace) - AccessPolicyScopedResourceType::Milestone => { + AccessPolicyResourceType::Milestone => { let Some(milestone_id) = selected_resource_id else { - return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyScopedResourceType::Milestone)); + return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyResourceType::Milestone)); }; - hierarchy.push((AccessPolicyScopedResourceType::Milestone, Some(milestone_id))); + hierarchy.push((AccessPolicyResourceType::Milestone, Some(milestone_id))); let milestone = match Milestone::get_by_id(&milestone_id, postgres_client).await { @@ -1148,7 +1212,7 @@ impl AccessPolicy { Err(error) => match error { - MilestoneError::NotFoundError(_) => return Err(AccessPolicyError::OrphanedResourceError(AccessPolicyScopedResourceType::Milestone)), + MilestoneError::NotFoundError(_) => return Err(AccessPolicyError::OrphanedResourceError(AccessPolicyResourceType::Milestone)), _ => return Err(AccessPolicyError::MilestoneError(error)) @@ -1162,11 +1226,11 @@ impl AccessPolicy { let Some(project_id) = milestone.parent_project_id else { - return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyScopedResourceType::Project)); + return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyResourceType::Project)); }; - selected_resource_type = AccessPolicyScopedResourceType::Project; + selected_resource_type = AccessPolicyResourceType::Project; selected_resource_id = Some(project_id); }, @@ -1175,11 +1239,11 @@ impl AccessPolicy { let Some(workspace_id) = milestone.parent_workspace_id else { - return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyScopedResourceType::Workspace)); + return Err(AccessPolicyError::ScopedResourceIDMissingError(AccessPolicyResourceType::Workspace)); }; - selected_resource_type = AccessPolicyScopedResourceType::Workspace; + selected_resource_type = AccessPolicyResourceType::Workspace; selected_resource_id = Some(workspace_id); } @@ -1192,7 +1256,7 @@ impl AccessPolicy { } - hierarchy.push((AccessPolicyScopedResourceType::Instance, None)); + hierarchy.push((AccessPolicyResourceType::Instance, None)); return Ok(hierarchy); diff --git a/src/resources/access_policy/tests.rs b/src/resources/access_policy/tests.rs index ee5cca5..bcbc0a9 100644 --- a/src/resources/access_policy/tests.rs +++ b/src/resources/access_policy/tests.rs @@ -15,7 +15,7 @@ use crate::{ AccessPolicyInheritanceLevel, AccessPolicyPermissionLevel, AccessPolicyPrincipalType, - AccessPolicyScopedResourceType, + AccessPolicyResourceType, DEFAULT_ACCESS_POLICY_LIST_LIMIT, EditableAccessPolicyProperties, InitialAccessPolicyProperties, Principal @@ -96,7 +96,7 @@ async fn create_access_policy() -> Result<()> { inheritance_level: AccessPolicyInheritanceLevel::Enabled, principal_type: AccessPolicyPrincipalType::User, principal_user_id: Some(user.id), - scoped_resource_type: AccessPolicyScopedResourceType::Instance, + scoped_resource_type: AccessPolicyResourceType::Instance, ..Default::default() }; let access_policy = AccessPolicy::create(&access_policy_properties, &mut postgres_client).await?; @@ -145,7 +145,7 @@ async fn list_access_policies_without_query() -> Result<()> { } - let retrieved_access_policies = AccessPolicy::list("", &mut postgres_client).await?; + let retrieved_access_policies = AccessPolicy::list("", &mut postgres_client, None).await?; assert_eq!(created_access_policies.len(), retrieved_access_policies.len()); for i in 0..created_access_policies.len() { @@ -182,7 +182,7 @@ async fn list_access_policies_with_query() -> Result<()> { inheritance_level: AccessPolicyInheritanceLevel::Enabled, principal_type: AccessPolicyPrincipalType::User, principal_user_id: if remaining_action_count == 1 { created_access_policies[0].principal_user_id } else { Some(user.id) }, - scoped_resource_type: AccessPolicyScopedResourceType::Instance, + scoped_resource_type: AccessPolicyResourceType::Instance, ..Default::default() }; let access_policy = AccessPolicy::create(&access_policy_properties, &mut postgres_client).await?; @@ -194,7 +194,7 @@ async fn list_access_policies_with_query() -> Result<()> { let principal_user_id = created_access_policies[0].principal_user_id.ok_or_else(|| anyhow!("Principal user ID is not set."))?; let query = format!("principal_user_id = \"{}\"", principal_user_id); - let retrieved_access_policies = AccessPolicy::list(&query, &mut postgres_client).await?; + let retrieved_access_policies = AccessPolicy::list(&query, &mut postgres_client, None).await?; let created_access_policies_with_specific_user: Vec<&AccessPolicy> = created_access_policies.iter().filter(|access_policy| access_policy.principal_user_id == Some(principal_user_id)).collect(); assert_eq!(created_access_policies_with_specific_user.len(), retrieved_access_policies.len()); @@ -230,7 +230,7 @@ async fn list_access_policies_with_default_limit() -> Result<()> { } - let retrieved_access_policies = AccessPolicy::list("", &mut postgres_client).await?; + let retrieved_access_policies = AccessPolicy::list("", &mut postgres_client, None).await?; assert_eq!(retrieved_access_policies.len(), DEFAULT_ACCESS_POLICY_LIST_LIMIT as usize); @@ -259,7 +259,7 @@ async fn count_access_policies() -> Result<()> { inheritance_level: AccessPolicyInheritanceLevel::Enabled, principal_type: AccessPolicyPrincipalType::User, principal_user_id: Some(user.id), - scoped_resource_type: AccessPolicyScopedResourceType::Instance, + scoped_resource_type: AccessPolicyResourceType::Instance, ..Default::default() }; let access_policy = AccessPolicy::create(&access_policy_properties, &mut postgres_client).await?; @@ -268,7 +268,7 @@ async fn count_access_policies() -> Result<()> { } - let retrieved_access_policy_count = AccessPolicy::count("", &mut postgres_client).await?; + let retrieved_access_policy_count = AccessPolicy::count("", &mut postgres_client, None).await?; assert_eq!(retrieved_access_policy_count, MAXIMUM_ACTION_COUNT); @@ -293,7 +293,7 @@ async fn list_access_policies_by_hierarchy() -> Result<()> { inheritance_level: AccessPolicyInheritanceLevel::Enabled, principal_type: AccessPolicyPrincipalType::User, principal_user_id: Some(user.id), - scoped_resource_type: AccessPolicyScopedResourceType::Instance, + scoped_resource_type: AccessPolicyResourceType::Instance, ..Default::default() }; let instance_access_policy = AccessPolicy::create(&instance_access_policy_properties, &mut postgres_client).await?; @@ -345,7 +345,7 @@ async fn update_access_policy() -> Result<()> { inheritance_level: AccessPolicyInheritanceLevel::Enabled, principal_type: AccessPolicyPrincipalType::User, principal_user_id: Some(user.id), - scoped_resource_type: AccessPolicyScopedResourceType::Instance, + scoped_resource_type: AccessPolicyResourceType::Instance, ..Default::default() }; let instance_access_policy = AccessPolicy::create(&instance_access_policy_properties, &mut postgres_client).await?; diff --git a/src/resources/http_transaction/mod.rs b/src/resources/http_transaction/mod.rs index be96a2c..c15d13f 100644 --- a/src/resources/http_transaction/mod.rs +++ b/src/resources/http_transaction/mod.rs @@ -62,7 +62,7 @@ impl HTTPTransaction { pub async fn create(properties: &InitialHTTPTransactionProperties, postgres_client: &mut deadpool_postgres::Client) -> Result { - let query = include_str!("../../queries/http-requests/insert-http-request-row.sql"); + let query = include_str!("../../queries/http-transactions/insert-http-transaction-row.sql"); let parameters: &[&(dyn ToSql + Sync)] = &[ &properties.method, &properties.url, @@ -89,7 +89,7 @@ impl HTTPTransaction { pub async fn initialize_http_transactions_table(postgres_client: &mut deadpool_postgres::Client) -> Result<(), HTTPTransactionError> { - let query = include_str!("../../queries/http-requests/create-http-requests-table.sql"); + let query = include_str!("../../queries/http-transactions/create-http-transactions-table.sql"); postgres_client.execute(query, &[]).await?; return Ok(()); diff --git a/src/resources/server_log_entry/mod.rs b/src/resources/server_log_entry/mod.rs index 7bd948f..6b2b52e 100644 --- a/src/resources/server_log_entry/mod.rs +++ b/src/resources/server_log_entry/mod.rs @@ -140,7 +140,7 @@ impl ServerLogEntry { pub async fn create<'a>(properties: &InitialServerLogEntryProperties<'a>, postgres_client: &mut deadpool_postgres::Client, should_print_to_console: bool) -> Result { - let query = include_str!("../../queries/server-log-entries/insert-server-log-entry-row.sql"); + let query = include_str!("../../queries/server_log_entries/insert-server-log-entry-row.sql"); let parameters: &[&(dyn ToSql + Sync)] = &[ &properties.message, &properties.http_request_id, diff --git a/src/routes/access-policies/mod.rs b/src/routes/access-policies/mod.rs index 09e269f..b406baa 100644 --- a/src/routes/access-policies/mod.rs +++ b/src/routes/access-policies/mod.rs @@ -1,19 +1,36 @@ -use axum::Router; +use std::sync::Arc; -use crate::AppState; +use axum::{Extension, Json, Router, extract::{Path, State}}; + +use crate::{AppState, HTTPError, resources::{access_policy::AccessPolicy, http_transaction::HTTPTransaction, user::User}}; #[path = "./{access_policy_id}/mod.rs"] mod access_policy_id; -async fn list_access_policies() { +#[axum::debug_handler] +async fn handle_list_access_policies_request( + Path(access_policy_id): Path, + State(state): State, + Extension(http_transaction): Extension>, + Extension(user): Extension>> +) -> Result>, HTTPError> { + + // let http_transaction = http_transaction.clone(); + // let mut postgres_client = state.database_pool.get().await.map_err(map_postgres_error_to_http_error)?; + // let access_policy = get_access_policy(&access_policy_id, &http_transaction, &mut postgres_client).await?; + // let user = get_user_from_option_user(&user, &http_transaction, &mut postgres_client).await?; + // let resource_hierarchy = get_resource_hierarchy(&access_policy, &http_transaction, &mut postgres_client).await?; + // let action = get_action_from_name("slashstep.accessPolicies.get", &http_transaction, &mut postgres_client).await?; + + return Err(HTTPError::NotImplementedError(Some(format!("Not implemented.")))); } pub fn get_router(state: AppState) -> Router { - let mut router = Router::::new(); - router = router.route("/access-policies", axum::routing::get(list_access_policies)); - router = router.merge(access_policy_id::get_router(state.clone())); + let router = Router::::new() + .route("/access-policies", axum::routing::get(handle_list_access_policies_request)) + .merge(access_policy_id::get_router(state.clone())); return router; } diff --git a/src/routes/access-policies/{access_policy_id}/mod.rs b/src/routes/access-policies/{access_policy_id}/mod.rs index 93396a2..a3fde84 100644 --- a/src/routes/access-policies/{access_policy_id}/mod.rs +++ b/src/routes/access-policies/{access_policy_id}/mod.rs @@ -93,6 +93,16 @@ async fn get_access_policy(access_policy_id: &str, http_transaction: &HTTPTransa let http_error = match error { AccessPolicyError::NotFoundError(_) => HTTPError::NotFoundError(Some(error.to_string())), + AccessPolicyError::PostgresError(error) => { + + match error.as_db_error() { + + Some(error) => HTTPError::InternalServerError(Some(error.to_string())), + None => HTTPError::InternalServerError(Some(error.to_string())) + + } + + } _ => HTTPError::InternalServerError(Some(error.to_string())) }; let _ = ServerLogEntry::from_http_error(&http_error, Some(&http_transaction.id), &mut postgres_client).await; diff --git a/src/routes/access-policies/{access_policy_id}/tests.rs b/src/routes/access-policies/{access_policy_id}/tests.rs index ab18490..6a87770 100644 --- a/src/routes/access-policies/{access_policy_id}/tests.rs +++ b/src/routes/access-policies/{access_policy_id}/tests.rs @@ -1,10 +1,34 @@ + use std::net::SocketAddr; use axum::middleware; use axum_extra::extract::cookie::Cookie; use axum_test::TestServer; use ntest::timeout; use uuid::Uuid; -use crate::{Action, AppState, SlashstepServerError, initialize_required_tables, middleware::http_request_middleware, pre_definitions::{initialize_pre_defined_actions, initialize_pre_defined_roles}, resources::{access_policy::{AccessPolicy, AccessPolicyError, AccessPolicyInheritanceLevel, AccessPolicyPermissionLevel, AccessPolicyPrincipalType, AccessPolicyScopedResourceType, InitialAccessPolicyProperties}, session::Session}, tests::TestEnvironment}; +use crate::{ + Action, + AppState, + SlashstepServerError, + initialize_required_tables, + middleware::http_request_middleware, + pre_definitions::{ + initialize_pre_defined_actions, + initialize_pre_defined_roles + }, + resources::{ + access_policy::{ + AccessPolicy, + AccessPolicyError, + AccessPolicyInheritanceLevel, + AccessPolicyPermissionLevel, + AccessPolicyPrincipalType, + AccessPolicyResourceType, + InitialAccessPolicyProperties + }, + session::Session + }, + tests::TestEnvironment +}; /// Verifies that the router can return a 200 status code and the requested access policy. #[tokio::test] @@ -37,7 +61,7 @@ async fn verify_returned_access_policy_by_id() -> Result<(), SlashstepServerErro inheritance_level: AccessPolicyInheritanceLevel::Enabled, principal_type: AccessPolicyPrincipalType::User, principal_user_id: Some(user.id), - scoped_resource_type: AccessPolicyScopedResourceType::Instance, + scoped_resource_type: AccessPolicyResourceType::Instance, ..Default::default() }; let access_policy = AccessPolicy::create(&access_policy_properties, &mut postgres_client).await?; @@ -225,7 +249,7 @@ async fn verify_successful_deletion_when_deleting_access_policy_by_id() -> Resul inheritance_level: AccessPolicyInheritanceLevel::Enabled, principal_type: AccessPolicyPrincipalType::User, principal_user_id: Some(user.id), - scoped_resource_type: AccessPolicyScopedResourceType::Instance, + scoped_resource_type: AccessPolicyResourceType::Instance, ..Default::default() }; let access_policy = AccessPolicy::create(&access_policy_properties, &mut postgres_client).await?; @@ -398,7 +422,7 @@ async fn verify_successful_patch_access_policy_by_id() -> Result<(), SlashstepSe inheritance_level: AccessPolicyInheritanceLevel::Enabled, principal_type: AccessPolicyPrincipalType::User, principal_user_id: Some(user.id), - scoped_resource_type: AccessPolicyScopedResourceType::Instance, + scoped_resource_type: AccessPolicyResourceType::Instance, ..Default::default() }; let access_policy = AccessPolicy::create(&access_policy_properties, &mut postgres_client).await?; @@ -585,7 +609,7 @@ async fn verify_authentication_when_patching_access_policy_by_id() -> Result<(), inheritance_level: AccessPolicyInheritanceLevel::Enabled, principal_type: AccessPolicyPrincipalType::User, principal_user_id: Some(user.id), - scoped_resource_type: AccessPolicyScopedResourceType::Instance, + scoped_resource_type: AccessPolicyResourceType::Instance, ..Default::default() }; let access_policy = AccessPolicy::create(&access_policy_properties, &mut postgres_client).await?; @@ -633,7 +657,7 @@ async fn verify_permission_when_patching_access_policy() -> Result<(), Slashstep inheritance_level: AccessPolicyInheritanceLevel::Enabled, principal_type: AccessPolicyPrincipalType::User, principal_user_id: Some(user.id), - scoped_resource_type: AccessPolicyScopedResourceType::Instance, + scoped_resource_type: AccessPolicyResourceType::Instance, ..Default::default() }; let access_policy = AccessPolicy::create(&access_policy_properties, &mut postgres_client).await?; diff --git a/src/tests.rs b/src/tests.rs index c3e876b..bc2b169 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -129,7 +129,7 @@ impl TestEnvironment { inheritance_level: crate::resources::access_policy::AccessPolicyInheritanceLevel::Enabled, principal_type: crate::resources::access_policy::AccessPolicyPrincipalType::User, principal_user_id: Some(user.id), - scoped_resource_type: crate::resources::access_policy::AccessPolicyScopedResourceType::Instance, + scoped_resource_type: crate::resources::access_policy::AccessPolicyResourceType::Instance, ..Default::default() };