ensure we do not delete user group assignments from regexes w/ burnettk (#690)
Co-authored-by: jasquat <jasquat@users.noreply.github.com>
This commit is contained in:
parent
f524b78368
commit
91ee554543
|
@ -2,6 +2,9 @@ from typing import TYPE_CHECKING
|
||||||
from typing import NewType
|
from typing import NewType
|
||||||
from typing import TypedDict
|
from typing import TypedDict
|
||||||
|
|
||||||
|
from spiffworkflow_backend.models.permission_assignment import PermissionAssignmentModel
|
||||||
|
from spiffworkflow_backend.models.user_group_assignment_waiting import UserGroupAssignmentWaitingModel
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from spiffworkflow_backend.models.process_group import ProcessGroup
|
from spiffworkflow_backend.models.process_group import ProcessGroup
|
||||||
|
|
||||||
|
@ -17,3 +20,26 @@ class ProcessGroupLite(TypedDict):
|
||||||
class ProcessGroupLitesWithCache(TypedDict):
|
class ProcessGroupLitesWithCache(TypedDict):
|
||||||
cache: dict[str, "ProcessGroup"]
|
cache: dict[str, "ProcessGroup"]
|
||||||
process_groups: list[ProcessGroupLite]
|
process_groups: list[ProcessGroupLite]
|
||||||
|
|
||||||
|
|
||||||
|
class UserToGroupDict(TypedDict):
|
||||||
|
username: str
|
||||||
|
group_identifier: str
|
||||||
|
|
||||||
|
|
||||||
|
class AddedPermissionDict(TypedDict):
|
||||||
|
group_identifiers: set[str]
|
||||||
|
permission_assignments: list[PermissionAssignmentModel]
|
||||||
|
user_to_group_identifiers: list[UserToGroupDict]
|
||||||
|
waiting_user_group_assignments: list[UserGroupAssignmentWaitingModel]
|
||||||
|
|
||||||
|
|
||||||
|
class DesiredGroupPermissionDict(TypedDict):
|
||||||
|
actions: list[str]
|
||||||
|
uri: str
|
||||||
|
|
||||||
|
|
||||||
|
class GroupPermissionsDict(TypedDict):
|
||||||
|
users: list[str]
|
||||||
|
name: str
|
||||||
|
permissions: list[DesiredGroupPermissionDict]
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import inspect
|
import inspect
|
||||||
import re
|
import re
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import TypedDict
|
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
@ -16,6 +15,9 @@ from spiffworkflow_backend.exceptions.error import PermissionsFileNotSetError
|
||||||
from spiffworkflow_backend.exceptions.error import UserDoesNotHaveAccessToTaskError
|
from spiffworkflow_backend.exceptions.error import UserDoesNotHaveAccessToTaskError
|
||||||
from spiffworkflow_backend.exceptions.error import UserNotLoggedInError
|
from spiffworkflow_backend.exceptions.error import UserNotLoggedInError
|
||||||
from spiffworkflow_backend.helpers.api_version import V1_API_PATH_PREFIX
|
from spiffworkflow_backend.helpers.api_version import V1_API_PATH_PREFIX
|
||||||
|
from spiffworkflow_backend.interfaces import AddedPermissionDict
|
||||||
|
from spiffworkflow_backend.interfaces import GroupPermissionsDict
|
||||||
|
from spiffworkflow_backend.interfaces import UserToGroupDict
|
||||||
from spiffworkflow_backend.models.db import db
|
from spiffworkflow_backend.models.db import db
|
||||||
from spiffworkflow_backend.models.group import SPIFF_GUEST_GROUP
|
from spiffworkflow_backend.models.group import SPIFF_GUEST_GROUP
|
||||||
from spiffworkflow_backend.models.group import GroupModel
|
from spiffworkflow_backend.models.group import GroupModel
|
||||||
|
@ -75,29 +77,6 @@ PATH_SEGMENTS_FOR_PERMISSION_ALL = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class UserToGroupDict(TypedDict):
|
|
||||||
username: str
|
|
||||||
group_identifier: str
|
|
||||||
|
|
||||||
|
|
||||||
class AddedPermissionDict(TypedDict):
|
|
||||||
group_identifiers: set[str]
|
|
||||||
permission_assignments: list[PermissionAssignmentModel]
|
|
||||||
user_to_group_identifiers: list[UserToGroupDict]
|
|
||||||
waiting_user_group_assignments: list[UserGroupAssignmentWaitingModel]
|
|
||||||
|
|
||||||
|
|
||||||
class DesiredGroupPermissionDict(TypedDict):
|
|
||||||
actions: list[str]
|
|
||||||
uri: str
|
|
||||||
|
|
||||||
|
|
||||||
class GroupPermissionsDict(TypedDict):
|
|
||||||
users: list[str]
|
|
||||||
name: str
|
|
||||||
permissions: list[DesiredGroupPermissionDict]
|
|
||||||
|
|
||||||
|
|
||||||
class AuthorizationService:
|
class AuthorizationService:
|
||||||
"""Determine whether a user has permission to perform their request."""
|
"""Determine whether a user has permission to perform their request."""
|
||||||
|
|
||||||
|
@ -675,6 +654,8 @@ class AuthorizationService:
|
||||||
* only works with PG and PM target macros
|
* only works with PG and PM target macros
|
||||||
"""
|
"""
|
||||||
permissions_to_assign: list[PermissionToAssign] = []
|
permissions_to_assign: list[PermissionToAssign] = []
|
||||||
|
if "," in permission_set:
|
||||||
|
raise Exception(f"Found comma in permission_set. This should be an array instead: {permission_set}")
|
||||||
permissions = permission_set.split(",")
|
permissions = permission_set.split(",")
|
||||||
if permission_set == "all":
|
if permission_set == "all":
|
||||||
permissions = ["create", "read", "update", "delete"]
|
permissions = ["create", "read", "update", "delete"]
|
||||||
|
@ -815,9 +796,13 @@ class AuthorizationService:
|
||||||
"group_identifier": group_identifier,
|
"group_identifier": group_identifier,
|
||||||
}
|
}
|
||||||
user_to_group_identifiers.append(user_to_group_dict)
|
user_to_group_identifiers.append(user_to_group_dict)
|
||||||
wugam = UserService.add_user_to_group_or_add_to_waiting(username, group_identifier)
|
(wugam, new_user_to_group_identifiers) = UserService.add_user_to_group_or_add_to_waiting(
|
||||||
|
username, group_identifier
|
||||||
|
)
|
||||||
if wugam is not None:
|
if wugam is not None:
|
||||||
waiting_user_group_assignments.append(wugam)
|
waiting_user_group_assignments.append(wugam)
|
||||||
|
if new_user_to_group_identifiers is not None:
|
||||||
|
user_to_group_identifiers = user_to_group_identifiers + new_user_to_group_identifiers
|
||||||
unique_user_group_identifiers.add(group_identifier)
|
unique_user_group_identifiers.add(group_identifier)
|
||||||
|
|
||||||
for group in group_permissions:
|
for group in group_permissions:
|
||||||
|
|
|
@ -4,6 +4,7 @@ from typing import Any
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from flask import g
|
from flask import g
|
||||||
from spiffworkflow_backend.exceptions.api_error import ApiError
|
from spiffworkflow_backend.exceptions.api_error import ApiError
|
||||||
|
from spiffworkflow_backend.interfaces import UserToGroupDict
|
||||||
from spiffworkflow_backend.models.db import db
|
from spiffworkflow_backend.models.db import db
|
||||||
from spiffworkflow_backend.models.group import SPIFF_GUEST_GROUP
|
from spiffworkflow_backend.models.group import SPIFF_GUEST_GROUP
|
||||||
from spiffworkflow_backend.models.group import GroupModel
|
from spiffworkflow_backend.models.group import GroupModel
|
||||||
|
@ -126,7 +127,9 @@ class UserService:
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_waiting_group_assignment(cls, username: str, group: GroupModel) -> UserGroupAssignmentWaitingModel:
|
def add_waiting_group_assignment(
|
||||||
|
cls, username: str, group: GroupModel
|
||||||
|
) -> tuple[UserGroupAssignmentWaitingModel, list[UserToGroupDict]]:
|
||||||
"""Only called from set-permissions."""
|
"""Only called from set-permissions."""
|
||||||
wugam: UserGroupAssignmentWaitingModel | None = (
|
wugam: UserGroupAssignmentWaitingModel | None = (
|
||||||
UserGroupAssignmentWaitingModel().query.filter_by(username=username).filter_by(group_id=group.id).first()
|
UserGroupAssignmentWaitingModel().query.filter_by(username=username).filter_by(group_id=group.id).first()
|
||||||
|
@ -138,12 +141,14 @@ class UserService:
|
||||||
|
|
||||||
# backfill existing users
|
# backfill existing users
|
||||||
wildcard_pattern = wugam.pattern_from_wildcard_username()
|
wildcard_pattern = wugam.pattern_from_wildcard_username()
|
||||||
|
user_to_group_identifiers: list[UserToGroupDict] = []
|
||||||
if wildcard_pattern is not None:
|
if wildcard_pattern is not None:
|
||||||
users = UserModel.query.filter(UserModel.username.regexp_match(wildcard_pattern)) # type: ignore
|
users = UserModel.query.filter(UserModel.username.regexp_match(wildcard_pattern)) # type: ignore
|
||||||
for user in users:
|
for user in users:
|
||||||
cls.add_user_to_group(user, group)
|
cls.add_user_to_group(user, group)
|
||||||
|
user_to_group_identifiers.append({"username": user.username, "group_identifier": group.identifier})
|
||||||
|
|
||||||
return wugam
|
return (wugam, user_to_group_identifiers)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def apply_waiting_group_assignments(cls, user: UserModel) -> None:
|
def apply_waiting_group_assignments(cls, user: UserModel) -> None:
|
||||||
|
@ -236,14 +241,14 @@ class UserService:
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_user_to_group_or_add_to_waiting(
|
def add_user_to_group_or_add_to_waiting(
|
||||||
cls, username: str | UserModel, group_identifier: str
|
cls, username: str | UserModel, group_identifier: str
|
||||||
) -> UserGroupAssignmentWaitingModel | None:
|
) -> tuple[UserGroupAssignmentWaitingModel | None, list[UserToGroupDict] | None]:
|
||||||
group = cls.find_or_create_group(group_identifier)
|
group = cls.find_or_create_group(group_identifier)
|
||||||
user = UserModel.query.filter_by(username=username).first()
|
user = UserModel.query.filter_by(username=username).first()
|
||||||
if user:
|
if user:
|
||||||
cls.add_user_to_group(user, group)
|
cls.add_user_to_group(user, group)
|
||||||
else:
|
else:
|
||||||
return cls.add_waiting_group_assignment(username, group)
|
return cls.add_waiting_group_assignment(username, group)
|
||||||
return None
|
return (None, None)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_user_to_group_by_group_identifier(cls, user: UserModel, group_identifier: str) -> None:
|
def add_user_to_group_by_group_identifier(cls, user: UserModel, group_identifier: str) -> None:
|
||||||
|
|
|
@ -557,9 +557,20 @@ class TestAuthorizationService(BaseTest):
|
||||||
waiting_assignments = UserGroupAssignmentWaitingModel.query.filter_by(username=user_regex).all()
|
waiting_assignments = UserGroupAssignmentWaitingModel.query.filter_by(username=user_regex).all()
|
||||||
assert len(waiting_assignments) == 1
|
assert len(waiting_assignments) == 1
|
||||||
assert waiting_assignments[0].username == user_regex
|
assert waiting_assignments[0].username == user_regex
|
||||||
|
assert len(user.groups) == 2
|
||||||
|
assert "group_one" in [g.identifier for g in user.groups]
|
||||||
self.assert_user_has_permission(user, "read", "/v1.0/process-groups/hey")
|
self.assert_user_has_permission(user, "read", "/v1.0/process-groups/hey")
|
||||||
self.assert_user_has_permission(user_two, "read", "/v1.0/process-groups/hey", expected_result=False)
|
self.assert_user_has_permission(user_two, "read", "/v1.0/process-groups/hey", expected_result=False)
|
||||||
|
|
||||||
|
# run again to ensure the user does NOT lose permission when refreshing permissions again
|
||||||
|
AuthorizationService.refresh_permissions(group_info)
|
||||||
|
waiting_assignments = UserGroupAssignmentWaitingModel.query.filter_by(username=user_regex).all()
|
||||||
|
assert len(waiting_assignments) == 1
|
||||||
|
assert waiting_assignments[0].username == user_regex
|
||||||
|
assert len(user.groups) == 2
|
||||||
|
assert "group_one" in [g.identifier for g in user.groups]
|
||||||
|
self.assert_user_has_permission(user, "read", "/v1.0/process-groups/hey")
|
||||||
|
|
||||||
user_three_dict = {
|
user_three_dict = {
|
||||||
"username": "user_three",
|
"username": "user_three",
|
||||||
"email": "user_three@example.com",
|
"email": "user_three@example.com",
|
||||||
|
@ -621,4 +632,3 @@ class TestAuthorizationService(BaseTest):
|
||||||
# test it can be permitted again
|
# test it can be permitted again
|
||||||
AuthorizationService.add_permission_from_uri_or_macro(user_group.identifier, "read", "PG:hey:yo")
|
AuthorizationService.add_permission_from_uri_or_macro(user_group.identifier, "read", "PG:hey:yo")
|
||||||
self.assert_user_has_permission(user, "read", "/v1.0/process-groups/hey:yo", expected_result=True)
|
self.assert_user_has_permission(user, "read", "/v1.0/process-groups/hey:yo", expected_result=True)
|
||||||
self.assert_user_has_permission(user, "read", "/v1.0/process-groups/hey:yo:me", expected_result=True)
|
|
||||||
|
|
Loading…
Reference in New Issue