The shutdown error only said "Error shutting down main logger" with no
hint at the cause, which is confusing when the real reason is an
unreachable log target (e.g. a remote TCP endpoint) blocking the
flush. Point the operator at the connection error logs for the
affected target instead.
UpdatePropertyFields panics when passed a nil context because
RequestContextWithMaster dereferences it. Use SystemCallerContext
to match the other sub-tests in the same function.
* [MM-70389] Add Android to the user_agent_platform session attribute
uasurfer has no Android platform, so Android devices were reported as
Linux and "Android" was missing from the user_agent_platform select
list, leaving no way to write a permission policy rule that matches
Android sessions.
Derive the platform name "Android" when the parsed OS is Android (or the
Mattermost Mobile user agent is not iOS) and add the matching option to
the seeded session attribute schema.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-70389] Cover the Android platform value end to end
Add a drift guard tying the platform names the server derives to the
options the user_agent_platform select offers, a session attribute test
proving an Android session stores "Android", and a migration test
proving a newly declared option reaches an already-seeded field without
regenerating the IDs of the options around it.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-70389] Trim comments and tighten the platform drift guard
Assert getPlatformName's own output against the schema options so a
platform name returned directly, rather than looked up in platformNames,
cannot drift out of the select either.
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>
* Migrate property field reads to request context
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Preserve nil property service request context behavior
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Use explicit session attributes system context
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Use non-nil property contexts for internal calls
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Thread request context through content flagging lookups
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Update content flagging helper tests for request context
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Thread request context through content flagging values
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Fix build: remove unused context imports left after request.CTX migration
Migrating PropertyFieldStore to request.CTX removed the last
context.Context usage from the store.go interface, leaving an unused
"context" import in store.go and in the generated retrylayer/timerlayer
files (regenerated via `make store-layers`, with layer_generators now
stripping the context import when it's unused). Also drop the same
now-unused import in localcachelayer/main_test.go.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Remove context import directly from store layer templates
Store no longer has any context.Context methods after the request.CTX
migration, so drop the hardcoded "context" import from the
retry/timer layer templates instead of stripping it at generation
time.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Remove deprecated store.WithMaster/sqlstore.WithMaster helpers
request.CTX is now used everywhere, so the deprecated
context.Context-based WithMaster helpers and their wrapper in
sqlstore have no remaining callers; inline the logic into
RequestContextWithMaster instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Fix TestDoSetupSessionAttributesProperties nil-context panic
Two subtests still constructed the pre-seed field state via
UpdatePropertyFields(nil, ...), which was the old system-caller
sentinel. isSystemCaller now requires an explicit SystemCallerContext
marker, so a bare nil rctx falls through into validateUpdate and
panics in RequestContextWithMaster. Use SystemCallerContext(th.Context)
like the rest of the suite already does.
* ci trigger
* ci trigger
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* [MM-70290] Run app migrations locked to the master DB
App migrations write rows and then read them back within the same
function. Those read-backs resolve to GetReplica(), so on a licensed
server with read replicas configured a replica that has not yet caught
up returns zero rows and doAppMigrations aborts startup via mlog.Fatal.
Reported after a 10.11.12 -> 11.7.8 upgrade on Aurora PostgreSQL with a
reader endpoint, crashing on the Managed Category Properties Setup
migration. Restarting is not a reliable workaround: the done flag is
written before the failing read, so the short-circuit path re-runs the
same replica read and a node can crash-loop while lag persists.
Wrapping doAppMigrations in LockToMaster/UnlockFromMaster covers every
read in both migration loops, including SqlPropertyGroupStore.Get and
SearchPropertyFields, which take no context and so cannot be fixed by
per-call-site routing. This mirrors the existing bulk import fix in
app/import.go, which locks to master for the same reason while the
server is serving live traffic.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Expect LockToMaster in the setup-functions store mock
doAppMigrations now locks the store to master, and it runs from
NewServer, so every helper that builds a server on the mock store hit
an unexpected-call panic. Registering both calls in
GetMockStoreForSetupFunctions covers the app, app/email, app/platform
and api4 helpers, which all share this mock.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
* Bump Go version to v1.26.7
* Fix digest
* Update glibc to v16 in runtime image as well
* Trigger E2E tests
* MM-70307: Fix FIPS E2E test failure due to short PostgreSQL password
OpenSSL FIPS requires HMAC keys to be at least 14 bytes (112 bits). The
password 'mostest' (6 bytes) triggers a panic in lib/pq's SCRAM-SHA-256
authentication when the server runs under go-msft-fips with the
glibc-openssl-fips:16 image. Replace it with 'mostest_password' (16
bytes) in all E2E test PostgreSQL connection strings and container
configs. LDAP admin passwords are unaffected (different protocol).
* Revert "MM-70307: Fix FIPS E2E test failure due to short PostgreSQL password"
This reverts commit d31b0eb5e8.
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
* Change test password to mostest_password
This makes the password compliant with the 112 bits minimum length
requirement. Otherwise, FIPS-compliant OpenSSL implementations will
panic when trying to connect from `lib/pq` with a shorter password.
* Simplify test templates' POSTGRES_PASSWORD values
* make generated
* Modify missing "mostest" strings
* MM-70071: Automatically select hosted push notification server based on license
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* test: fix mock-store fallout from push endpoint license listener
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* MM-70071: address review feedback on push endpoint sync
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* ci: retrigger enterprise tests against updated companion branch
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* ci: retrigger flaky artifact build
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* MM-70071: revert any hosted push endpoint to test on entitlement loss
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* MM-70071: add nil-safe License.HasMHPNS entitlement check
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* MM-70071: drop preview-tree docs for auto-selected push server
Monorepo MDX is still unpublished; this belongs in mattermost/docs.
Co-authored-by: Cursor <cursoragent@cursor.com>
* MM-70071: stub InitEmailBatching on guest-invite email mocks
License teardown now SaveConfigs the push endpoint, which fires the existing email-batching config listener.
Co-authored-by: Cursor <cursoragent@cursor.com>
* MM-70071: don't re-init email batching on push-server license sync
License teardown SaveConfigs the push endpoint, which fired the existing
email-batching listener and panicked tests that mock EmailService.
Re-init batching only when EnableEmailBatching changes, and stub the
remaining invite mock used during helper cleanup.
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
* MM-70071: re-init email batching when the interval setting changes
Keep EmailBatchingInterval live at runtime; only ignore unrelated
config writes such as the push-server license sync.
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
* MM-70071: isolate mock tests from push endpoint sync
Use custom push endpoints in shared mock fixtures so unrelated tests do not need config-listener expectations.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
* [MM-57808][MM-57809][MM-57811] Graduate theme settings to Site Configuration > Customization
Move ThemeSettings.EnableThemeSelection, ThemeSettings.AllowCustomThemes, and
ThemeSettings.DefaultTheme out of System Console > Experimental > Features and
into Site Configuration > Customization, alongside the other branding settings.
The config keys, defaults, license gating, and the AllowCustomThemes dependency
on EnableThemeSelection are unchanged; only the System Console page and the
access classification move. The access tags become site_customization, so the
settings are now governed by sysconsole_{read,write}_site_customization rather
than sysconsole_{read,write}_experimental_features.
i18n ids move from admin.experimental.* to the Customization page's
admin.customization.* convention, and the documentation entries move from the
experimental configuration settings page to the site configuration settings
page.
* [MM-57812][MM-57813] Graduate tutorial and onboarding settings to Site Configuration > Customization
Move ServiceSettings.EnableTutorial and ServiceSettings.EnableOnboardingFlow out
of System Console > Experimental > Features and into Site Configuration >
Customization, next to the desktop app landing page setting that also governs a
user's first-run experience.
The config keys and defaults are unchanged; only the System Console page and the
access classification move. The access tags become site_customization, so the
settings are now governed by sysconsole_{read,write}_site_customization rather
than sysconsole_{read,write}_experimental_features.
i18n ids move from admin.experimental.* to the Customization page's
admin.customization.* convention, and the documentation entries move from the
experimental configuration settings page to the site configuration settings
page.
* [MM-57810] Surface ThemeSettings.AllowedThemes in Site Configuration > Customization
ThemeSettings.AllowedThemes has always existed in the server config and has
always been honoured by the theme picker, but it was never represented in the
System Console schema — not under Experimental > Features and not anywhere else.
Add it to Site Configuration > Customization alongside the theme settings it
constrains.
The server model is []string, so this uses the existing `type: 'text'` with
`multiple: true` widget, the same one ServiceSettings.DCRRedirectURIAllowlist
uses for its []string. The admin console joins the array with commas for display
and splits it back into an array on save, which matches how the client config
already serialises the value, so no behaviour or serialisation changes. The
setting picks up an access tag of site_customization, where it previously had
none, and the documentation entry moves from the self-hosted-only section of the
experimental configuration settings page to the site configuration settings page.
Also add a test asserting the graduated settings are present on Customization,
absent from Experimental > Features, and that AllowedThemes round-trips as a
string array.
* Add runtime-effect tests for graduated theme and onboarding settings
Expand coverage for the graduated Customization settings so that they are
verified to affect their features, not just to save:
- ThemeSettings.AllowedThemes: premade theme chooser only renders the
allow-listed themes (theme enforcement on/off).
- ServiceSettings.EnableOnboardingFlow: onboarding task list is gated on
the config value.
- ServiceSettings.EnableTutorial: the Channels tour tip is gated on the
config value.
- EnableTutorial/EnableOnboardingFlow (and the theme bools) round-trip
their value through the admin console schema.
Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>
* [MM-57806] Graduate Enable Channel Viewed WebSocket Messages
Move ServiceSettings.EnableChannelViewedMessages out of System Console >
Experimental > Features and into Environment > Web Server, alongside the
other ServiceSettings transport and performance knobs it belongs with.
The access tag changes from experimental_features to environment_web_server,
so the setting is now governed by sysconsole_read/write_environment_web_server.
write_restrictable and cloud_restrictable are preserved, the default remains
true, and the client config continues to publish the value. No runtime
behavior changes.
The i18n ids move from the admin.experimental.* namespace to the
admin.service.* namespace used by the rest of the Web Server page.
* [MM-57806] Document channel viewed WebSocket messages under Environment
Move the Enable Channel Viewed WebSocket Messages entry from the
experimental configuration settings page to the Web Server section of the
environment configuration settings page, matching its new System Console
location, and reformat it to the two-column table style used there.
* [MM-57806] Cover the new location of the channel viewed setting
Assert that the setting is defined on the Environment > Web Server page,
that it is gated on write access to the Web Server console resource rather
than to Experimental Features, and that searching the admin console for
"channel_viewed" resolves to environment/web_server.
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* WIP:
* WIP:
* Added API integration
* Removed some unneeded functions and cleaned up unnecessery comments
* CI
* Test improvements
* Coderabit fixes
* Report data updates
* Handled commas and few other chaaracters in channel name
* Data spillage exposure radius UI integration (#37820)
* UI implementation and integration of exposure report APIs
* Minor cleanup
* Coderabit fixes
* Allowed generating exposure report irrespective of status
* Used the new button component
* fixed lint error
* [MM-70252] Reject malformed date filters in logs query API
The POST /api/v4/logs/query endpoint parsed date_from/date_to with a fixed
layout and swallowed parse errors, silently dropping the bound instead of
signalling the caller. A malformed date_from became the zero time and a
malformed date_to became now, so the request returned HTTP 200 with an
unfiltered result set.
Add LogFilter.IsValid, which rejects a non-empty bound that cannot be parsed
with the shared LogFilterDateLayout while keeping empty strings meaning
"unbounded", and call it from queryLogs so a bad filter returns 400 naming the
offending field and the expected layout.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-70252] Add tests for logs query date filter validation
Add a unit test for LogFilter.IsValid covering empty (unbounded), valid, and
malformed bounds, and an api4 integration test that drives POST /logs/query
through the real router to assert malformed date_from/date_to return 400 with
the offending field id while empty and valid bounds return 200.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-70252] Harden logs query date filter tests
Address test-quality review: exercise the DateTo validation branch with a valid
non-empty DateFrom, move fallible checks out of the require.Eventually condition
to avoid a cross-goroutine failure, and make each api4 subtest self-contained by
polling for the expected messages via a shared helper so valid-bounds also
verifies filtering still returns records.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-70252] Retrigger CI/CodeRabbit after invalid public-module feedback
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-70252] Note shared LogFilterDateLayout usage in date filter
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-70252] Add Client4.QueryLogs to simplify logs query date filter tests
* Address PR feedback: 2 answered, 1 resolved, 0 declined
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
Co-authored-by: Ben Schumacher <ben.schumacher@mattermost.com>
* introduce mlogFieldNaming
* apply vet-fix changes
* Cover every keyed mlog constructor in the analyzer fixture
* clarify end result in comment
* Check mlog field keys on explicitly instantiated constructors
* [MM-57557] Remove dead Email login button color settings from the server
EmailSettings.LoginButtonColor, LoginButtonBorderColor and LoginButtonTextColor
were plumbed into the client config as EmailLoginButtonColor /
EmailLoginButtonBorderColor / EmailLoginButtonTextColor, but no client — web or
mobile — ever consumed them, so the email login button was never colored by
these values.
Remove the fields from the config struct and its defaults, drop the three client
config props, and update the config fixtures that carried them.
Also fixes MM-57556 and MM-57804, and follows the same removal already done for
the AD/LDAP (MM-70140) and SAML (MM-70141) equivalents.
* [MM-57557] Remove Email login button colors from the webapp and docs
Drop the three Email Login Button Color settings from the Admin Console
Experimental Features section along with their en.json strings, remove the
matching ClientConfig and AdminConfig EmailSettings entries to stay in sync with
the server model, and delete the corresponding documentation entries.
The experimental settings doc's jq example referenced
EmailSettings.LoginButtonColor, which no longer exists; point it at
EmailSettings.EmailBatchingBufferSize instead.
* [MM-57814][MM-57815] Graduate user typing settings to Site Configuration > Posts
Move ServiceSettings.EnableUserTypingMessages and
ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds out of
System Console > Experimental > Features into the Performance & Limits
section of System Console > Site Configuration > Posts, and reclassify
their access tags from experimental_features to site_posts (preserving
write_restrictable and cloud_restrictable).
The two settings stay adjacent, and the timeout remains disabled while
typing messages are off. The timeout label now states its unit, since
"User Typing Timeout" alone did not convey milliseconds. The i18n ids
move from admin.experimental.* to the Posts page's admin.posts.*
convention; the "E.g.: 5000" placeholder previously shared with the
experimental user status and profile fetching poll interval is now
defined once per setting.
No config keys, defaults, or runtime behavior change.
* [MM-57814][MM-57815] Assert user typing settings are searchable under Posts
Searching the System Console for "typing" now also matches
Site Configuration > Posts, guarding the new location of the user typing
settings. Experimental Features still matches on unrelated help text
about typing a tilde to trigger channel autocomplete.
* [MM-57814][MM-57815] Move user typing settings docs out of Experimental
Document "Enable user typing messages" and "User typing timeout" in the
Posts section of the site configuration settings guide, and drop them
from the experimental configuration settings guide.
* Omit sanitized channel member timestamps from JSON
The channel member sanitization introduced in #33835 replaced other
users' LastViewedAt and LastUpdateAt with -1, which clients decode as
Dec 31 1969. Serialize the sanitized sentinel as an absent field instead
so the API no longer returns an invalid timestamp for other users.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* Add tests and API docs for omitted sanitized member timestamps
Verify at the JSON layer that last_viewed_at and last_update_at are
omitted for other users' memberships (across the channel and user
endpoints) while remaining present for the requester, including a
legitimate zero timestamp. Document the omission in the API spec.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* Strengthen sanitized-timestamp test coverage
Cover the NDJSON streaming branch of getChannelMembersForUser and the
getChannelMembersForTeamForUser endpoint, assert the requester's own
timestamps are valid (not the sentinel), use the sanitizedTimestamp
constant, and note the ChannelMemberForExport marshaling footgun.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* Marshal team data via a typed struct in ChannelMemberWithTeamData
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* Avoid shadowing err in ChannelMemberWithTeamData.MarshalJSON
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* Use omitzero tags to omit sanitized member timestamps
Replace the custom ChannelMember/ChannelMemberWithTeamData MarshalJSON
round-trip with the Go 1.24 omitzero tag on LastViewedAt/LastUpdateAt.
SanitizeForCurrentUser now zeroes another user's timestamps so they are
omitted from API responses, per reviewer feedback.
* Give current user a real last_viewed_at in sanitization test
With omitzero, a zero last_viewed_at is legitimately omitted. Have user2
post an unread message and the current user view the channel so the
current-user assertions verify a genuine timestamp survives sanitization.
* Use -1 sentinel for sanitized member timestamps with single-pass marshal
A last_viewed_at of 0 legitimately means "never viewed", so it cannot
double as the sanitization sentinel. Restore the -1 sentinel and omit it
during serialization via shadowing pointer fields, avoiding the previous
marshal/unmarshal/marshal round-trip.
* Clarify ChannelMember.MarshalJSON doc comment per review feedback
* Address PR feedback: 0 answered, 4 resolved, 0 declined
- Simplify sanitizedTimestamp and SanitizeForCurrentUser doc comments per review
- Document that new ChannelMemberWithTeamData fields must be added to MarshalJSON
- Add round-trip test guarding against fields dropped by MarshalJSON
* Address PR feedback: remove round-trip MarshalJSON test
The round-trip test did not guard against forgetting to add a new field to
MarshalJSON, since the same field would also be missing from the test.
* Address PR feedback: assert legitimate zero last_update_at is serialized
* Mark sanitized channel member timestamp fields as nullable in OpenAPI spec
---------
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>
* [MM-69646] Disallow MoveThreadsEnabled feature flag
Reject the MoveThreadsEnabled feature flag during config validation so the
server fails to start while it is enabled. The feature is being retired in
favor of Wrangler and will be removed later.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-69646] Cover nil FeatureFlags guard in config validation test
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* Move MoveThreadsEnabled comment into isValid method body
Keep isValid's doc comment generic since it will validate more flag
combinations in the future, and place the MoveThreadsEnabled-specific
rationale next to the actual flag check.
* [MM-69646] Update TestMoveThread for retired MoveThreadsEnabled flag
Config.IsValid now rejects enabling MoveThreadsEnabled, so the
move-thread API stays disabled. Replace the enabled-path suite with
assertions that the flag cannot be turned on and MoveThread returns 501.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-69646] Stop forcing MoveThreadsEnabled in e2e environments
E2E was setting MM_FEATUREFLAGS_MOVETHREADSENABLED=true, which now fails
Config.IsValid and prevents the test server from starting. Remove the
override and skip Cypress move-thread specs that require the retired flag.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-69646] Skip TestMoveThread instead of asserting disabled flag
Mirror the E2E describe.skip approach: retain the original TestMoveThread
body and skip it at the top, since MoveThreadsEnabled is retired and
rejected by Config.IsValid.
* [MM-69646] Park cursor away from post dot menu in edit_file_attachment specs
---------
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: Jesse Hallam <jesse@mattermost.com>
The webapp Slack import path was deprecated in v6.0 in favor of mmetl
and Mattermost bulk import; remove the leftover API, importer package,
CLI command, and import_team permission.
Co-authored-by: Cursor <cursoragent@cursor.com>
Remove the deprecated backward-compatibility aliases introduced in #35445:
SlackAttachment, SlackAttachmentField, ParseSlackAttachment, and
StringifySlackFieldValue. Plugins should now use the MessageAttachment
equivalents directly.
SlackCompatibleBool is retained as it is still actively used.
Claude-Session: https://claude.ai/code/session_01KnMUsaSbm4HQsNEEtH5zp8
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
* MM-67157: Remove unused format flag from /license/client endpoint
The `format` query parameter on the `/license/client` (and local
variant) endpoint was effectively dead: it was required but only ever
accepted the single value `old`, returning an error otherwise. This
mirrors the earlier removal of the same flag from `/config/client`,
where the server now ignores the parameter while clients continue to
send `format=old` for compatibility with pre-v11 servers.
The server no longer inspects the `format` parameter, so requests with
no format, `format=old`, or any other value all succeed. The unused
i18n string and the parameter/response documentation in the OpenAPI
spec are removed accordingly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Audz4JLNQN5SJxcPwyQBd
* MM-67157: document format=old retention in webapp client
Mirror the getClientConfig comment so the format=old query param on
getClientLicenseOld is not mistakenly removed; clients keep sending it
for compatibility with pre-v11 servers even though current servers now
ignore it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Audz4JLNQN5SJxcPwyQBd
* MM-67157: stop sending format=old from webapp client
Now that the server ignores the format parameter on /license/client,
drop format=old from the @mattermost/client getClientLicenseOld call and
update the e2e intercepts/helpers that matched the old query string.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Audz4JLNQN5SJxcPwyQBd
* Drop unneeded wildcard from license/client cy.intercept path
The format query param is gone from GET /license/client requests, so
the trailing * used to match it is no longer needed.
---------
Co-authored-by: Claude <noreply@anthropic.com>
* [MM-69895] Delete bot access tokens on permanent bot deletion
App.PermanentDeleteBot removed the bot and user rows but left the
bot's UserAccessToken rows (and their sessions) orphaned, since the
UserAccessTokens table has no FK cascade to Users. Call
UserAccessToken().DeleteAllForUser to match PermanentDeleteUser.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-69895] Strengthen bot access token deletion regression test
Assert specific not-found errors, cover sessions for every bot token,
and add a control bot to prove deletion is scoped to the deleted bot.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-69895] Assert not-found status on deleted bot tokens
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-69895] Clear session cache when permanently deleting a bot
Deleting the access token rows via DeleteAllForUser is plain SQL and never
clears the in-memory session cache, so the bot's tokens kept authenticating
after PermanentDeleteBot. Mirror PermanentDeleteUser: delete sessions, delete
tokens, then clear the session cache (which also broadcasts to the cluster).
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* Prevent system-owned bots from being disabled
System-owned bots (system-bot, content-review) could be disabled either
directly via the API or via the owner-deactivation path when
DisableBotsWhenOwnerIsDeactivated=true. Once disabled, they never
self-healed, silently breaking post reminders, reports, and channel
notifications.
- Add model.ProtectedBotUsernames and remove the dead
BotWarnMetricBotUsername constant.
- Guard UpdateBotActive so protected bots cannot be disabled (403),
covering both the API and disableUserBots paths.
- Auto-heal the system bot in GetOrCreateSystemOwnedBot by fetching
including deleted and re-enabling if disabled.
- Hide the Edit and Disable controls for protected bots in the System
Console bot list.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* Strengthen tests for protected system bots
- Parameterize the app-layer guard test over both protected usernames
(system-bot and content-review).
- Assert the underlying user is also reactivated by the auto-heal path.
- Drive the owner-deactivation test through the real UpdateActive path and
add a non-protected bot to prove the batch keeps disabling other bots.
- Add an API-layer test asserting a 403 when disabling the system bot.
- Make the webapp recovery test click Enable and assert the action fires.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* Address CodeRabbit feedback on protected bot reactivation
- Add reactivateProtectedBot to bypass active-user limit checks when
auto-healing or re-enabling disabled system-owned bots
- Fail closed on bot store lookup errors in UpdateBotActive before
mutating user state
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* Fix govet shadow lint in reactivateProtectedBot
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* Update unknown bot test for bot-first lookup in UpdateBotActive
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* Address PR feedback: DRY protected bot reactivation, range over ProtectedBotUsernames, label system bots as Managed by Mattermost
* Refactor UpdateActive to share inner updateActive with protected bot reactivation
* Address PR feedback: remove user-limit bypass for bot activation
Bot accounts are excluded from the active-user/license counts (User().Count
defaults to IncludeBotAccounts=false), so the dedicated bypass path was guarding
a case that cannot occur. Revert the UpdateActive/updateActive split and the
protected-bot branch in UpdateBotActive; bot (re)activation goes through the
normal UpdateActive path again.
* Replace hardcoded webapp protected-bot list with server-driven system_owned field
Addresses marianunez's review comment: the webapp kept its own copy of the
protected bot usernames (system-bot, content-review), duplicating
model.ProtectedBotUsernames and risking silent drift if a new system-owned
bot is added server-side without updating the client list.
model.Bot now computes IsSystemOwned() from ProtectedBotUsernames and
serializes it as system_owned via a custom MarshalJSON, so the webapp reads
it directly off the bot instead of matching usernames itself.
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
Co-authored-by: Ben Schumacher <ben.schumacher@mattermost.com>
* MM-69403 filter job websocket updates by permission
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Fix websocket event deep copy test setup
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Move job read permission mapping out of public model
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Address job websocket permission review findings
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Remove shared job read permission helper
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Remove extra generic job permission mappings
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Document cached websocket manage system lookup
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Fail closed on job_updated permission filtering during mixed-version rollouts
RequiredPermissions is silently dropped by nodes running a version that
predates it, so keep setting ContainsSensitiveData as a sysadmin-only
fallback for those nodes instead of broadcasting the unfiltered job.
ShouldSendEvent on upgraded nodes ignores ContainsSensitiveData whenever
RequiredPermissions is present.
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* MM-69881: Cap image size buffered by the local image proxy's direct fetch
ServeImage now accepts an optional max byte count.
* Log only the host, not the full URL, when discarding an oversized image
* Clarify ServeImage doc comment: make maxBytes=0 behavior explicit
* MM-70095: Adjust Slack import user handling based on import type
See MM-70095.
* MM-70095: Add test coverage for non-admin import save failure on email conflict
Ensures the non-admin account-creation fallback path doesn't report success when the underlying save is rejected.
* MM-70095: Assert Save is invoked in email-conflict save-failure test
Explicitly verify the mocked Save call is exercised rather than relying on its return value alone.
* MM-70095: Fix non-admin Slack import user handling
* MM-70095: Fix non-admin Slack import user handling
* MM-70100: Clarify Slack import log message for matching account emails
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---------
Co-authored-by: Bill Gardner <billg@wavearts.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* promote EnableExportDirectDownload to a Cloud-only configuration setting
Replace the FeatureFlags.EnableExportDirectDownload feature flag with a
FileSettings.EnableCloudExportDirectDownload configuration setting, gated
to Mattermost Cloud environments.
The /exportlink slash command and the export generate-presigned-url API
now require FileSettings.EnableCloudExportDirectDownload to be enabled and
a Cloud license. Operators previously enabling the feature via
MM_FEATUREFLAGS_ENABLEEXPORTDIRECTDOWNLOAD should transition to
MM_FILESETTINGS_ENABLECLOUDEXPORTDIRECTDOWNLOAD.
* add tests for Cloud-only export direct download gating
Cover the new EnableCloudExportDirectDownload + Cloud-license gate on
GeneratePresignURLForExport (app) and the generate-presigned-url API
(api4).
* add EnableCloudExportDirectDownload to FileSettings type
* gate export direct download on Cloud alone, without a configuration setting
* Use a bounded HTTP client in export presigned-URL tests
http.Get has no total timeout; if MinIO accepts the connection but
stalls, these tests can hang until the suite timeout.
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-69889] Improve handling of RelayState in SAML flow
RelayState was base64-decoded and trusted without any integrity check,
letting its contents be tampered with client-side. Sign relayProps with
an HMAC key (generated once, cached, stored like AsymmetricSigningKey)
before handing it to the IdP, and verify the signature before trusting
any of its fields on the way back.
* Add short expiry to signed RelayState
Bound the signed RelayState's validity to 5 minutes to restrict the
window in which a captured, unmodified RelayState could be replayed.
* [MM-69889] Use maps.Copy in SignSamlRelayState
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
* MM-70072: Update team admin assignment during team join
* Assert SchemeUser in team rejoin test case
* MM-70072: Fix team admin assignment in bulk import path
* Preserve computed admin status through scheme role sync in bulk import
---------
Co-authored-by: Bill Gardner <billg@wavearts.com>
* Add weekly recurring scheduled posts.
Extend scheduled posts so users can schedule weekly repeats and keep the series healthy across sends, reschedules, and UI updates instead of falling back to one-shot behavior.
Made-with: Cursor
* Add Playwright coverage for recurring scheduled posts.
Cover weekly recurring scheduled messages in the scheduled-messages spec so the recurring UI and reschedule flow stay protected without adding a separate test surface.
Made-with: Cursor
* Fix recurring scheduled post CI failures.
Resolve the initial lint and formatting issues and renumber the new scheduled-post migration so it no longer collides with master during Postgres-backed test setup.
Made-with: Cursor
* Sync recurring scheduled post translation files.
Regenerate the affected English translation catalogs so the recurring scheduled post strings match the source extraction order expected by CI.
Made-with: Cursor
* Fix recurring scheduled post review follow-ups.
Preserve overdue cleanup behavior during weekly catch-up, defer delete websocket events until deletion succeeds, and address the remaining migration and UI review nits.
Made-with: Cursor
* Address recurring scheduled post review feedback
Made-with: Cursor
* Fix recurring scheduled post CI checks
Made-with: Cursor
* Make pending scheduled post keyset cursor index-scannable
EXPLAIN ANALYZE on a 5M-row ScheduledPosts table showed the pure OR-form
cursor forced Postgres to scan idx_scheduledposts_pending_scheduled_at_id
from the top on every page (~383ms/page, ~2M rows filtered). Keeping the
ScheduledAt <= beforeTime bound outside the tie-break restores the index
boundary (~0.12ms/page). Adds storetest coverage for cursor pagination.
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Address code quality review findings for recurring scheduled posts
Server:
- Replace silent-fallback AdvanceWeeklyScheduledNextOccurrence with
error-returning ScheduledPost.ComputeNextScheduledAt; a recurring post
whose timezone fails to load is now routed through the failed-post path
instead of being reposted every job run or silently deleted
- Add ScheduledPost.IsRecurring and partition batches in
processScheduledPostBatch; advance/delete now run independently so a
store failure in one path can't cause reposts in the other
- Drop dead generality in UpdateRecurringScheduledPosts (only ScheduledAt
varies per row; ErrorCode/ProcessedAt are constants)
- Simplify redundant repeat-type condition in GetPendingScheduledPosts
Webapp:
- Add shared isRecurringScheduledPost helper, replacing six scattered
repeat_type === 'weekly' literals
- Recurrence timezone is now simply the scheduler's current timezone;
removes initialRepeatTimezone/effectiveTimezone plumbing and the
modal-label/picker timezone mismatch
- Single enforcement point for hiding send-now on recurring posts
- Use canonical getTeamIdByChannelId at both SCHEDULED_POST_UPDATED
dispatch sites; rewrite errorsByTeamId update case as remove-then-add
and add reducer tests
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Simplify recurring scheduled post code per review
- End a recurring series when its channel no longer exists instead of
advancing it forever (matches the one-shot channel-not-found handling)
- Collapse the errorsByTeamId SCHEDULED_POST_UPDATED case into the
identical SINGLE_SCHEDULED_POST_RECEIVED case (a scheduled post's team
can't change) and combine duplicate byId cases; preserves state
references on no-op updates
- Drop no-op timezone conversions in ComputeNextScheduledAt
- Remove redundant checkbox aria-label (label htmlFor already names it)
- Schedule new job tests in the past instead of sleeping a real second
- Remove redundant test assignment and e2e positional boolean
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Remove slop from recurring scheduled post changes
- Drop the business-rule CHECK constraint from the recurrence migration;
no other migration enforces model-layer validation in the database,
and BaseIsValid plus the job's failure handling already own it
- Fold the standalone repeat-validation test file into the existing
TestScheduledPostBaseIsValid, matching its conventions
- Revert unrelated benchmark modernization in utils_test.go
- Drop an unneeded cast, unused fixture fields, and naming/assertion
inconsistencies in tests
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Regenerate ScheduledPostStore mock with mockery ordering
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Address CodeRabbit review feedback
- Reject the host-dependent 'Local' value for RepeatTimezone; recurring
schedules need a fixed zone (UTC or IANA name)
- Hide reschedule for deactivated DMs, matching send-now eligibility
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Resolve scheduled post team bucket from existing state when channel is unloaded
An admin can reschedule before fetchMissingChannels resolves, in which case
deriving the team from the channel returns undefined and the update was
misfiled under directChannels. getScheduledPostTeamId falls back to the
byTeamId bucket that already holds the post.
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Retrigger CI after transient enterprise npm network failure
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Clear hover/focus before asserting scheduled post header details in e2e
The drafts panel hides its timestamp/tag info section while hovered or
focus-within; after the reschedule modal closes, focus returns to the row
and the 'Repeats weekly' tag assertion saw a hidden element.
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Move recurrence columns into baseColumns and dispatch ComputeNextScheduledAt on repeat type
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Preserve recurrence when scheduled post updates omit repeat fields
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Disallow file attachments on recurring scheduled posts
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Stop pinning the channel indicator for recurring-only scheduled posts
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Re-home nullable-Type comment onto baseColumns
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Gate recurring scheduled posts behind a default-off feature flag
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Move presence-preservation rationale to the copy site
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Extract draftHasAttachments helper and require allowRecurring at single-caller layers
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Return null from the indicator selector when nothing should show
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Gate only recurrence transitions and preserve existing series in the modal
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Avoid err shadowing in scheduled post update handler
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Disable the repeat weekly checkbox with a tooltip when the message has attachments
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Return an explicit disposition from postScheduledPost
The batch loop inferred 'channel permanently gone' from the one return
path with a nil error and an error code set - an invariant a future
change could silently break, deleting recurring series by accident.
postScheduledPost now returns posted/failed/unsendable explicitly, the
switch refuses to delete on an unhandled disposition, and a test pins
that both recurring and one-shot posts in a nonexistent channel are
permanently deleted.
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
* Fix Repeat weekly attachments tooltip centering on the modal row (#37927)
Shrink-wrap the repeat checkbox row so WithTooltip anchors to the
control/label instead of the full modal body width.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>