diff --git a/spiffworkflow-backend/poetry.lock b/spiffworkflow-backend/poetry.lock
index ee51f2ace..a67570020 100644
--- a/spiffworkflow-backend/poetry.lock
+++ b/spiffworkflow-backend/poetry.lock
@@ -1651,44 +1651,44 @@ yaml = ["pyyaml"]
[[package]]
name = "mypy"
-version = "1.10.1"
+version = "1.11.1"
description = "Optional static typing for Python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "mypy-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e36f229acfe250dc660790840916eb49726c928e8ce10fbdf90715090fe4ae02"},
- {file = "mypy-1.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:51a46974340baaa4145363b9e051812a2446cf583dfaeba124af966fa44593f7"},
- {file = "mypy-1.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:901c89c2d67bba57aaaca91ccdb659aa3a312de67f23b9dfb059727cce2e2e0a"},
- {file = "mypy-1.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0cd62192a4a32b77ceb31272d9e74d23cd88c8060c34d1d3622db3267679a5d9"},
- {file = "mypy-1.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:a2cbc68cb9e943ac0814c13e2452d2046c2f2b23ff0278e26599224cf164e78d"},
- {file = "mypy-1.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bd6f629b67bb43dc0d9211ee98b96d8dabc97b1ad38b9b25f5e4c4d7569a0c6a"},
- {file = "mypy-1.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1bbb3a6f5ff319d2b9d40b4080d46cd639abe3516d5a62c070cf0114a457d84"},
- {file = "mypy-1.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8edd4e9bbbc9d7b79502eb9592cab808585516ae1bcc1446eb9122656c6066f"},
- {file = "mypy-1.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6166a88b15f1759f94a46fa474c7b1b05d134b1b61fca627dd7335454cc9aa6b"},
- {file = "mypy-1.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bb9cd11c01c8606a9d0b83ffa91d0b236a0e91bc4126d9ba9ce62906ada868e"},
- {file = "mypy-1.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d8681909f7b44d0b7b86e653ca152d6dff0eb5eb41694e163c6092124f8246d7"},
- {file = "mypy-1.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:378c03f53f10bbdd55ca94e46ec3ba255279706a6aacaecac52ad248f98205d3"},
- {file = "mypy-1.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bacf8f3a3d7d849f40ca6caea5c055122efe70e81480c8328ad29c55c69e93e"},
- {file = "mypy-1.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:701b5f71413f1e9855566a34d6e9d12624e9e0a8818a5704d74d6b0402e66c04"},
- {file = "mypy-1.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c4c2992f6ea46ff7fce0072642cfb62af7a2484efe69017ed8b095f7b39ef31"},
- {file = "mypy-1.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:604282c886497645ffb87b8f35a57ec773a4a2721161e709a4422c1636ddde5c"},
- {file = "mypy-1.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37fd87cab83f09842653f08de066ee68f1182b9b5282e4634cdb4b407266bade"},
- {file = "mypy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8addf6313777dbb92e9564c5d32ec122bf2c6c39d683ea64de6a1fd98b90fe37"},
- {file = "mypy-1.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5cc3ca0a244eb9a5249c7c583ad9a7e881aa5d7b73c35652296ddcdb33b2b9c7"},
- {file = "mypy-1.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:1b3a2ffce52cc4dbaeee4df762f20a2905aa171ef157b82192f2e2f368eec05d"},
- {file = "mypy-1.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe85ed6836165d52ae8b88f99527d3d1b2362e0cb90b005409b8bed90e9059b3"},
- {file = "mypy-1.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2ae450d60d7d020d67ab440c6e3fae375809988119817214440033f26ddf7bf"},
- {file = "mypy-1.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6be84c06e6abd72f960ba9a71561c14137a583093ffcf9bbfaf5e613d63fa531"},
- {file = "mypy-1.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2189ff1e39db399f08205e22a797383613ce1cb0cb3b13d8bcf0170e45b96cc3"},
- {file = "mypy-1.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:97a131ee36ac37ce9581f4220311247ab6cba896b4395b9c87af0675a13a755f"},
- {file = "mypy-1.10.1-py3-none-any.whl", hash = "sha256:71d8ac0b906354ebda8ef1673e5fde785936ac1f29ff6987c7483cfbd5a4235a"},
- {file = "mypy-1.10.1.tar.gz", hash = "sha256:1f8f492d7db9e3593ef42d4f115f04e556130f2819ad33ab84551403e97dd4c0"},
+ {file = "mypy-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a32fc80b63de4b5b3e65f4be82b4cfa362a46702672aa6a0f443b4689af7008c"},
+ {file = "mypy-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1952f5ea8a5a959b05ed5f16452fddadbaae48b5d39235ab4c3fc444d5fd411"},
+ {file = "mypy-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1e30dc3bfa4e157e53c1d17a0dad20f89dc433393e7702b813c10e200843b03"},
+ {file = "mypy-1.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c63350af88f43a66d3dfeeeb8d77af34a4f07d760b9eb3a8697f0386c7590b4"},
+ {file = "mypy-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:a831671bad47186603872a3abc19634f3011d7f83b083762c942442d51c58d58"},
+ {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"},
+ {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"},
+ {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"},
+ {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"},
+ {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"},
+ {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"},
+ {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"},
+ {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"},
+ {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"},
+ {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"},
+ {file = "mypy-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:749fd3213916f1751fff995fccf20c6195cae941dc968f3aaadf9bb4e430e5a2"},
+ {file = "mypy-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b639dce63a0b19085213ec5fdd8cffd1d81988f47a2dec7100e93564f3e8fb3b"},
+ {file = "mypy-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c956b49c5d865394d62941b109728c5c596a415e9c5b2be663dd26a1ff07bc0"},
+ {file = "mypy-1.11.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45df906e8b6804ef4b666af29a87ad9f5921aad091c79cc38e12198e220beabd"},
+ {file = "mypy-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:d44be7551689d9d47b7abc27c71257adfdb53f03880841a5db15ddb22dc63edb"},
+ {file = "mypy-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2684d3f693073ab89d76da8e3921883019ea8a3ec20fa5d8ecca6a2db4c54bbe"},
+ {file = "mypy-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79c07eb282cb457473add5052b63925e5cc97dfab9812ee65a7c7ab5e3cb551c"},
+ {file = "mypy-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11965c2f571ded6239977b14deebd3f4c3abd9a92398712d6da3a772974fad69"},
+ {file = "mypy-1.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a2b43895a0f8154df6519706d9bca8280cda52d3d9d1514b2d9c3e26792a0b74"},
+ {file = "mypy-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:1a81cf05975fd61aec5ae16501a091cfb9f605dc3e3c878c0da32f250b74760b"},
+ {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"},
+ {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"},
]
[package.dependencies]
mypy-extensions = ">=1.0.0"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
-typing-extensions = ">=4.1.0"
+typing-extensions = ">=4.6.0"
[package.extras]
dmypy = ["psutil (>=4.0)"]
diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py
index 7b7de044c..a11a5361a 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py
@@ -468,6 +468,7 @@ class AuthorizationService:
desired_group_identifiers = None
if current_app.config["SPIFFWORKFLOW_BACKEND_OPEN_ID_IS_AUTHORITY_FOR_USER_GROUPS"]:
+ desired_group_identifiers = []
if "groups" in user_info:
desired_group_identifiers = user_info["groups"]
@@ -502,7 +503,7 @@ class AuthorizationService:
if desired_group_identifiers is not None:
if not isinstance(desired_group_identifiers, list):
- current_app.logger.error(
+ current_app.logger.error( # type: ignore
f"Invalid groups property in token: {desired_group_identifiers}.If groups is specified, it must be a list"
)
else:
diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/user_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/user_service.py
index 72fa6ecca..8cc4837e4 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/services/user_service.py
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/user_service.py
@@ -4,6 +4,7 @@ from typing import Any
from flask import current_app
from flask import g
from sqlalchemy import and_
+from sqlalchemy import or_
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.interfaces import UserToGroupDict
@@ -183,7 +184,22 @@ class UserService:
def update_human_task_assignments_for_user(cls, user: UserModel, new_group_ids: set[int], old_group_ids: set[int]) -> None:
current_assignments = HumanTaskUserModel.query.filter_by(user_id=user.id).all()
current_human_task_ids = [ca.human_task_id for ca in current_assignments]
- human_tasks = HumanTaskModel.query.filter(HumanTaskModel.lane_assignment_id.in_(new_group_ids)).all() # type: ignore
+ human_tasks = (
+ HumanTaskModel.query.outerjoin(HumanTaskUserModel)
+ .filter(
+ HumanTaskModel.lane_assignment_id.in_(new_group_ids), # type: ignore
+ HumanTaskModel.completed == False, # noqa: E712
+ or_(
+ and_(
+ HumanTaskUserModel.user_id != user.id,
+ HumanTaskUserModel.added_by == HumanTaskUserAddedBy.lane_assignment.value,
+ ),
+ HumanTaskUserModel.user_id == None, # noqa: E711
+ ),
+ )
+ .all()
+ )
+
for human_task in human_tasks:
if human_task.id not in current_human_task_ids:
human_task_user = HumanTaskUserModel(
@@ -196,6 +212,7 @@ class UserService:
HumanTaskUserModel.user_id == user.id,
HumanTaskUserModel.added_by == HumanTaskUserAddedBy.lane_assignment.value,
HumanTaskModel.lane_assignment_id.in_(old_group_ids), # type: ignore
+ HumanTaskModel.completed == False, # noqa: E712
)
.all()
)
diff --git a/spiffworkflow-backend/tests/data/model_with_lanes/lanes_with_extra_tasks.bpmn b/spiffworkflow-backend/tests/data/model_with_lanes/lanes_with_extra_tasks.bpmn
new file mode 100644
index 000000000..cec189025
--- /dev/null
+++ b/spiffworkflow-backend/tests/data/model_with_lanes/lanes_with_extra_tasks.bpmn
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+ StartEvent_1
+ initiator_one
+ Event_06f4e68
+ initiator_two
+
+
+ finance_approval
+ finance_confirmation
+
+
+
+ Flow_1tbyols
+
+
+
+
+
+ This is for the initiator user
+
+ Flow_1tbyols
+ Flow_16ppta1
+
+
+ Flow_0x92f7d
+
+
+
+
+ This is initiator again
+
+
+ Flow_1q487da
+ Flow_0x92f7d
+
+
+
+ This is for a Finance Team user
+
+ Flow_16ppta1
+ Flow_0rug230
+
+
+
+
+ Flow_0rug230
+ Flow_1q487da
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spiffworkflow-backend/tests/data/model_with_lanes/lanes_with_lane_owners.bpmn b/spiffworkflow-backend/tests/data/model_with_lanes/lanes_with_lane_owners.bpmn
new file mode 100644
index 000000000..f70a288d9
--- /dev/null
+++ b/spiffworkflow-backend/tests/data/model_with_lanes/lanes_with_lane_owners.bpmn
@@ -0,0 +1,266 @@
+
+
+
+
+
+
+
+
+ Activity_GetMoreData
+ Gateway_1chm5qn
+ Event_09rkmlh
+ Event_0t6257a
+ Activity_Review
+ Event_12mvls1
+ Event_0er19np
+ Gateway_1y0gjbb
+ Gateway_1v7vc1z
+
+
+ StartEvent_1
+ Activity_GetData
+ Activity_GetReviewers
+ Gateway_0l5x84t
+
+
+
+
+ Flow_1msk5h9
+
+
+
+
+
+
+
+
+ Flow_1loqmjv
+ Flow_0rydrx7
+ Flow_0c8hg1n
+
+
+ Flow_1msk5h9
+ Flow_1loqmjv
+ #lane_owners = {"Reviewer": ["core.contributor@status.im", "madhurya@sartography.com"]}
+
+current_approver_role = "/Infra"
+sme_group_members = get_group_members(current_approver_role)
+lane_owners = {"/Infra": sme_group_members}
+
+
+
+
+
+
+
+
+
+ data = {"item_id": itemId, "item_desc": itemName}
+ User entered data : {{data}}
+
+ Flow_0c8hg1n
+ Flow_1xtmg0t
+
+
+ Flow_10rvmdc
+ Flow_1efovhl
+ Flow_19tmelr
+ Flow_0l3zw86
+
+
+
+ Flow_0q4r19g
+
+
+ approval == "Approve"
+
+
+ Flow_1efovhl
+ Flow_05xruvm
+
+
+ approval == "Reject"
+
+
+
+ ## Approve
+### Data
+{{ data }}
+
+
+
+
+
+ Flow_1xtmg0t
+ Flow_10rvmdc
+
+
+ Flow_0l3zw86
+ Flow_12ty548
+
+
+
+ Flow_19tmelr
+ Flow_0zg89xt
+
+
+
+
+
+
+ Flow_05xruvm
+ Flow_0zg89xt
+ Flow_12ty548
+ Flow_1ohb8ht
+
+
+ Flow_1ohb8ht
+ Flow_042vla2
+ Flow_0q4r19g
+
+
+ Flow_042vla2
+ Flow_0rydrx7
+
+
+ approval == "Needmoreinfo"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py b/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py
index 31ca36386..7e41c9d2e 100644
--- a/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py
+++ b/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py
@@ -603,14 +603,17 @@ class BaseTest:
processor: ProcessInstanceProcessor,
execution_mode: str | None = None,
data: dict | None = None,
+ user: UserModel | None = None,
) -> None:
user_task = processor.get_ready_user_tasks()[0]
human_task = HumanTaskModel.query.filter_by(task_guid=str(user_task.id)).first()
+ if user is None:
+ user = processor.process_instance_model.process_initiator
ProcessInstanceService.complete_form_task(
processor=processor,
spiff_task=user_task,
data=data or {},
- user=processor.process_instance_model.process_initiator,
+ user=user,
human_task=human_task,
execution_mode=execution_mode,
)
diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py
index c13536cf9..7da253b31 100644
--- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py
+++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py
@@ -3,6 +3,7 @@ from flask import Flask
from flask.testing import FlaskClient
from spiffworkflow_backend.exceptions.error import InvalidPermissionError
from spiffworkflow_backend.models.group import GroupModel
+from spiffworkflow_backend.models.human_task import HumanTaskModel
from spiffworkflow_backend.models.human_task_user import HumanTaskUserModel
from spiffworkflow_backend.models.user_group_assignment_waiting import UserGroupAssignmentWaitingModel
from spiffworkflow_backend.services.authorization_service import AuthorizationService
@@ -733,3 +734,128 @@ class TestAuthorizationService(BaseTest):
assert len(human_task_users) == 0
tmp_group = GroupModel.query.filter_by(identifier="tmp_group").first()
assert tmp_group is not None
+
+ ##### run test again but this time without the groups key at all to remove groups
+ user_two = AuthorizationService.create_user_from_sign_in(
+ {
+ "username": "user_two",
+ "sub": "user_two",
+ "iss": "https://test.stuff",
+ "email": "user_two@example.com",
+ "groups": ["Finance Team", "tmp_group"],
+ }
+ )
+ assert len(user_two.groups) == 3
+ assert sorted([g.identifier for g in user_two.groups]) == sorted(["Finance Team", "everybody", "tmp_group"])
+ tmp_group = GroupModel.query.filter_by(identifier="tmp_group").first()
+ assert tmp_group is not None
+ assert tmp_group.source_is_open_id is True
+ human_task_users = HumanTaskUserModel.query.filter_by(user_id=user_two.id).all()
+ assert len(human_task_users) == 1
+ user_two = AuthorizationService.create_user_from_sign_in(
+ {
+ "username": "user_two",
+ "sub": "user_two",
+ "iss": "https://test.stuff",
+ "email": "user_two@example.com",
+ }
+ )
+ assert len(user_two.groups) == 1
+ assert user_two.groups[0].identifier == "everybody"
+ human_task_users = HumanTaskUserModel.query.filter_by(user_id=user_two.id).all()
+ assert len(human_task_users) == 0
+ tmp_group = GroupModel.query.filter_by(identifier="tmp_group").first()
+ assert tmp_group is not None
+
+ def test_user_can_complete_all_tasks_after_assignment(
+ self,
+ app: Flask,
+ client: FlaskClient,
+ with_db_and_bpmn_file_cleanup: None,
+ ) -> None:
+ process_model = load_test_spec(
+ process_model_id="test_group/model_with_lanes",
+ process_model_source_directory="model_with_lanes",
+ bpmn_file_name="lanes_with_extra_tasks.bpmn",
+ )
+ process_instance = self.create_process_instance_from_process_model(process_model)
+ user_one = self.find_or_create_user(username="user_one")
+ user_group = UserService.find_or_create_group("Finance Team")
+ UserService.add_user_to_group(user_one, user_group)
+ processor = ProcessInstanceProcessor(process_instance)
+ processor.do_engine_steps(save=True)
+ self.complete_next_manual_task(processor, data={"itemId": "item1", "itemName": "Item One"})
+
+ with self.app_config_mock(app, "SPIFFWORKFLOW_BACKEND_OPEN_ID_IS_AUTHORITY_FOR_USER_GROUPS", True):
+ user_two = AuthorizationService.create_user_from_sign_in(
+ {
+ "username": "user_two",
+ "sub": "user_two",
+ "iss": "https://test.stuff",
+ "email": "user_two@example.com",
+ "groups": ["Finance Team"],
+ }
+ )
+ human_task_users = (
+ HumanTaskUserModel.query.filter_by(user_id=user_two.id)
+ .join(HumanTaskModel)
+ .filter(HumanTaskModel.completed == False) # noqa: E712
+ .all()
+ )
+ assert len(human_task_users) == 1
+ self.complete_next_manual_task(processor, user=user_two)
+ human_task_users = (
+ HumanTaskUserModel.query.filter_by(user_id=user_two.id)
+ .join(HumanTaskModel)
+ .filter(HumanTaskModel.completed == False) # noqa: E712
+ .all()
+ )
+ assert len(human_task_users) == 1
+ self.complete_next_manual_task(processor, user=user_two)
+
+ user_two = AuthorizationService.create_user_from_sign_in(
+ {
+ "username": "user_two",
+ "sub": "user_two",
+ "iss": "https://test.stuff",
+ "email": "user_two@example.com",
+ }
+ )
+ human_task_count = (
+ HumanTaskUserModel.query.join(HumanTaskModel)
+ .filter(HumanTaskUserModel.user_id == user_two.id, HumanTaskModel.completed == True) # noqa: E712
+ .count()
+ )
+ assert human_task_count == 2
+
+ def test_user_can_is_not_assigned_task_if_lane_owners_in_use(
+ self,
+ app: Flask,
+ client: FlaskClient,
+ with_db_and_bpmn_file_cleanup: None,
+ ) -> None:
+ process_model = load_test_spec(
+ process_model_id="test_group/model_with_lanes",
+ process_model_source_directory="model_with_lanes",
+ bpmn_file_name="lanes_with_lane_owners.bpmn",
+ )
+ process_instance = self.create_process_instance_from_process_model(process_model)
+ user_one = self.find_or_create_user(username="user_one")
+ user_group = UserService.find_or_create_group("/Infra")
+ UserService.add_user_to_group(user_one, user_group)
+ processor = ProcessInstanceProcessor(process_instance)
+ processor.do_engine_steps(save=True)
+ self.complete_next_manual_task(processor, data={"itemId": "item1", "itemName": "Item One"})
+
+ with self.app_config_mock(app, "SPIFFWORKFLOW_BACKEND_OPEN_ID_IS_AUTHORITY_FOR_USER_GROUPS", True):
+ user_two = AuthorizationService.create_user_from_sign_in(
+ {
+ "username": "user_two",
+ "sub": "user_two",
+ "iss": "https://test.stuff",
+ "email": "user_two@example.com",
+ "groups": ["/Infra"],
+ }
+ )
+ human_task_users = HumanTaskUserModel.query.filter_by(user_id=user_two.id).all()
+ assert len(human_task_users) == 0