diff --git a/application/single_app/config.py b/application/single_app/config.py index 7778bc66a..670ab4808 100644 --- a/application/single_app/config.py +++ b/application/single_app/config.py @@ -101,7 +101,7 @@ EXECUTOR_TYPE = 'thread' EXECUTOR_MAX_WORKERS = 30 SESSION_TYPE = 'filesystem' -VERSION = "0.261.258" +VERSION = "0.261.259" IS_DEVELOPMENT = is_development_env_enabled() # Opt-out for deployments where App Service Easy Auth is active but the platform diff --git a/application/single_app/functions_collaboration.py b/application/single_app/functions_collaboration.py index 1ad274248..c70156586 100644 --- a/application/single_app/functions_collaboration.py +++ b/application/single_app/functions_collaboration.py @@ -1260,6 +1260,11 @@ def ensure_group_collaboration_for_legacy_conversation(source_conversation_id, o if not allowed: raise PermissionError(reason) + invited_participants = ( + _normalize_group_conversation_participants(group_doc, invited_participants) + if invited_participants else [] + ) + collaboration_conversation_doc = None linked_collaboration_id = str(source_conversation_doc.get('collaboration_conversation_id') or '').strip() if linked_collaboration_id: diff --git a/docs/explanation/fixes/GROUP_COLLABORATION_INVITEE_VALIDATION_ORDER_FIX.md b/docs/explanation/fixes/GROUP_COLLABORATION_INVITEE_VALIDATION_ORDER_FIX.md new file mode 100644 index 000000000..ad1cfeb4a --- /dev/null +++ b/docs/explanation/fixes/GROUP_COLLABORATION_INVITEE_VALIDATION_ORDER_FIX.md @@ -0,0 +1,66 @@ +# Group Collaboration Invitee Validation Order Fix (v0.261.259) + +## Overview + +Group conversation conversion now proves that every requested invitee is a current member of the source group before reading the conversation's message history. This keeps authorization checks ahead of dependent data access while preserving the existing conversion and invitation behavior. + +Fixed in version: **0.261.259** + +Related issue: [#1651](https://github.com/microsoft/simplechat/issues/1651) + +Dependencies: group membership normalization, group role authorization, and Microsoft 365 history publication. + +The application version was updated in `application/single_app/config.py` from `0.261.258` to `0.261.259`. + +## Issue Description + +An owner converting an eligible legacy group conversation could include an identity that was not a current member of the source group. The request was rejected and no records were changed, but Microsoft 365 publication preparation queried the source message history before participant normalization raised the rejection. + +The group document collaboration suite also replaced the workflow-alert safety module with a test stub that no longer matched the production module's exported constants. That mismatch stopped the suite during fixture setup and hid its sharing assertions. + +## Root Cause + +`ensure_group_collaboration_for_legacy_conversation` passed the raw invitee list into history publication before `create_group_collaboration_conversation_record` normalized those invitees against the current group document. The correct validation existed, but it ran after a dependent transcript read. + +Separately, `functions_notifications` gained an import path through `functions_workflow_alerts`, whose safety contract requires three public diagnostic constants. The isolated test stub exported only the sanitizer function. + +## Technical Details + +### Files Modified + +- `application/single_app/functions_collaboration.py` +- `application/single_app/config.py` +- `functional_tests/test_group_collaboration_source_storage_fix.py` +- `functional_tests/test_group_document_collaboration.py` +- `docs/explanation/release_notes.md` + +### Code Changes + +The conversion path now normalizes requested invitees immediately after it verifies the owner, source conversation, group, current owner role, and active group status. Only the validated participant summaries can reach an existing collaborative conversation, Microsoft 365 publication preparation, conversation record creation, or transcript copying. + +The document collaboration fixture now provides the exact workflow-alert diagnostic constants exported by production while retaining its assertion that document sharing must not enter workflow-alert rendering. + +No route, response status, or request payload contract changed. + +## Testing And Validation + +The focused regression command exercises both invalid-invitee storage modes, the V1 route contract, and the document-sharing fixture import path. It completed with 3 tests and 38 subtests passing. + +The complete group source-storage suite completed with 20 tests and 106 subtests passing. The targeted document-sharing fixture case passed, editor diagnostics reported no errors, and the broken-access-control guardrail passed for the changed production module. Documentation validation completed with 7 of 7 application-surface checks and 6 of 6 site-quality checks passing. + +## Impact Analysis + +### Before + +- An out-of-group invitee was rejected without mutation, but only after the source transcript was queried. +- The document collaboration suite stopped during fixture setup because its safety stub was incomplete. + +### After + +- Every invitee is authorized against current group membership before transcript or publication evidence access. +- Rejected conversion requests do not read either possible source message container. +- The document collaboration suite reaches its route, policy, Search, notification, and storage assertions. + +## Known Limitations + +The fix does not change who may convert a conversation, which groups may be used, or which participant roles are assigned. It only moves the existing invitee normalization to the earliest point after the source group and caller have been authorized. \ No newline at end of file diff --git a/docs/explanation/release_notes.md b/docs/explanation/release_notes.md index cde8b127f..ebc32df8c 100644 --- a/docs/explanation/release_notes.md +++ b/docs/explanation/release_notes.md @@ -2,6 +2,15 @@ For feature-focused and fix-focused drill-downs by version, see [Features by Version](https://github.com/microsoft/simplechat/tree/main/docs/explanation/features) and [Fixes by Version](https://github.com/microsoft/simplechat/tree/main/docs/explanation/fixes). +### **(v0.261.259)** + +#### Bug Fixes + +* **Rejected Group Conversation Invites Stop Before History Reads** + * Converting an existing group conversation now verifies every invited participant against the group's current membership before preparing Microsoft 365 publication evidence or reading the conversation transcript. An identity outside the group is still rejected without changing any records, but the rejection now happens before message history is queried. + * The document collaboration regression fixture now exposes the current workflow-alert safety contract, so its sharing and repair checks reach their assertions instead of stopping during module import. + * (Ref: #1651, `ensure_group_collaboration_for_legacy_conversation`, `test_group_collaboration_source_storage_fix.py`, `test_group_document_collaboration.py`, [Group Collaboration Invitee Validation Order Fix](fixes/GROUP_COLLABORATION_INVITEE_VALIDATION_ORDER_FIX.md)) + ### **(v0.261.258)** #### User Interface Enhancements diff --git a/functional_tests/test_group_collaboration_source_storage_fix.py b/functional_tests/test_group_collaboration_source_storage_fix.py index 8fe9b0283..ebb02864e 100644 --- a/functional_tests/test_group_collaboration_source_storage_fix.py +++ b/functional_tests/test_group_collaboration_source_storage_fix.py @@ -2,11 +2,12 @@ #!/usr/bin/env python3 """ Functional regression for group conversation source storage. -Version: 0.261.257 +Version: 0.261.259 Implemented in: 0.261.024 Ported to the React branch in: 0.261.106 Soft-delete helper seeded in the harness in: 0.261.257 -Related issue: microsoft/simplechat#1472 +Invitee validation ordering fixed in: 0.261.259 +Related issues: microsoft/simplechat#1472, microsoft/simplechat#1651 Exercise production conversion and route helpers against isolated Cosmos stores. Group context must not imply group-container storage or weaken authorization. diff --git a/functional_tests/test_group_document_collaboration.py b/functional_tests/test_group_document_collaboration.py index b432468c0..00c3bcb27 100644 --- a/functional_tests/test_group_document_collaboration.py +++ b/functional_tests/test_group_document_collaboration.py @@ -1,8 +1,9 @@ # test_group_document_collaboration.py """ Functional tests for immutable group document sharing and repair. -Version: 0.261.131 +Version: 0.261.259 Implemented in: 0.261.131 +Workflow alert safety fixture updated in: 0.261.259 Real Flask routes, policy, conditional source writes, Search write guards, access index projections and notification persistence run against local service seams. @@ -179,7 +180,14 @@ def directory_query(query, parameters=None, **kwargs): get_user_public_workspaces=Mock(), )) patch.setitem(sys.modules, "functions_workflow_alert_safety", module_stub( - "functions_workflow_alert_safety", sanitize_workflow_alert_record=Mock( + "functions_workflow_alert_safety", + WORKFLOW_ALERT_EVALUATION_ERROR_CODE="workflow_alert_evaluation_failed", + WORKFLOW_ALERT_EVALUATION_ERROR_MESSAGE=( + "Alert conditions could not be evaluated. " + "Review the workflow model configuration or contact an administrator." + ), + WORKFLOW_ALERT_EVALUATOR_UNAVAILABLE_MESSAGE="No model evaluator was available for this run.", + sanitize_workflow_alert_record=Mock( side_effect=AssertionError("Sharing must not enter workflow alert rendering."), ), ))