Feature/add support permissions (#445)

* added support perm macro which removes secrets perms w/ burnettk

* support perm macro inherits from basic now and updated docs on permissions to be more accurate w/ burnettk

---------

Co-authored-by: jasquat <jasquat@users.noreply.github.com>
This commit is contained in:
jasquat 2023-08-21 23:23:26 -04:00 committed by GitHub
parent a9b6f05e14
commit 29620cb17f
3 changed files with 129 additions and 110 deletions

View File

@ -4,74 +4,52 @@ The permission URL, or target URI, refers to the specific endpoint or resource t
- **PG:** [process_group_identifier]: Applies to the specified process group, including all sub process groups and process models.
- **PM:** [process_model_identifier]: Applies to the specified process model.
- **BASIC:** Provides basic access to complete tasks and use the site.
- **ELEVATED:** Enables operations that require elevated permissions.
- **ALL:** Grants access to all API endpoints, providing admin-like permissions.
- **BASIC:** Allows basic access to complete tasks and use the site.
- **SUPPORT:** BASIC permissions and add significant administrative permissions.
- **ELEVATED:** Includes SUPPORT permissions and adds the ability to view and modify secrets. Does not include the ability to view or modify process groups and process models.
- **ALL:** Grants access to all API endpoints, with no limitations.
```{admonition} Note
An asterisk (*) can be used as a wildcard to give access to everything within a specific category. For example, "/process-models/", allows access to all resources related to process models.
An asterisk (*) can be used as a wildcard to give access to everything within a specific category. For example, `/process-models/*`, allows access to all resources related to process models.
```
This functionality is implemented in [authorization service.py](https://github.com/sartography/spiff-arena/blob/main/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py).
(pg)=
## PG
Process Groups permissions controls access rights granted to users or entities within that particular process model. By assigning permissions to process groups, you can determine what actions or operations users can perform within those groups.
[View GIT Repository - BASIC](https://github.com/sartography/spiff-arena/blob/main/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py#L557)
```python
def set_process_model_permissions(cls, target: str, permission_set: str) -> list[PermissionToAssign]:
```
Process Groups permissions controls access rights granted to users or entities within the given process group.
(pm)=
## PM
These permissions relates to process models. It defines the permissions and access rights assigned to users or entities specifically within a given process model.
These permissions relate to process models, and assigns permissions and access rights to users or entities specifically within a given process model.
[View GIT Repository - BASIC](https://github.com/sartography/spiff-arena/blob/main/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py#L574)
## BASIC
```python
def set_process_group_permissions(cls, target: str, permission_set: str) -> list[PermissionToAssign]:
```
These permissions cover basic actions such as signing in to the site and completing tasks that are assigned to you.
## BASIC
## SUPPORT
These permissions cover basic actions such as creating users and process instances, checking user existence, and reading various entities like process groups, models, and tasks.
These permissions are significant, allowing support personnel to debug process instances and take corrective action when errors occur.
In typical scenarios, a user with SUPPORT permissions would also be assigned access to view or modify process groups and models.
See [PG](#pg) and [PM](#pm).
[View GIT Repository - BASIC](https://github.com/sartography/spiff-arena/blob/main/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py#L494)
```python
def set_basic_permissions(cls) -> list[PermissionToAssign]:
```
## ELEVATED
These permissions cover basic actions such as creating users and process instances, checking user existence, and reading various entities like process groups, models, and tasks.
[View GIT Repository - BASIC](https://github.com/sartography/spiff-arena/blob/main/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py#L494)
```python
def explode_permissions(cls, permission_set: str, target: str) -> list[PermissionToAssign]:
```
## ELEVATED
A user with elevated permissions can do anything on the site except interact with process models.
In typical scenarios, a user with ELEVATED permissions would also be assigned access to view or modify process groups and models.
## ALL
The "ALL" permission grants unrestricted access to all API endpoints. It essentially provides administrator-like permissions, allowing the user to perform any action or operation available within the system.
```python
elif target == "ALL":
for permission in permissions:
permissions_to_assign.append(PermissionToAssign(permission=permission, target_uri="/*"))
elif target.startswith("/"):
for permission in permissions:
permissions_to_assign.append(PermissionToAssign(permission=permission, target_uri=target))
```
The "ALL" permission grants unrestricted access to all API endpoints.
It provides administrator-level permissions, allowing the user to perform any action or operation available within the system.
### ALL URLs
```python
/active-users/unregister/{last_visited_identifier}:
% use bash syntax here to avoid syntax highlighting. otherwise it gets highlighted as if it's python
```bash
/active-users/unregister/{last_visited_identifier}:
/active-users/updates/{last_visited_identifier}:
/authentication_callback/{service}/{auth_method}:
/authentications:

View File

@ -530,7 +530,20 @@ class AuthorizationService:
@classmethod
def set_elevated_permissions(cls) -> list[PermissionToAssign]:
permissions_to_assign: list[PermissionToAssign] = []
"""This is basically /* without write access to Process Groups and Process Models.
Useful for admin-like permissions on readonly environments like a production environment.
"""
permissions_to_assign = cls.set_support_permissions()
for permission in ["create", "read", "update", "delete"]:
permissions_to_assign.append(PermissionToAssign(permission=permission, target_uri="/secrets/*"))
return permissions_to_assign
@classmethod
def set_support_permissions(cls) -> list[PermissionToAssign]:
"""Just like elevated permissions minus access to secrets."""
permissions_to_assign = cls.set_basic_permissions()
for process_instance_action in ["resume", "terminate", "suspend", "reset"]:
permissions_to_assign.append(
PermissionToAssign(permission="create", target_uri=f"/process-instance-{process_instance_action}/*")
@ -562,7 +575,6 @@ class AuthorizationService:
for permission in ["create", "read", "update", "delete"]:
permissions_to_assign.append(PermissionToAssign(permission=permission, target_uri="/process-instances/*"))
permissions_to_assign.append(PermissionToAssign(permission=permission, target_uri="/secrets/*"))
permissions_to_assign.append(PermissionToAssign(permission="read", target_uri="/data-stores/*"))
return permissions_to_assign
@ -614,6 +626,8 @@ class AuthorizationService:
* Basic access to complete tasks and use the site
ELEVATED
* Operations that require elevated permissions
SUPPORT
* Like elevated minus access to secrets
Permission Macros:
all
@ -638,6 +652,8 @@ class AuthorizationService:
permissions_to_assign += cls.set_basic_permissions()
elif target.startswith("ELEVATED"):
permissions_to_assign += cls.set_elevated_permissions()
elif target.startswith("SUPPORT"):
permissions_to_assign += cls.set_support_permissions()
elif target == "ALL":
for permission in permissions:
permissions_to_assign.append(PermissionToAssign(permission=permission, target_uri="/*"))

View File

@ -275,77 +275,29 @@ class TestAuthorizationService(BaseTest):
client: FlaskClient,
with_db_and_bpmn_file_cleanup: None,
) -> None:
expected_permissions = sorted(
[
("/active-users/*", "create"),
("/connector-proxy/typeahead/*", "read"),
("/debug/version-info", "read"),
("/extensions", "read"),
("/onboarding", "read"),
("/process-groups", "read"),
("/process-instances/find-by-id/*", "read"),
("/process-instances/for-me", "create"),
("/process-instances/report-metadata", "read"),
("/process-instances/reports/*", "create"),
("/process-instances/reports/*", "delete"),
("/process-instances/reports/*", "read"),
("/process-instances/reports/*", "update"),
("/process-models", "read"),
("/processes", "read"),
("/processes/callers/*", "read"),
("/service-tasks", "read"),
("/tasks/*", "create"),
("/tasks/*", "delete"),
("/tasks/*", "read"),
("/tasks/*", "update"),
("/user-groups/for-current-user", "read"),
("/users/exists/by-username", "create"),
("/users/search", "read"),
]
)
expected_permissions = self._expected_basic_permissions()
permissions_to_assign = AuthorizationService.explode_permissions("all", "BASIC")
permissions_to_assign_tuples = sorted([(p.target_uri, p.permission) for p in permissions_to_assign])
assert permissions_to_assign_tuples == expected_permissions
def test_explode_permissions_support(
self,
app: Flask,
client: FlaskClient,
with_db_and_bpmn_file_cleanup: None,
) -> None:
expected_permissions = self._expected_support_permissions()
permissions_to_assign = AuthorizationService.explode_permissions("all", "SUPPORT")
permissions_to_assign_tuples = sorted([(p.target_uri, p.permission) for p in permissions_to_assign])
assert permissions_to_assign_tuples == expected_permissions
def test_explode_permissions_elevated(
self,
app: Flask,
client: FlaskClient,
with_db_and_bpmn_file_cleanup: None,
) -> None:
expected_permissions = sorted(
[
("/authentications", "read"),
("/can-run-privileged-script/*", "create"),
("/data-stores/*", "read"),
("/debug/*", "create"),
("/event-error-details/*", "read"),
("/extensions-get-data/*", "read"),
("/extensions/*", "create"),
("/logs/*", "read"),
("/messages", "read"),
("/messages/*", "create"),
("/process-data-file-download/*", "read"),
("/process-data/*", "read"),
("/process-instance-reset/*", "create"),
("/process-instance-resume/*", "create"),
("/process-instance-suspend/*", "create"),
("/process-instance-terminate/*", "create"),
("/process-instances/*", "create"),
("/process-instances/*", "delete"),
("/process-instances/*", "read"),
("/process-instances/*", "update"),
("/secrets/*", "create"),
("/secrets/*", "delete"),
("/secrets/*", "read"),
("/secrets/*", "update"),
("/send-event/*", "create"),
("/task-assign/*", "create"),
("/task-complete/*", "create"),
("/task-data/*", "update"),
("/task-data/*", "read"),
]
)
expected_permissions = self._expected_elevated_permissions()
permissions_to_assign = AuthorizationService.explode_permissions("all", "ELEVATED")
permissions_to_assign_tuples = sorted([(p.target_uri, p.permission) for p in permissions_to_assign])
assert permissions_to_assign_tuples == expected_permissions
@ -489,3 +441,76 @@ class TestAuthorizationService(BaseTest):
self.assert_user_has_permission(user_two, "read", "/v1.0/process-groups/hey2")
self.assert_user_has_permission(user_two, "read", "/v1.0/process-groups/hey2:yo")
self.assert_user_has_permission(user_two, "create", "/v1.0/process-groups/hey2:yo")
def _expected_basic_permissions(self) -> list[tuple[str, str]]:
return sorted(
[
("/active-users/*", "create"),
("/connector-proxy/typeahead/*", "read"),
("/debug/version-info", "read"),
("/extensions", "read"),
("/onboarding", "read"),
("/process-groups", "read"),
("/process-instances/find-by-id/*", "read"),
("/process-instances/for-me", "create"),
("/process-instances/report-metadata", "read"),
("/process-instances/reports/*", "create"),
("/process-instances/reports/*", "delete"),
("/process-instances/reports/*", "read"),
("/process-instances/reports/*", "update"),
("/process-models", "read"),
("/processes", "read"),
("/processes/callers/*", "read"),
("/service-tasks", "read"),
("/tasks/*", "create"),
("/tasks/*", "delete"),
("/tasks/*", "read"),
("/tasks/*", "update"),
("/user-groups/for-current-user", "read"),
("/users/exists/by-username", "create"),
("/users/search", "read"),
]
)
def _expected_support_permissions(self) -> list[tuple[str, str]]:
return sorted(
self._expected_basic_permissions()
+ [
("/authentications", "read"),
("/can-run-privileged-script/*", "create"),
("/data-stores/*", "read"),
("/debug/*", "create"),
("/event-error-details/*", "read"),
("/extensions-get-data/*", "read"),
("/extensions/*", "create"),
("/logs/*", "read"),
("/messages", "read"),
("/messages/*", "create"),
("/process-data-file-download/*", "read"),
("/process-data/*", "read"),
("/process-instance-reset/*", "create"),
("/process-instance-resume/*", "create"),
("/process-instance-suspend/*", "create"),
("/process-instance-terminate/*", "create"),
("/process-instances/*", "create"),
("/process-instances/*", "delete"),
("/process-instances/*", "read"),
("/process-instances/*", "update"),
("/send-event/*", "create"),
("/task-assign/*", "create"),
("/task-complete/*", "create"),
("/task-data/*", "update"),
("/task-data/*", "read"),
]
)
def _expected_elevated_permissions(self) -> list[tuple[str, str]]:
return sorted(
self._expected_support_permissions()
+ [
("/secrets/*", "create"),
("/secrets/*", "delete"),
("/secrets/*", "read"),
("/secrets/*", "update"),
]
)