mirror of
https://github.com/mattermost/mattermost.git
synced 2026-09-19 10:12:47 +08:00
2c925ccf88245bef4fe9bb84c8d1e97b65dee3e5
22527
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2c925ccf88 | MM-68151: Update server dependencies (#36571) | ||
|
|
448a642835 |
Add inline action buttons for bot-posted markdown (#36219)
* Add inline action buttons for bot-posted markdown
Bots, webhooks, and plugins can now embed clickable action buttons
inside markdown (including table cells) using mmaction://actionId
links, with row-specific parameters forwarded to the integration on
click. This enables use cases like a per-row "Mx Plan" button in a
fleet-status table that opens a dialog scoped to the clicked row.
Design
- New post prop inline_actions maps actionId (alphanumeric) to a
PostActionIntegration {URL, Context}, capped at 50 entries.
- Markdown link with scheme mmaction:// emits a placeholder span that
messageHtmlToComponent converts to the InlineActionButton component.
- Click POSTs inline_context (parsed from the URL query string) to the
existing /posts/{id}/actions/{action_id} endpoint; the server merges
it into the integration request as context.inline_params while
preserving the post-level context.
- Only bot, webhook, and plugin posts render the button; non-integration
posts have inline_actions stripped on create, update, and ephemeral
broadcast. Hardened-mode also covers the new prop.
- Reuses the existing PostAction dialog pipeline: plugin handlers reply
with a trigger_id and call /actions/dialogs/open as before.
Security
- InlineContext capped at 50 entries / 128-char keys / 2 KB values.
- Integration Context cloned per click so per-click inline_params and
selected_option cannot leak into the cached post for other clickers.
- Plugin response updates cannot add inline_actions to a post that did
not already have them; invalid entries are dropped with a warn log.
- Label content and data attributes are escaped; labels are flattened
to plain text (tags stripped, entities decoded, then escaped).
- Malformed JSON request bodies now return 400 instead of falling
through with an empty inline_context.
Tests
- Model: validators, normalization, GetInlineAction, strip, fallback.
- App: create strip, update guard (4 subtests including
AllowInlineActionsUpdate bypass), ephemeral strip, inline_params
merge, context-map isolation, plugin-response guards, from_bot and
from_plugin retention across plugin updates.
- API: inline_context validation (size bounds + error id),
omitempty backward compat, malformed JSON 400.
- Webapp: renderer scheme handling, allow/deny flags, size caps,
HTML escape, tag strip, entity decode, attribute-injection defense;
component click dispatch, double-click race guard, unmount safety,
error-result recovery, aria state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* lint fix
* i18n-extract
* Review fixes for inline action buttons
- renderer: preserve actionId case; reject opaque mmaction: URI
- app: require bot AND integration session to preserve inline_actions
- app: restore original inline_actions when plugin response is invalid
- i18n: rename key to ...app_error to match convention
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Tighten UpdatePost inline_actions guard; fix test seeds
- app: UpdatePost now requires AllowInlineActionsUpdate to modify
inline_actions. Integration session alone is insufficient — a
PAT-wielding user could otherwise inject inline_actions on any
post they could edit.
- tests: seed bot posts with inline_actions via an integration
session (intSeedCtx) so they survive the create-time strip.
- renderer: lint fix (blank line before comment block).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Reject malformed inline-action authorities at render time
- renderer: enforce ^[A-Za-z0-9]+$ on actionId, mirroring the server
regex. Authorities like mmaction://plan:443 or mmaction://user@plan
now fall through to plain text instead of rendering a dead button.
- post: clarify in the strip comment that webhooks and plugins bypass
CreatePostAsUser entirely (they call CreatePost / CreatePostMissingChannel
directly), so the strip block does not apply to them.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Tighten inline-action renderer tests
- Replace oversized-params test with boundary pair (at-cap and over-cap)
to lock in the > vs >= behavior of the size-limit check.
- Add a "surrounding text survives" assertion for the tag-strip path so
a future swap from regex strip to a DOM sanitizer won't silently
drop legitimate content along with tags.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Inline action buttons via mmaction:// markdown links
Adds inline action buttons rendered from mmaction:// links in markdown,
with the click pipeline reusing the existing post-action infrastructure.
Aligned with the broader mm_blocks_actions framework (Daniel's PR).
* fix lint, DoS hardening, fix and rename test
* Address review feedback
* lint fix
* Reject percent-encoded path traversal in validateIntegrationURL (e.g. %2e%2e%2f) by parsing the URL and checking the decoded path.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
|
||
|
|
a84941bec1 | Remove Legacy Interactive Dialog code (#35874) | ||
|
|
6189a3f54a | [MM-66489] Pull and populate certificate from metadata endpoint (#36557) | ||
|
|
0790fc7281 | Upgrade Go to 1.26.3 (#36656) | ||
|
|
c74e51f35e | chore(ci): upgrade Go to 1.26.3 in build container Dockerfiles (#36648) | ||
|
|
51c6d5219f |
Fix config Sanitize fields missing from desanitize, causing FakeSetting to be persisted (#36619)
* Add TestDesanitizeRemovesAllFakeSettings to catch future omissions Walks every string field in the config after a Sanitize+desanitize round-trip and fails if any still holds FakeSetting. This catches the case where a field is added to Sanitize without a corresponding desanitize entry. * Fix ElasticsearchSettings.ClientKey being incorrectly masked as a secret ClientKey is a file path, not a secret value. Masking it caused the asterisk string to be persisted to the database on config writes, which broke TLS client auth on restart. * Fix desanitize missing entries for fields added in |
||
|
|
41f3b22679 |
Fix flaky E2E tests (Cypress + Playwright) (#36637)
* Fix flaky email sort test by ignoring punctuation in localeCompare PostgreSQL's en_US.UTF-8 collation ignores hyphens at the primary sort level, but JS localeCompare() on a C-locale CI runner uses byte order, causing the expected and actual sort orders to diverge for emails containing hyphens. Passing ignorePunctuation:true aligns JS collation with Postgres behavior. * E2E/Cypress: re-enable CYPRESS_* env var overrides allowCypressEnv: false was introduced in the v15.13 upgrade (PR #36091) but broke the existing CYPRESS_adminUsername / CYPRESS_adminPassword override mechanism that local and CI runners depend on. * E2E/Cypress: fix MM-T1508 accessibility image test flakiness The test was failing because the admin user could have a stale compact display mode preference from a previous spec, causing post avatars to render with pointer-events: none and blocking the .status-wrapper click. Two fixes: - resetUserPreference() now resets message_display to 'clean' so compact mode doesn't leak across spec files - accessibility_image_spec before() now runs as a fresh user with default preferences rather than the shared admin account |
||
|
|
2db507464d |
Add auth token to flaky test webhook (#36636)
Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
345a0b76a6 |
Mm 68506 fe abac mask fe table editor cel and e2e (#36517)
* MM-68501 - implement GetMaskedVisualAST and wire API handler Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * add missing test and fix style issues * fix styles * implement coderabbit feedback * MM-68501 - PR review: split masking file, model-level access mode, reject contradictory config Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68501 - apply shared_only filter to non-option field values (binary masking) * MM-68501 - consolidate masking flag check and log corrupt text value during masking * MM-68503 - add CEL utilities, write-path validation, and merge helpers Combined set of helpers consumed by BE-5's save path: CEL construction / serialization - extractStringValues, buildCELFromConditions, conditionToCEL, celStringLiteral, celValueLiteral. Used to rebuild a CEL string from a VisualExpression, including for GetMaskedExpression on the read-side of policy GET / search responses. Merge-on-save helpers - getHiddenValues (per-condition, with pre-fetched fields map for N+1 avoidance) — finds which stored values are not visible to the caller. - mergeConditionValues — re-injects the hidden values into a submitted condition without duplicates. - Together, these let BE-5 preserve attribute values the caller cannot see while still letting them edit the visible parts of a policy. Write-path value-hold validation - validatePolicyExpressionValues, invalidValueError, validateConditionValues. - Generic "Invalid value." error on every rejection — no signal about whether the value exists or is merely not held (prevents enumeration). - Rejects the masked-token sentinel "--------" if submitted as a literal. These all live in access_control_masking.go alongside the masking primitives that BE-2 introduced. i18n entries added for the two new error IDs (app.pap.save_policy.invalid_value, app.pap.validate_expression_values.app_error). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68503 - handle the masked-token sentinel in validation and merge When the GET /policies endpoint returns a policy via MaskPolicyExpressions, the raw expression contains the masked-token sentinel "--------" in place of hidden values. If the frontend round-trips that expression unchanged back to the server (e.g., the admin only modified channel assignment, not the rules), the sentinel reaches the save path. The previous code in validateConditionValues rejected the sentinel as "Invalid value." This blocks the legitimate round-trip case. Fix: - validateConditionValues: treat the sentinel as a placeholder and skip it during visibility / source-only / unknown-mode checks. Other values are still validated normally. - mergeConditionValues: strip the sentinel from submitted values before appending hidden values, so it never propagates to the stored result. Both array and single-value forms (string == "--------") are handled. TestMaskedTokenRejection (which asserted the old rejection behavior) is replaced by TestMaskedTokenConstant which only verifies the sentinel string itself. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68504 - integrate save-path masking: 403 block on delete, merge-on-save, response masking Save path (CreateOrUpdateAccessControlPolicy): * validatePolicyExpressionValues runs on the submitted expression before merge so re-injected hidden values are never validated against the caller's holdings. * mergeStoredPolicyExpressions re-injects hidden values from the stored policy and blocks (HTTP 403) any attempt to remove a condition that contained values the caller cannot see — closes the row-deletion gap in classified environments. * mergeExpressionWithMaskedValues unwraps single-element arrays for scalar operators after restoring the stored operator (avoids "attr == [val]" invalid CEL when the frontend submits "attr in []" as the masked-row placeholder for an originally-scalar condition). * checkSelfInclusion is bypassed for system admins (they may legitimately write conditions for values they do not hold); masking and value-hold validation still apply to system admins. Delete path (DeleteAccessControlPolicy): * Same masked-values 403 block — a caller with masked values cannot delete the policy at all (UI Delete button is also disabled in FE-3). Response masking: * createAccessControlPolicy and setAccessControlPolicyActiveStatus run MaskPolicyExpressions on the response so even a save reply doesn't leak the values the caller does not hold. GetMaskedExpression, maskConditionValuesWithToken, replaceHiddenValuesWithToken, MaskPolicyExpressions live alongside the rest of the masking helpers in access_control_masking.go. team_access_control.go: corrects ValidateChannelEligibilityForAccessControl call site (drops the spurious receiver and rctx; it's a package-level helper that only takes channel). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68503 - address PR review: batch field fetches, propagate errors, fail-closed write path * MM-68503 - restore team-admin api4 tests accidentally dropped during BE-5 rebuild * MM-68503 - address review and CodeRabbit feedback on save-path masking * add tests for delete masking, self-inclusion, GET mask * add assertions to strengten tests * MM-68505 - add has_masked_values type and MaskedChip component Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * MM-68506 - add masking support to TableEditor and team settings modal TableEditor (table_editor.tsx, table_editor.scss): - hasMaskedValues plumbed through rows; lock operator/attribute selectors on masked rows. - Row remove (trash) button disabled on masked rows; disabled-state CSS so the icon doesn't show the destructive hover colour or a pointer cursor. - Test Rules button disabled when any row has masked values, with tooltip. - onMaskedStateChange callback to notify the parent for cross-component states (CEL editor read-only, Save disabled, banners). Value selectors (single_value_selector_menu.tsx, multi_value_selector_menu.tsx, selector_menus.scss, value_selector_menu.tsx): - Append MaskedChip after visible chips on multi-value rows. - Render MaskedChip as the sole value on single-value rows where the caller holds no visible value. Policy details (policy_details.tsx, .scss, .test.tsx): - Track hasMaskedRows state; receive from TableEditor via onMaskedStateChange. - Show masked-values warning banner above the editor when present. - Same banner on the Delete confirmation modal so admins understand why deletion is consequential. Team settings modal (team_policy_editor.tsx, .scss): - Same masked-values plumbing; delete button uses the disabled state when a policy has masked values, regardless of whether channels are assigned. - Pre-save check no longer treats "in []" as an incomplete rule — that placeholder comes from fully-masked rows that merge-on-save will fill in. i18n entries added for the new strings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68506 - fix hook order in SingleValueSelector when masked state changes The early return for `hasMaskedValues && !value` sat between useState and useCallback declarations, so when a parent re-render flipped the masked state (e.g. after deleting a sibling rule) React saw a different hook count and crashed with "Rendered fewer hooks than expected". Move the read-only short-circuit after all hook declarations so the hook order stays stable across renders. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68507 - CEL editor read-only when masked + system console wiring CEL editor (editor.tsx, editor.scss): - hasMaskedRows prop: when true, Monaco is set to read-only and a banner explains why ("This expression contains restricted values. Switch to Simple mode to edit the values you have access to, or delete the entire rule."). - Test Rules button disabled in CEL mode when hasMaskedRows is true. Policy details (policy_details.tsx, .scss): - hasMaskedRows state plumbed to CELEditor, TableEditor, and the Save / Delete buttons. - Save button disabled while masked rows are present (kept after the save-allowed-with-masked-values change in BE-5? — no, here we keep Save enabled so admins can add/modify rules; only row removal of masked rows is blocked). - Delete Policy button disabled when hasMaskedRows; a SectionNotice above the Delete card explains why ("This policy contains restricted values - Deletion not allowed"). - New save error messages: invalid_value and self_exclusion are surfaced from the server's generic responses. Policies list (policies.tsx): minor wiring change for the new state plumbing. Table editor (table_editor.tsx): cross-component coordination — emits onMaskedStateChange and respects the disabled-for-masked-row policy. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68508 - E2E suite for attribute-value masking Covers the full read+write masking flow against a real server: - Masked chip rendering, operator/attribute lock, Test Rules disabled. - System admin subject to masking like any other caller (no role bypass). - Save with masked values: hidden values preserved by merge-on-save. - Trash button disabled on masked rows; server returns 403 on direct API attempt to remove a masked condition. - Delete Policy button disabled + server 403 when policy has masked values (both system console and team settings modal paths). - Self-inclusion failure only fires when the caller holds full visibility. - CEL editor read-only with banner when masked rows present. - Direct API validation: non-held values and the masked-token sentinel rejected with a generic "Invalid value." error. - Feature-flag-off path: no masking, all values visible. - Text-field shared_only masking (binary) with `in` and `==` operators. A pluggable DB-setup helper marks specific CPA fields as shared_only for the duration of a test (with per-test cleanup) since the API blocks setting access_mode=shared_only without a source_plugin_id. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68506 - fix lint, jest mock factory, and unreachable delete-modal test * MM-68506 - localize masked-condition-deleted save error * MM-68506 - fix masked-policy delete warning detection and localize masked_rule_deleted * fix linter issues * MM-68506 - surface delete error, lock value selector on masked rows, drop dead remove-modal * fix linter, add translations, adjust specs * import wittoltip from shared * fix linter and use the correct button variant * MM-68506 - drop dangling rationale comment in access_control_field_test * fix linter, translation and e2e tests * use pg ts types and dependencies for e2e types mocks * adjust switch mode persistance restriction * fix team settings style buttons * fail-closed guard for advanced expressions in merge-on-save, plus helper unit tests, and FF/test-helper cleanups * MM-68505 - add has_masked_values type and MaskedChip component Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * MM-68506 - add masking support to TableEditor and team settings modal TableEditor (table_editor.tsx, table_editor.scss): - hasMaskedValues plumbed through rows; lock operator/attribute selectors on masked rows. - Row remove (trash) button disabled on masked rows; disabled-state CSS so the icon doesn't show the destructive hover colour or a pointer cursor. - Test Rules button disabled when any row has masked values, with tooltip. - onMaskedStateChange callback to notify the parent for cross-component states (CEL editor read-only, Save disabled, banners). Value selectors (single_value_selector_menu.tsx, multi_value_selector_menu.tsx, selector_menus.scss, value_selector_menu.tsx): - Append MaskedChip after visible chips on multi-value rows. - Render MaskedChip as the sole value on single-value rows where the caller holds no visible value. Policy details (policy_details.tsx, .scss, .test.tsx): - Track hasMaskedRows state; receive from TableEditor via onMaskedStateChange. - Show masked-values warning banner above the editor when present. - Same banner on the Delete confirmation modal so admins understand why deletion is consequential. Team settings modal (team_policy_editor.tsx, .scss): - Same masked-values plumbing; delete button uses the disabled state when a policy has masked values, regardless of whether channels are assigned. - Pre-save check no longer treats "in []" as an incomplete rule — that placeholder comes from fully-masked rows that merge-on-save will fill in. i18n entries added for the new strings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68506 - fix hook order in SingleValueSelector when masked state changes The early return for `hasMaskedValues && !value` sat between useState and useCallback declarations, so when a parent re-render flipped the masked state (e.g. after deleting a sibling rule) React saw a different hook count and crashed with "Rendered fewer hooks than expected". Move the read-only short-circuit after all hook declarations so the hook order stays stable across renders. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68507 - CEL editor read-only when masked + system console wiring CEL editor (editor.tsx, editor.scss): - hasMaskedRows prop: when true, Monaco is set to read-only and a banner explains why ("This expression contains restricted values. Switch to Simple mode to edit the values you have access to, or delete the entire rule."). - Test Rules button disabled in CEL mode when hasMaskedRows is true. Policy details (policy_details.tsx, .scss): - hasMaskedRows state plumbed to CELEditor, TableEditor, and the Save / Delete buttons. - Save button disabled while masked rows are present (kept after the save-allowed-with-masked-values change in BE-5? — no, here we keep Save enabled so admins can add/modify rules; only row removal of masked rows is blocked). - Delete Policy button disabled when hasMaskedRows; a SectionNotice above the Delete card explains why ("This policy contains restricted values - Deletion not allowed"). - New save error messages: invalid_value and self_exclusion are surfaced from the server's generic responses. Policies list (policies.tsx): minor wiring change for the new state plumbing. Table editor (table_editor.tsx): cross-component coordination — emits onMaskedStateChange and respects the disabled-for-masked-row policy. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68508 - E2E suite for attribute-value masking Covers the full read+write masking flow against a real server: - Masked chip rendering, operator/attribute lock, Test Rules disabled. - System admin subject to masking like any other caller (no role bypass). - Save with masked values: hidden values preserved by merge-on-save. - Trash button disabled on masked rows; server returns 403 on direct API attempt to remove a masked condition. - Delete Policy button disabled + server 403 when policy has masked values (both system console and team settings modal paths). - Self-inclusion failure only fires when the caller holds full visibility. - CEL editor read-only with banner when masked rows present. - Direct API validation: non-held values and the masked-token sentinel rejected with a generic "Invalid value." error. - Feature-flag-off path: no masking, all values visible. - Text-field shared_only masking (binary) with `in` and `==` operators. A pluggable DB-setup helper marks specific CPA fields as shared_only for the duration of a test (with per-test cleanup) since the API blocks setting access_mode=shared_only without a source_plugin_id. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68506 - fix lint, jest mock factory, and unreachable delete-modal test * MM-68506 - localize masked-condition-deleted save error * MM-68506 - fix masked-policy delete warning detection and localize masked_rule_deleted * fix linter issues * MM-68506 - surface delete error, lock value selector on masked rows, drop dead remove-modal * fix linter, add translations, adjust specs * import wittoltip from shared * fix linter and use the correct button variant * MM-68506 - drop dangling rationale comment in access_control_field_test * fix linter, translation and e2e tests * use pg ts types and dependencies for e2e types mocks * adjust switch mode persistance restriction * fix team settings style buttons * fail-closed guard for advanced expressions in merge-on-save, plus helper unit tests, and FF/test-helper cleanups * Refactor access control methods to use GetPropertyGroup for CPA group ID retrieval * fix styles * disable delete on masked policies in list view and remove dead modal warnings * fix unit tests * preserve hasAnyOf operator display for fully-masked multiselect conditions * address PR feedback: lock Actions on masked save, filter source/shared_only from /attributes, add unit tests and e2e tests * fix e2e tests * comment out e2e to isolate issue * completely remove the files to pass linter --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
1ffa4d8994 |
Add Docker Hub login to Cloud Agent start hook. (#36632)
Authenticate DinD pulls at runtime using Cursor dashboard secrets so agents avoid anonymous Docker Hub rate limits. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
5566604e03 |
MM-68838: Ping a restored plugin remote immediately on re-register (#36592)
* MM-68838: ping restored plugin remote immediately on re-register RegisterPluginForSharedChannels' restore branch updated the row but did not call PingNow, leaving the restored remote offline until the next pingLoop tick (up to PingFreq, default 1 minute). The new-connection branch already calls PingNow; the restore branch now mirrors it so sync attempts immediately after a plugin restart no longer fail with "offline remote cluster". * MM-68838: gob-encode error returns in apiRPCServer.ReceiveSharedChannelAttachmentSyncMsg The apiRPCServer wrapper for ReceiveSharedChannelAttachmentSyncMsg assigned the hook's error return directly to the gob-encoded response struct. When the framework's App.ReceiveSharedChannelAttachmentSyncMsg returned an error wrapped with %w (*fmt.wrapError, an unexported type), gob refused to encode it and the RPC server broke the connection with "type not registered for interface: fmt.wrapError". Every subsequent plugin/server RPC call then returned the zero-value response struct, causing plugins that dereferenced the nil returns to crash. Apply the existing encodableError() helper so the returned error becomes a gob-safe ErrorString, matching every other apiRPCServer method in this file. |
||
|
|
5cd26002d3 |
Hide Download Apps link when running in Desktop app (#36614)
* Hide Download Apps UI when running in Desktop app Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com> * Fix ESLint import order for Desktop app visibility changes Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> |
||
|
|
7bb6fb347b |
Fix AI toolbar separator visibility (#36356)
Co-authored-by: Cursor Agent <cursoragent@cursor.com> |
||
|
|
92f6870a2b |
Add "last used" field for incoming webhooks (#36416)
* Add "last used" field for incoming webhooks * Address feedback * Rename migrations * Fix web lint |
||
|
|
0675d0ea0b |
Automations for config.json, API, audit log event, and Go release notes (#36075)
* Create config-change-checker.yml
* Create check_config_changes_ci.py
* Update config-change-checker.yml
* Update check_config_changes_ci.py
* Update check_config_changes_ci.py
* Update check_config_changes_ci.py
* Update check_config_changes_ci.py
* Update config-change-checker.yml
* Update check_config_changes_ci.py
* Update config-change-checker.yml
* Update config.go
* Fix check_api to detect multi-line and multi-method endpoints
The previous implementation matched the .Handle(...).Methods(...) regex
line-by-line against diff lines. This silently missed two real and
common patterns in api4/:
1. Multi-line .Handle(...) declarations — e.g. group.go has 18 of
them, where the path lives on one line and the wrapper/handler on
the next. The regex never matched, so PRs adding such endpoints
produced empty release-note entries.
2. Multi-method declarations like
.Methods(http.MethodGet, http.MethodHead) (4 instances in file.go)
— the old regex required a closing paren immediately after the
first method.
The fix:
- Add a file_at(ref, path) helper that snapshots a file at a git ref
via 'git show', so checkers can compare full file states instead of
pattern-matching diff text.
- Add _scan_endpoints() that whitespace-collapses the file before
matching, letting the regex span what were originally multiple
lines.
- Loosen _HANDLE_RE to capture the methods list as a substring and
extract individual HTTP verbs with a known-method allowlist, so
multi-method declarations produce one entry per verb.
- Switch check_api to set-diff (after - before) / (before - after)
on the parsed endpoint sets. This also cleanly handles routes
that move within a file (no fragile add/remove dedup needed).
- Anchor the new/deleted file detection to '^new file mode \d+' to
avoid false positives from stray text in source files.
Made-with: Cursor
* Track enclosing struct in check_config to avoid dedup collisions
The previous check_config keyed its add/remove dedup on the bare field
name. The dedup intent was to ignore fields that were merely reordered
within config.go (which appear in the diff as both '-Foo' and '+Foo').
But because the key was just the field name, an unrelated rename in one
struct could silently cancel out a real new field with the same name in
a different struct. For example, in a single PR:
- EnableFoo *bool // removed from ServiceSettings
+ EnableFooV2 *bool
- EnableBar *bool // removed from EmailSettings
+ EnableFoo *bool // newly added — but wrongly cancelled below
The dedup would see 'EnableFoo' in both lists and drop both entries,
hiding the brand-new EmailSettings.EnableFoo from the release-note
output.
The fix tracks each field's enclosing struct using a brace-depth stack
that walks the file at BASE_SHA and HEAD_SHA. Fields are keyed as
(struct_name, field_name) tuples, so identically-named fields in
different structs are distinct, and the dedup only collapses true
reorderings. As a side benefit the rendered output is now
'StructName.FieldName' which is much more useful to reviewers.
Switching to file-at-revision scanning + set diff also removes the
custom dedup logic entirely — set arithmetic handles "moved within
file" naturally.
Made-with: Cursor
* Switch remaining checkers to file-at-revision style; drop lines_by_sign
check_audit_events and check_go_version still parsed +/- diff lines
directly, with the same brittle dedup-and-cancel logic that was used in
the previous check_config. After the previous two commits the rest of
the file uses the file_at(ref, path) helper to compare full file
states between BASE_SHA and HEAD_SHA, which:
- removes the entire moved-within-file dedup dance (set arithmetic
handles it for free),
- aligns all four checkers on a single, easy-to-reason-about pattern,
- is robust to whitespace-only or reordering edits in the watched
files.
For Dockerfile.buildenv the helper also avoids a subtle case where the
old code only inspected +/- lines: an edit to an unrelated RUN line
that didn't touch the FROM line could in theory leave both old_ver and
new_ver as None even though the version was effectively unchanged.
Reading the file at each revision compares the actual current and
previous FROM line directly.
The lines_by_sign helper now has no callers, so remove it.
Made-with: Cursor
* Update config.go
* Update config.go
* Update check_config_changes_ci.py
* Update check_config_changes_ci.py
* Update check_config_changes_ci.py
* Update check_config_changes_ci.py
* Tighten check_config_changes_ci.py: regex coverage + idempotency
- Restore tolerant `_HANDLE_RE` so 2-arg wrappers (e.g. `api.APISessionRequired(handler, handlerParamFileAPI)`)
are not silently dropped from the api4 endpoint scan; broaden the `.Methods(...)`
capture so string-literal variants (`Methods("GET")`) work too. Filtering moves
back to the `_HTTP_METHODS` allowlist in `_parse_methods` to keep stray
identifiers from being treated as HTTP verbs.
- Make `strip_old_note` also remove auto-generated lines that landed outside
the ```release-note fence (the inject_note fallback paths) so reruns no
longer accumulate duplicates when a PR has no fence.
- Skip the GitHub PATCH when the PR description is already up to date, so
every commit no longer triggers an unconditional write.
- Wire up `check_go_version`'s `additions` path in `_format_lines` and
`_AUTO_LINE_RE` so a freshly-added Dockerfile.buildenv emits a note.
- Remove the now-dead `CheckResult.to_markdown` method (replaced by
`_format_lines`).
Made-with: Cursor
* Restore ExperimentalSettings.EnableWatermark
The field was removed in
|
||
|
|
9d318dc4cd |
refactor: speed up E2E test workflows and eliminate npm cache-restore failures (#36599)
Workers no longer run `npm ci` — `node_modules` and framework binaries are restored from actions/cache populated once by a new `prep-deps` job. This closes the intermittent EEXIST/ENOENT failure inside npm's own cacache writer that occasionally fails `npm ci` on a runner. Removing `npm ci` from workers also cuts ~5 min of duplicated install work per worker. dispatch-begin now runs as its own job after prep-deps so it fires once the per-worker test-server setup is the only remaining work before dispatch-run. |
||
|
|
1d1580cb3c | chore: update reusable workflows to specific commit sha (#36600) | ||
|
|
23b4d8275b |
MM-68197 Show classification banners in web and desktop apps (#36490)
* Add Classification Markings admin console page Adds a new admin console page under Site Configuration for managing classification markings. This allows system administrators to define classification levels (e.g., UNCLASSIFIED, SECRET, TOP SECRET) with associated colors and rank ordering, which will be used for system-wide and per-channel classification banners. The page includes: - Enable/disable toggle backed by the property field system (field existence = enabled) - Country preset dropdown (US DoD, NATO, UK GSCP, Canada, Australia PSPF) that auto-fills standard classification levels - Editable classification levels table with drag-and-drop reorder, inline text editing, color picker, and delete - Auto-switch to "Custom" preset when levels are manually modified - Confirmation dialog when switching presets would overwrite custom data Also adds: - ClassificationMarkings feature flag (default off) - Generic property field client methods (get/create/patch/delete) for the /api/v4/properties/ endpoints - Enterprise license + feature flag gating on the admin page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix classification markings: add validation, error handling, and system object type - Add "system" as a valid property field object type so the classification markings API calls succeed - Surface load errors instead of silently swallowing them (only suppress 404 for unconfigured state) - Validate before save: require at least one level, non-empty names, and no duplicates - Default to custom preset with empty levels on first open - Add section strings to searchableStrings for admin console search Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Move classification field to CPA group targeting users Store the classification markings property field in the custom_profile_attributes group with object_type 'user' instead of the attributes group with object_type 'system'. Clear target_id for PSAv2 system target compliance and mark the field as admin-managed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Stabilize preset option IDs and add danger warning on preset switch Hardcode deterministic IDs for all preset classification levels so switching away and back preserves option IDs, preventing orphaned property values. Compare only level data (not preset label) for change detection so cosmetic preset switches don't trigger false save states. Show a danger modal with red confirm button when changing presets on an existing field, warning about system-wide impact on classified resources. The warning appears once per session then allows frictionless switching. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove system object type from property fields Not needed yet — will be added when system/channel banners are implemented. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix ESLint errors in classification markings admin page Fix import ordering and remove unused generateId import. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address CodeRabbit review feedback for classification markings - Register property field API endpoints when ClassificationMarkings flag is enabled (not just IntegratedBoards) to prevent 404s - Preserve preset option IDs when creating a new classification field instead of blanking them with empty strings - Add sysconsole read/write permission constants for classification markings across server and webapp, and wire up resource-level permission checks in the admin definition Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add rank attribute to classification marking options Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add classification markings permissions migration and read-only support Add a permissions migration to grant classification markings sysconsole permissions to existing roles on upgrade. Wire up the disabled prop so read-only users can view but not edit classification settings. Register the permission in the Delegated Granular Administration UI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Paginate loadField to find classification field beyond first page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix lint errors and warnings in classification markings Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove classification markings sysconsole permissions; gate on sysadmin instead Classification markings admin page no longer uses feature-specific read/write permissions. Visibility is gated on license + feature flag, editing is gated on system admin role. This avoids coupling feature-specific permissions to the generic property service. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Set sysadmin-level permissions on classification markings field creation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Use stable IDs instead of array indices for classification level operations Switch updateLevel/deleteLevel to identify levels by ID rather than index, sort levels by rank on load, and extract i18n strings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Refactor classification markings into extracted helper functions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add tests for classification markings admin console feature Add unit and component tests covering: - Pure function tests for detectPreset, optionsToLevels, levelsToOptions, processClassificationField, and fetchClassificationField pagination logic - React component tests for rendering states, validation, and user interactions - Client4 property field method tests for URL construction and HTTP verbs - Server routing test verifying routes register with ClassificationMarkings flag - Feature flag default and serialization test Export pure functions from classification_markings.tsx to enable direct testing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix lint errors in classification markings tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix test compilation error * Fix color input auto-filling after 3 hex characters in classification markings Buffer ColorInput onChange in a LevelColorCell wrapper so the table doesn't re-render mid-typing, preventing the input from losing its focus-guarded local state. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fixing style issues with color picker z-index * Added fix to prevent immediate dismissal when clicking inside color picker * Adding E2E test suite for configuration * Removing duplicates * Fixing unrelated linter error * Fixing test linting issues * Updating tests to skip appropriately * Matching configuration to UX specs * Fixing style lint * Added informational banner for presentational nature of markings * Enabling the markings flag on playwright server * Added missing feature flag to e2e test environment in ci * Reverting changes to color_input - Not needed as we're using a custom component * Added and polished global banner configuration * Refactoring webapp for readability - Separating components - Adding unit tests - Isolating helper methods into utilities * Fixing linter errors * linter fix * Manually fixing linter issues * Separating global classification component * Added persistence of classification marking configuration * Changing LevelID with LevelName * Making changes for PR reviews * Changing property object of classification field to template * syncing i18n file * Removing inaccurate note from comments * PR fixes for UX review * Cleaning up unused value * Added GlobalClassificationBanner component - Made sure it syncs on change by using normal configuration values on it - Works with "top" and "top_and_bottom" - Renders on both root and admin_console * Adding E2E test cases for global classification * Linter fixes, i18n extract * PR Fixes * Linter fix * Matching default messages * Fixing type errors * Fixing pipeline and runtime errors * Fixing announcementbar rendering on top of global classifications * Increasing banner & font sizes * Fixing font size to 12px instead of 16px - I read it wrong * Replacing config values with property * Test linter fixes * Fixing type errors and go format error * Making changes needed to align with specs - Ensuring system_classification is a separate linked property that differs from the template - Saving the global classification banner values as a propertyvalue * Added missing arguments in e2e tests * Added missing conditions for useEffect - Also fixing E2E error in pipeline * Fixing issues with V1 and V2 group mismatch * Fixes for linter errors and coderabbit review * Addressing more issues found by coderabbit * Fixing issues found by coderabbit * Migrating to use system properties * Ran all linters and prettier - Resolving coding style drift that happened from not running prettier on the webapp (even though CI doesn't check for this) * Undoing the prettier changes in webapp * Cleaning up unwanted autoformatted changes * Reverting prettier changes to clean diff * Fixing E2E test * Import fixes in test * Applying changes for PR feedback * Fixing issues with failing e2e tests * Changing key of selection from name to id * Replacing field setup in E2E tests to use levelId instead of levelName * Added classification setup per channel on channel creation * WIP: Adding classification banner integrated with channel banners - Using a hook to resolve which values should be evaluated when displaying the banner * Fixing style of dropdown input for classifications * Fixing visual issues with dropdown inputs * Adding E2E Tests and linter fixes * General fixes and improvements * Applying linter fixes * Resolving lingering linter issues * Updated snapshot and extracted i18n * Adding test cleanup to prevent failures due to duplicates * Addressing nitpick comment for test mapping of values * Applying more fixes to E2E tests * Improving test coverage and e2e test cleanup * Resolving type issues * Refactoring classification constant names an documentation * Ensuring propertyvalue only stores single id, storing banner text in banner_info * Fixing issues with linter alongside style issues on header * Updating test assertion to account for fallback * Fixing issues found during testing - Removing custom selection from being an option and turned it into a state - Ensuring only system administrators can set channel classification levels * Fixing z-index issue with color input popover * Setting classification level to lowest available value when switching it on * Updating unit tests to match new spec for preselection --------- Co-authored-by: David Krauser <david@krauser.org> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: David Krauser <david@kruser.org> Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
548183d748 |
Mm 68282 admin ephemeral mode (#36194)
* adds feature flag to enable mattermost ephemeral mode * add ephemeral mode config settings to system console When feature flag is set to true a new section for Mobile Ephemeral Mode settings shows under the Mobile Security section in case a valid Enterprise Advanced License is active. * adds Mobile Ephemeral Mode settings playwright tests * improve descriptions for settings * improves error messages and hints * move validation to common helper and add new tests * reverts package-lock.json changes * proper struct alignment * proper message sorting in json file * use generic doc url for MEM section while docs are not ready * Proper formatting for playwright tests * fixes test |
||
|
|
9bd77d3fc4 |
MM-68702: Reject demoting bot accounts to guest (#36487)
* MM-68702: Reject demoting bot accounts to guest Deny DemoteUserToGuest when the target is a bot so User Managers cannot degrade bot capabilities via guest conversion without bot administration permissions. Adds API error string and tests. Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com> * Fix TestDemoteUserToGuest bot subtest: enable bot creation in config Default test config disables bot accounts; enable ServiceSettings EnableBotAccountCreation for the subtest and restore afterward. Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com> |
||
|
|
d4471bece1 |
Mm 68503 be abac mask save path masking (#36513)
* MM-68501 - implement GetMaskedVisualAST and wire API handler Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * add missing test and fix style issues * fix styles * implement coderabbit feedback * MM-68501 - PR review: split masking file, model-level access mode, reject contradictory config Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68501 - apply shared_only filter to non-option field values (binary masking) * MM-68501 - consolidate masking flag check and log corrupt text value during masking * MM-68503 - add CEL utilities, write-path validation, and merge helpers Combined set of helpers consumed by BE-5's save path: CEL construction / serialization - extractStringValues, buildCELFromConditions, conditionToCEL, celStringLiteral, celValueLiteral. Used to rebuild a CEL string from a VisualExpression, including for GetMaskedExpression on the read-side of policy GET / search responses. Merge-on-save helpers - getHiddenValues (per-condition, with pre-fetched fields map for N+1 avoidance) — finds which stored values are not visible to the caller. - mergeConditionValues — re-injects the hidden values into a submitted condition without duplicates. - Together, these let BE-5 preserve attribute values the caller cannot see while still letting them edit the visible parts of a policy. Write-path value-hold validation - validatePolicyExpressionValues, invalidValueError, validateConditionValues. - Generic "Invalid value." error on every rejection — no signal about whether the value exists or is merely not held (prevents enumeration). - Rejects the masked-token sentinel "--------" if submitted as a literal. These all live in access_control_masking.go alongside the masking primitives that BE-2 introduced. i18n entries added for the two new error IDs (app.pap.save_policy.invalid_value, app.pap.validate_expression_values.app_error). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68503 - handle the masked-token sentinel in validation and merge When the GET /policies endpoint returns a policy via MaskPolicyExpressions, the raw expression contains the masked-token sentinel "--------" in place of hidden values. If the frontend round-trips that expression unchanged back to the server (e.g., the admin only modified channel assignment, not the rules), the sentinel reaches the save path. The previous code in validateConditionValues rejected the sentinel as "Invalid value." This blocks the legitimate round-trip case. Fix: - validateConditionValues: treat the sentinel as a placeholder and skip it during visibility / source-only / unknown-mode checks. Other values are still validated normally. - mergeConditionValues: strip the sentinel from submitted values before appending hidden values, so it never propagates to the stored result. Both array and single-value forms (string == "--------") are handled. TestMaskedTokenRejection (which asserted the old rejection behavior) is replaced by TestMaskedTokenConstant which only verifies the sentinel string itself. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68504 - integrate save-path masking: 403 block on delete, merge-on-save, response masking Save path (CreateOrUpdateAccessControlPolicy): * validatePolicyExpressionValues runs on the submitted expression before merge so re-injected hidden values are never validated against the caller's holdings. * mergeStoredPolicyExpressions re-injects hidden values from the stored policy and blocks (HTTP 403) any attempt to remove a condition that contained values the caller cannot see — closes the row-deletion gap in classified environments. * mergeExpressionWithMaskedValues unwraps single-element arrays for scalar operators after restoring the stored operator (avoids "attr == [val]" invalid CEL when the frontend submits "attr in []" as the masked-row placeholder for an originally-scalar condition). * checkSelfInclusion is bypassed for system admins (they may legitimately write conditions for values they do not hold); masking and value-hold validation still apply to system admins. Delete path (DeleteAccessControlPolicy): * Same masked-values 403 block — a caller with masked values cannot delete the policy at all (UI Delete button is also disabled in FE-3). Response masking: * createAccessControlPolicy and setAccessControlPolicyActiveStatus run MaskPolicyExpressions on the response so even a save reply doesn't leak the values the caller does not hold. GetMaskedExpression, maskConditionValuesWithToken, replaceHiddenValuesWithToken, MaskPolicyExpressions live alongside the rest of the masking helpers in access_control_masking.go. team_access_control.go: corrects ValidateChannelEligibilityForAccessControl call site (drops the spurious receiver and rctx; it's a package-level helper that only takes channel). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68503 - address PR review: batch field fetches, propagate errors, fail-closed write path * MM-68503 - restore team-admin api4 tests accidentally dropped during BE-5 rebuild * MM-68503 - address review and CodeRabbit feedback on save-path masking * add tests for delete masking, self-inclusion, GET mask * add assertions to strengten tests * fail-closed guard for advanced expressions in merge-on-save, plus helper unit tests, and FF/test-helper cleanups * Refactor access control methods to use GetPropertyGroup for CPA group ID retrieval --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
f0360a838a |
Data spillage report generation UI (#36340)
* Added base fr report generation * WIP * implemented UI flow * implemented UI flow * restructured the modal code into sub components * Refactoring and cleanup * lint fixes, added new tests * i18n fix * test fix * Updated test * CI * Several improvements * WIP * Added tests * Addressed some security enhancements * Created zip writer entery later * Improved a test to check for file content * Improved error handling * Made a geneeric function * Updated classes * accepting comment in report API * Added more tests * Integrated new API param * Removed an unnecessary check * Made a geneeric function * Made a geneeric function * Made the comment body not required and updated API docs * Updated report generation API call in download report button * Included decision in report and removed confirmation when keeping message * Updated test * Add explicit wait for removeWithoutReportButton visibility in test Prevent race condition by waiting for the button to be visible after UI transitions to skip-confirm step before clicking it. Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com> * PR Feedback * explicitelly added return statement * Included actor details in report * Updated tests --------- Co-authored-by: maria.nunez <maria.nunez@mattermost.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
479103d868 |
chore: Update NOTICE.txt file with updated dependencies (#36609)
Automatic Merge |
||
|
|
bab9009825 |
MM-68592: Add leave confirmation modal for policy-added public channels (#36439)
* MM-68592: Add leave confirmation modal for policy-added public channels When a user attempts to leave a public channel they were auto-added to via a membership policy (channel.policy_enforced), show a confirmation modal informing them that the leave is permanent and offering a 'Mute instead' option as a lighter alternative. The flow follows the existing pattern used for private channel leave confirmation. The modal is opened from: - Channel header menu Leave action - Sidebar channel menu Leave action - /leave slash command The Mute instead button is hidden when the channel is already muted. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68592: address CodeRabbit review - Make handleMuteInstead async and only close the modal when the mute action resolves successfully, leaving it open on error so the user can retry or choose to leave instead. - Move autoFocus from the destructive 'Leave channel' button to the non-destructive secondary action ('Mute instead' or 'Cancel') so pressing Enter does not default-confirm a permanent leave. - Cover the failure path with a new unit test that asserts the modal remains open when muteChannel returns an error. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> |
||
|
|
f067fcde92 |
MM-66339 Hide empty content-flagging "With comment" section in reviewer DM (#36552)
* Add Cursor Cloud Agent Docker environment Co-authored-by: Cursor <cursoragent@cursor.com> * Fix Cloud Agent enterprise and Docker access Co-authored-by: Cursor <cursoragent@cursor.com> * Fix Cloud Agent Go path setup Co-authored-by: Cursor <cursoragent@cursor.com> * MM-66339 Stop double-JSON-stringifying content flagging comments The flagPost, removeFlaggedPost, and keepFlaggedPost Client4 helpers were calling JSON.stringify on the comment value before placing it in the JSON request body. When the reporter or reviewer left the optional comment blank, JSON.stringify('') returned the literal two-character string '""', which the server then stored as the comment and embedded in the reviewer DM as 'With comment:\n\n> ""'. Send comment as the plain string instead so an empty comment stays empty and the 'With comment' section is omitted entirely. Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com> --------- Co-authored-by: Nick Misasi <nick.misasi@mattermost.com> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
9d06155540 |
Update bot checks (#36503)
* Fix bot permission checks in revokeSession, revokeAllSessionsForUser, and updatePassword MM-68701: Align permission checks with the bot-aware pattern used by updateUser, patchUser, deleteUser, and (via MM-68686) updateUserActive. Three handlers were missing the IsBot branch: - revokeSession / revokeAllSessionsForUser: both gated access through SessionHasPermissionToUser, which only requires EditOtherUsers (an ancillary permission granted to User Managers). Switching to SessionHasPermissionToUserOrBot routes bot targets through SessionHasPermissionToManageBot first and falls back to the user path only when the target is not a bot. - updatePassword: the permission flag canUpdatePassword was set by checking PermissionSysconsoleWriteUserManagementUsers (or PermissionManageSystem for system admins) with no IsBot branch. Adding an else-if user.IsBot guard routes bot targets through SessionHasPermissionToManageBot, consistent with every other handler in the file that touches bot accounts. Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> * Improve TestRevokeSessionBotPermissions: revoke a real bot session Seed a session directly via th.App.CreateSession instead of passing a fake ID and expecting a 400. The test now validates the full happy path: the session row is created, the privileged user revokes it, and the call returns 200 OK. Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> * Strengthen forbidden sub-test: revoke a real bot session with no perms Seed a real session for the bot before the unprivileged revoke call. The test now proves the permission gate blocks access even when the target session ID genuinely exists in the database. Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> * Address review feedback: add post-conditions to bot session revoke tests - TestRevokeSessionBotPermissions: after RevokeSession succeeds, assert GetSessionById returns an error to confirm the row is gone. - TestRevokeAllSessionsForUserBotPermissions: seed a real session before RevokeAllSessions so the call is not a no-op, then assert GetSessions returns an empty list afterwards. Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
669eb104c6 |
Fix webhook list ordering instability when paginating (MM-65732) (#36470)
* Fix webhook list ordering instability when paginating (MM-65732) The webhook list view reorders entries when navigating between pages. The first page initially shows webhooks in insertion order (from the server), but after loading additional pages the display settles into alphabetical order. Going back to page 1 then shows different items than were originally visible. Root causes: 1. Server: GetIncomingByTeamByUser, GetIncomingListByUser, GetOutgoingByTeamByUser, and GetOutgoingListByUser had no ORDER BY clause, so the database could return rows in any order. 2. Client (incoming webhooks): incomingWebhookCompare only resolved the channel-name fallback for the 'a' argument, not 'b', making the comparator asymmetric and producing an unstable sort. 3. Client: both installed_incoming_webhooks and installed_outgoing_webhooks called Array.prototype.sort() directly on the props array, mutating it. Fix: - Add ORDER BY DisplayName, Id to the four listing SQL queries so API pages always come back in alphabetical order. With a stable server order, the client sort over merged pages produces the same slice for each page number regardless of how many pages have been loaded. - Symmetrise incomingWebhookCompare by applying the same channel-name and 'Private Webhook' fallback to the 'b' argument. - Sort a copy ([...hooks].sort()) in both webhook list components so the original prop arrays are never mutated. Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> * Fix lint: remove space before JSX closing tag in webhook test Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> * Fold ordering tests into existing webhook store test functions Instead of four separate top-level test registrations (GetIncomingListByUserOrdering, etc.), each ordering assertion is now a t.Run sub-test inside its corresponding existing function: testWebhookStoreGetIncomingListByUser └─ "GetIncomingListByUser, ordered alphabetically by display name" TestWebhookStoreGetIncomingByTeamByUser └─ "GetIncomingByTeamByUser, ordered alphabetically by display name" testWebhookStoreGetOutgoingListByUser └─ "GetOutgoingListByUser, ordered alphabetically by display name" testWebhookStoreGetOutgoingByTeamByUser └─ "GetOutgoingByTeamByUser, ordered alphabetically by display name" Each sub-test creates fresh hooks (Charlie, Alpha, Bravo in insertion order) scoped to its own IDs so they do not interfere with the outer test's fixtures. Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> * Fix govet shadow and gofmt issues in webhook store tests - Rename the outer err variable to errSave in three functions (testWebhookStoreGetIncomingListByUser, TestWebhookStoreGetIncomingByTeamByUser, testWebhookStoreGetOutgoingByTeamByUser) so that hooks, err := declarations in sub-test closures no longer shadow it. - Change hookC, err = to hookC, err := in each ordering sub-test to declare a local err instead of capturing the outer one. - Remove a trailing blank line at the end of the file (gofmt). Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> * Remove jest.mock from installed_incoming_webhooks test The mock for delete_integration_link was copied from the outgoing webhooks list test but is not needed here: the real component renders fine within renderWithContext (as shown by installed_incoming_webhook.test.tsx which tests the individual item without any mocks). Since the ordering tests do not interact with delete functionality, drop the mock and align the action stub style to mockReturnValue(Promise.resolve()). Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> * Add ORDER BY to GetOutgoingByChannelByUser; add ordering sub-test GetOutgoingByChannelByUser was the last paginated webhook listing function without an ORDER BY clause. Add OrderBy("DisplayName", "Id") consistent with all other listing functions. Add the corresponding ordering sub-test inside testWebhookStoreGetOutgoingByChannelByUser, following the same errSave pattern established for the other functions to avoid govet shadow warnings. Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> |
||
|
|
238867e247 |
MM-68732: Remove global mutex for login attempts in favour of database serialization (#36515)
* Add atomic login-attempt counter primitives to UserStore
Two new store methods back the upcoming switch from a global
per-node mutex to per-user atomic slot claiming:
TryIncrementFailedPasswordAttempts(userID, maxAttempts) (bool, error)
UPDATE Users SET FailedAttempts = FailedAttempts + 1
WHERE Id = ? AND FailedAttempts < maxAttempts
Returns true when a slot was claimed (rows affected == 1) and
false when the cap was already reached. The conditional UPDATE
serialises concurrent attempts on the same user via the row
lock, so the cap is enforced without any application-level
locking and without serialising attempts across users.
DecrementFailedPasswordAttempts(userID) error
UPDATE Users SET FailedAttempts = FailedAttempts - 1
WHERE Id = ? AND FailedAttempts > 0
Releases a slot previously claimed by TryIncrement when the
in-flight authentication turns out not to be a credential
failure. The conditional UPDATE means concurrent decrements
cannot underflow.
Storetest covers both primitives: claim-below-cap, reject-at-cap,
reject-above-cap, no-op for unknown user, and a 50-goroutine
concurrent test with a start barrier asserting exactly
maxAttempts slots are ever claimed and that decrement clamps at
zero under contention.
The testify mock is regenerated here so the storetest package
that returns *mocks.UserStore as a store.UserStore still satisfies
the interface; the wrapper layers are regenerated in the next
commit.
------
AI assisted commit
* Regenerate store layers for the new primitives
Pick up TryIncrementFailedPasswordAttempts and
DecrementFailedPasswordAttempts in every generated wrapper:
- retrylayer: retry on repeatable errors using the standard
three-attempt loop.
- timerlayer: record store-method duration metrics under
UserStore.TryIncrementFailedPasswordAttempts and
UserStore.DecrementFailedPasswordAttempts.
- localcachelayer: invalidate the profile cache only after the
underlying conditional UPDATE actually changes a row; an
at-cap no-op return on TryIncrement no longer produces
unnecessary cluster invalidation traffic.
------
AI assisted commit
* Drop login-attempt mutex; use per-user slot claiming
Replace the global per-node mutex that serialised every login
attempt with the database-side atomic slot machine added on the
Users row. Each of the three authentication entry points now
pre-claims a slot via TryIncrementFailedPasswordAttempts before
running the expensive password / LDAP / MFA check, and releases
the slot when the failure path is not a real credential mismatch:
- CheckPasswordAndAllCriteria (email/password): refunds the
slot on backend errors during the password check (malformed
stored hash, hasher misc failure, password-migration write
failure) so a transient infra issue cannot ratchet
FailedAttempts to a lockout for a user with valid credentials;
refunds on the MFA pre-flight probe (empty mfaToken on an
MFA-enabled user) so the probe is not counted as a real
attempt.
- DoubleCheckPassword: same backend-error refund predicate.
- checkLdapUserPasswordAndAllCriteria: pre-claims only for
existing users (first-time LDAP users have no local row to
claim against); refunds non-credential DoLogin errors (server
unreachable, transient) so an LDAP outage cannot lock out
everyone; refunds the MFA pre-flight probe; for first-time
users, explicitly bumps the counter via UpdateFailedPasswordAttempts
on a real bad-password or bad-MFA attempt, matching the
pre-refactor counting behaviour.
If the refund itself fails the underlying authentication error is
preserved and returned to the caller (the failure is logged); a
leaked slot is annoying, but masking the real failure with a
generic store 500 would be a clear observability regression.
Cluster-wide behaviour also changes: the previous design honoured
MaximumLoginAttempts per node, so an n-node cluster effectively
permitted n * MaximumLoginAttempts attempts. The cap is now
enforced globally.
------
AI assisted commit
* Cover app-layer behaviors of the new login slot machine
The store-layer tests already exercise TryIncrement and Decrement
under concurrency and at the cap boundary. The new behavioural
contracts at the app layer were not covered, so a regression that
flipped a refund predicate, a probe condition, or a first-time
LDAP path would have slipped through type checking and existing
unit tests.
Add tests around the three callers of the new path:
- CheckPasswordAndAllCriteria: an MFA pre-flight probe (empty
token) does not consume a slot; a real attempt with a wrong
non-empty token does; a backend error during the password
check (malformed stored hash) refunds the slot; the happy
path also asserts FailedAttempts resets to zero.
- DoubleCheckPassword: gets its first test coverage, covering
the happy path, rate-limit rejection once max attempts is
reached, and the backend-error refund path.
- checkLdapUserPasswordAndAllCriteria: covers paths the table
loop did not exercise, first-time LDAP user with a bad
password (uses GetUserByAuth to reach the freshly created
row), first-time LDAP user with a wrong MFA token, existing
LDAP user with a non-credential DoLogin error (slot
refunded), and the existing LDAP user MFA pre-flight probe
(slot refunded).
------
AI assisted commit
* Address coderabbit review
------
AI assisted commit
* Fix race in first-time LDAP failed-attempt counter
For first-time LDAP users we have no local row to pre-claim, so
the bad-password and bad-MFA branches fell back to an absolute
UpdateFailedPasswordAttempts(id, ldapUser.FailedAttempts+1) based
on a snapshot from GetUserByAuth. Concurrent first-attempt
requests for the same user could all read FailedAttempts == 0 and
all write 1, losing increments. As a secondary issue the absolute
set did not enforce MaximumLoginAttempts, so the counter could
also drift past the cap.
Switch both branches to TryIncrementFailedPasswordAttempts, the
atomic conditional UPDATE already used on every other path. The
row lock serialises concurrent increments and the predicate caps
at MaximumLoginAttempts.
A new concurrent storetest-style subtest runs
3 * maxFailedLoginAttempts goroutines through the first-time
bad-password path against the same fresh LDAP row and asserts
FailedAttempts lands at exactly maxFailedLoginAttempts. Against
the previous absolute-set implementation the test fails (observed
FailedAttempts = 4 with maxFailedLoginAttempts = 3, either a lost
increment or a cap overshoot).
The first-time bad-password branch also switches from a wrapped
500 return on store error to log-and-continue, matching the rest
of the file's refund/probe error handling: the underlying LDAP
authentication failure is the more useful error for the caller.
------
AI assisted commit
* Address review comments
------
AI assisted commit
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
|
||
|
|
8eb97fa6c3 |
refactor: remove redundant status update jobs from E2E test workflows (#36579)
* refactor: remove redundant status update jobs from E2E test workflows * refactor: rename context-name to commit-status-context in E2E test workflows |
||
|
|
02023f0328 | [MM-68463] New endpoint to GET user by auth_data (#36352) | ||
|
|
deafd88fd5 |
MM-68762: Discoverable Private Channels — Server data layer (#36539)
* MM-68762: Add Postgres migrations for discoverable private channels Three online-safe migrations introduce the schema that supports the Discoverable Private Channels feature (PRs 2-5 of MM-68430 will land behind it): - 000175 adds Channels.Discoverable BOOLEAN NOT NULL DEFAULT FALSE. Metadata-only on Postgres >= 11; no table rewrite. - 000176 creates a partial index on (TeamId) WHERE Discoverable AND Type='P' AND DeleteAt=0 using CREATE INDEX CONCURRENTLY (-- morph:nontransactional) so the build never blocks writes on the populated Channels table. - 000177 creates the ChannelJoinRequests table with three indexes, the important one being the partial unique index on (ChannelId, UserId) WHERE Status = 'pending'. That keeps the full audit history intact while still enforcing at-most-one active pending request per (channel, user). Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Add FeatureFlagDiscoverableChannels (default false) Gates the per-channel Discoverable toggle and the channel-join-request flow. Default-OFF so all PRs in the MM-68430 series can land on master without exposing partial UX. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Add Discoverable + ChannelJoinRequest models - Channel gains a Discoverable bool, ChannelPatch a *bool, both serialized as 'discoverable'. Patch() applies it, Auditable() logs it, and IsValid() rejects Discoverable=true on any non-private channel so a misconfigured patch can never produce a public discoverable channel. - New ChannelJoinRequest type captures the per-row state of a non-member's request: pending -> approved | denied | withdrawn. Rows are append-only with reviewer and timestamps so the table is also the audit trail. IsValid() enforces: * recognized status, * Message and DenialReason rune limits, * DenialReason only on denied rows (no orphan reasons), * reviewer + reviewed_at present for any terminal review (approved / denied) but not for self-service withdrawal. - Two new WebSocket event constants -- channel_join_request_created and channel_join_request_updated -- that later PRs broadcast on the admin queue and the requester's My Pending Requests panel. Unit tests cover Patch(), the new IsValid() rule on Discoverable, the PreSave/PreUpdate timestamp behavior on ChannelJoinRequest, and every IsValid branch including the reviewer-required-on-review invariant. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Add discoverable-channel permissions Two new channel-scoped permissions, each independently rebindable from the System Console: - manage_private_channel_discoverability gates the per-channel toggle so admins can restrict who can flip discoverability without also handing out manage_private_channel_properties. - manage_channel_join_requests gates the queue list / approve / deny / count endpoints (added in PR 2). Both are added to the channel_admin role bootstrap so new deployments get them by default, and a new permissions migration (add_discoverable_channel_permissions) grants them to channel_admin, team_admin and system_admin scheme roles on existing deployments. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Add ChannelJoinRequestStore and wire Discoverable into channel store - channelSliceColumns / channelToSlice / updateChannelT now include the new Discoverable column so Save() and Update() round-trip the field. Existing select paths inherit the column automatically because every read goes through channelSliceColumns. - New ChannelJoinRequestStore interface and SQL implementation: Save / Get / GetPendingForChannelAndUser / GetForChannel / GetForUser / Update / CountPending. Save translates the idx_channeljoinrequests_pending_unique partial unique index violation into store.ErrConflict so the app layer (PR 2) can return 409 without re-parsing pq errors. - Storetest suite at storetest/channel_join_request_store.go is invoked from sqlstore via the existing StoreTest harness; covers insert / partial-unique conflict / re-insert after withdrawal / NotFound / status filtering / pagination with TotalCount / Update / CountPending. - Mocks and retrylayer / timerlayer are regenerated via make store-mocks and go generate ./channels/store -- no hand-written generator output. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Add TS types for Discoverable channels + join requests webapp/platform/types: - Channel.discoverable?: boolean alongside existing policy_enforced / policy_is_active so the web client sees the same wire shape the server emits. - ChannelJoinRequest, ChannelJoinRequestStatus, ChannelJoinRequestList, GetChannelJoinRequestsOptions for the API contract surfaced in PR 2. webapp/platform/client: - WebSocketEvents enum gains ChannelJoinRequestCreated and ChannelJoinRequestUpdated so PR 3 can hang WS handlers off them without redeclaring constants. These are model-only updates with no UI consumer yet; PR 3 introduces the toggle, request flow, and admin queue surfaces. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Split ChannelJoinRequests indexes into concurrent migrations The mattermost-govet concurrentIndex lint check enforces CREATE INDEX CONCURRENTLY on every CREATE INDEX statement, even on an empty freshly-created table where it would be a no-op. The original 000177 file inlined three CREATE INDEX statements; that failed check-style. Mirror the convention used by 000166_create_views + 000167_create_views_channel_id_delete_at_index: keep the CREATE TABLE in its own (transactional) file, and move each index into a separate nontransactional file that runs CREATE INDEX CONCURRENTLY. Verified locally against Postgres 15 that all four new migrations apply in order and the storetest suite (partial unique constraint + paged list + count) still passes. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Wire new permission migration into test fixtures Two CI test surfaces missed when the channel_admin role and the permission-migration list gained the new manage_private_channel_discoverability and manage_channel_join_requests entries: - testlib/store.go: the shared mocked SystemStore used by SetupWithStoreMock / SetupEnterpriseWithStoreMock needs an explicit GetByName expectation for every migration key (because the mock panics on unexpected calls). Add the new MigrationKeyAddDiscoverableChannelPermissions key so TestCreateOrUpdateAccessControlPolicy, the elasticsearch aggregation_job_test, and every other mock-store test stop panicking on server bootstrap. - cmd/mmctl/commands/permissions_test.go: TestResetPermissionsCmd hard-codes the channel_admin default permission list and expects PatchRole to be called with exactly that slice. Extend the expected slice with the two new permission ids so the mmctl reset path stays in sync with the role bootstrap. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Register new idx_channels_discoverable_team in TestGetSchemaDefinition The schema-dump test asserts an exact index count and definition map for the channels table. Migration 000176 added idx_channels_discoverable_team — a partial btree on (teamid) gated by discoverable=true AND type='P' AND deleteat=0. Bump the expected count from 12 to 13 and add the index's CREATE INDEX definition as produced by pg_indexes (note: type is cast to channel_type, the existing domain). Verified locally against Postgres 15. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Fix golangci-lint findings in ChannelJoinRequest store Two golangci-lint findings on the freshly-added files: - sqlstore/channel_join_request_store.go:133 (modernize): collapse the 'if page < 0 { page = 0 }' clamp into max(opts.Page, 0). - storetest/channel_join_request_store.go:243 (govet shadow): the inner Save loop redeclared err with :=, shadowing the outer err captured from the first CountPending call. Switch to plain assignment so the same err is reused. Verified locally with golangci-lint v2.11.4 across public/..., channels/app/..., channels/store/..., channels/testlib/... and cmd/mmctl/commands/... — 0 issues. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Sync channel_admin bootstrap with TestDoAdvancedPermissionsMigration app_test.go pins the exact list of permissions the channel_admin role is expected to hold after DoAdvancedPermissionsMigration completes. The role bootstrap in role.go grew two entries (manage_private_channel_discoverability and manage_channel_join_requests), so the test's expected slice needs the same two entries appended in the same order, otherwise assert.Equal fails on slice ordering. This is the same class of fix as the mmctl/permissions_test.go change in a previous commit -- two parallel test fixtures encode the channel_admin defaults and have to be updated in lockstep with the bootstrap. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Add English translations for new model error keys 12 keys were emitted by the new Discoverable + ChannelJoinRequest validation paths but had no en.json entry, which trips i18n-check on CI. Add the missing entries with one-line English copy that mirrors adjacent model errors (Invalid <field>., Create at must be a valid time., etc.). The new entries are: - model.channel.is_valid.discoverable.app_error - model.channel_join_request.is_valid.channel_id.app_error - model.channel_join_request.is_valid.create_at.app_error - model.channel_join_request.is_valid.denial_reason.app_error - model.channel_join_request.is_valid.denial_reason_status.app_error - model.channel_join_request.is_valid.id.app_error - model.channel_join_request.is_valid.message.app_error - model.channel_join_request.is_valid.reviewed_by.app_error - model.channel_join_request.is_valid.reviewer.app_error - model.channel_join_request.is_valid.status.app_error - model.channel_join_request.is_valid.update_at.app_error - model.channel_join_request.is_valid.user_id.app_error Generated through 'make i18n-extract'; verified clean with 'make i18n-check'. Per the workspace rule, only en.json was modified -- no other locale files. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Address CodeRabbit review: stable pagination + redact denial reason from audit log Two production-code findings from CodeRabbit on the freshly-added ChannelJoinRequest server code: - sqlstore/channel_join_request_store.go (GetForChannel / GetForUser): OrderBy("CreateAt DESC") alone is unstable when two rows share a millisecond (NewId is monotonic-ish but CreateAt is millisecond resolution), so offset paging could duplicate or skip rows between pages. Add Id DESC as a deterministic tie-breaker on both list queries. - model/channel_join_request.Auditable: the denial reason is admin-typed free text and could carry sensitive content. Mirror the existing has_message pattern by emitting has_denial_reason as a boolean presence flag instead of the raw value. Reviewer id, review timestamp, and status are still logged, so the audit trail keeps every piece needed for compliance review. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Tighten model tests per CodeRabbit review Two test-only findings from CodeRabbit: - TestChannelJoinRequestPreUpdateAdvancesUpdateAt previously asserted GreaterOrEqual(r.UpdateAt, originalCreate). Because validRequest initialises UpdateAt to GetMillis() (same call site as CreateAt), a no-op PreUpdate would still pass that check. Seed r.UpdateAt = 1 before calling PreUpdate() and assert Greater(r.UpdateAt, int64(1)) so any regression that drops the GetMillis assignment fails the test. - TestChannelIsValidDiscoverable did not cover ChannelTypeGroup. Add the case alongside ChannelTypeOpen and ChannelTypeDirect so the contract that 'only ChannelTypePrivate accepts Discoverable=true' is fully pinned across all four channel types. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Mock ChannelJoinRequest accessor in retrylayer test retrylayer_test.go's genStore() helper mocks every Store() accessor because retrylayer.New() wraps the entire surface. The new ChannelJoinRequest() method I added on Store was missing from the mock, so TestRetry/on_regular_error_should_not_retry panicked with 'Unexpected Method Call ChannelJoinRequest()' on Postgres shard 0. Add the mock alongside the other accessors. No production code change. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> |
||
|
|
3f3d8408b2 |
Return descriptive errors from Role.IsValid and Role.IsValidWithoutId (#36582)
* Return descriptive errors from Role.IsValid and Role.IsValidWithoutId Previously both methods returned bool, leaving callers with no context about which validation check failed. Now both return error with a message identifying the specific constraint that was violated. * Add tests for Role.IsValid and Role.IsValidWithoutId * Log migration key on doPermissionsMigration failure --------- Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
6aae94f20b |
Add Display Name to User Properties in Webapp (#36363)
* Phase 1: CPA display_name + CEL-safe name validation (server) - Add typed DisplayName field to CPAAttrs + display_name attr key constant. - Add ValidateCPAFieldName helper enforcing CEL IDENTIFIER + reserved-word blacklist. - Wire validation into App.CreateCPAField (always) and App.PatchCPAField (lenient grandfather: skip when Name unchanged). - Trim + 255-rune cap DisplayName in CPAField.SanitizeAndValidate. - Developer-facing godoc note documenting rule, sources of truth, and Option C scoping. - Asserting test for documented Option C plugin-API bypass (closed by PR #36173). Spec: planner/projects/property-display-name/ideas/001-cpa-display-name/spec.md Plan: .planning/phase-1/PLAN.md Made-with: Cursor * Phase 1 (review): address Reza's Major + Minor findings - Rename misleading subtest "empty DisplayName is omitted from attrs" to "empty DisplayName round-trips as empty string" (Major #1). - Add TestCPAAttrs_JSONOmitEmpty pinning the omitempty wire-format contract that PR #36173's typed-attrs strategy relies on (Major #1). - Extend TestValidateCPAFieldName: case-sensitivity (IN/In ok), single-character names (a/_/A ok), missing "as" reserved word (Minor #2). Add whitespace-only DisplayName case (Minor #2). - Document PropertyFieldNameMaxRunes reuse in SanitizeAndValidate to prevent drift (Minor #3). - Replace broken PLAN-server.md reference in bypass-test docstring with in-tree CPAAttrs godoc reference (Minor #4). - Document omitempty semantics on CPAAttrs.DisplayName field to prevent the same misreading caught in review (Minor #5). - Document grouping intent above CPAFieldNameReservedWords (Minor #8). Review: .planning/phase-1/REVIEW.md Made-with: Cursor * Phase 2: in-app backfill migration for CPA display_name - Add cpaDisplayNameBackfillKey + cpaDisplayNameBackfillVersion constants. - Implement (*Server).doSetupCPADisplayNameBackfill: idempotent, cursor-paged scan over CPA group fields; backfill attrs.display_name = name when empty. - Register in m1 migration slice in doAppMigrations (mlog.Fatal on error, matching existing convention). - Three migration tests: NoExistingFields, BackfillsMissing, Idempotent. System-key idempotency + per-field DisplayName-empty check together provide HA-safe behavior on rolling deploys (last-write-wins on the System key; data-level idempotency from the per-field check). Spec: planner/projects/property-display-name/ideas/001-cpa-display-name/spec.md Plan: .planning/phase-2/PLAN.md Made-with: Cursor * Phase 2 (review): document race + harden idempotency test - Document SearchPropertyFields→UpdatePropertyFields rolling-deploy race: stale snapshot can revert concurrent admin CPA rename. Pre- existing systemic shape (no UpdateAt optimistic-lock); narrow window; bounded blast radius (admin re-rename, ABAC ID-keyed). Accepted limitation per spec Out of Scope (Major #1, Option C). - Tighten TestCPADisplayNameBackfill_Idempotent: snapshot UpdateAt before second run; assert no DB write on the System key or the field row (Major #2). - Extract clearCPABackfillMarker helper with explanatory godoc to centralize the 3x-repeated test precondition (Minor #1). - Comment fieldA seed as the "key-present-as-empty-string" idempotency boundary case (Minor #6). - Add godoc to doSetupCPADisplayNameBackfill (Minor #10). Review: .planning/phase-2/REVIEW.md Made-with: Cursor * Linting * Removing unnecessary comments * Clean up tests * Linting * Fix tests * Updated API doc * Phase 3: webapp helper + render-site migration for CPA display_name - Add display_name?: string to UserPropertyField.attrs type. - New getUserPropertyFieldLabel(field) helper: returns attrs.display_name?.trim() || name. Defensive against missing attrs. - Migrate ~10 user-facing CPA-name render sites to the helper: profile popover, user settings general (4 usages incl. line 1673 missed by high-level plan), admin user detail, admin CPA list (2 usages), and ABAC editor's selected-attribute UI (3 usages incl. the button label found in planning-stage research). - CEL paths (table_editor, attribute_selector_menu user.attributes expression construction, ABAC search filters) keep using `name` per spec — display_name is label-only. - Phase 4 boundary marker: TODOs in admin table + delete modal for follow-up admin-edit UX + client-side validator. Spec: planner/projects/property-display-name/ideas/001-cpa-display-name/spec.md Plan: .planning/phase-3/PLAN.md * Phase 3 (review): add Unicode test + correct helper docblock scope Address Reza's Phase 3 review: - Major #1: add missing test case for non-ASCII display_name (Latin-extended + CJK), pinning the trim/passthrough contract. - Nitpick #3: correct the helper's JSDoc to reflect that the delete modal is intentionally not migrated until Phase 4. No production behavior change. No new dependencies. Made-with: Cursor * Phase 4: admin CPA edit UX + client-side identifier validation Made-with: Cursor * docs: append Phase 4 implementation summary Made-with: Cursor * Phase 4: admin CPA edit UX + client-side identifier validation Complete the Phase 4 takeover from the existing dirty worktree and record the verified Stage 2 scope for admin CPA display-name editing, client-side identifier validation, and the required grandfather regression follow-ups. Document the targeted Jest, typecheck, and lint-equivalent validation results in the Phase 4 plan without widening the implementation scope or rewriting the prior in-scope work. Made-with: Cursor * docs: finalize Phase 4 implementation summary Made-with: Cursor * docs: correct Phase 4 summary commit reference Made-with: Cursor * Phase 4 (review): fix empty-name warning precedence Required-name validation now short-circuits before uniqueness checks so empty identifiers keep the correct warning. Add duplicate collision regression coverage for the dot-menu flow and add a stable validation-error testid for Phase 5 automation. Made-with: Cursor * Test updates * Fix merge issue * Fix tests * PR Feedback * Move migration to PropertyService * Updates to UX * Comment cleanup * Add webapp tests for CPA display_name and fix CEL-affected specs Update E2E seeds to use CEL-safe identifiers with display_name, add ABAC selector spec, and extend Jest coverage for label-rendering sites, auto-fill guard rails, and required-warning suppression. Co-authored-by: Cursor <cursoragent@cursor.com> * Remove .planning/phase-4/PLAN.md This planning artifact was committed inadvertently and should not be part of the codebase. Co-authored-by: Cursor <cursoragent@cursor.com> * Address CodeRabbit review comments - Fix e2e test to use display_name in label assertions - Make getIncrementedCELName case-insensitive to prevent collisions - Update tooltip to mention reserved CEL words - Replace hasSpaces check with full CEL identifier validation - Use CPA_FIELD_NAME_MAX_RUNES for consistent maxLength - Fix race condition by removing global cleanupAllFields - Enable IntegratedBoards flag for legacy field seeding - Replace fixed sleeps with state-based waits in tests Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com> * Fix linting errors in getIncrementedCELName - Use camelCase for destructured delete_at parameter - Place dots on same line for method chaining Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com> * Remove unused imports in user_attributes_display_name.spec.ts - Remove unused deleteCustomProfileAttributes import - Remove unused getFieldsMap function - Remove unused FieldsMap type Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com> * Fix webapp test failure - remove htmlFor assertion The htmlFor attribute assertion was failing in the test environment, likely due to a testing library issue. The important functionality (displaying display_name in labels) is still properly tested. Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com> * Fix post-merge CI failures: i18n drift and Playwright Prettier - Re-extract webapp en.json so the identifier tooltip string matches user_properties_table.tsx (source of truth was already shortened in Phase 4; en.json was not regenerated). - Apply Prettier formatting to three CPA display_name Playwright specs (whitespace and import/expression collapsing only). No test logic changes. Co-authored-by: Cursor <cursoragent@cursor.com> * Address CodeRabbit feedback: use stable locators, add reserved words to tooltip, remove regex from hasText Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com> * Fix i18n drift: align defaultMessage with en.json for identifier tooltip Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com> * Comment cleanup * Slugify CPA duplicate names to snake_case slugifyForCEL now lowercases and inserts underscores at camel/PascalCase boundaries (e.g. MyField -> my_field, XMLParser -> xml_parser) so duplicated CPA fields get conventional snake_case names instead of preserving the source casing. Co-authored-by: Cursor <cursoragent@cursor.com> * UX improvements: CEL identifier tooltip, validation, and attribute picker dual-name display - Add info tooltip to the Attribute column header explaining CEL identifier rules - Add client-side CEL identifier validation (pattern + reserved words) with a descriptive error message - Show both display name and unique identifier in the policy attribute picker - Filter attribute picker search by both display name and unique name - Add display_name to UserPropertyField attrs TypeScript type - Expand "CEL" to "Common Expression Language (CEL)" in the attribute-spaces tooltip Co-authored-by: Cursor <cursoragent@cursor.com> * Linting * PR Feedback * Restore name limit * Fix tests * Revert stray comment block above TestCPADisplayNameBackfill_BackfillsProtectedSourceOnlyField Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com> * Revert extended fieldA comment in TestCPADisplayNameBackfill_BackfillsMissing Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com> * Fix E2E tests * Fix test --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
d75155b39d |
Add flaky test webhook notification (#36573)
* Add flaky test webhook notification Co-authored-by: Cursor <cursoragent@cursor.com> * Bound flaky test webhook request time Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
fa1255f149 | Update Calls to v1.11.5 (#36574) | ||
|
|
54bee00622 |
MM-68332: consistently enforce query timeouts (#36522)
* Remove QueryRowx (no-timeout) from sqlxDBWrapper; migrate caller to QueryRowX QueryRowx forwarded to context.Background() with no timeout, while QueryRowX (uppercase) already enforces the wrapper timeout. Removing the no-timeout variant eliminates an accidental footgun and migrates the one sqlstore caller (plugin_store) to the timeout-enforcing method. * sqlxRow, with timeout cancel after Scan Introduce sqlxRow, which pairs *sqlx.Row with its context cancel function. QueryRowX (on both sqlxDBWrapper and sqlxTxWrapper) now returns *sqlxRow; Scan calls cancel immediately after the row is consumed, releasing the timeout context as soon as possible rather than waiting for the timer. * sqlxRows, with timeout cancel on Close Introduce sqlxRows, which embeds *sqlx.Rows and holds the timeout context's cancel function. Close() cancels the context immediately after the rows are done, releasing timeout resources as soon as iteration completes rather than waiting for the timer to fire. Introduce rowScanner interface (Next/Scan/Err) so the internal helpers scanRowsIntoMap and scanRetentionIdsForDeletion accept any row iterator rather than the concrete *sql.Rows, accommodating the new return types without threading *sqlxRows through every caller. * abolish the X suffix altogether * delete unused NamedQuery * add timeout tests for Query and QueryRow on db and tx wrappers * fix tx/timeout tests: handle pq driver.ErrBadConn on killed connection * fixup! fix tx/timeout tests: handle pq driver.ErrBadConn on killed connection * override Next() on sqlxRows to cancel on EOF * rm redundant row.Err(), handled by Scan * apply timeout to context unless deadline set * rebind consistently * address review feedback: rename, trace, and QueryContext fix - Rename withQueryTimeout → ensureQueryTimeout to better convey that it respects existing deadlines rather than overriding them. - Add missing w.trace blocks to QueryRowContext and ExecContext. - Change QueryContext to use ensureQueryTimeout and return *sqlxRows (instead of *sql.Rows) so the cancel is deferred to Close/Next rather than released prematurely. * fix Beginx → Begin after master merge * fix golangci-lint inline warning in sqlx_wrapper_test --------- Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
51fd952ae6 |
MM-67771: Update Report a Problem to email flow (#35900)
* MM-67771 Update Report a Problem to email flow for licensed servers Change the default "Report a Problem" behavior for licensed servers to open a mailto link to reportaproblem@mattermost.com with pre-filled metadata instead of redirecting to the support portal. Unlicensed servers continue to redirect to the troubleshooting forums. Admin console help text is now license-aware with separate descriptions for each plan type. * Add isFreeEdition check for Report a Problem flow Treat both unlicensed servers and licensed servers with entry SKU as free edition. This affects the Report a Problem default behavior (forum redirect vs mailto) and the admin console help text shown. - Add isFreeEdition to general.ts selectors and admin_definition_helpers - Add SKUEntry constant to general constants - Reuse isFreeEdition in product_menu.tsx - Add entry SKU test case for report_a_problem * Add the link to forums for free edition * Add permission and restricted-mode guards to ReportAProblemType dropdown The ReportAProblemType dropdown was missing the write-permission check and RestrictSystemAdmin guard that all other fields in the section have. |
||
|
|
d4fc0ecb1c |
MM-68150: Upgrade golangci-lint to v2.12.2 (#36554)
* Simplify invite_people email parsing Replace backwards in-place mutation loop with a straightforward forward filter into a new slice. Extract into parseEmailList so the logic can be unit tested directly. * MM-68150: Upgrade golangci-lint to v2.12.2 Remove //go:fix inline from NewPointer, which is a generic function not yet supported by the inline analyzer, and fix 11 slicesbackward modernize issues flagged by the new version. * MM-68150: Enable all linters by default; disable those with >20 existing issues Switch from opt-in (default: none) to opt-out (default: all) so new linters added to golangci-lint are evaluated automatically. Explicitly disable every linter that has more than 20 pre-existing violations, deferring those for later cleanup. Also disable a handful of linters whose violations are intentional patterns in this codebase (nilerr, dogsled, sqlclosecheck, iotamixing, predeclared, containedctx, iface, gocheckcompilerdirectives, promlinter, goprintffuncname, gomoddirectives). * MM-68150: Fix mirror linter issues Replace Write([]byte(s)) with WriteString(s), and FindIndex([]byte(s)) with FindStringIndex(s), to avoid unnecessary allocations. * MM-68150: Fix nosprintfhostport linter issue Use net.JoinHostPort to construct host:port strings instead of fmt.Sprintf with a manually formatted pattern. * MM-68150: Fix rowserrcheck and sqlclosecheck linter issues Check rows.Err() after iteration loops in schema_dump.go. In the sqlx_wrapper test, defer rows.Close() rather than closing inline. * MM-68150: Fix nilnesserr linter issues — wrong variable in error handlers In 11 places, a stale variable (often the outer err from a prior assignment) was used instead of the freshly-checked error variable (appErr, rowErr, jsonErr, writeErr, esErr). Each produces a typed-nil wrapped in a non-nil interface, silently discarding the real error. * MM-68150: Add i18n string for app.compile_csv_chunks.write_error --------- Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
d43dbe972e | Update Playbooks plugin to v2.9.0 (incl. FIPS) (#36570) | ||
|
|
f604ec7a5c |
MM-68662: Add Azure Blob Storage filestore backend (#36498)
* Generalize file backend error types
Replace S3FileBackendAuthError and S3FileBackendNoBucketError with
backend-agnostic FileBackendAuthError and FileBackendNoBucketError so
non-S3 drivers can return them and the admin "Test Connection" flow
keeps surfacing useful messages.
The old S3-prefixed names are kept as type aliases of the generic
types so external code (plugins, historical consumers) continues to
compile, and so existing S3 construction sites stay untouched.
The type switch in connectionTestErrorToAppError now matches the
generic types, with new i18n keys (test_connection_auth.app_error
and test_connection_no_bucket.app_error) whose wording does not name
S3. The old S3-specific i18n keys are dropped via `make i18n-extract`
since they are no longer referenced from code; the api4 test that
asserted on those keys is updated, and the Cypress
`MM-T996 Amazon S3 connection error messaging` spec that asserted
on the old user-facing string is updated to the new wording.
------
AI assisted commit
* Pull in Azure SDK and uuid dependencies
Bring in github.com/Azure/azure-sdk-for-go/sdk/azcore and
.../sdk/storage/azblob (with .../sdk/internal as their indirect
dependency). The two are needed by the upcoming Azure Blob Storage
filestore backend and its lazy-Range-backed reader. The bump of
golang.org/x/{crypto,net,sys,term,text} comes transitively from
azblob's minimum versions.
Also promotes github.com/google/uuid from indirect to direct,
since the Azure backend uses it to generate block IDs that share
the same wire format the SDK itself produces in UploadStream.
------
AI assisted commit
* Add azureRangeReader, a seekable Range-backed blob reader
A small standalone type that satisfies the FileBackend interface's
ReadCloseSeeker + the broader io.ReaderAt contract on top of Azure
Blob Storage HTTP Range requests. Lands as its own commit because
the upcoming Azure FileBackend driver builds on it, and the reader
itself is independently useful — and independently testable against
a fake downloader without standing up an Azure client.
Design notes:
* Read opens an HTTP Range stream lazily at the current offset and
reuses it for sequential reads. Seek to a different offset closes
the open stream; the next Read re-opens it.
* Seek to the same offset is a no-op and does not close the open
stream, so callers like zip.NewReader that probe with redundant
seeks don't kick off a fresh download.
* ReadAt issues a dedicated ranged DownloadStream per call and does
not touch the streaming cursor — matches the io.ReaderAt contract
the bulk-import worker's zip.NewReader path relies on.
* Close cancels the context (which any in-flight Azure call will
observe and abort), stops the deadline timer, and closes the
current body if any. It is safe to call when no body was ever
opened.
* CancelTimeout lets long-running consumers like the import worker
opt out of the per-operation deadline that would otherwise kill
multi-minute downloads partway through.
The implementation talks to a small blobDownloader interface rather
than *blob.Client directly so the unit tests can substitute a fake
downloader that records every requested Range and tracks Close
calls on the bodies it hands out.
------
AI assisted commit
* Add Azure Blob Storage filestore driver
Implements the FileBackend interface against Azure Blob Storage in
a new azurestore.go (~520 LOC). The driver is not yet selectable
via NewFileBackend's switch — that wiring lands in the next commit
together with the admin config surface — but the driver itself is
complete and self-contained behind the FileBackendSettings struct.
Filesstore.go grows three pieces of supporting infrastructure that
the driver consumes:
* a `driverAzure = "azureblob"` constant alongside the existing
driverS3 and driverLocal,
* an Azure-specific block on FileBackendSettings (storage account,
access key, container, path prefix, endpoint, SSL flag, request
timeout),
* a CheckMandatoryAzureFields validator that mirrors
CheckMandatoryS3Fields.
Behavioural notes that warrant calling out:
* Reader returns the previously-added azureRangeReader, so reads
stream lazily over HTTP Range and ReadAt is available for the
bulk-import worker's zip.NewReader path. The deadline timer is
armed before the initial GetProperties call so the HEAD itself
is bounded.
* WriteFile and AppendFile both go through StageBlock +
CommitBlockList via a shared stageBlocks helper, never the SDK's
UploadStream. UploadStream's small-payload fast path falls back
to single-shot PutBlob, which leaves the resulting blob with no
committed block list; a subsequent AppendFile that calls
CommitBlockList on that blob would then clobber its content.
Routing every write through the block-list mechanism keeps
AppendFile correct regardless of payload size.
* AppendFile stages the new chunk as one or more blocks and commits
the existing committed block list plus the newly staged IDs.
The new bytes go up exactly once — no re-download, no
re-concatenate, no re-upload of the prior contents.
* WriteFileContext does not wrap the caller-supplied context with
its own timeout — that timeout is applied in WriteFile only,
matching the S3 driver, so long-running TryWriteFileContext
callers (like message-export bulk writes) opt out of the
per-operation timeout the way the abstraction documents.
Authentication is shared-key only for this drop; Microsoft Entra
ID / managed identity is deferred to a follow-up. The endpoint is
configurable so the same code targets the production Azure host
(vhost style — {account}.blob.core.windows.net) or Azurite /
Azure Government / sovereign clouds (path style —
host[:port]/{account}).
------
AI assisted commit
* Wire Azure backend into config, validation, and driver selection
This commit registers the previously-added AzureFileBackend driver
with the rest of the system. Until now the driver was usable only
via direct construction; after this commit, `DriverName: "azureblob"`
in config.json is a fully-supported deployment configuration.
Five integration sites are touched:
* `newFileBackend` in filesstore.go now dispatches `driverAzure` to
NewAzureFileBackend, alongside the existing s3 and local cases.
NewFileBackendSettingsFromConfig (and its export counterpart) gain
an Azure branch that maps the model.FileSettings fields onto the
Azure-specific FileBackendSettings fields.
* `model.FileSettings` grows the user-facing Azure config schema:
storage account, access key, container, path prefix, endpoint,
SSL flag, request timeout, plus matching Export* fields for the
dedicated export store. SetDefaults populates them so deployments
that never opted into Azure don't carry nil pointers. `isValid`
accepts the new ImageDriverAzure constant.
* `Config.Sanitize()` masks AzureAccessKey and ExportAzureAccessKey
the same way it masks AmazonS3SecretAccessKey, so the shared key
never reaches an API consumer in plain text.
* `desanitize()` restores the masked keys on a config write so a
PATCH that doesn't touch the key doesn't clobber it with the
FakeSetting placeholder.
* `configSensitivePaths` covers both Azure key paths so audit
diffs don't include them either.
* `ConfigToFileBackendSettings` in the `mattermost db` CLI helper
gets the Azure branch its production counterpart already has —
without it, `mattermost db migrate` / `db downgrade` would fail
on Azure-configured deployments with "missing azure storage
account setting".
Finally, the shared FileBackendTestSuite is now wired against
Azurite via TestAzureFileBackendTestSuite, which skips when
CI_AZURITE_HOST is unreachable. The test-infra wiring (the docker
service, the env vars, the start_dependencies entry) landed in a
previous PR; this commit is what makes the suite actually exercise
the Azure driver end to end.
------
AI assisted commit
* Validate Azure timeout and path prefix in Config.IsValid
Parity with the S3-side checks that already cover
AmazonS3RequestTimeoutMilliseconds and AmazonS3PathPrefix. Without
these, a zero/negative AzureRequestTimeoutMilliseconds passes
validation and later creates immediately-expired request contexts,
and leading/trailing whitespace in AzurePathPrefix produces blob
keys that don't match what the admin configured.
Same checks added for the Export* counterparts. The
file_driver.app_error translation is updated to mention the new
'azureblob' option alongside 'local' and 'amazons3'.
------
AI assisted commit
* Stream zip entries from the Azure backend
writeZipEntry was calling ReadFile, which loads the entire blob
into memory before writing it to the archive. For large blobs or
deep directories this spikes RSS or OOMs the goroutine. Switch to
Reader (the streaming azureRangeReader) and io.Copy into the zip
entry so memory stays bounded regardless of blob size.
------
AI assisted commit
* Use a backend-agnostic fallback for FileBackendNoBucketError
The fallback Error() message was "no such bucket", which leaks S3
terminology when an Azure caller returns the type with no wrapped
Err. Use "no such bucket or container" so logs and external error
handling stay neutral across backends.
------
AI assisted commit
* Defend Azure path prefix against directory traversal
Reject ".." in AzurePathPrefix and ExportAzurePathPrefix at config
validation time, since path.Join collapses traversal segments and a
prefix like "../other-tenant" would otherwise escape the configured
isolation boundary.
Harden the prefix helper as a second line of defense: if the joined
path no longer sits inside pathPrefix, fall back to joining the prefix
with the base name of the caller-supplied path. That preserves the
prefix invariant for plugin and import paths that the upload code does
not sanitize uniformly.
------
AI assisted commit
* Honor SkipVerify when constructing the Azure client
FileBackendSettings.SkipVerify is plumbed through from the System Console
the same way it is for S3, so admins toggling the flag for self-signed
endpoints (Azurite, sovereign clouds) get the behavior they expect
without having to drop SSL entirely and send the shared key in clear
text.
------
AI assisted commit
* Warn when the Azure request timeout falls back to its default
Config.IsValid already rejects non-positive AzureRequestTimeoutMilliseconds
for any path that goes through config validation, so this warn only fires
for direct callers that bypass validation (tests, helpers). Logging the
substitution turns a silent coercion into something an operator can
correlate against unexpected request behavior.
------
AI assisted commit
* Cap Azure request timeout at 10 minutes
Reject AzureRequestTimeoutMilliseconds values above the ceiling so an
operator (or someone who has admin access) cannot effectively disable
timeouts by setting the value to math.MaxInt64. A hung Azure call then
holds a goroutine open until the OS gives up.
Applies the same bound to ExportAzureRequestTimeoutMilliseconds. S3 has
the same gap; treating it is out of scope here but worth a follow-up.
------
AI assisted commit
* Refuse AppendFile on blobs without a committed block list
A blob written by another tool (Azure portal, azcopy, a migration script,
a plugin using Put Blob) has its content in the blob but an empty
committed-block list. Committing a new block list against such a blob
silently replaces the existing content with only the appended bytes.
Check the blob's properties before staging when the committed-block list
is empty, and refuse with a clear error if the blob has content. Same
hazard for an admin pointing the backend at an existing container with
pre-existing files.
Adds an integration test against Azurite to lock the behavior in.
------
AI assisted commit
* Surface truncated reads from azureRangeReader
Read closed the body cleanly and returned io.EOF even when the remote
stream terminated before the blob's content length. Callers (and any
retry layer above) then accepted a partial blob as complete.
ReadAt unconditionally rewrote io.ErrUnexpectedEOF to io.EOF, which made
truncated downloads indistinguishable from clean reads. That is exactly
what zip.NewReader consumes for archive readers, so the bulk-import
worker would silently import partial archives.
Read now closes the body, nils it, and returns io.ErrUnexpectedEOF when
EOF arrives before offset reaches size. ReadAt only collapses
ErrUnexpectedEOF to EOF when the full count was delivered and the stream
was consumed to the end of the blob. Otherwise the truncation
propagates with context.
Both code paths are exercised by new fakeDownloader-backed tests.
------
AI assisted commit
* Move container provisioning out of Azure TestConnection
Auto-creating the container inside TestConnection meant a typo in the
System Console (mattermosst instead of mattermost) silently provisioned
an unwanted container in the admin's Azure subscription, with no audit
log and no warning. They'd discover it later when uploads landed
somewhere unexpected.
TestConnection now returns FileBackendNoBucketError when the container
is missing, mirroring the S3 contract. A new MakeContainer method
mirrors S3FileBackend.MakeBucket, and Server.Start dispatches via two
capability interfaces (bucketMaker / containerMaker) instead of a hard
S3 type assertion — so the NoBucket error is no longer silently
swallowed for backends Server.Start has not been taught about.
------
AI assisted commit
* Carry file backend auth detail through to AppError
The Test Connection button collapsed every typed backend failure into
the same generic i18n message. Operators trying to debug bad credentials
or a missing bucket only saw "Unable to authenticate against the file
storage backend" with no SDK code to grep for in their logs.
Use errors.As so the typed checks survive future wrapping, and pass the
underlying error string through the NewAppError details argument. The
AppError serializer surfaces that detail to the admin console alongside
the translated message, so a bad S3 InvalidAccessKeyId or an Azure
AuthenticationFailed shows up in the toast without an i18n schema
change.
------
AI assisted commit
* Remove non-ascii characters from comments
------
AI assisted commit
* Make linter happy
------
AI assisted commit
* Harden Azure prefix boundary check
strings.HasPrefix on the joined path is a string-level check, not a
path-level one, so a configured prefix of "mattermost" accepts a joined
result of "mattermost-evil/...". A crafted caller path like
"../mattermost-evil/secrets" would collapse via path.Join to that exact
sibling and slip through the boundary check, escaping the configured
prefix scope.
Require the joined path to be the cleaned prefix itself or to start with
the prefix followed by a path separator. The fallback path.Join uses the
same cleaned prefix for consistency.
------
AI assisted commit
* Provision Azurite container in standalone test setup
The shared FileBackendTestSuite's SetupTest already handles a missing
container by detecting FileBackendNoBucketError from TestConnection and
calling MakeContainer, but TestAzureFileBackendAppendRefusesNonBlockBlob
bypasses SetupTest and calls TestConnection directly. On a fresh Azurite
instance the test would fail before exercising the append-refusal logic.
Extract a newAzuriteBackend(t) helper alongside azuriteSettings(t) that
builds the backend and ensures the container exists, mirroring the
suite's setup. Use errors.As for forward compatibility with future
wrapping.
------
AI assisted commit
* Fix grammar in email-settings i18n string
"Email settings has unset values." -> "Email settings have unset values."
------
AI assisted commit
* Make Azure MakeContainer idempotent
Treat a ContainerAlreadyExists response as success so that two nodes
racing through TestConnection plus MakeContainer at boot both converge
instead of having the loser fail. Mirrors how the S3 backend handles
the equivalent BucketAlreadyOwnedByYou case.
------
AI assisted commit
* Narrow AzureEndpoint comment to path-style only
The setting only builds path-style URLs, so it cannot reach sovereign
clouds like Azure Government or Azure China, which require vhost-style
endpoints. Update the comment to reflect what the code actually does
and document that sovereign-cloud support is out of scope.
------
AI assisted commit
|
||
|
|
9f1fe90b69 | Migrate CPA to the v2 Property System (#36180) | ||
|
|
4aa1c58e37 | ci: invalidate poisoned shard-timing cache and guard future saves (#36568) | ||
|
|
d1fb57bc37 | Add .envrc to .gitignore (#36567) | ||
|
|
0c98113a17 |
MM-65058 Make Direct Messages modal load GMs when needed (#36548)
* Changed batchGetProfilesInChannel to batchGetProfilesInGroupChannel and have it use bulk API * MM-65058 Add useUserIdsInGroupChannel and use to populate Direct Messages modal * Address feedback * Run Prettier * Fix types |
||
|
|
47d4720ff4 | chore(ci): consolidate openldap runner prep into a composite action (#36563) | ||
|
|
323841e9c5 |
Add board channel types (BO/BP) for Integrated Boards (#35887)
* Add board channel types (BO/BP) with POST /boards API
Introduces board channel types as a new channel variant that reuses the
Channels table but is fully isolated from all /channels endpoints.
Model:
- Add ChannelTypeOpenBoard ("BO") and ChannelTypePrivateBoard ("BP")
- Add IsBoard(), IsOpenBoard(), IsPrivateBoard() helpers
- Add board-specific websocket events (board_created/updated/deleted/restored)
Store:
- SaveBoardChannel: atomic channel + view creation in a single transaction
- Save() rejects board types (forces use of SaveBoardChannel)
- Exclude boards from all channel listing/search queries (GetTeamChannels,
GetAll, GetChannels, GetChannelsByUser, GetDeleted, autocomplete, search)
API:
- POST /boards: create board channel (feature-flagged behind IntegratedBoards)
- All /channels write endpoints reject board types with 400
- All /channels read endpoints reject or exclude board types
- Open boards get same public-read semantics as open channels
Tests:
- 15 rejection tests covering every /channels write + read endpoint
- 9 exclusion tests covering every listing/search endpoint
- 8 store tests for SaveBoardChannel + Save rejection
- 4 board creation API tests (create, private, flag off, sidebar exclusion)
- 3 authorization tests for board permission semantics
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Update generated files: i18n, go.mod, migrations list
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add i18n translations for board channel error strings
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix board guard ordering in getChannelMembers and getChannelStats
Move the board rejection check after the permission check so that
nonexistent channel IDs still return 403 (not 404) matching the
original behavior expected by TestGetChannelMembers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Filter boards at store level instead of API guards
Store.Get() now excludes board types via WHERE clause, making boards
invisible to all /channels endpoints. Added GetBoardChannel() for
/boards endpoints. Removed redundant API-level rejectBoardChannel
guards from 10 handlers that already call GetChannel(). Kept explicit
guards only on 3 handlers that don't fetch the channel.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix empty i18n translation for app.channel.save_member.app_error
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add board system properties, kanban column config, and audit logging
Migration:
- Register "boards" property group with system-wide Assignee (user) and
Status (select: Todo/In Progress/Complete) fields, both protected
- Idempotent migration following content flagging pattern
Board creation:
- Look up boards fields by name, set board:linked_properties on channel
- Build kanban view props with group_by mapping status options to columns
- Add typed KanbanProps/KanbanColumn/KanbanGroupBy structs with
ToProps()/KanbanPropsFromProps() for round-tripping
- Add audit record logging for POST /boards
- Add early team_id validation in API handler
- Error on missing status options instead of silent empty columns
Tests:
- Migration test: field creation + idempotent re-run
- Board creation test: verify kanban props + linked_properties
- Fix updateChannelMemberRoles test to use valid role string
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add boards migration mock to testlib store setup
The boards property migration calls System().GetByName() which needs
a matching mock expectation, same pattern as content_flagging_setup_done.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add kanban view props validation and tests
Validate kanban View.Props in IsValid(): group_by required with valid
field_id, 1-100 columns, each column needs id, name, and at least one
option_id. Update all test helpers to produce valid kanban props.
11 dedicated validation tests + round-trip test for KanbanProps.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Validate board display name is not empty
Add early DisplayName validation in CreateBoardChannel with a clear
error. Add tests for empty and whitespace-only display names.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Exclude boards from GetMany and getChannelsMemberCount
Add board type exclusion to Store.GetMany() and use filtered channel
IDs in getChannelsMemberCount handler so board channels don't leak
into member count results. Add test covering the endpoint.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix review issues: drop search indexing for boards, use request context
- Remove search layer indexing of board channels so they stay invisible
to Elasticsearch/Bleve-powered search and autocomplete
- Replace context.Background() with rctx.Context() for proper
cancellation and tracing in CreateBoardChannel
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix gofmt alignment in websocket_message.go after merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Regenerate server i18n after merge
* Restore translation for permission_policy.app_error
* Filter board channels in name lookups, autocomplete, and indexing
The store-layer board exclusion filter was missing from getByName,
getByNames, GetDeletedByName, the global Autocomplete, and
GetChannelsBatchForIndexing — leaving boards reachable via name
lookups, the no-team-filter search path, and admin reindex jobs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Reject boards in id-batch lookups, unread, and member-mutation endpoints
- GetChannelsByIds, GetChannelsWithTeamDataByIds, and GetChannelUnread
now exclude BO/BP at the store layer so boards can't slip through if
callers stop filtering first.
- updateChannelMemberNotifyProps, updateChannelMemberAutotranslation,
and viewChannel now reject board IDs explicitly via the existing
rejectBoardChannelByID helper, matching the other write endpoints.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Gate boards properties setup on the IntegratedBoards feature flag
doSetupBoardsProperties registered the boards property group and
fields at every server boot regardless of the IntegratedBoards
feature flag. Skip the migration when the flag is disabled so the
property metadata only appears once boards are actually enabled.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Use App accessors for boards property lookups
CreateBoardChannel reached into a.Srv().PropertyService() directly
instead of going through the App-level GetPropertyGroup and
GetPropertyFieldByName methods that already wrap the service. Switch
to the standard App accessors so the calls match the rest of the
codebase.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Log full channel input on createBoard audit record
createBoard only captured team_id and type on the audit record, so
failed creations lost most of the request payload. Use
AddEventParameterAuditableToAuditRec with the full channel struct,
matching createChannel.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Use allow-list of message channel types in store filters
Inverted every sq.NotEq{[BO, BP]} filter into sq.Eq{messageChannelTypes}
(or teamMessageChannelTypes for queries that also exclude direct
channels) so that any future non-message channel type — wikis, etc. —
is excluded by default rather than requiring every existing call site
to be updated. Also rewrote GetChannelUnread on top of the squirrel
builder so the same allow-list slice can be reused.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* go.mod: promote prometheus/common to direct after merge
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Use model.NewPointer for boards property permission field
Drops the local permNone variable in doSetupBoardsProperties and uses
model.NewPointer(model.PermissionLevelNone) inline, matching the
surrounding ContentFlagging/ManagedCategory code.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Extract saveViewT to share Views insert between Save and SaveBoardChannel
ViewStore.Save and SaveBoardChannel both built the same INSERT INTO
Views statement, so a future column addition would need updates in two
places. Extract the insert (plus PreSave/IsValid) into a private
saveViewT method that accepts any sqlxExecutor — the regular master
handle for ViewStore.Save, and the channel transaction for
SaveBoardChannel.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add IsMessageChannel helper on model.Channel
Mirrors IsBoard for the positive case: returns true for Open, Private,
Direct, and Group channel types. Lets future filtering code be
expressed against the allow-list rather than enumerating board types,
so newly introduced non-message channel types are excluded by default.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Move board input validation into Channel.IsValidBoard
CreateBoardChannel inlined four guards for type, team, and display
name. Move the type/team_id/display_name checks into a new
Channel.IsValidBoard method so the rules live with the model and
return the AppError directly. The TrimSpace on DisplayName stays at
the call site to match how CreateChannel sanitizes before validating.
Drops the now-unused app.channel.create_board_channel.{invalid_type,
no_team,no_display_name} translations and adds matching
model.channel.is_valid_board.* keys.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Extract buildBoardKanbanView from CreateBoardChannel
The kanban view construction (read status options, build columns,
serialize props, assemble *model.View) only depends on the status
property field and the creator id. Pulling it into its own helper
shrinks CreateBoardChannel and makes the column-building logic
testable in isolation.
Adds board_test.go with coverage for the empty-options error path,
the standard happy path, and the option-skipping branches.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Test Channel.IsValidBoard
Cover the four reject cases (wrong type, missing team_id, empty
display name) plus the open and private board accept cases.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* gofmt board_test.go
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Document POST /api/v4/boards in OpenAPI spec
* Add valid kanban props to api4 makeTestViewForAPI helper
* Assert kanban.ToProps error in makeTestViewForAPI helper
The helper used to swallow the error from kanban.ToProps. Take *testing.T
and require.NoError so a serialization failure surfaces immediately at the
call site instead of producing a malformed view.
* Run boards properties setup unconditionally
The feature-flag gate added in
|
||
|
|
11b55b77f3 |
Document Mattermost cloud startup flow (#36559)
* Add Cursor Cloud Agent Docker environment Co-authored-by: Cursor <cursoragent@cursor.com> * Fix Cloud Agent enterprise and Docker access Co-authored-by: Cursor <cursoragent@cursor.com> * Fix Cloud Agent Go path setup Co-authored-by: Cursor <cursoragent@cursor.com> * Use Cursor multi-repo enterprise checkout Co-authored-by: Cursor <cursoragent@cursor.com> * Use sibling enterprise checkout layout Co-authored-by: Cursor <cursoragent@cursor.com> * Add tmux to Cloud Agent image Co-authored-by: Cursor <cursoragent@cursor.com> * Document Mattermost cloud startup flow Co-authored-by: nick.misasi <nick.misasi@mattermost.com> * Document Mattermost license behavior in cloud Co-authored-by: nick.misasi <nick.misasi@mattermost.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
d8612e378f |
[MM-2541] Shortcut to mark all channels as read for a team (#34012)
* feat(webapp): added keyboard shortcut for Mark All As Read (MM-2541)
- Added shortcut (within sidebar) for Shift+ESC to mark _all_ messages, teams as read
- Desktop only
- Added feature toasts for new features and localStorage support
- Added feature toast for mark-all-as-read feature
- Should decide when/how people want this shown, I just followed designs
- Will only show if the user has not clicked 'Got it' before, and is not on mobile
- Added confirmation modal for mark all as read shortcut
- Contains option to not show again, saved in localStorage
- Added English translations for read shortcut
- Will need i18n aid on other languages
This is a draft version of this feature update that still needs testing and i18n support, along with a11y validation.
* feat(webapp): feature flags and fixes for mark all as read shortcut
- Added feature flags surrounding rollout of mark-all-as-read shortcut
- Added shortcut to list of shortcuts in help section
- Extended tests for new components
- Updated snapshot for sidebar_list, keyboard_shortcuts_modal
- Fixed styling and CSS issues
Still in draft, needs documentation and e2e support.
* fix(webapp): fixed some issues with new mark-all-read feature
- Scoped persistent storage to current user ID
so that subsequent new logins also get the notification
- Replaced LocalStorage calls with useGlobalState calls, sad
that I missed that this updated call was being used.
- Fixed an issue that would have caused the new shortcut to
show up in the Help menu's shortcuts without being enabled.
* Fixed a snapshot test and a missing i18n member
* Replaced useGlobalState with backend-ready usePreference. Previous version was just a mistake as we didnt know about the supported API
* fix(server): fix lint issue with gofmt
* feat(server,webapp): added cleaner and more effective method with which to mark-all-read
- Added 2 new routes to the API (need to find docs to update those):
- `PUT /api/v4/channels/members/<userId>/direct/read` will mark a user's non-team DMs and GMs as read
- `PUT /api/v4/users/<userId>/teams/<teamId>/read` will do a similar action as the multi-channel mark_read action, but with a teamId signifier. Because this is using a teamId, it will _not_ handle DMs or GMs.
- Updated sidebar_list.tsx to use these new routes for the new shortcut
- Added extensive testing, including feature flag assurance.
* fix from upstream changes
* fix: eslint errors in teams actions
* document new API endpoints
* fix i18n
* fix err id
* remove unused localhost methods
* use ShortcutKey and ShortcutSequence
* feature_enhancements, mark as read toast enchancements
* read all modal mount point, use openModal
* use handler
* fix style
* fix: fix refactoring typo
* Merge fix: realign branch with upstream changes
Upstream MM-67319/MM-67320 (#36037) moved ShortcutKey and
WithTooltip into the shared package and rewrote the keyboard
shortcuts test to snapshot real DOM instead of a
react-test-renderer tree. The merge resolution missed several
follow-on consequences; clean them up so the branch builds, type
checks, lints, passes i18n-extract-check and runs without
throwing at mount.
- Port the inline-content variant from the deleted channels-side
shortcut_key.scss to the new shared shortcut_key.css.
- Refresh the keyboard_shortcuts_sequence snapshot so it matches
Testing Library's container output (DOM only, no component
nodes, class= not className=).
- Repoint mark_all_as_read_modal and mark_all_as_read_toast at
components/shortcut_key for ShortcutKeys and use
ShortcutKeys.escape; the channels-side with_tooltip is now a
thin re-export and the field was renamed in the shared keys
map. Without this both consumers threw "Cannot read properties
of undefined" at mount.
- Switch mark_all_as_read_toast's UserAgent import to
@mattermost/shared/utils/user_agent; the channels-local
utils/user_agent path no longer resolves.
- Drop the orphan mark_all_threads_as_read_modal.cancel string
from en.json so formatjs extraction is in sync.
* Clean up TestReadAllInTeam
Drop four lines left from debugging and replace them with a real
assertion: LastViewedAtTimes must contain the test channel with a
value at or after the most recent post.
Update three client.GetChannel calls to the (ctx, id) signature;
the prior etag argument no longer compiles after upstream removed
it.
* Use SelectBuilder for team channels query
GetTeamChannelsWithUnreadAndMentions built a squirrel query and
then manually called ToSql before handing the string+args to
GetReplica().Select. SelectBuilder accepts the builder directly
and removes the intermediate dance, matching the pattern used
elsewhere in this store.
* Mark all team-channel threads on team read
MarkTeamChannelsAndThreadsViewed used Thread().MarkAllAsReadByTeam
unconditionally, writing every thread membership in the team for
the user even when nothing was stale. Scoping the call to
channelsToView (channels with unread channel-level messages) would
have closed the perf concern but introduced a regression: in CRT
mode a thread reply does not bump the channel's TotalMsgCount, so
a channel can be read at the channel level while still having
unread thread replies, and those would have been silently skipped.
Build the channel-id list from the keys of the times map instead.
GetTeamChannelsWithUnreadAndMentions already populates that map
for every team channel the user belongs to, so no extra query is
needed. MarkAllAsReadByChannels then filters the actual UPDATE
through its LastReplyAt > LastViewed clause, keeping writes
bounded to genuinely stale rows.
Gate the channel-level work (UpdateLastViewedAt, push clearing,
the MultipleChannelsViewed event) on channelsToView being
non-empty, but always run the thread mark and broadcast
ThreadReadChanged for every team channel so CRT clients refresh
thread state in channels that had no channel-level change.
* Mark mark-read audit records as success
The handlers for mark all DM/GM and mark team read created an
audit record with status Fail and never updated it on success,
so successful calls were always logged as failures.
* Mark all DM/GM threads on full read
MarkAllDirectAndGroupMessagesViewed early-returned when no
channel had unreads, so followed threads in DMs/GMs whose
channel-level counters were already current stayed unread under
CRT. Mirror MarkTeamChannelsAndThreadsViewed and call
MarkAllAsReadByChannels for every DM/GM in times.
* Polish DM/GM channels-with-unreads query
Use model.ChannelTypeDirect/Group constants instead of bare
"D"/"G" literals, and update the error wrap to mention DM/GM
channels (it was copied from the team variant).
* Fix stale ReadAllMessages godoc
* Type last_viewed_at_times as int64 map in OpenAPI
The response field was declared as a generic object. Add
additionalProperties so generated clients see it as a
channelId -> int64 timestamp map.
* Gate MarkAllAsReadToast mount on feature flag
The toast was mounted unconditionally, so its async chunk loaded
even when EnableShiftEscapeToMarkAllRead was off. Gate the mount
with the flag so the chunk only loads when the feature is on.
* Return data from markAllInTeamAsRead thunk
Match the {data: response} shape used by adjacent thunks instead
of returning {}, so callers can read the API payload.
* Coerce undefined suffix in createStoredKey
createStoredKey('foo') returned 'fooundefined' when the suffix
arg was omitted. Coerce a missing suffix to ''.
* Refactor mark-read websocket events
* Polish DM/GM channels-with-unreads query
* Fix import order in shortcut_key consumers
* Fix CI
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
Co-authored-by: Jesse Hallam <jesse@mattermost.com>
Co-authored-by: Caleb Roseland <caleb@calebroseland.com>
Co-authored-by: Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
|
||
|
|
8a8a4ac8b1 |
Add Session field to Subject (#36523)
|