mirror of
https://github.com/langgenius/dify.git
synced 2026-04-27 11:06:46 +08:00
fix(api): validate workflow mentions against tenant members
This commit is contained in:
parent
3288f5e100
commit
977af3399e
@ -28,8 +28,10 @@ class WorkflowCommentService:
|
|||||||
raise ValueError("Comment content cannot exceed 1000 characters")
|
raise ValueError("Comment content cannot exceed 1000 characters")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _filter_valid_mentioned_user_ids(mentioned_user_ids: Sequence[str]) -> list[str]:
|
def _filter_valid_mentioned_user_ids(
|
||||||
"""Return deduplicated UUID user IDs in the order provided."""
|
mentioned_user_ids: Sequence[str], *, session: Session, tenant_id: str
|
||||||
|
) -> list[str]:
|
||||||
|
"""Return deduplicated UUID user IDs that belong to the tenant, preserving input order."""
|
||||||
unique_user_ids: list[str] = []
|
unique_user_ids: list[str] = []
|
||||||
seen: set[str] = set()
|
seen: set[str] = set()
|
||||||
for user_id in mentioned_user_ids:
|
for user_id in mentioned_user_ids:
|
||||||
@ -41,7 +43,20 @@ class WorkflowCommentService:
|
|||||||
continue
|
continue
|
||||||
seen.add(user_id)
|
seen.add(user_id)
|
||||||
unique_user_ids.append(user_id)
|
unique_user_ids.append(user_id)
|
||||||
return unique_user_ids
|
if not unique_user_ids:
|
||||||
|
return []
|
||||||
|
|
||||||
|
tenant_member_ids = {
|
||||||
|
str(account_id)
|
||||||
|
for account_id in session.scalars(
|
||||||
|
select(TenantAccountJoin.account_id).where(
|
||||||
|
TenantAccountJoin.tenant_id == tenant_id,
|
||||||
|
TenantAccountJoin.account_id.in_(unique_user_ids),
|
||||||
|
)
|
||||||
|
).all()
|
||||||
|
}
|
||||||
|
|
||||||
|
return [user_id for user_id in unique_user_ids if user_id in tenant_member_ids]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _format_comment_excerpt(content: str, max_length: int = 200) -> str:
|
def _format_comment_excerpt(content: str, max_length: int = 200) -> str:
|
||||||
@ -220,7 +235,11 @@ class WorkflowCommentService:
|
|||||||
session.flush() # Get the comment ID for mentions
|
session.flush() # Get the comment ID for mentions
|
||||||
|
|
||||||
# Create mentions if specified
|
# Create mentions if specified
|
||||||
mentioned_user_ids = WorkflowCommentService._filter_valid_mentioned_user_ids(mentioned_user_ids or [])
|
mentioned_user_ids = WorkflowCommentService._filter_valid_mentioned_user_ids(
|
||||||
|
mentioned_user_ids or [],
|
||||||
|
session=session,
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
)
|
||||||
for user_id in mentioned_user_ids:
|
for user_id in mentioned_user_ids:
|
||||||
mention = WorkflowCommentMention(
|
mention = WorkflowCommentMention(
|
||||||
comment_id=comment.id,
|
comment_id=comment.id,
|
||||||
@ -293,7 +312,11 @@ class WorkflowCommentService:
|
|||||||
session.delete(mention)
|
session.delete(mention)
|
||||||
|
|
||||||
# Add new mentions
|
# Add new mentions
|
||||||
mentioned_user_ids = WorkflowCommentService._filter_valid_mentioned_user_ids(mentioned_user_ids or [])
|
mentioned_user_ids = WorkflowCommentService._filter_valid_mentioned_user_ids(
|
||||||
|
mentioned_user_ids or [],
|
||||||
|
session=session,
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
)
|
||||||
new_mentioned_user_ids = [
|
new_mentioned_user_ids = [
|
||||||
user_id for user_id in mentioned_user_ids if user_id not in existing_mentioned_user_ids
|
user_id for user_id in mentioned_user_ids if user_id not in existing_mentioned_user_ids
|
||||||
]
|
]
|
||||||
@ -380,7 +403,11 @@ class WorkflowCommentService:
|
|||||||
session.flush() # Get the reply ID for mentions
|
session.flush() # Get the reply ID for mentions
|
||||||
|
|
||||||
# Create mentions if specified
|
# Create mentions if specified
|
||||||
mentioned_user_ids = WorkflowCommentService._filter_valid_mentioned_user_ids(mentioned_user_ids or [])
|
mentioned_user_ids = WorkflowCommentService._filter_valid_mentioned_user_ids(
|
||||||
|
mentioned_user_ids or [],
|
||||||
|
session=session,
|
||||||
|
tenant_id=comment.tenant_id,
|
||||||
|
)
|
||||||
for user_id in mentioned_user_ids:
|
for user_id in mentioned_user_ids:
|
||||||
# Create mention linking to specific reply
|
# Create mention linking to specific reply
|
||||||
mention = WorkflowCommentMention(comment_id=comment_id, reply_id=reply.id, mentioned_user_id=user_id)
|
mention = WorkflowCommentMention(comment_id=comment_id, reply_id=reply.id, mentioned_user_id=user_id)
|
||||||
@ -425,7 +452,15 @@ class WorkflowCommentService:
|
|||||||
session.delete(mention)
|
session.delete(mention)
|
||||||
|
|
||||||
# Add mentions
|
# Add mentions
|
||||||
mentioned_user_ids = WorkflowCommentService._filter_valid_mentioned_user_ids(mentioned_user_ids or [])
|
raw_mentioned_user_ids = mentioned_user_ids or []
|
||||||
|
comment = session.get(WorkflowComment, reply.comment_id)
|
||||||
|
mentioned_user_ids = []
|
||||||
|
if comment:
|
||||||
|
mentioned_user_ids = WorkflowCommentService._filter_valid_mentioned_user_ids(
|
||||||
|
raw_mentioned_user_ids,
|
||||||
|
session=session,
|
||||||
|
tenant_id=comment.tenant_id,
|
||||||
|
)
|
||||||
new_mentioned_user_ids = [
|
new_mentioned_user_ids = [
|
||||||
user_id for user_id in mentioned_user_ids if user_id not in existing_mentioned_user_ids
|
user_id for user_id in mentioned_user_ids if user_id not in existing_mentioned_user_ids
|
||||||
]
|
]
|
||||||
@ -436,7 +471,6 @@ class WorkflowCommentService:
|
|||||||
session.add(mention)
|
session.add(mention)
|
||||||
|
|
||||||
mention_email_payloads: list[dict[str, str]] = []
|
mention_email_payloads: list[dict[str, str]] = []
|
||||||
comment = session.get(WorkflowComment, reply.comment_id)
|
|
||||||
if comment:
|
if comment:
|
||||||
mention_email_payloads = WorkflowCommentService._build_mention_email_payloads(
|
mention_email_payloads = WorkflowCommentService._build_mention_email_payloads(
|
||||||
session=session,
|
session=session,
|
||||||
|
|||||||
@ -39,20 +39,28 @@ class TestWorkflowCommentService:
|
|||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
WorkflowCommentService._validate_content("a" * 1001)
|
WorkflowCommentService._validate_content("a" * 1001)
|
||||||
|
|
||||||
def test_filter_valid_mentioned_user_ids_deduplicates_and_preserves_order(self) -> None:
|
def test_filter_valid_mentioned_user_ids_filters_by_tenant_and_preserves_order(self, mock_session: Mock) -> None:
|
||||||
|
tenant_member_1 = "123e4567-e89b-12d3-a456-426614174000"
|
||||||
|
tenant_member_2 = "123e4567-e89b-12d3-a456-426614174002"
|
||||||
|
non_tenant_member = "123e4567-e89b-12d3-a456-426614174001"
|
||||||
|
mock_session.scalars.return_value = _mock_scalars([tenant_member_1, tenant_member_2])
|
||||||
|
|
||||||
result = WorkflowCommentService._filter_valid_mentioned_user_ids(
|
result = WorkflowCommentService._filter_valid_mentioned_user_ids(
|
||||||
[
|
[
|
||||||
"123e4567-e89b-12d3-a456-426614174000",
|
tenant_member_1,
|
||||||
"",
|
"",
|
||||||
123, # type: ignore[list-item]
|
123, # type: ignore[list-item]
|
||||||
"123e4567-e89b-12d3-a456-426614174000",
|
tenant_member_1,
|
||||||
"123e4567-e89b-12d3-a456-426614174001",
|
non_tenant_member,
|
||||||
]
|
tenant_member_2,
|
||||||
|
],
|
||||||
|
session=mock_session,
|
||||||
|
tenant_id="tenant-1",
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result == [
|
assert result == [
|
||||||
"123e4567-e89b-12d3-a456-426614174000",
|
tenant_member_1,
|
||||||
"123e4567-e89b-12d3-a456-426614174001",
|
tenant_member_2,
|
||||||
]
|
]
|
||||||
|
|
||||||
def test_format_comment_excerpt_handles_short_and_long_limits(self) -> None:
|
def test_format_comment_excerpt_handles_short_and_long_limits(self) -> None:
|
||||||
@ -140,7 +148,7 @@ class TestWorkflowCommentService:
|
|||||||
with (
|
with (
|
||||||
patch.object(service_module, "WorkflowComment", return_value=comment),
|
patch.object(service_module, "WorkflowComment", return_value=comment),
|
||||||
patch.object(service_module, "WorkflowCommentMention", return_value=Mock()),
|
patch.object(service_module, "WorkflowCommentMention", return_value=Mock()),
|
||||||
patch.object(service_module, "uuid_value", side_effect=[True, False]),
|
patch.object(WorkflowCommentService, "_filter_valid_mentioned_user_ids", return_value=["user-2"]),
|
||||||
):
|
):
|
||||||
result = WorkflowCommentService.create_comment(
|
result = WorkflowCommentService.create_comment(
|
||||||
tenant_id="tenant-1",
|
tenant_id="tenant-1",
|
||||||
@ -192,7 +200,7 @@ class TestWorkflowCommentService:
|
|||||||
existing_mentions = [Mock(), Mock()]
|
existing_mentions = [Mock(), Mock()]
|
||||||
mock_session.scalars.return_value = _mock_scalars(existing_mentions)
|
mock_session.scalars.return_value = _mock_scalars(existing_mentions)
|
||||||
|
|
||||||
with patch.object(service_module, "uuid_value", side_effect=[True, False]):
|
with patch.object(WorkflowCommentService, "_filter_valid_mentioned_user_ids", return_value=["user-2"]):
|
||||||
result = WorkflowCommentService.update_comment(
|
result = WorkflowCommentService.update_comment(
|
||||||
tenant_id="tenant-1",
|
tenant_id="tenant-1",
|
||||||
app_id="app-1",
|
app_id="app-1",
|
||||||
@ -218,7 +226,11 @@ class TestWorkflowCommentService:
|
|||||||
mock_session.scalars.return_value = _mock_scalars([existing_mention])
|
mock_session.scalars.return_value = _mock_scalars([existing_mention])
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch.object(service_module, "uuid_value", side_effect=[True, True]),
|
patch.object(
|
||||||
|
WorkflowCommentService,
|
||||||
|
"_filter_valid_mentioned_user_ids",
|
||||||
|
return_value=["user-2", "user-3"],
|
||||||
|
),
|
||||||
patch.object(
|
patch.object(
|
||||||
WorkflowCommentService,
|
WorkflowCommentService,
|
||||||
"_build_mention_email_payloads",
|
"_build_mention_email_payloads",
|
||||||
@ -369,7 +381,7 @@ class TestWorkflowCommentService:
|
|||||||
with (
|
with (
|
||||||
patch.object(service_module, "WorkflowCommentReply", return_value=reply),
|
patch.object(service_module, "WorkflowCommentReply", return_value=reply),
|
||||||
patch.object(service_module, "WorkflowCommentMention", return_value=Mock()),
|
patch.object(service_module, "WorkflowCommentMention", return_value=Mock()),
|
||||||
patch.object(service_module, "uuid_value", side_effect=[True, False]),
|
patch.object(WorkflowCommentService, "_filter_valid_mentioned_user_ids", return_value=["user-2"]),
|
||||||
):
|
):
|
||||||
result = WorkflowCommentService.create_reply(
|
result = WorkflowCommentService.create_reply(
|
||||||
comment_id="comment-1",
|
comment_id="comment-1",
|
||||||
@ -405,7 +417,7 @@ class TestWorkflowCommentService:
|
|||||||
mock_session.get.return_value = reply
|
mock_session.get.return_value = reply
|
||||||
mock_session.scalars.return_value = _mock_scalars([Mock()])
|
mock_session.scalars.return_value = _mock_scalars([Mock()])
|
||||||
|
|
||||||
with patch.object(service_module, "uuid_value", side_effect=[True, False]):
|
with patch.object(WorkflowCommentService, "_filter_valid_mentioned_user_ids", return_value=["user-2"]):
|
||||||
result = WorkflowCommentService.update_reply(
|
result = WorkflowCommentService.update_reply(
|
||||||
reply_id="reply-1",
|
reply_id="reply-1",
|
||||||
user_id="owner",
|
user_id="owner",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user