* 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
* 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>
* [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
* 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
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>
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.
* 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>
* 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>
* [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
* 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>
* 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>
* 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>
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>
* 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>
* 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
* 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>
* 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>
* 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
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>
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>
* [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>
* 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
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>
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>
* 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>
* 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>
* 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>
* 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>
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.
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>
* 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
* 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>
* Read available value when viable
* Added tests
* Restored package lock
* CI
* Used saved actor details when available
* i18n fix
* Updated data in log
* 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>
* 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>
* [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>
* 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
* 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.
* 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
* 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>