Commit Graph
3191 Commits
Author SHA1 Message Date
Alejandro García MontoroandGitHub 2e3c0ff278 MM-69175: Fix broken CI steps (#36989)
* Fix broken migration/codegen CI self-checks

The git status self-checks in server-ci.yml became silent no-ops once
these jobs moved into the build container (#33679): the resolved shell
is sh, where the bash-only [[ ]] errors out, and git rejects the
checkout with "dubious ownership". Switch to POSIX [ ], mark the
workspace safe so git status runs, and print the diff on failure across
all affected checks.

* Regenerate stale migrations.list

The broken check-migrations step let an outdated migrations.list ship.
Regenerate it with make migrations-extract to add migration 000193.

* make mocks

* make gen-serialized

* make mmctl-docs
2026-06-10 13:54:10 +02:00
unified-ci-app[bot]GitHubunified-ci-app[bot] <121569378+unified-ci-app[bot]@users.noreply.github.com>Harrison HealeyMattermost Build
de0779d1bf Update latest minor version to 11.9.0 (#36976)
* Update latest minor version to 11.9.0

* Update update-versions script to include components package

* Update components package and its dependencies to 11.9.0

---------

Co-authored-by: unified-ci-app[bot] <121569378+unified-ci-app[bot]@users.noreply.github.com>
Co-authored-by: Harrison Healey <harrisonmhealey@gmail.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-06-10 05:43:51 +00:00
Ben CookeandGitHub cc1547ac46 [MM-68618] Harden file removals (#36427) 2026-06-09 13:27:09 -04:00
Devin BinnieandGitHub 684ddb32a9 [MM-68988][MM-68989][MM-68990][MM-68991][MM-68997][MM-68998] Session Attributes MVF - Server-work (#36934)
* [MM-68988][MM-68989][MM-68990][MM-68991][MM-68997] Session Attributes MVF - Server-work

* PR feedback

* [MM-68998] Add web app hooks for Desktop App to signal a refresh of attributes/manifest

* Fix types

* Adjust the test to test the license first

* PR feedback

* Coderabbit feedback

* More tests
2026-06-09 13:10:53 -04:00
Felipe MartinandGitHub 755925fb73 MM-68830: Preserve unknown permissions during migrations on downgrade (#36888)
* MM-68830: Preserve unknown permissions during migrations on downgrade

A server that was upgraded to a newer release (which introduced new
permissions and wrote them into roles) and then downgraded fails fatally
at startup: the permissions migration re-saves every role, and
Role.Save() rejects any permission the older binary does not recognize,
making the downgrade unrecoverable.

Add RoleStore.SavePreservingUnknownPermissions, used only by
doPermissionsMigration, which tolerates and preserves permissions this
build does not recognize (logging a warning) instead of rejecting the
role. The regular Save() — and therefore the role API path — stays
strict, so unknown permissions cannot be introduced through user input.

Unrecognized permissions are kept on disk so they are not lost on a
later re-upgrade.

* MM-68830: assert save forwarding in role cache tests

Address review feedback: assert the underlying store's Save and
SavePreservingUnknownPermissions are actually invoked (the cache
invalidation defer fires regardless of forwarding), and check the
returned errors.

* MM-68830: address review feedback

- Shorten log message in validateForSave
- Rename validationRole -> roleCopy for clarity
- Trim doc comments to describe behavior only
- List all unknown permissions in IsValidWithoutId error
- Assert specific error type in storetest

* MM-68830: add Role.Clone and use it in validateForSave

* MM-68830: add tests for Role.Clone

* MM-68830: fix scheme id deep copy assertion in Role.Clone test
2026-06-09 11:18:19 +02:00
27b2525e88 Fix flaky TestPluginAPIGetUserPreferences (#36855)
GetPreferencesForUser returns default preferences in non-deterministic
order from Postgres (no ORDER BY in preference_store.GetAll), but the
test asserted fixed slice indices. Look up each default preference by
category instead, matching TestPluginAPIUpdateUserPreferences (#36458).

Tests-only change. Verified with go test -run '^TestPluginAPIGetUserPreferences$' -race -count=100 ./channels/app.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
2026-06-08 10:52:55 -04:00
Felipe MartinandGitHub ca87bd7d24 MM-60669 Prevent bot users from becoming the first system admin (#36867)
On a fresh install, the first user created in the system is granted the
system_admin role based solely on the user store being empty. Bot users
created by plugins on a fresh database could therefore be promoted to the
first system admin.

Skip the first-user system_admin promotion for bot users so that only the
first non-bot user is granted the role.
2026-06-08 12:35:15 +02:00
Devin BinnieandGitHub 3fc5b94292 [MM-69115] Fixed issue where channels could end up in two categories (#36875)
* [MM-69115] Fixed issue where channels could end up in two categories

* Additional fix

* PR feedback
2026-06-04 13:22:59 +00:00
M-ZubairAhmedandGitHub ca19b0b834 Remove dynamic-virtualized-list from ignoreDependencies in config.yaml (#36897) 2026-06-04 09:14:20 -04:00
85dae1b884 MM-68417, MM-68420: API support for PAT expiry and admin policy settings (#36706)
* MM-68417, MM-68420: API support for PAT expiry and admin policy settings

POST /users/{id}/tokens now accepts a client-supplied expires_at
(previously stripped per the TODO in api4/user.go), and the create app
method enforces two new ServiceSettings:

  - EnforcePersonalAccessTokenExpiry (bool, default false): when on,
    rejects creates with expires_at == 0
  - MaximumPersonalAccessTokenLifetimeDays (int, default 0 = unlimited):
    caps how far in the future expires_at may be

Rejections return distinct app error ids so clients can disambiguate:
expires_at_required, expires_at_in_past, expires_at_too_far.

GET /users/{id}/tokens already serializes expires_at via the model's
JSON tag added in MM-68419; clients derive token status (active /
expired / inactive) from is_active + expires_at without a separate
server-side field, keeping the response shape minimal.

The Client4 helper CreateUserAccessToken gained an optional variadic
expiresAt parameter (and the mmctl Client interface + mock match)
rather than introducing a parallel WithExpiry method.

Refs: MM-68417, MM-68420

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* MM-68417: exempt bot accounts from PAT expiry enforcement

Mirrors the existing EnableUserAccessTokens bypass at session.go:453,
where bot tokens are allowed even when human PATs are disabled. Bots
are programmatic clients that typically need long-lived credentials,
and integrations that provision them would otherwise break the moment
an admin enables EnforcePersonalAccessTokenExpiry — turning a settings
toggle into a footgun. The expiry policy now applies only to human
users; bots can still be given a future expires_at by callers that
want it, but the server won't require one.

Locked in by a new TestCreateUserAccessToken/bot_tokens_are_exempt
subtest that enables enforcement plus a 30-day cap, creates a bot,
and asserts a non-expiring token is accepted.

Refs: MM-68417

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* MM-68420: bound MaximumPersonalAccessTokenLifetimeDays in isValid

Negative values silently meant "unlimited" (the runtime check is `> 0`),
and very large values overflow int64 when computing
`now + days*86_400_000` at token-creation time, producing a wrap-around
that either rejects all reasonable expiries or accepts past timestamps
as valid.

Bound the setting in ServiceSettings.isValid to [0, MaxPersonalAccess
TokenLifetimeDays] where the cap is 36500 (100 years) — past any
realistic operational use and well clear of int64 overflow. Surfaces
as a config validation error rather than a silently-broken runtime
check. New TestServiceSettingsIsValid cases lock in zero, negative,
upper-bound, and above-upper-bound behavior.

Refs: MM-68420

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* MM-68417: reject multiple expiresAt values in Client4 helper

CodeRabbit caught that the variadic CreateUserAccessToken silently used
expiresAt[0] when callers passed more than one value, masking a
mis-call instead of failing fast. Return an error in that case so the
misuse is visible at the call site rather than producing a token with
the wrong (or right-but-coincidental) expiry.

Refs: MM-68417

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* MM-68417: make expiresAt a required parameter on Client4.CreateUserAccessToken

Drop the variadic in favor of a regular int64 parameter. The variadic
form silently dropped extra values and made a misuse undetectable at
the call site (per CodeRabbit review on PR 36706); the previous fix
guarded against >1 values at runtime, but a required parameter is
strictly better — the compiler now refuses the misuse and every caller
is forced to make a deliberate decision about expiry. Existing callers
that want the old behavior pass 0 (== never expires).

Refs: MM-68417

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* MM-68417: add --expires-in flag to mmctl user token generate

Adds an --expires-in duration flag so operators can create expiring
PATs via the CLI. Accepts the standard Go duration syntax plus a
trailing 'd' for days (the common case for token lifetimes), e.g.
--expires-in 90d, --expires-in 12h, --expires-in 1h30m. Empty (the
default) means no expiry — matching prior behavior. Without this
flag the command was unusable once an admin enables
EnforcePersonalAccessTokenExpiry, since every create would fail with
app.user_access_token.expires_at_required.app_error.

Flag parsing now happens before the user-lookup API call so the
command fails fast on invalid input. Regenerated mmctl docs reflect
the new flag.

Refs: MM-68417

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* MM-68417: cap --expires-in day count to prevent time.Duration overflow

parseExpiresIn returns time.Duration(days) * 24 * time.Hour, which is
int64 nanoseconds and overflows past ~106751 days (CodeRabbit caught
this). Cap at model.MaxPersonalAccessTokenLifetimeDays (36500) so the
CLI rejects values the server would reject anyway, well below the
int64-overflow point. Adds two test cases (at-cap and beyond-cap) to
TestParseExpiresIn.

Refs: MM-68417

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* MM-68420: collapse PAT expiry settings into a single max-lifetime setting

Remove ServiceSettings.EnforcePersonalAccessTokenExpiry and fold the
policy onto MaximumPersonalAccessTokenLifetimeDays: 0 means no policy
(never-expiring tokens allowed, no cap), while a value > 0 requires every
new token to expire within that many days. The two-setting design let an
admin set a maximum but leave enforcement off, silently allowing
never-expiring tokens to sidestep the cap; the only combination the
boolean added (require expiry, no upper bound) has little practical
value. The removed field was introduced on this branch and never
released, so this is not a breaking change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-04 14:21:35 +02:00
fb8cfbaef7 Fix flaky TestSharedChannelPostMetadataSync (#36862)
* Fix flaky TestSharedChannelPostMetadataSync

STEP 6 incorrectly required Cluster A to receive sync traffic after a
resync trigger. When echo prevention suppresses unchanged acknowledgement
payloads, no message arrives and the Eventually timeout fails. Wait for
pending sync tasks, assert the DB still has exactly one acknowledgement,
and only validate sync payload duplicates when traffic is received.

Tests-only change. Verified with `go test -run '^TestSharedChannelPostMetadataSync$' -race -count=50` locally.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Assert sync payload ack count outside muA lock

Copy the matching post under muA before calling require.Len so a
failed assertion cannot leave the mutex locked and hang teardown.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Retrigger CI after Playwright infra flake

Co-authored-by: mattermost-code <matty-code@mattermost.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
2026-06-03 12:15:45 -04:00
Devin BinnieandGitHub 50952dec3f [MM-68648] Implement GetForGroup to get fields in the Property System, add caching for fields (#36836)
* [MM-68648] Implement GetForGroup to get fields in the Property System, add caching for fields

* Re-add CRUD functions for the cache to invalidate when updates happen
2026-06-03 10:39:53 -04:00
61643e1066 MM-68952: Resolve public channel mentions for non-members under Compliance (#36815)
* MM-68952: Resolve public channel mentions for non-members under Compliance

Channel mention name resolution reused HasPermissionToReadChannel, a
content-read check that returns false for non-members of a public channel
when Compliance Monitoring is enabled (MM-45272) or when the channel is on
another team (MM-66791). As a result, the channel_mentions post prop was
stripped per-viewer (since #34235), and the webapp fell back to rendering the
raw (anonymized) channel slug instead of a clickable link.

Introduce HasPermissionToResolveChannelMention, which exposes only a public
channel display name and link (not content) and is therefore independent of
ComplianceSettings, while still requiring team membership for public channels
(blocks cross-team disclosure) and channel membership for private/DM/GM
channels. Switch the three mention call sites (FillInPostProps,
sanitizeChannelMentionsForUser, channelMentionsBroadcastHook) to the new
helper. HasPermissionToReadChannel and all content-read paths are unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>

* MM-68952: Add author-side and E2E coverage for channel mention resolution

Add a Go test (TestFillInPostPropsChannelMentionResolution) that exercises the
author-side persistence of the channel_mentions prop in FillInPostProps. It
locks in the new behavior: an author who is a team member but not a member of a
referenced public channel now persists the mention prop even when Compliance
Monitoring is enabled, while public channels on other teams and private
channels the author is not in are still dropped.

Add a Playwright spec (channel_mention_resolution.spec.ts) with a license-free
cross-team case (a public channel mention stays unresolved for a viewer not on
the channel's team) and a license-gated case (with Compliance enabled, a team
member who is not in the channel sees the resolved mention link).

Co-authored-by: Cursor <cursoragent@cursor.com>

* MM-68952: Tighten channel mention test assertions and fix lint

Use strings.Builder when assembling the test message to avoid the
golangci-lint stringsbuilder (modernize) warning about string += string
in a loop. Tighten the cross-team public and private non-member cases to
assert.Nil on the resolved mentions so the contract requires no persisted
channel_mentions map rather than merely an empty one.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 08:42:39 -04:00
6dac3b9df4 Harden post action request verification (#36840)
* MM-69065 - Authorize post actions against the target post's channel

Require a supplied action cookie to belong to the post named in the request so the authorized channel always matches the channel where the action runs.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Rename test client variable to nonMember

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 08:41:01 -04:00
ab31663fce MM-69010: Validate incoming webhook user membership (#36811)
* MM-69010: Validate incoming webhook user membership

Incoming webhook creation/update did not verify that the assigned
user_id had legitimate access to the target team or channel, allowing a
team admin to attribute persisted posts to an arbitrary user.

Validate that the assigned user can read the target channel and does not
hold privileges the requester lacks at creation, re-check channel access
when a hook is moved, and require a shared team before a webhook creates
a direct message via an @username payload.

Co-authored-by: Cursor <cursoragent@cursor.com>

* MM-69010: Add regression test for owner+channel update

Verify that changing both the channel and the supplied user_id in a
single update still validates against the retained owner, since the
owner is immutable on update.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-06-02 20:26:42 -04:00
1e0bdaf068 MM-69057: Verify post ownership on inbound shared-channel edit/delete (#36814)
The inbound shared-channel sync handler applied edits and deletes from a
remote cluster without checking that the existing post belonged to that
remote, allowing a remote to modify or delete posts it did not own.

Enforce the same ownership check already used for reactions and
acknowledgements before editing or deleting a synced post, and add
regression tests for the cross-remote cases.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 16:11:55 -04:00
Julien TantandGitHub 6ef5d58b7f Board channel bookmarks with target_id and readonly bookmark API (#36572)
Automatic Merge
2026-06-02 21:24:05 +02:00
9d27c06085 Restrict group_constrained to channels that support group sync (#36812)
* Restrict group_constrained to channels that support group sync

Enforce that the group_constrained flag can only be applied to public
and private channels across the API handler, model validation, and the
membership cleanup query.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Reject group_constrained on board channels

Extend validation to board channel types (BO/BP) in model, API patch,
and group member cleanup query. Add SupportsGroupSync helper.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
2026-06-02 14:17:12 -04:00
Alejandro García MontoroandGitHub 8e8b807a42 MM-68665: Implement FileBackendWithLinkGenerator for Azure (SAS for export downloads) (#36758)
* Implement FileBackendWithLinkGenerator for Azure (SAS for export downloads)

Restores feature parity with the S3 driver for the optional presigned
export-download path. Today on Azure-backed deployments the path falls
through with "driver doesn't support link generation"; with this change
admins can opt into direct downloads of bulk export archives just like
they can on S3.

Auth-mode aware:

* Shared key signs a Service SAS in process with the credential the
  backend was constructed with.
* Default credential fetches a user-delegation key from Entra ID per
  call and signs a user-delegation SAS with it.

The link forces Content-Disposition: attachment to mirror the S3
driver's response-content-disposition behavior, and pins HTTPS-only when
the backend was configured with TLS so the SAS cannot be exfiltrated
over plaintext. Plaintext setups (Azurite, on-prem reverse proxies) keep
working because we fall back to allowing both schemes.

Adds ExportAzurePresignExpiresSeconds to FileSettings, defaulting to
21600 seconds (6h), to match the S3 export presign field. The primary
backend never issues SAS, so no AzurePresignExpiresSeconds.

Covered by new unit tests against Azurite (Service SAS round trip,
tamper detection, missing configuration, unknown auth mode). The user-
delegation SAS path needs Entra ID and is verified manually per the
recipe in the docs PR.

------
AI assisted commit

* Rename GeneratePublicLink's argument p to path

* Improve comment on AzureFileBackend.sharedKey

* Simplify the clock skew fix

* Remove MaxAzurePresignExpiresSeconds
2026-06-02 14:29:58 +00:00
127552ce84 Add user setting to disable auto-follow on channel-wide mentions (#36068)
* Add server config to disable auto-follow on channel-wide mentions

Adds `ServiceSettings.ChannelMentionAutoFollowThreads` (default: true)
which, when disabled, prevents @channel/@here/@all mentions in thread
replies from automatically adding users as thread followers. Users still
receive mention notifications; only the thread membership is skipped.

* Refactor: move channel-mention auto-follow to per-user notification setting

Replaces the server-level ServiceSettings.ChannelMentionAutoFollowThreads
config with a per-user notification preference
channel_mention_auto_follow_threads (default: true).

Users can now opt out individually via Notification Settings ->
"Auto-follow threads on channel-wide mentions" (placed above
"Keywords that trigger notifications"), without requiring admin
intervention. Behavior is unchanged for users who have not modified the
setting.

* Add additional test case

* linter fixes and webapp snapshot update

* update user setting description

* em dash removed in description

* Update E2E tests

* prettier:fix

---------

Co-authored-by: gtsaturyan <gtsaturyan@ozon.ru>
Co-authored-by: Harrison Healey <harrisonmhealey@gmail.com>
2026-06-02 09:11:59 -04:00
Ibrahim Serdar AcikgozandGitHub 1b3dc63784 [MM-69078] Surface plugin upload rejections as a toast (parity with download rejections) (#36838)
* Surface plugin upload rejections as a toast (parity with download rejections)

* add some tests
2026-06-02 12:16:35 +02:00
19e7a2be28 Fix flaky TestCheckUsersEmojiIntegrity (#36756)
* Fix flaky TestCheckUsersEmojiIntegrity

Integrity checks scan the full database for orphaned emoji rows, so
parallel sqlstore tests and leftover rows from sibling tests can inflate
global record counts and flip index-based assertions.

Reset tables at test start and scope the one-record assertion to the
emoji child ID created in that subtest.

Tests-only change. Verified compilation locally; full test loop requires
PostgreSQL (CI).

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* ci: nudge CodeRabbit after all checks green

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Address PR feedback: 1 items resolved, 0 declined

* Use t.Cleanup with require.NoError for emoji test fixture cleanup

Replace silent defer dbmap.Exec cleanup calls with t.Cleanup handlers
that assert on errors, addressing CodeRabbit review feedback.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
2026-06-02 14:11:15 +05:30
Harrison HealeyandGitHub a84032f40a MM-69053 Log server message when a user has concurrent React enabled (#36837)
* MM-69053 Log server message when a user has concurrent React enabled

* Add session_id and user_id to server-logged messages sent by the client

* Fix linting

* Fix test
2026-06-02 05:43:55 +00:00
5c360d8077 MM-68995: reject deactivated guests on REST magic-link login (#36746)
Apply CheckUserAllAuthenticationCriteria after guest magic-link token
authentication in POST /api/v4/users/login, matching the web one-time-link
handler and password login paths.

Add regression test ensuring deactivated guests receive 401 inactive while
active guests can still log in via magic_link_token.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>
2026-06-01 16:33:24 -07:00
ea6ac3f229 MM-68983: Tighten OAuth token issuance and cleanup on user deactivation (#36743)
Reject OAuth grants for users with DeleteAt != 0 across the
implicit, authorization code, and refresh token paths, and purge
stored OAuth access data for the user during deactivation.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 11:58:17 -04:00
Jesse HallamandGitHub 5283ca54b4 add PushNotification.Transport and related const (#36829)
* add PushNotification.Transport and related const

Supporting change for
https://github.com/mattermost/mattermost-plugin-calls/pull/1189,
originally in https://github.com/mattermost/mattermost/pull/36726.

* updated API docs to match
2026-06-01 14:12:44 +00:00
307452bb55 [MM-67113] Add license preview/diff view when uploading a new license (#34877)
* [MM-67113] Add license preview/diff view when uploading a new license

Add a preview step when uploading a new license file in the Admin Console,
allowing administrators to review differences before applying.

Backend:
- Add POST /api/v4/license/preview endpoint
- Add PreviewLicenseFile method to Go client

Frontend:
- Add previewLicense method to TypeScript client and Redux action
- Add LicenseDiffView component to display license comparison
- Refactor UploadLicenseModal to 3-step flow: loading → preview → success
- Fix License type field name (sku_short_name)
- Update date formatting on License details page for consistency
- For "entry" licenses, show only new license info (no comparison)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-06-01 11:34:23 +02:00
Alejandro García MontoroandGitHub bcf5119682 MM-68854: Dispatch Test Connection on ExportDriverName when dedicated export filestore is active (#36759)
* Dispatch Test Connection on ExportDriverName when dedicated export filestore is active

The /file/test handler picked the mandatory-field check based on the primary
DriverName even when the backend that gets constructed and tested is the
export backend. A deployment with primary=S3 / export=Azure (or vice versa)
therefore ran the wrong driver's field validation -- e.g. surfaced an
S3-bucket error while the admin was configuring Azure, or skipped the Azure
mandatory-field check entirely when the primary was Local.

Introduce App.UseExportFileStore() / App.ResolvedFileStoreDriverName() and
have the handler dispatch through them so validation, field selection inside
the mandatory-field checks, and backend construction all agree on which side
of the filestore is being tested.

------
AI assisted commit

* Use refactored boolean condition in both branches
2026-05-29 16:34:54 +00:00
9fdcad41c2 Fix flaky TestCheckTeamsChannelsIntegrity (#36754)
Integrity checks scan the full database for orphaned channels, so
parallel sqlstore tests and leftover rows from sibling subtests can
inflate global record counts and flip index-based assertions.

Reset tables at test start and scope assertions to child IDs created
in each subtest. Use ElementsMatch for the two-record direct-channel
case so merge order cannot flake.

Tests-only change. Verified with `go test -run '^TestCheckTeamsChannelsIntegrity$' -race -count=100` locally.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
2026-05-29 09:36:29 -04:00
Ibrahim Serdar AcikgozandGitHub 1f4f1b4c59 [MM-69025] Enable session attributes in simulation (#36773)
* Unblock session attributes from simulation

* lint
2026-05-29 14:32:47 +02:00
e3823a51ce Fix flaky TestScheduleOnceSequential (#36805)
The subtest used a fixed sleep before asserting callback counts, but
job.run waits until runAt plus up to scheduleOnceJitter. Under the race
detector or loaded CI that window can elapse after the sleep, so
newCount3 is still 0 and the assertion flakes.

Poll with require.Eventually (same approach as the paging subtest in
#35891) so the test waits for the scheduled callback without weakening
assertions.

Tests-only change. Verified with:
go test -run '^TestScheduleOnceSequential$/adding_two_callback_works' \
  -race -count=100 ./pluginapi/cluster/... (from server/public)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
2026-05-29 12:39:07 +02:00
495fbc8437 MM-68978 - Harden ABAC masking guards and fix sentinel detection (#36740)
* MM-68978 - Harden ABAC masking guards and fix sentinel detection

* address pr feedback

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
2026-05-29 11:49:58 +02:00
5ad66d3f76 Fix flaky TestUpdatePropertyValues_WriteAccessControl (#36784)
* Fix flaky TestUpdatePropertyValues_WriteAccessControl

GetPropertyValues delegates to PropertyValueStore.GetMany, which does not
preserve the caller's ID slice order. Subtests asserted on retrieved[0] and
retrieved[1] by position, so a planner that returns rows sorted by ID or
UpdateAt (e.g. with idx_propertyvalues_groupid_updateat_id) could swap
values and fail intermittently.

Look up returned values by ID before comparing JSON payloads.

Tests-only change. Verified with `go test -run '^TestUpdatePropertyValues_WriteAccessControl$' -race -count=100` locally.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Refactor property value test helper

Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com>

* Remove redundant property test nil checks

Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com>
2026-05-29 08:53:34 +00:00
David KrauserandGitHub 800810e880 [MM-69028] Enable ClassificationMarkings feature flag by default (#36776) 2026-05-28 10:42:49 -04:00
1379beae98 MM-68840: Apply team sanitization on scheme teams endpoint (#36640)
* MM-68840: Apply team sanitization on scheme teams endpoint

Align GET /api/v4/schemes/{scheme_id}/teams with the team-returning
handlers in server/channels/api4/team.go by calling
App.SanitizeTeams before marshaling the response.

Adds a regression test that locks in the sanitized response for
non-admin callers and a positive control for system admins.

Co-authored-by: Cursor <cursoragent@cursor.com>

* MM-68840: Drop descriptive comments from regression test

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 08:43:16 -04:00
bf39b41ca8 MM-68845: Tighten authorization on /share-channel autocomplete (#36662)
* MM-68845: Tighten authorization on /share-channel autocomplete

Require manage_shared_channels for the /share-channel slash command's
dynamic autocomplete suggestions, matching the equivalent gate already
enforced by /secure-connection autocomplete and by /share-channel
command execution.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Clean up test comments and naming on /share-channel autocomplete tests

Drop verbose test docstrings, rename helpers and subtests to neutral
behavior-contract phrasing.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 08:41:37 -04:00
23d83b74d2 MM-65723 Validate user auth update requests (#36749)
* MM-65723 validate user auth updates

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* MM-65723 assert auth update persistence

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* MM-65723 move auth service allowlist into model

Extracts the allowlist used by isValidUpdateUserAuthRequest into a new
model.IsValidUserAuthService helper next to IsSSOUser/IsOAuthUser so the
auth service list lives next to its constants. Adds a unit test for the
helper.

* go mod tidy

Drops stale github.com/bep/imagemeta v0.12.0 entry left in go.sum.

* Address PR feedback: 2 items resolved, 1 declined

* Address PR feedback: 1 items resolved, 1 declined

* Address PR feedback: 1 items resolved, 3 declined

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
Co-authored-by: Julien Tant <julien@craftyx.fr>
2026-05-28 09:30:44 +00:00
Ben SchumacherandGitHub b632c9ed1c Stop logging email subject when sending mail (#36765)
The "sending mail" Info log includes the subject line, which for DM and
mention notifications contains the sender's name and other PII visible
to anyone with log access. The "to" address is enough operational
signal to confirm that a notification fired.
2026-05-28 09:14:12 +02:00
David KrauserandGitHub a6e019863e [MM-68999] Add SchemaVersion to PropertyGroup for group-specific field schema versioning (#36747) 2026-05-27 14:27:16 -04:00
159ed5ad96 Return error when plugins use deprecated custom_profile_attributes group name (#36748)
The plugin API was silently redirecting calls using the old
"custom_profile_attributes" group name to the new "access_control" group.
Replace the silent alias with an explicit error so plugin developers get
a clear message telling them what to change.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 12:24:01 -04:00
Harshil SharmaandGitHub 88e48a0e79 Fixed a bug where deleted post was broadcasted by the server and rest… (#36646)
* Fixed a bug where deleted post was broadcasted by the server and restored posts weren't visible

* i18n fix

* Handled a possible undefined dereference

* Undeleted post in-place instead of fetching from database

* i18n fix

* Added currrent channel check
2026-05-27 19:02:31 +05:30
472e4a01d2 MM-68664: Microsoft Entra ID / Default Credential authentication for Azure Blob Storage (#36733)
* Add DefaultAzureCredential authentication for Azure Blob Storage

Adds a second authentication mode for the Azure filestore backend
alongside the existing shared-key path. The new "default credential"
mode constructs azidentity.NewDefaultAzureCredential, which discovers
managed identity, workload identity, service principal env vars, and
az login in that order at runtime - the standard Microsoft pattern for
host-provided identities.

The credential type is configured via FileSettings.AzureAuthMode (and
ExportAzureAuthMode for the dedicated export store). Both default to
shared_key so existing deployments are unaffected. The access-key field
is only required under shared_key; default_credential reads identity
from the host environment and needs no per-mode config.

------
AI assisted commit

* Add Azure authentication selector to the System Console

Adds an "Azure Authentication" dropdown to both the primary file-storage
panel and the dedicated export-store panel. Two options: "Shared key"
(the existing default) and "Default credential (Microsoft Entra ID)".
The Azure Storage Account Key field is hidden when default credential
is selected; it has no role in that auth mode.

The Cypress spec is extended to cover the new dropdown's visibility
toggling.

------
AI assisted commit

* Use fmt.Errorf instead of pkg/errors

* Do not support empty AzureAuthModeSharedKey

There is no need to support legacy settings when a feature is not yet
released.

* Bring in master's Azure Blob Storage Cypress spec and scroll the access key into view

Two related changes:

- The merge commit just before this one missed master's MM-68787 updates
  to the Azure Blob Storage Cypress spec (the AzureClouddropdown
  visibility, the disabled -> not.exist tightening when S3 driver is
  selected, and the new "shows the custom endpoint only for the Custom
  cloud" test). This commit pulls those in.
- The new "hides the access key when the authentication mode is default
  credential" spec asserts the access key field is visible immediately
  after selecting the Azure driver. With the AzureAuthMode and (now
  landed) AzureCloud dropdowns above it, the field sits below the
  visible area of the System Console scroll container, and Cypress's
  strict be.visible check fails on overflow clipping. scrollIntoView
  mirrors what the Test Connection spec already does for the same
  reason.

------
AI assisted commit

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
2026-05-27 10:57:26 +02:00
Harshil SharmaandGitHub d563fdd5ac Data spillage report api use available data (#36699)
* Read available value when viable

* Added tests

* Restored package lock

* CI

* Used saved actor details when available

* i18n fix

* Updated data in log
2026-05-27 12:26:02 +05:30
8a957afce2 [MM-68458] Improve diagnostics.yaml readability: reorder server fields and add inline YAML comments (#36703)
* MM-68458: Improve diagnostics.yaml readability — reorder server fields and add inline comments

Regroup the SupportPacketDiagnostics.Server fields into Machine /
Capacity / Process lifecycle / Software clusters (declaration order
determines YAML emit order in goccy/go-yaml), and emit YAML head /
line comments at marshal time via yaml.MarshalWithOptions +
yaml.WithComment.

Comments cover the non-obvious cases support engineers hit during
triage: host RAM vs cgroup quota, omitempty fields that silently
disappear when no container limit is set, sql.DBStats counters that
are cumulative-since-process-start, and PostgreSQL-only fields that
vanish on MySQL. file_store: local-driver-only fields are similarly
annotated.

No struct reorder outside server:; no public-API or JSON shape
changes; no new dependencies (goccy/go-yaml already supports
WithComment).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* gofmt: realign diagnosticsYAMLComments map literal

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 08:43:42 +02:00
ecd72f79bd MM-68787: Support sovereign-cloud endpoints for Azure Blob Storage (#36732)
* Support sovereign-cloud endpoints in the Azure Blob Storage backend

Adds an AzureCloud enum to FileBackendSettings and the matching
FileSettings / ExportFileSettings entries, defaulting to commercial.
The supported values pick the service URL the SDK signs against:

  - commercial: vhost-style against blob.core.windows.net, exactly
    the existing default. Only the storage account name is needed.
  - government: vhost-style against blob.core.usgovcloudapi.net, no
    user-supplied endpoint. Only the storage account name is needed.
  - custom: the admin-provided AzureEndpoint is the full service URL,
    scheme and storage account included, and Mattermost passes it to
    the SDK unchanged. Covers Azurite, reverse proxies, Azure China,
    and any future Azure cloud Mattermost has not codified a preset
    for.

Wires the new field through NewFileBackendSettingsFromConfig,
NewExportFileBackendSettingsFromConfig, ConfigToFileBackendSettings,
SetDefaults, the System Console (File Storage and Export Storage
panels) with matching i18n strings, and a dedicated Cypress spec.
Azurite test settings switch to AzureCloud=custom with the full
http://host:port/account/ URL so the existing integration suites
keep driving Azurite without any conditional URL plumbing inside the
driver. buildAzureServiceURL is now total: it returns an error for
custom-without-endpoint and for unknown enum values rather than
producing a malformed URL.

------
AI assisted commit

* Hide S3 File Storage fields when a different driver is selected

Match the existing Azure-field behavior: when the selected file storage
driver is not Amazon S3, hide the S3-only fields entirely instead of
just disabling them. Both driver groups already use this hide-when-
inactive pattern in the dedicated export storage section.

The Cypress assertion in azure_blob_storage_spec.js that codified the
old disable-only behavior is updated to expect not.exist.

------
AI assisted commit

* Address review feedback on AzureCloud validation and config-test endpoints

- Switch FileSettings.isValid AzureCloud / ExportAzureCloud value
  checks from slices.Contains to a switch statement, matching the
  closed-enum style used a few lines above for DriverName.
- Revert the io.EOF suppression in testFileStore and testEmail. The
  suppression was bot-suggested log-noise polish; reviewer prefers
  to keep the original error handling for those endpoints.
- Drop the redundant "http(s)" qualifier from the scheme check comment
  in buildAzureServiceURL.

------
AI assisted commit

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
2026-05-26 16:34:48 +00:00
Devin BinnieGitHubcoderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>Mattermost Buildmaria.nunez
d9c1388461 [MM-68649] Add Session Attributes from user agent for use in Permission Policies (#36511)
* [MM-68649] Add Session Attributes from user agent for use in Permission Policies

* Update server/channels/app/session_attributes.go

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Fix test

* fix i18n

* Allow session attributes for permission policies when no user attributes are configured

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
Co-authored-by: maria.nunez <maria.nunez@mattermost.com>
2026-05-26 11:24:03 -04:00
Jesse HallamandGitHub 67b49ad5c0 MM-68154: Upgrade imagemeta to v0.17.2 (#36588)
* MM-68154: Upgrade imagemeta to v0.17.2

* MM-68154: Add test coverage for GetImageOrientation edge cases

Cover MIME-type format strings ("image/jpeg") and unsupported formats.

* fixup! MM-68154: Upgrade imagemeta to v0.17.2
2026-05-25 21:05:10 +02:00
Doug LauderandGitHub e1189a3005 MM-68914 - Fix DM/GM channel member import defaulting SchemeUser to false (#36661)
* Fix DM/GM channel member import defaulting SchemeUser to false

  The direct-channel participant import path only assigned scheme flags
  when the import data carried them, so imports that omitted those
  fields (notably mmetl's Slack export) produced channel members with
  no effective role. The web client then routed those channels to the
  "missing channel role" loading state and the message input never
  loaded. A customer reported 444 affected channel member rows for a
  single user.

  Default SchemeGuest and SchemeUser from the resolved user before the
  existing nil-checked overrides, matching the regular user-channel
  import path at lines 1273-1275. Explicit values in the import data
  still win. Adds regression tests for both DM and GM channels.

  Fixes MM-68914.

* Use fresh users in SchemeUser regression tests

  The new subtests added in the previous commit reused BasicUser,
  BasicUser2, and user3, which already had channels created by earlier
  subtests in TestImportImportDirectChannel with non-zero LastViewedAt.
  The freshness guard in importDirectChannel then skipped the
  participants and the assertions ran against pre-existing channel
  members rather than the import path. As a result the tests passed
  regardless of whether the fix was present.

  Switch to fresh users created via th.CreateUser so there is no
  pre-existing channel, no LastViewedAt mismatch, and
  UpdateMultipleMembers actually runs with the import-built members.
  Verified by reverting the fix in import_functions.go: both subtests
  now fail without the fix and pass with it.
2026-05-25 14:30:43 -04:00
Jesse HallamandGitHub 462f34ac6c Generate default_roles_permissions.js from a live server snapshot (#36698)
* Generate default_roles_permissions.js from a live server snapshot

Replace the hand-maintained default_roles_permissions.js with a
generated file. The generator (scripts/default_permissions_generator/)
boots a real app.NewServer() against a temporary database created by
storetest.MakeSqlSettings, lets all schema and app-level migrations run
naturally, then snapshots the resulting Roles table into the JS file.

This eliminates the drift risk of the previous hand-maintained map:
new migrations are picked up automatically because the same server
initialization path is used here as in production.

The CI job gets a postgres service and sets IS_CI=true so the generator
uses the same host-switching logic as the rest of the test suite.
Locally, make start-docker provides the postgres instance.

* Address CodeRabbit feedback: pin checkout SHA, add permissions, add .PHONY entries
2026-05-25 15:03:11 -03:00
c6b59cc9a7 MM-68663: Admin console support and Test Connection generalization for Azure Blob Storage (#36583)
* Generalize the file storage Test Connection endpoint

Replaces the S3-only /api/v4/file/s3_test handler with a backend-agnostic
POST /api/v4/file/test that validates mandatory fields per driver and
runs a write/read/delete probe against the configured backend. The
legacy /file/s3_test route stays as a thin wrapper so existing clients
keep working.

The driver switch validates S3 and Azure mandatory fields explicitly,
treats Local as a no-op (no required credentials), and rejects unknown
or empty driver names with a 400 and a specific error code so admins
get a useful message instead of a generic backend failure.

Reuses config.Desanitize (renamed from the package-private desanitize)
so the FakeSetting placeholder swap for secrets is shared with the
PUT /api/v4/config save path. Adding a new driver-secret in the future
only requires touching config.Desanitize once. Desanitize is also made
nil-safe on every pointer dereference so callers can hand it a partial
config without first running SetDefaults().

Mattermost-redux and the webapp client gain a corresponding
TestFileStoreConnection method that the admin console action layer
calls instead of the deprecated S3-specific method.

------
AI assisted commit

* Wire Azure Blob Storage into the file storage admin console

Adds the Azure Blob Storage option to the File Storage panel in the
System Console. Selecting it enables Azure-specific fields for the
storage account name, container, optional path prefix, shared key,
optional endpoint override, secure-connections toggle, and request
timeout. The fields are hidden and disabled when the driver is set to
Local or S3, matching the existing pattern.

Help text and placeholders are added in the webapp i18n catalog so
admins see the same field labels documented in the admin guide.

The same set of fields is repeated for the Files Export panel when
DedicatedExportStore is enabled, keeping the export backend
configurable independently of the primary file store.

------
AI assisted commit

* Document /api/v4/file/test in the OpenAPI spec

Adds the new backend-agnostic file storage Test Connection endpoint to
the public OpenAPI surface. The request body is optional: callers that
omit it test the running server configuration, callers that include a
full AdminConfig test the supplied configuration without persisting
anything. The deprecated /api/v4/file/s3_test endpoint is left
unchanged in the spec for the existing S3-only flow.

------
AI assisted commit

* Add UI-only Cypress coverage for the Azure file storage panel

Adds a Cypress spec that drives the System Console File Storage panel,
switches the driver to Azure Blob Storage, fills in the Azure fields,
and asserts the expected fields appear (and S3 fields are hidden). The
spec is UI-only and does not depend on an Azure backend or Azurite, so
it can run in CI without external infrastructure.

Updates the existing environment_spec.js so it tolerates the new Azure
option in the driver dropdown.

------
AI assisted commit

* Nil-guard file storage mandatory-field checks

CheckMandatoryS3Fields and CheckMandatoryAzureFields built a
FileBackendSettings via NewFileBackendSettingsFromConfig before
validating, but that constructor dereferences pointers
unconditionally and would panic if a caller skipped the api
handler's reflective nil check. Validate the required pointers
directly against FileSettings instead, dropping the throwaway
constructor call so the methods are safe to call from any path.

------
AI assisted commit

* Check permission before validating file settings

The /file/test handler ran checkHasNilFields before
SessionHasPermissionTo, so an unauthorized caller posting a partial
config got a 400, leaking config shape, rather than a 403. Swap the two
blocks so the permission decision happens first.

------
AI assisted commit

* Preserve FakeSetting when desanitize has no actual

The Azure access key, export Azure access key, and S3 secret access key
branches in Desanitize reassigned target to actual without checking
actual for nil. When the running config had no value, the FakeSetting
placeholder in target was replaced with nil, dropping the field from the
round-trip. Guard the assignment so the placeholder stays in place when
actual is unset.

------
AI assisted commit

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
2026-05-25 11:36:02 +00:00