mirror of
https://github.com/mattermost/mattermost.git
synced 2026-09-19 10:12:47 +08:00
d563fdd5ac287044bf82c79b9ce87e81c3dfac8a
3149
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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 |
||
|
|
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> |
||
|
|
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>
|
||
|
|
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> |
||
|
|
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
|
||
|
|
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. |
||
|
|
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 |
||
|
|
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> |
||
|
|
4d8c25f040 |
[MM-68736] Reconcile partial GM membership in bulk import (#36542)
* [MM-68736] Reconcile partial GM membership in bulk import importDirectChannel failed with "ChannelMember not found" when the group channel hash already existed in Channels but one or more participants were missing from ChannelMembers (concurrent import workers, a prior import that crashed mid SaveMember loop, or a pre-existing GM whose membership has drifted). The function only used existingMembers to compare LastViewedAt and still appended absent participants to UpdateMultipleMembers, whose UPDATE-then-SELECT returns sql.ErrNoRows for the missing row. Insert the missing ChannelMembers row via SaveMember (logging the join event) before the UPDATE path. ErrConflict is treated as benign so concurrent workers racing on the same MPIM hash do not fail each other. * [MM-68736] Add missing i18n key for new bulk import error The save_member.error key was added in the importDirectChannel fix but not registered in i18n/en.json. * [MM-68736] Address review feedback - Broaden the save_member.error translation to also cover DM imports, since importDirectChannel runs the same SaveMember reconciliation path for both 2-participant DMs and 3+-participant GMs. - In the idempotent re-import test, assert that ChannelMemberHistory.GetMembershipChanges returns no rows after the second import. LogJoinEvent is only invoked when the missing-member branch runs, so an empty result proves SaveMember was not called for any already-present participant. |
||
|
|
508f1551e3 |
Fix flaky TestScrubPost (#36686)
The should_preserve_non-content_fields subtest asserted UpdateAt was unchanged after scrubPost, but scrubPost refreshes UpdateAt via model.GetMillis(). When the scrub runs in a later millisecond than the fixture setup, the strict equality fails intermittently. Align the assertion with scrubPost behavior (UpdateAt >= pre-scrub value). Tests-only change. Verified with `go test -run '^TestScrubPost$' -race -count=100` and 10000 runs of the subtest locally. Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: mattermost-code <matty-code@mattermost.com> |
||
|
|
44ba06ee3c |
MM-68248: Handle missing OpenSearch indexes gracefully before reindex (#36712)
* MM-68248: Handle missing indexes gracefully before reindex OpenSearch v3 rejects _update_by_query and _delete_by_query with no index argument (405), and returns index_not_found_exception (404) when querying an exact index name that hasn't been created yet. Both arise before any reindex has run, since indexes are created on first document write. Return nil/empty instead of an error from all affected operations, and add test coverage for each in the no-indexes state. * MM-68248: Fix copy-paste operation names in DeleteFilesBatch * MM-68248: Add i18n string for delete_files_batch error * MM-68248: Also check error type in isIndexNotFound |
||
|
|
834a86b5e3 |
MM-68248: Support OpenSearch v3 (#36617)
* Remove build-opensearch-image.yml -- the published image is unused * MM-68248: Support OpenSearch v3 * MM-68248: Add CI job to test OpenSearch v2 backwards compatibility * MM-68248: Handle missing indexes gracefully before reindex OpenSearch v3 rejects _update_by_query and _delete_by_query with no index argument (405), and returns index_not_found_exception (404) when querying an exact index name that hasn't been created yet. Both arise before any reindex has run, since indexes are created on first document write. Return nil/empty instead of an error from all affected operations, and add test coverage for each in the no-indexes state. * MM-68248: Fix copy-paste operation names in DeleteFilesBatch * MM-68248: Add i18n string for delete_files_batch error * Revert "MM-68248: Add i18n string for delete_files_batch error" This reverts commit |
||
|
|
e8632bd456 |
[MM-68777] Add admin property field permission level (#36558)
|
||
|
|
4e01dae534 |
Fix flaky TestPreparePostForClient/files (#36631)
The files subtest polled for post file metadata with assert.Eventually using only a one-second total wait. Under CI load, PreparePostForClient can take longer than that to see persisted file rows, causing intermittent timeouts while the final assert would still pass if given time. Tests-only change. Verified with go test -run '^TestPreparePostForClient$/^files$' -race -count=100 locally. Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com> |
||
|
|
c63154598c |
MM-66162: harden GET /sharedchannels/{id}/remotes error path and add WS guard (#36696)
* MM-66162: harden GET /sharedchannels/{id}/remotes error path and add WS guard
Two small follow-ups on top of the existing fix that returns 200 with an
empty list when a channel is not shared.
Server: getSharedChannelRemotes built its 500 AppError with nil params,
so the i18n template "Could not fetch status for secure connections:
{{.Error}}." rendered as "<no value>" in logs and response bodies when a
genuinely unexpected error occurred. Pass map[string]any{"Error":
err.Error()} so the underlying error surfaces, matching the pattern
already used by the slash command caller in
channels/app/slashcommands/command_share.go.
Webapp: handleSharedChannelRemoteUpdatedEvent dispatched
fetchChannelRemotes unconditionally on every shared_channel_remote_updated
WS event, including for channels the local state already knows are not
shared. The server publishes this event from the onInvite callback after
a remote accepts a channel invitation, by which point the channel has
already been flipped to shared and the channel_updated event that did so
has already fired, so the local channel should reflect shared=true when
this event arrives. If it does not, the event is not actionable and we
skip the fetch.
Adds jest coverage for the four cases of the new guard.
* add regression test for stale shared channel state
Pins the DB inconsistency scenario suggested in the original ticket
investigation: Channel.shared is true and a SharedChannelRemote row
exists, but the SharedChannels row is missing. The endpoint should
return 200 with an empty list rather than surfacing the stale state
as an internal server error. Also tightens the existing
TestGetSharedChannelRemotes by asserting the status code on the
success path.
|
||
|
|
efdebec6ad | Fix flaky TestPatchTeam GroupConstrained subtest (#36689) | ||
|
|
182049583b |
Skip flaky TestGetLogs (MM-68910) (#36659)
* Skip flaky TestGetLogs Skipping while the root cause is investigated under MM-68910. Tests-only change. Co-authored-by: mattermost-code <matty-code@mattermost.com> * Address PR feedback: 2 items resolved, 0 declined * Address PR feedback: 2 items resolved, 0 declined * Address PR feedback: 2 items resolved, 0 declined --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: mattermost-code <matty-code@mattermost.com> |
||
|
|
b4aa46a4be |
Bumping version of prepackaged boards plugin (#36701)
Automatic Merge |
||
|
|
209606f15b |
MM-68419: Add expires_at to PAT data model and enforce expiry at token validation (#36243)
* Add expires_at to PAT data model and enforce expiry at token validation Adds an ExpiresAt field (int64 millis, 0 = never expires) to the UserAccessToken model and DB table, enforces expiry when a PAT is used to create a session, clamps the resulting session's ExpiresAt to the token's expiry so cached sessions also honor it, ships a background job (cleanup_expired_access_tokens) that periodically deletes expired tokens along with any sessions minted from them, and emits audit events for rejected and reaped expired tokens. Refs: MM-68419 Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> * Fix govet shadow warnings in DeleteExpired Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> * Stabilize expired PAT test by persisting an already-expired token The previous variant created a live token, used it to mint a session, then backdated the row and revoked the cached session to force a re-validation. That flow was race-prone under parallel test execution and was flagged as flaky in CI. Replace it with a direct store write that persists the PAT with ExpiresAt already in the past, so createSessionForUserAccessToken is exercised deterministically on the first HTTP call and no session cache races are possible. Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> * Address PR review: batched cleanup, consistent filters, audit ordering - server.go: initialize s.Audit before initJobs() so the cleanup worker never captures a nil audit logger. - Replace DeleteExpired(cutoff) with DeleteByIds([]string) on UserAccessTokenStore. The worker now fetches a batch via GetExpiredBefore, emits one audit record per token, then deletes exactly that batch by id — guaranteeing 1:1 audit/delete pairing and eliminating the IsActive-filter mismatch between reads and deletes. The worker loops up to maxBatches (=1000) x batchLimit (=1000) rows per run and stops when GetExpiredBefore returns less than batchLimit or zero rows. - GetExpiredBefore now selects an explicit column set that omits the secret Token column, so the PAT secret never travels from DB to app. - DeleteByIds surfaces an error from RowsAffected instead of silently returning 0. - Remove dead job.Data initialization in the worker. - api4 test: set IsActive: true explicitly, walk the AppError chain and assert the specific Id app.user_access_token.expired so future 401 regressions are caught. Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> * Use named returns in DeleteByIds and clean up expired fixture in test - DeleteByIds now declares (deleted int64, err error) and uses bare returns on every error path so finalizeTransactionX can append a rollback failure to the returned error via merror.Append. Previously early returns short-circuited the deferred rollback's error contribution. - Add the expired token to the test cleanup so all three fixtures are removed even on early test exit. Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> * Guard GetExpiredBefore against non-positive limit + tighten store test - GetExpiredBefore now short-circuits when limit <= 0 and returns an empty slice without hitting the DB. This prevents the int -> uint64 cast on a negative value from wrapping into an effectively unbounded query. - Store test now asserts row.Token is empty for every row returned by GetExpiredBefore (not just the matched one) to catch any future query change that accidentally re-introduces the secret column. - Added store-level coverage for the limit=0 and limit<0 short-circuit contract. Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> * Add session-clamping and worker tests; bump migration to 172 Address PR test-coverage analysis (issuecomment-4336010565): - api4/user_test.go: add two subtests covering session.ExpiresAt behavior — clamped to token.ExpiresAt when the PAT has a non-zero ExpiresAt, and untouched (long-lived) when the PAT has no expiry. - cleanup_expired_access_tokens/worker.go: extract the batching/audit/ error orchestration into a package-private cleanupExpired() taking small interfaces (expiredTokenStore, auditRecorder) so it can be unit-tested without spinning up a job server. - cleanup_expired_access_tokens/worker_test.go (new): seven unit tests cover happy path, empty result, full-batch -> next iteration, maxIter cap, GetExpiredBefore error propagation, DeleteByIds error propagation, and nil auditLogger guard. - Bump migration 000170_add_expiresat_to_user_access_tokens to 000172 to slot in behind the master-side 000170 (property_groups_version) and 000171 (drop_property_fields_protected_index). Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> * Fix PAT expiry audit ordering and cleanup scheduler gate - Move IsExpired() check after EnableUserAccessTokens gate in createSessionForUserAccessToken so the AuditEventRejectExpiredUserAccessToken event only fires when PATs are active for the user, not when the feature is globally disabled. - Tie the cleanup_expired_access_tokens scheduler to EnableUserAccessTokens so the hourly job does not schedule on servers where PATs are disabled. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Remove per-token audit logging from expired PAT cleanup job Background system jobs do not emit audit events in this codebase — only user/admin-initiated actions do. The cleanup worker's per-token AuditEventExpireUserAccessToken records were inconsistent with that pattern (cleanup_desktop_tokens and other session jobs log nothing). Also removes the early s.Audit init in NewServer that existed solely to supply a non-nil logger to the worker. The AuditEventRejectExpiredUserAccessToken event (emitted by createSessionForUserAccessToken when a live request is rejected) is unchanged — that is an auth gate firing in response to a request and warrants auditing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Replace hand-rolled IN-clause helpers with squirrel query builder Remove placeholders() and idsToArgs() from DeleteByIds — squirrel's sq.Eq{"column": slice} generates the IN clause and argument list automatically, matching the pattern used throughout the sqlstore package. Also restructures the sessions delete from a PostgreSQL-specific USING join to a portable subquery, keeping both statements expressible via the query builder. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Drop redundant logger.Error calls in cleanup worker SimpleWorker already logs any error returned from execute at the Error level (base_workers.go:86). The extra logger.Error calls before return were double-logging every failure. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Use mlog.CreateConsoleTestLogger in cleanup worker tests Replaces the hand-rolled newTestLogger helper with the established mlog.CreateConsoleTestLogger(t) pattern used by other job tests in this package (jobs_test.go, recap/worker_test.go). It wires cleanup and test-runner output automatically. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Consolidate cleanup worker tests into subtests Groups the six top-level TestCleanupExpiredXxx functions under a single TestCleanupExpired parent with t.Run subtests. One shared logger is created at the parent level; each subtest gets its own fakeStore. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Revert incidental configureAudit restructure in server.go The separation of s.Audit init from configureAudit was an unintended side effect of an earlier commit. Restore the original pattern where configureAudit is only called when s.Audit was nil at startup. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Remove api4 PAT expiry tests until API endpoint exists The three tests (expired token rejected, session clamped, no-expiry default) bypass the API to inject ExpiresAt via the store directly, since no API endpoint exists yet to create tokens with an expiry. They belong in the PR that adds that endpoint. The same behaviors are covered at the appropriate layer by storetest/user_access_token_store.go and model/user_access_token_test.go. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Prevent ExtendSessionExpiryIfNeeded from overriding PAT session expiry PAT-authenticated sessions have their ExpiresAt clamped to the token's ExpiresAt in createSessionForUserAccessToken. However, ExtendSessionExpiryIfNeeded was resetting that expiry to now+SessionLengthWebInHours on the first subsequent request, effectively bypassing PAT expiry for cached sessions. Guard the extension to skip SessionTypeUserAccessToken sessions until GetSessionLengthInMillis learns to return a length bounded by token.ExpiresAt. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Strip ExpiresAt in create-token handler until API officially supports it The JSON decoder populates the full accessToken struct from the request body, and only UserId and Token were being overwritten before the store call. This allowed clients to set an arbitrary expires_at (including 0 for non-expiring) through the existing endpoint, contradicting the PR description. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Clear session cache for affected users after DeleteByIds in cleanup worker DeleteByIds removes sessions from the DB but did not invalidate the in-memory session cache. This left stale sessions readable from cache until eviction, inconsistent with the RevokeSession path. Thread a clearSessionCache callback through MakeWorker and cleanupExpired. After each successful batch delete, call it for each unique UserId in the batch. The callback is deduplicated per batch to avoid redundant cache invalidations when a user has multiple expired tokens. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add partial index on useraccesstokens.expiresat The cleanup job queries expiresat on every scheduled run (hourly). Without an index this is a full sequential scan. Add a partial index WHERE expiresat > 0 to match the query's filter, keeping the index small since most tokens have no expiry set. Mirrors the idx_sessions_expires_at pattern on the sessions table. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Register JobTypeCleanupExpiredAccessTokens in job permission switches Without entries in SessionHasPermissionToReadJob, SessionHasPermissionToCreateJob, and SessionHasPermissionToManageJob, the job type falls through to (false, nil), which API handlers treat as HTTP 400. This made the cleanup job invisible to System Console and unmanageable via API (list, cancel, manual trigger all 400). Add the job type to the PermissionManageJobs / PermissionReadJobs groups in all three switches, matching how other internal jobs like JobTypeMigrations are handled. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Teach GetSessionLengthInMillis to honor PAT ExpiresAt Replace the blunt guard in ExtendSessionExpiryIfNeeded with proper logic in GetSessionLengthInMillis: for PAT sessions with a fixed ExpiresAt, return the remaining lifetime instead of the configured web-session hours. This means newExpiry = now + (ExpiresAt - now) = ExpiresAt, so extension never pushes the session past the token's own expiry. The elapsed threshold collapses to zero for such sessions, so no spurious DB writes occur either. Non-expiring PAT sessions (ExpiresAt == 0) continue to use normal web-session extension, which is correct behavior. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Split expiresat index into separate non-transactional migration (000175) CREATE INDEX CONCURRENTLY cannot run inside a transaction block. The morph migration runner wraps each file in a transaction by default, causing the combined migration to fail. Split the index creation out of 000174 into a new 000175 migration file with the -- morph:nontransactional directive, following the same pattern used by 000135, 000155, 000173 and others. The 174 down migration no longer needs to drop the index since 175 owns it. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Update Beginx call to renamed Begin (sqlx wrapper API change on master) --------- 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 4.6 <noreply@anthropic.com> |
||
|
+11 |
b0d2f83620 |
Translations update from Mattermost Weblate (#36695)
* Translated using Weblate (Lithuanian) Currently translated at 62.6% (4601 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/lt/ * Translated using Weblate (Lithuanian) Currently translated at 62.6% (4601 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/lt/ * Translated using Weblate (Estonian) Currently translated at 0.4% (31 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/et/ * Translated using Weblate (Estonian) Currently translated at 0.4% (31 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/et/ * Translated using Weblate (Danish) Currently translated at 8.1% (266 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/da/ * Translated using Weblate (Danish) Currently translated at 8.1% (266 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/da/ * Translated using Weblate (Italian) Currently translated at 44.6% (3281 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/it/ * Translated using Weblate (Italian) Currently translated at 44.6% (3281 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/it/ * Translated using Weblate (Belarusian) Currently translated at 88.6% (6509 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/be/ * Translated using Weblate (Khmer (Central)) Currently translated at 0.6% (48 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/km/ * Translated using Weblate (Khmer (Central)) Currently translated at 0.6% (48 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/km/ * Translated using Weblate (Latin) Currently translated at 0.0% (0 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/la/ * Translated using Weblate (Latin) Currently translated at 0.0% (0 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/la/ * Translated using Weblate (Latin) Currently translated at 0.0% (0 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/la/ * Translated using Weblate (Persian) Currently translated at 54.5% (4006 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/fa/ * Translated using Weblate (Persian) Currently translated at 54.5% (4006 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/fa/ * Translated using Weblate (Portuguese) Currently translated at 27.1% (1993 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pt/ * Translated using Weblate (Portuguese) Currently translated at 27.1% (1993 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pt/ * Translated using Weblate (Albanian) Currently translated at 3.1% (228 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/sq/ * Translated using Weblate (Albanian) Currently translated at 3.1% (228 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/sq/ * Translated using Weblate (Catalan) Currently translated at 33.3% (1089 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/ca/ * Translated using Weblate (Lao) Currently translated at 0.1% (1 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/lo/ * Translated using Weblate (Macedonian) Currently translated at 1.7% (126 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/mk/ * Translated using Weblate (Macedonian) Currently translated at 1.7% (126 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/mk/ * Translated using Weblate (Hungarian) Currently translated at 62.9% (2058 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/hu/ * Translated using Weblate (Estonian) Currently translated at 0.5% (19 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/et/ * Translated using Weblate (Georgian) Currently translated at 7.1% (525 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ka/ * Translated using Weblate (Georgian) Currently translated at 7.1% (525 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ka/ * Translated using Weblate (Georgian) Currently translated at 7.1% (525 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ka/ * Translated using Weblate (Norwegian Bokmål) Currently translated at 4.4% (147 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/nb_NO/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 99.4% (7304 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/zh_Hans/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 99.4% (7304 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/zh_Hans/ * Translated using Weblate (Nepali) Currently translated at 0.1% (6 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/ne/ * Translated using Weblate (Malayalam) Currently translated at 3.7% (272 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ml/ * Translated using Weblate (Malayalam) Currently translated at 3.7% (272 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ml/ * Translated using Weblate (Mongolian) Currently translated at 0.6% (21 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/mn/ * Translated using Weblate (French) Currently translated at 65.2% (4788 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/fr/ * Translated using Weblate (French) Currently translated at 65.2% (4788 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/fr/ * Translated using Weblate (Swedish) Currently translated at 83.6% (2733 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/sv/ * Translated using Weblate (Japanese) Currently translated at 91.4% (6715 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ja/ * Translated using Weblate (Japanese) Currently translated at 91.4% (6715 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ja/ * Translated using Weblate (Croatian) Currently translated at 25.8% (1896 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/hr/ * Translated using Weblate (Croatian) Currently translated at 25.8% (1896 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/hr/ * Translated using Weblate (Filipino) Currently translated at 0.0% (0 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/fil/ * Translated using Weblate (Filipino) Currently translated at 0.0% (0 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/fil/ * Translated using Weblate (Filipino) Currently translated at 0.0% (0 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/fil/ * Translated using Weblate (Romanian) Currently translated at 48.3% (3551 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ro/ * Translated using Weblate (Romanian) Currently translated at 48.3% (3551 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ro/ * Translated using Weblate (Amharic) Currently translated at 0.1% (4 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/am/ * Translated using Weblate (Amharic) Currently translated at 0.1% (4 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/am/ * Translated using Weblate (Sinhala) Currently translated at 0.3% (12 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/si/ * Translated using Weblate (French) Currently translated at 65.1% (2129 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/fr/ * Translated using Weblate (Bengali) Currently translated at 0.9% (70 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/bn/ * Translated using Weblate (Bengali) Currently translated at 0.9% (70 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/bn/ * Translated using Weblate (Bengali) Currently translated at 0.9% (70 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/bn/ * Translated using Weblate (Georgian) Currently translated at 3.6% (120 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/ka/ * Translated using Weblate (Georgian) Currently translated at 3.6% (120 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/ka/ * Translated using Weblate (Romanian) Currently translated at 58.0% (1897 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/ro/ * Translated using Weblate (Frisian) Currently translated at 56.6% (4161 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/fy/ * Translated using Weblate (Frisian) Currently translated at 56.6% (4161 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/fy/ * Translated using Weblate (Frisian) Currently translated at 56.6% (4161 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/fy/ * Translated using Weblate (Russian) Currently translated at 80.7% (2638 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/ru/ * Translated using Weblate (Kazakh) Currently translated at 12.7% (938 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/kk/ * Translated using Weblate (Kazakh) Currently translated at 12.7% (938 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/kk/ * Translated using Weblate (Kazakh) Currently translated at 12.7% (938 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/kk/ * Translated using Weblate (English (Australia)) Currently translated at 88.0% (2875 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/en_AU/ * Translated using Weblate (English (Australia)) Currently translated at 88.0% (2875 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/en_AU/ * Translated using Weblate (Hindi) Currently translated at 26.7% (1961 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/hi/ * Translated using Weblate (Hindi) Currently translated at 26.7% (1961 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/hi/ * Translated using Weblate (Dutch) Currently translated at 98.4% (7230 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/nl/ * Translated using Weblate (Dutch) Currently translated at 98.4% (7230 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/nl/ * Translated using Weblate (Galician) Currently translated at 19.8% (1458 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/gl/ * Translated using Weblate (Galician) Currently translated at 19.8% (1458 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/gl/ * Translated using Weblate (Swedish) Currently translated at 88.4% (6497 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/sv/ * Translated using Weblate (Swedish) Currently translated at 88.4% (6497 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/sv/ * Translated using Weblate (Arabic) Currently translated at 0.1% (7 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ar/ * Translated using Weblate (Arabic) Currently translated at 0.1% (7 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ar/ * Translated using Weblate (Albanian) Currently translated at 0.9% (32 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/sq/ * Translated using Weblate (Vietnamese) Currently translated at 66.5% (2174 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/vi/ * Translated using Weblate (German) Currently translated at 90.6% (2962 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/de/ * Translated using Weblate (German) Currently translated at 90.6% (2962 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/de/ * Translated using Weblate (Latvian) Currently translated at 0.3% (24 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/lv/ * Translated using Weblate (Latvian) Currently translated at 0.3% (24 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/lv/ * Translated using Weblate (Latvian) Currently translated at 0.3% (24 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/lv/ * Translated using Weblate (Thai) Currently translated at 1.0% (77 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/th/ * Translated using Weblate (Thai) Currently translated at 1.0% (77 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/th/ * Translated using Weblate (English (Australia)) Currently translated at 94.2% (6922 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/en_AU/ * Translated using Weblate (Serbian) Currently translated at 8.4% (275 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/sr/ * Translated using Weblate (Turkish) Currently translated at 83.4% (2725 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/tr/ * Translated using Weblate (Turkish) Currently translated at 83.4% (2725 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/tr/ * Translated using Weblate (Portuguese) Currently translated at 6.3% (207 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/pt/ * Translated using Weblate (Greek) Currently translated at 25.3% (829 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/el/ * Translated using Weblate (Gujarati) Currently translated at 0.1% (9 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/gu/ * Translated using Weblate (Gujarati) Currently translated at 0.1% (9 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/gu/ * Translated using Weblate (Arabic (Saudi Arabia)) Currently translated at 0.0% (0 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ar_SA/ * Translated using Weblate (Arabic (Saudi Arabia)) Currently translated at 0.0% (0 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ar_SA/ * Translated using Weblate (English (Pirate)) Currently translated at 0.1% (2 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/en@pirate/ * Translated using Weblate (English (Pirate)) Currently translated at 0.1% (2 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/en@pirate/ * Translated using Weblate (English (Pirate)) Currently translated at 0.1% (2 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/en@pirate/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 98.1% (3208 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/zh_Hans/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 98.1% (3208 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/zh_Hans/ * Translated using Weblate (Bulgarian) Currently translated at 61.5% (2011 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/bg/ * Translated using Weblate (Bulgarian) Currently translated at 61.5% (2011 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/bg/ * Translated using Weblate (Korean) Currently translated at 79.5% (2600 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/ko/ * Translated using Weblate (Korean) Currently translated at 79.5% (2600 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/ko/ * Translated using Weblate (Indonesian) Currently translated at 27.0% (883 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/id/ * Translated using Weblate (Basque) Currently translated at 1.3% (43 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/eu/ * Translated using Weblate (Slovenian) Currently translated at 13.8% (1015 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/sl/ * Translated using Weblate (Slovenian) Currently translated at 13.8% (1015 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/sl/ * Translated using Weblate (Arabic (Saudi Arabia)) Currently translated at 0.0% (0 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/ar_SA/ * Translated using Weblate (Breton) Currently translated at 0.1% (7 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/br/ * Translated using Weblate (Breton) Currently translated at 0.1% (7 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/br/ * Translated using Weblate (Bulgarian) Currently translated at 59.0% (4335 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/bg/ * Translated using Weblate (Bulgarian) Currently translated at 59.0% (4335 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/bg/ * Translated using Weblate (Amharic) Currently translated at 3.0% (101 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/am/ * Translated using Weblate (Thai) Currently translated at 0.7% (23 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/th/ * Translated using Weblate (Polish) Currently translated at 99.4% (7304 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pl/ * Translated using Weblate (Polish) Currently translated at 99.4% (7304 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pl/ * Translated using Weblate (Korean) Currently translated at 84.1% (6179 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ko/ * Translated using Weblate (Korean) Currently translated at 84.1% (6179 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ko/ * Translated using Weblate (Slovenian) Currently translated at 33.4% (1092 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/sl/ * Translated using Weblate (Slovenian) Currently translated at 33.4% (1092 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/sl/ * Translated using Weblate (Chinese (Traditional Han script)) Currently translated at 75.3% (5535 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/zh_Hant/ * Translated using Weblate (Chinese (Traditional Han script)) Currently translated at 75.3% (5535 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/zh_Hant/ * Translated using Weblate (Finnish) Currently translated at 38.2% (1249 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/fi/ * Translated using Weblate (Japanese) Currently translated at 81.5% (2665 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/ja/ * Translated using Weblate (Japanese) Currently translated at 81.5% (2665 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/ja/ * Translated using Weblate (Catalan) Currently translated at 1.6% (121 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ca/ * Translated using Weblate (Catalan) Currently translated at 1.6% (121 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ca/ * Translated using Weblate (Mongolian) Currently translated at 1.6% (124 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/mn/ * Translated using Weblate (Mongolian) Currently translated at 1.6% (124 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/mn/ * Translated using Weblate (Polish) Currently translated at 96.6% (3159 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/pl/ * Translated using Weblate (Polish) Currently translated at 96.6% (3159 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/pl/ * Translated using Weblate (Hebrew) Currently translated at 0.6% (50 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/he/ * Translated using Weblate (Hebrew) Currently translated at 0.6% (50 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/he/ * Translated using Weblate (Belarusian) Currently translated at 85.6% (2799 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/be/ * Translated using Weblate (Belarusian) Currently translated at 85.6% (2799 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/be/ * Translated using Weblate (Turkish) Currently translated at 91.8% (6747 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/tr/ * Translated using Weblate (Turkish) Currently translated at 91.8% (6747 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/tr/ * Translated using Weblate (Turkish) Currently translated at 91.8% (6747 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/tr/ * Translated using Weblate (Italian) Currently translated at 62.0% (2026 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/it/ * Translated using Weblate (Spanish) Currently translated at 60.2% (4426 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/es/ * Translated using Weblate (Spanish) Currently translated at 60.2% (4426 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/es/ * Translated using Weblate (Basque) Currently translated at 0.1% (11 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/eu/ * Translated using Weblate (Basque) Currently translated at 0.1% (11 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/eu/ * Translated using Weblate (Vietnamese) Currently translated at 62.1% (4562 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/vi/ * Translated using Weblate (Vietnamese) Currently translated at 62.1% (4562 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/vi/ * Translated using Weblate (Czech) Currently translated at 82.2% (6037 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/cs/ * Translated using Weblate (Czech) Currently translated at 82.2% (6037 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/cs/ * Translated using Weblate (Icelandic) Currently translated at 0.0% (0 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/is/ * Translated using Weblate (Icelandic) Currently translated at 0.0% (0 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/is/ * Translated using Weblate (Icelandic) Currently translated at 0.0% (0 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/is/ * Translated using Weblate (Danish) Currently translated at 8.9% (659 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/da/ * Translated using Weblate (Danish) Currently translated at 8.9% (659 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/da/ * Translated using Weblate (Serbian) Currently translated at 7.6% (559 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/sr/ * Translated using Weblate (Serbian) Currently translated at 7.6% (559 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/sr/ * Translated using Weblate (Breton) Currently translated at 0.8% (29 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/br/ * Translated using Weblate (German) Currently translated at 96.2% (7071 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/de/ * Translated using Weblate (German) Currently translated at 96.2% (7071 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/de/ * Translated using Weblate (Indonesian) Currently translated at 2.6% (191 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/id/ * Translated using Weblate (Indonesian) Currently translated at 2.6% (191 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/id/ * Translated using Weblate (Chinese (Traditional Han script)) Currently translated at 73.3% (2396 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/zh_Hant/ * Translated using Weblate (Arabic) Currently translated at 0.5% (18 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/ar/ * Translated using Weblate (Kazakh (Latin script)) Currently translated at 3.2% (235 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/kk_Latn/ * Translated using Weblate (Kazakh (Latin script)) Currently translated at 3.2% (235 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/kk_Latn/ * Translated using Weblate (Kazakh (Latin script)) Currently translated at 3.2% (235 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/kk_Latn/ * Translated using Weblate (Gujarati) Currently translated at 0.5% (19 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/gu/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 74.5% (2436 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/pt_BR/ * Translated using Weblate (Czech) Currently translated at 81.7% (2670 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/cs/ * Translated using Weblate (Czech) Currently translated at 81.7% (2670 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/cs/ * Translated using Weblate (Persian) Currently translated at 60.0% (1963 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/fa/ * Translated using Weblate (Hungarian) Currently translated at 59.6% (4383 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/hu/ * Translated using Weblate (Hungarian) Currently translated at 59.6% (4383 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/hu/ * Translated using Weblate (Hungarian) Currently translated at 59.6% (4383 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/hu/ * Translated using Weblate (Hebrew) Currently translated at 1.1% (39 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/he/ * Translated using Weblate (Finnish) Currently translated at 20.9% (1538 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/fi/ * Translated using Weblate (Finnish) Currently translated at 20.9% (1538 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/fi/ * Translated using Weblate (Croatian) Currently translated at 8.0% (264 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/hr/ * Translated using Weblate (Sinhala) Currently translated at 0.1% (10 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/si/ * Translated using Weblate (Sinhala) Currently translated at 0.1% (10 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/si/ * Translated using Weblate (Greek) Currently translated at 6.5% (482 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/el/ * Translated using Weblate (Greek) Currently translated at 6.5% (482 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/el/ * Translated using Weblate (Malayalam) Currently translated at 8.9% (292 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/ml/ * Translated using Weblate (Galician) Currently translated at 1.7% (58 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/gl/ * Translated using Weblate (Ukrainian) Currently translated at 80.4% (5905 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/uk/ * Translated using Weblate (Ukrainian) Currently translated at 80.4% (5905 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/uk/ * Translated using Weblate (Nepali) Currently translated at 5.7% (424 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ne/ * Translated using Weblate (Nepali) Currently translated at 5.7% (424 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ne/ * Translated using Weblate (Norwegian Bokmål) Currently translated at 76.4% (5613 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/nb_NO/ * Translated using Weblate (Norwegian Bokmål) Currently translated at 76.4% (5613 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/nb_NO/ * Translated using Weblate (Norwegian Bokmål) Currently translated at 76.4% (5613 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/nb_NO/ * Translated using Weblate (Hindi) Currently translated at 58.5% (1913 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/hi/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 62.0% (4557 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pt_BR/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 62.0% (4557 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pt_BR/ * Translated using Weblate (Khmer (Central)) Currently translated at 0.7% (26 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/km/ * Translated using Weblate (Lithuanian) Currently translated at 6.7% (221 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/lt/ * Translated using Weblate (Ukrainian) Currently translated at 78.5% (2567 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/uk/ * Translated using Weblate (Ukrainian) Currently translated at 78.5% (2567 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/uk/ * Translated using Weblate (Spanish) Currently translated at 67.0% (2191 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/es/ * Translated using Weblate (Spanish) Currently translated at 67.0% (2191 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/es/ * Translated using Weblate (Spanish) Currently translated at 67.0% (2191 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/es/ * Translated using Weblate (Dutch) Currently translated at 95.8% (3131 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/nl/ * Translated using Weblate (Macedonian) Currently translated at 6.7% (221 of 3267 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/mk/ * Translated using Weblate (Lao) Currently translated at 3.9% (290 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/lo/ * Translated using Weblate (Lao) Currently translated at 3.9% (290 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/lo/ * Translated using Weblate (Russian) Currently translated at 76.7% (5636 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ru/ * Translated using Weblate (Russian) Currently translated at 76.7% (5636 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ru/ * Translated using Weblate (Russian) Currently translated at 76.7% (5636 of 7343 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ru/ * Update translation files Updated by "Remove blank strings" hook in Weblate. Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ * Update translation files Updated by "Remove blank strings" hook in Weblate. Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/ --------- Co-authored-by: jprusch <rs@schaeferbarthold.de> Co-authored-by: Martin Popp Fredslund (SektorCERT) <martin@sektorcert.dk> Co-authored-by: Tom De Moor <tom@controlaltdieliet.be> Co-authored-by: Ekaterine Papava <papava.e@gtu.ge> Co-authored-by: Sharuru <mave@foxmail.com> Co-authored-by: Matthew Williams <Matthew.Williams@outlook.com.au> Co-authored-by: Kristoffer Grundström <swedishsailfishosuser@tutanota.com> Co-authored-by: Kaya Zeren <kayazeren@gmail.com> Co-authored-by: Nikolai Zahariev <nikolaiz@yahoo.com> Co-authored-by: master7 <marcin.karkosz@rajska.info> Co-authored-by: DeepL <noreply-mt-deepl@weblate.org> Co-authored-by: Takayuki Maruyama <bis5.wsys@gmail.com> Co-authored-by: Vadim Asadchi <vadim.asadchi@codex-soft.com> Co-authored-by: Martin Mičuda <micuda@rematiptop.cz> Co-authored-by: ritchierope <ritchierope@users.noreply.translate.mattermost.com> Co-authored-by: Frank Paul Silye <frankps@gmail.com> Co-authored-by: Serhii Khomiuk <sergiy.khomiuk@gmail.com> Co-authored-by: Ricardo Obregón <robregonm@gmail.com> Co-authored-by: Carloswaldo <waldosaurio@gmail.com> Co-authored-by: Dmitriy Q <krotesk@mail.ru> |
||
|
|
3570814ddd |
MM-68316: add mattermost db ping subcommand (#36406)
* MM-68316: add `mattermost db ping` subcommand Adds a one-shot DB-readiness CLI that opens a connection to the configured PostgreSQL DSN and exits 0 when reachable. Honours --timeout (default 5m) and --retry-interval (default 2s); reads the DSN from --config / MM_CONFIG / MM_SQLSETTINGS_DATASOURCE matching the existing `db init`/`db migrate` semantics. Intended primarily as an init-container readiness check for the mattermost-operator, removing its dependency on a separate postgres:13 image (relevant for air-gapped deployments). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68316: address PR feedback (sanitize ping retry log) Drop mlog.Err(err) from the per-attempt "Waiting for database" log line in pingWithRetry: lib/pq error strings can echo DSN fragments back at operators when DSN parsing fails. Replace with a non-sensitive status field. Also clarifies the TestDBPingInvalidDSN comment to reflect actual lib/pq behavior (parse error surfaces at PingContext, not Open, so the retry loop treats it as transient — desired for a readiness probe). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68316 Preserve safe db ping DSN errors Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> |
||
|
|
03f2eaaa0b |
[MM-68400] Four plugin hooks and ChannelGuard enforcement (#36152)
* allow workflow_dispatch trigger for Server CI (for plugins CI) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * [MM-68402] MBE Phase 2: declare four generic plugin hooks (#36291) * new hooks-only phase 2 * remove ChannelWillBeMoved * remove RecapWillBeProcessed and MessageWillBeRewrittenByAI Drop the AI/recap hooks from the new-hook surface; AI-LLM paths remain uncovered in tech preview and are documented as residuals. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [MM-68403] MBE Phase 3: ChannelGuards primitive (storage + cache + plugin API) (#36365) * phase 3 * phase 3: register ChannelGuard mock in test setup helper NewChannels' startup-time call to reloadGuardCache invokes s.ChannelGuard().GetAll(); without an expectation on the mock store, every test that sets up the server with GetMockStoreForSetupFunctions panics during init. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * phase 3: register ChannelGuard mock in retrylayer test retrylayer.New walks every store getter to wrap it; without the mock expectation on ChannelGuard, TestRetry panics during layer construction. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * use rctx properly in the store methods * phase 3: match rctx arg in testlib ChannelGuard mock GetAll now takes request.CTX, so the testify expectation must include mock.Anything; otherwise the call panics under the mocked store. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * phase 3: set api.ctx in TestChannelGuardLowercaseNormalization The test constructs PluginAPI directly without a ctx, which used to work when App.RegisterChannelGuard built its own EmptyContext. Now that the App methods take rctx from the caller, the nil ctx panics inside RequestContextWithMaster. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [MM-68404] MBE Phase 4: App-layer plugin hook wiring (#36407) * phase 4 * Fix nil rctx in TestChannelGuardLowercaseNormalization The PluginAPI struct literal was missing ctx: rctx after a refactor moved the rctx declaration below the struct construction, leaving api.ctx as nil. This caused a nil pointer dereference in reloadGuardCache when RegisterChannelGuard called store.RequestContextWithMaster(nil). Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> * Remove ChannelWillBeMoved hook call from MoveChannel (phase 4) The hook and its ID were removed from mbe-phase-2 but the call site in MoveChannel and its i18n string were not cleaned up during the rebase. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * remove channel will be moved test * Remove RecapWillBeProcessed and MessageWillBeRewrittenByAI hook calls (phase 4) The hooks and their IDs were removed from mbe-phase-2 but the call sites in ProcessRecapChannel and RewriteMessage, their i18n strings, and their tests were not cleaned up during the rebase. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Revert channel_id plumbing on rewrite endpoint (phase 4) The channel_id field on RewriteRequest was added in phase 4 to feed the synthetic post passed to MessageWillBeRewrittenByAI. With that hook removed from mbe-phase-2, channel_id has no consumer; revert the field, the api4 validation, the app.RewriteMessage parameter, and the corresponding webapp client + hook plumbing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [MM-68555] MBE Phase 5: Channel-guard enforcement + two-phase dispatch (#36473) * phase 5 * Bake plugin counter-file paths into source instead of env vars t.Setenv panics when an ancestor test calls t.Parallel, so the two channel-guard tests broke under ENABLE_FULLY_PARALLEL_TESTS in CI. Build each plugin source per-subtest with its temp file path embedded as a Go literal — same pattern as TestPluginUploadsAPI. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Remove guarded helpers and tests for dropped hooks (phase 5) The runGuardedRecapWillBeProcessed and runGuardedMessageWillBeRewrittenByAI helpers were never wired (their app-layer call sites were already removed in the phase-4 cleanup), and the corresponding sub-tests across panic / allow / reject / partial plugins reference hooks that no longer exist. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [MM-68405] MBE Phase 6: fire MessagesWillBeConsumed on the edit path (#36475) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * rebase onto master --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
b9e8d5ce82 | MM-68763: fix BuildAccessControlSubject call missing channelID argument (#36681) | ||
|
|
e6c59693af | MM-68763: Discoverable Private Channels — Server feature complete (visibility, ABAC, queue API) (#36580) | ||
|
|
ba1cec51a5 | [MM-68693] Resource level permission policies and new simulation (#36472) | ||
|
|
7739b349a0 |
[MM-68578] Add support packet DB performance diagnostics (#36324)
* Add support packet DB diagnostics for pool and pg_stat Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> * Fix support packet mock store for new DB diagnostics Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> * Add context timeouts to support packet pg diagnostics Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> * Move support packet DB diagnostics queries into sqlstore Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> * Fix support packet diagnostics lint and partial data handling Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> * Stabilize support packet pool idle assertion Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> * Relax live support packet DB counter assertions Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> * Fix deterministic pool diagnostics test wiring Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> * Mock support packet diagnostics in app test store Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> * Move SupportPacketDatabaseDiagnostics out of public model The struct is an internal store→platform transport (no yaml tags, never serialized directly) so it doesn't belong in server/public/model where it would form a public API contract for plugins. Move it into the store package as the natural return type of GetSupportPacketDatabaseDiagnostics. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Describe SupportPacketDatabaseDiagnostics by content, not history Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * wording * Align store diagnostics with sqlstore conventions - Rename Store.GetSupportPacketDatabaseDiagnostics to Store.GetDiagnostics and rename the holding files to diagnostics{,_test}.go. - Drop the queryRowScanner / rowScanner / sqlQueryRowScanner test seam. The collectors now use the sqlxDBWrapper master handle and bind result rows into local structs via sqlx GetContext, matching how the rest of the sqlstore package talks to Postgres. - Replace the hand-rolled mock-based unit tests for the Postgres collector with an integration test driven through StoreTest, the pattern used by the other sqlstore tests (e.g. schema_dump_test.go). The pure pool-stats unit test (TestApplyDBPoolStats) is kept. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Drop MasterDBStats/ReplicaDBStats from the Store interface After GetDiagnostics moved into the store layer, no caller outside the sqlstore package itself reads MasterDBStats/ReplicaDBStats through the Store interface — the diagnostics collector calls them on the concrete *SqlStore receiver. Remove them from the interface, the retry/timer layer wrappers, the storetest fake, and the generated mock; drop the now-redundant fixedDBStatsStore shim methods and mock setups in the support packet tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Rename SupportPacketDatabaseDiagnostics to DatabaseDiagnostics Now that the type lives in the store package and is returned by Store.GetDiagnostics, the SupportPacket prefix is just legacy framing — support packets are one consumer of the data, not its identity. Rename to store.DatabaseDiagnostics for consistency with the package and method name. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Inline diagnostics SQL into the collector functions Each pg_stat query has a single caller, so a package-level constant just adds indirection between the function and the SQL it owns. Move the query strings to local consts inside the collectors that use them. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Fix Connectios typo to Connections Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Fix sqlstore diagnostics build: call GetMaster().DB() method The recent rebase brought in a change that turned the SqlStore DB field into a method, so passing ss.GetMaster().DB to collectPostgresDatabaseDiagnostics (which expects *sqlx.DB) no longer compiles. Call the method instead. * Fix gofmt alignment in SupportPacketDiagnostics The post-merge struct had extra spaces on MasterConnections / ReplicaConnections that broke gofmt alignment. --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
2ab07701b5 |
[MM-68577] Add OAuth2/OpenID Connect provider status to support packet (#36451)
* [MM-68577] Add OAuth2/OpenID Connect provider status to support packet Probe configured GitLab, Google, Office365, and OpenID providers and report their connectivity status in the support packet diagnostics. For providers with a DiscoveryEndpoint, the probe verifies a valid OIDC discovery document (JSON with an "issuer" field) is returned; otherwise it probes the TokenEndpoint host, treating any HTTP response as reachable since token endpoints reject GETs. Disabled providers report status: disabled, enabled providers report ok or fail with the underlying error. No secrets are read or transmitted; only public endpoint URLs are probed. * [MM-68577] Drain response body in probeOAuthTokenEndpoint Closing resp.Body without first reading it leaves unread bytes on the wire, which prevents net/http from returning the underlying TCP connection to the idle pool for keep-alive reuse. Drain with io.Copy + io.LimitReader (1MB cap to bound a misbehaving server) and use defer for the close. * [MM-68577] Extract drainAndCloseBody helper for HTTP probe responses Three call sites in this file (probeOIDCDiscovery, probeOAuthTokenEndpoint, testPushProxyConnection) now share the same drain-then-close idiom needed to keep TCP connections eligible for keep-alive reuse. Replace the inline copies with a single drainAndCloseBody helper that bounds the discard at 1 MiB to limit exposure to a misbehaving server. Also fixes the same un-drained Close() bug in the pre-existing testPushProxyConnection while we're here. * Add comment explaining 1 MiB discovery response size cap Addresses review feedback asking for clarity on the 1<<20 limit. |
||
|
|
a7ef484fee |
[MM-68576] Add SAML connectivity status to support packet diagnostics (#36321)
* Add SAML connectivity status to support packet diagnostics Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> * Fix SAML diagnostics tests for config validation Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> * Add enterprise SAML diagnostics hook for support packet Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> * Cleanup * Fix SAML support packet tests to use enterprise mock interface Tests were expecting the platform layer to perform HTTP metadata URL checks directly, but that logic belongs in the enterprise SAML diagnostic implementation. Updated tests to install a mock enterprise interface (matching the existing pattern in the override test) instead of relying on bare HTTP calls that only work without the enterprise interface registered. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Simplify SamlDiagnosticInterface to return error instead of (string, string) The status return was always either StatusOk or StatusFail, which maps directly to nil/non-nil error. Removing the redundant status string makes the interface idiomatic Go and lets the call site derive status from error presence. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * lint fix --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
981e5341ca |
Fix flaky TestUserHasJoinedChannel (#36660)
* Fix flaky TestUserHasJoinedChannel The UserHasJoinedChannel plugin hook is invoked from AddChannelMember via Srv().Go, so the hook post can appear after the join system post. Under CI load the 5s Eventually window was sometimes too short. Confirm the plugin is active before triggering the hook, poll posts in channel order like the sibling subtest, and extend the wait to 10s. Tests-only change. Verified with `go test -run '^TestUserHasJoinedChannel$' -race -count=50` locally. Co-authored-by: mattermost-code <matty-code@mattermost.com> * test: add plugin activation assertion messages * test: deduplicate plugin hook post assertion --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: mattermost-code <matty-code@mattermost.com> |
||
|
|
2c925ccf88 | MM-68151: Update server dependencies (#36571) | ||
|
|
448a642835 |
Add inline action buttons for bot-posted markdown (#36219)
* Add inline action buttons for bot-posted markdown
Bots, webhooks, and plugins can now embed clickable action buttons
inside markdown (including table cells) using mmaction://actionId
links, with row-specific parameters forwarded to the integration on
click. This enables use cases like a per-row "Mx Plan" button in a
fleet-status table that opens a dialog scoped to the clicked row.
Design
- New post prop inline_actions maps actionId (alphanumeric) to a
PostActionIntegration {URL, Context}, capped at 50 entries.
- Markdown link with scheme mmaction:// emits a placeholder span that
messageHtmlToComponent converts to the InlineActionButton component.
- Click POSTs inline_context (parsed from the URL query string) to the
existing /posts/{id}/actions/{action_id} endpoint; the server merges
it into the integration request as context.inline_params while
preserving the post-level context.
- Only bot, webhook, and plugin posts render the button; non-integration
posts have inline_actions stripped on create, update, and ephemeral
broadcast. Hardened-mode also covers the new prop.
- Reuses the existing PostAction dialog pipeline: plugin handlers reply
with a trigger_id and call /actions/dialogs/open as before.
Security
- InlineContext capped at 50 entries / 128-char keys / 2 KB values.
- Integration Context cloned per click so per-click inline_params and
selected_option cannot leak into the cached post for other clickers.
- Plugin response updates cannot add inline_actions to a post that did
not already have them; invalid entries are dropped with a warn log.
- Label content and data attributes are escaped; labels are flattened
to plain text (tags stripped, entities decoded, then escaped).
- Malformed JSON request bodies now return 400 instead of falling
through with an empty inline_context.
Tests
- Model: validators, normalization, GetInlineAction, strip, fallback.
- App: create strip, update guard (4 subtests including
AllowInlineActionsUpdate bypass), ephemeral strip, inline_params
merge, context-map isolation, plugin-response guards, from_bot and
from_plugin retention across plugin updates.
- API: inline_context validation (size bounds + error id),
omitempty backward compat, malformed JSON 400.
- Webapp: renderer scheme handling, allow/deny flags, size caps,
HTML escape, tag strip, entity decode, attribute-injection defense;
component click dispatch, double-click race guard, unmount safety,
error-result recovery, aria state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* lint fix
* i18n-extract
* Review fixes for inline action buttons
- renderer: preserve actionId case; reject opaque mmaction: URI
- app: require bot AND integration session to preserve inline_actions
- app: restore original inline_actions when plugin response is invalid
- i18n: rename key to ...app_error to match convention
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Tighten UpdatePost inline_actions guard; fix test seeds
- app: UpdatePost now requires AllowInlineActionsUpdate to modify
inline_actions. Integration session alone is insufficient — a
PAT-wielding user could otherwise inject inline_actions on any
post they could edit.
- tests: seed bot posts with inline_actions via an integration
session (intSeedCtx) so they survive the create-time strip.
- renderer: lint fix (blank line before comment block).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Reject malformed inline-action authorities at render time
- renderer: enforce ^[A-Za-z0-9]+$ on actionId, mirroring the server
regex. Authorities like mmaction://plan:443 or mmaction://user@plan
now fall through to plain text instead of rendering a dead button.
- post: clarify in the strip comment that webhooks and plugins bypass
CreatePostAsUser entirely (they call CreatePost / CreatePostMissingChannel
directly), so the strip block does not apply to them.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Tighten inline-action renderer tests
- Replace oversized-params test with boundary pair (at-cap and over-cap)
to lock in the > vs >= behavior of the size-limit check.
- Add a "surrounding text survives" assertion for the tag-strip path so
a future swap from regex strip to a DOM sanitizer won't silently
drop legitimate content along with tags.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Inline action buttons via mmaction:// markdown links
Adds inline action buttons rendered from mmaction:// links in markdown,
with the click pipeline reusing the existing post-action infrastructure.
Aligned with the broader mm_blocks_actions framework (Daniel's PR).
* fix lint, DoS hardening, fix and rename test
* Address review feedback
* lint fix
* Reject percent-encoded path traversal in validateIntegrationURL (e.g. %2e%2e%2f) by parsing the URL and checking the decoded path.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
|
||
|
|
a84941bec1 | Remove Legacy Interactive Dialog code (#35874) | ||
|
|
0790fc7281 | Upgrade Go to 1.26.3 (#36656) | ||
|
|
c74e51f35e | chore(ci): upgrade Go to 1.26.3 in build container Dockerfiles (#36648) | ||
|
|
51c6d5219f |
Fix config Sanitize fields missing from desanitize, causing FakeSetting to be persisted (#36619)
* Add TestDesanitizeRemovesAllFakeSettings to catch future omissions Walks every string field in the config after a Sanitize+desanitize round-trip and fails if any still holds FakeSetting. This catches the case where a field is added to Sanitize without a corresponding desanitize entry. * Fix ElasticsearchSettings.ClientKey being incorrectly masked as a secret ClientKey is a file path, not a secret value. Masking it caused the asterisk string to be persisted to the database on config writes, which broke TLS client auth on restart. * Fix desanitize missing entries for fields added in |
||
|
|
345a0b76a6 |
Mm 68506 fe abac mask fe table editor cel and e2e (#36517)
* MM-68501 - implement GetMaskedVisualAST and wire API handler Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * add missing test and fix style issues * fix styles * implement coderabbit feedback * MM-68501 - PR review: split masking file, model-level access mode, reject contradictory config Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68501 - apply shared_only filter to non-option field values (binary masking) * MM-68501 - consolidate masking flag check and log corrupt text value during masking * MM-68503 - add CEL utilities, write-path validation, and merge helpers Combined set of helpers consumed by BE-5's save path: CEL construction / serialization - extractStringValues, buildCELFromConditions, conditionToCEL, celStringLiteral, celValueLiteral. Used to rebuild a CEL string from a VisualExpression, including for GetMaskedExpression on the read-side of policy GET / search responses. Merge-on-save helpers - getHiddenValues (per-condition, with pre-fetched fields map for N+1 avoidance) — finds which stored values are not visible to the caller. - mergeConditionValues — re-injects the hidden values into a submitted condition without duplicates. - Together, these let BE-5 preserve attribute values the caller cannot see while still letting them edit the visible parts of a policy. Write-path value-hold validation - validatePolicyExpressionValues, invalidValueError, validateConditionValues. - Generic "Invalid value." error on every rejection — no signal about whether the value exists or is merely not held (prevents enumeration). - Rejects the masked-token sentinel "--------" if submitted as a literal. These all live in access_control_masking.go alongside the masking primitives that BE-2 introduced. i18n entries added for the two new error IDs (app.pap.save_policy.invalid_value, app.pap.validate_expression_values.app_error). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68503 - handle the masked-token sentinel in validation and merge When the GET /policies endpoint returns a policy via MaskPolicyExpressions, the raw expression contains the masked-token sentinel "--------" in place of hidden values. If the frontend round-trips that expression unchanged back to the server (e.g., the admin only modified channel assignment, not the rules), the sentinel reaches the save path. The previous code in validateConditionValues rejected the sentinel as "Invalid value." This blocks the legitimate round-trip case. Fix: - validateConditionValues: treat the sentinel as a placeholder and skip it during visibility / source-only / unknown-mode checks. Other values are still validated normally. - mergeConditionValues: strip the sentinel from submitted values before appending hidden values, so it never propagates to the stored result. Both array and single-value forms (string == "--------") are handled. TestMaskedTokenRejection (which asserted the old rejection behavior) is replaced by TestMaskedTokenConstant which only verifies the sentinel string itself. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68504 - integrate save-path masking: 403 block on delete, merge-on-save, response masking Save path (CreateOrUpdateAccessControlPolicy): * validatePolicyExpressionValues runs on the submitted expression before merge so re-injected hidden values are never validated against the caller's holdings. * mergeStoredPolicyExpressions re-injects hidden values from the stored policy and blocks (HTTP 403) any attempt to remove a condition that contained values the caller cannot see — closes the row-deletion gap in classified environments. * mergeExpressionWithMaskedValues unwraps single-element arrays for scalar operators after restoring the stored operator (avoids "attr == [val]" invalid CEL when the frontend submits "attr in []" as the masked-row placeholder for an originally-scalar condition). * checkSelfInclusion is bypassed for system admins (they may legitimately write conditions for values they do not hold); masking and value-hold validation still apply to system admins. Delete path (DeleteAccessControlPolicy): * Same masked-values 403 block — a caller with masked values cannot delete the policy at all (UI Delete button is also disabled in FE-3). Response masking: * createAccessControlPolicy and setAccessControlPolicyActiveStatus run MaskPolicyExpressions on the response so even a save reply doesn't leak the values the caller does not hold. GetMaskedExpression, maskConditionValuesWithToken, replaceHiddenValuesWithToken, MaskPolicyExpressions live alongside the rest of the masking helpers in access_control_masking.go. team_access_control.go: corrects ValidateChannelEligibilityForAccessControl call site (drops the spurious receiver and rctx; it's a package-level helper that only takes channel). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68503 - address PR review: batch field fetches, propagate errors, fail-closed write path * MM-68503 - restore team-admin api4 tests accidentally dropped during BE-5 rebuild * MM-68503 - address review and CodeRabbit feedback on save-path masking * add tests for delete masking, self-inclusion, GET mask * add assertions to strengten tests * MM-68505 - add has_masked_values type and MaskedChip component Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * MM-68506 - add masking support to TableEditor and team settings modal TableEditor (table_editor.tsx, table_editor.scss): - hasMaskedValues plumbed through rows; lock operator/attribute selectors on masked rows. - Row remove (trash) button disabled on masked rows; disabled-state CSS so the icon doesn't show the destructive hover colour or a pointer cursor. - Test Rules button disabled when any row has masked values, with tooltip. - onMaskedStateChange callback to notify the parent for cross-component states (CEL editor read-only, Save disabled, banners). Value selectors (single_value_selector_menu.tsx, multi_value_selector_menu.tsx, selector_menus.scss, value_selector_menu.tsx): - Append MaskedChip after visible chips on multi-value rows. - Render MaskedChip as the sole value on single-value rows where the caller holds no visible value. Policy details (policy_details.tsx, .scss, .test.tsx): - Track hasMaskedRows state; receive from TableEditor via onMaskedStateChange. - Show masked-values warning banner above the editor when present. - Same banner on the Delete confirmation modal so admins understand why deletion is consequential. Team settings modal (team_policy_editor.tsx, .scss): - Same masked-values plumbing; delete button uses the disabled state when a policy has masked values, regardless of whether channels are assigned. - Pre-save check no longer treats "in []" as an incomplete rule — that placeholder comes from fully-masked rows that merge-on-save will fill in. i18n entries added for the new strings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68506 - fix hook order in SingleValueSelector when masked state changes The early return for `hasMaskedValues && !value` sat between useState and useCallback declarations, so when a parent re-render flipped the masked state (e.g. after deleting a sibling rule) React saw a different hook count and crashed with "Rendered fewer hooks than expected". Move the read-only short-circuit after all hook declarations so the hook order stays stable across renders. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68507 - CEL editor read-only when masked + system console wiring CEL editor (editor.tsx, editor.scss): - hasMaskedRows prop: when true, Monaco is set to read-only and a banner explains why ("This expression contains restricted values. Switch to Simple mode to edit the values you have access to, or delete the entire rule."). - Test Rules button disabled in CEL mode when hasMaskedRows is true. Policy details (policy_details.tsx, .scss): - hasMaskedRows state plumbed to CELEditor, TableEditor, and the Save / Delete buttons. - Save button disabled while masked rows are present (kept after the save-allowed-with-masked-values change in BE-5? — no, here we keep Save enabled so admins can add/modify rules; only row removal of masked rows is blocked). - Delete Policy button disabled when hasMaskedRows; a SectionNotice above the Delete card explains why ("This policy contains restricted values - Deletion not allowed"). - New save error messages: invalid_value and self_exclusion are surfaced from the server's generic responses. Policies list (policies.tsx): minor wiring change for the new state plumbing. Table editor (table_editor.tsx): cross-component coordination — emits onMaskedStateChange and respects the disabled-for-masked-row policy. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68508 - E2E suite for attribute-value masking Covers the full read+write masking flow against a real server: - Masked chip rendering, operator/attribute lock, Test Rules disabled. - System admin subject to masking like any other caller (no role bypass). - Save with masked values: hidden values preserved by merge-on-save. - Trash button disabled on masked rows; server returns 403 on direct API attempt to remove a masked condition. - Delete Policy button disabled + server 403 when policy has masked values (both system console and team settings modal paths). - Self-inclusion failure only fires when the caller holds full visibility. - CEL editor read-only with banner when masked rows present. - Direct API validation: non-held values and the masked-token sentinel rejected with a generic "Invalid value." error. - Feature-flag-off path: no masking, all values visible. - Text-field shared_only masking (binary) with `in` and `==` operators. A pluggable DB-setup helper marks specific CPA fields as shared_only for the duration of a test (with per-test cleanup) since the API blocks setting access_mode=shared_only without a source_plugin_id. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68506 - fix lint, jest mock factory, and unreachable delete-modal test * MM-68506 - localize masked-condition-deleted save error * MM-68506 - fix masked-policy delete warning detection and localize masked_rule_deleted * fix linter issues * MM-68506 - surface delete error, lock value selector on masked rows, drop dead remove-modal * fix linter, add translations, adjust specs * import wittoltip from shared * fix linter and use the correct button variant * MM-68506 - drop dangling rationale comment in access_control_field_test * fix linter, translation and e2e tests * use pg ts types and dependencies for e2e types mocks * adjust switch mode persistance restriction * fix team settings style buttons * fail-closed guard for advanced expressions in merge-on-save, plus helper unit tests, and FF/test-helper cleanups * MM-68505 - add has_masked_values type and MaskedChip component Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * MM-68506 - add masking support to TableEditor and team settings modal TableEditor (table_editor.tsx, table_editor.scss): - hasMaskedValues plumbed through rows; lock operator/attribute selectors on masked rows. - Row remove (trash) button disabled on masked rows; disabled-state CSS so the icon doesn't show the destructive hover colour or a pointer cursor. - Test Rules button disabled when any row has masked values, with tooltip. - onMaskedStateChange callback to notify the parent for cross-component states (CEL editor read-only, Save disabled, banners). Value selectors (single_value_selector_menu.tsx, multi_value_selector_menu.tsx, selector_menus.scss, value_selector_menu.tsx): - Append MaskedChip after visible chips on multi-value rows. - Render MaskedChip as the sole value on single-value rows where the caller holds no visible value. Policy details (policy_details.tsx, .scss, .test.tsx): - Track hasMaskedRows state; receive from TableEditor via onMaskedStateChange. - Show masked-values warning banner above the editor when present. - Same banner on the Delete confirmation modal so admins understand why deletion is consequential. Team settings modal (team_policy_editor.tsx, .scss): - Same masked-values plumbing; delete button uses the disabled state when a policy has masked values, regardless of whether channels are assigned. - Pre-save check no longer treats "in []" as an incomplete rule — that placeholder comes from fully-masked rows that merge-on-save will fill in. i18n entries added for the new strings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68506 - fix hook order in SingleValueSelector when masked state changes The early return for `hasMaskedValues && !value` sat between useState and useCallback declarations, so when a parent re-render flipped the masked state (e.g. after deleting a sibling rule) React saw a different hook count and crashed with "Rendered fewer hooks than expected". Move the read-only short-circuit after all hook declarations so the hook order stays stable across renders. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68507 - CEL editor read-only when masked + system console wiring CEL editor (editor.tsx, editor.scss): - hasMaskedRows prop: when true, Monaco is set to read-only and a banner explains why ("This expression contains restricted values. Switch to Simple mode to edit the values you have access to, or delete the entire rule."). - Test Rules button disabled in CEL mode when hasMaskedRows is true. Policy details (policy_details.tsx, .scss): - hasMaskedRows state plumbed to CELEditor, TableEditor, and the Save / Delete buttons. - Save button disabled while masked rows are present (kept after the save-allowed-with-masked-values change in BE-5? — no, here we keep Save enabled so admins can add/modify rules; only row removal of masked rows is blocked). - Delete Policy button disabled when hasMaskedRows; a SectionNotice above the Delete card explains why ("This policy contains restricted values - Deletion not allowed"). - New save error messages: invalid_value and self_exclusion are surfaced from the server's generic responses. Policies list (policies.tsx): minor wiring change for the new state plumbing. Table editor (table_editor.tsx): cross-component coordination — emits onMaskedStateChange and respects the disabled-for-masked-row policy. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68508 - E2E suite for attribute-value masking Covers the full read+write masking flow against a real server: - Masked chip rendering, operator/attribute lock, Test Rules disabled. - System admin subject to masking like any other caller (no role bypass). - Save with masked values: hidden values preserved by merge-on-save. - Trash button disabled on masked rows; server returns 403 on direct API attempt to remove a masked condition. - Delete Policy button disabled + server 403 when policy has masked values (both system console and team settings modal paths). - Self-inclusion failure only fires when the caller holds full visibility. - CEL editor read-only with banner when masked rows present. - Direct API validation: non-held values and the masked-token sentinel rejected with a generic "Invalid value." error. - Feature-flag-off path: no masking, all values visible. - Text-field shared_only masking (binary) with `in` and `==` operators. A pluggable DB-setup helper marks specific CPA fields as shared_only for the duration of a test (with per-test cleanup) since the API blocks setting access_mode=shared_only without a source_plugin_id. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68506 - fix lint, jest mock factory, and unreachable delete-modal test * MM-68506 - localize masked-condition-deleted save error * MM-68506 - fix masked-policy delete warning detection and localize masked_rule_deleted * fix linter issues * MM-68506 - surface delete error, lock value selector on masked rows, drop dead remove-modal * fix linter, add translations, adjust specs * import wittoltip from shared * fix linter and use the correct button variant * MM-68506 - drop dangling rationale comment in access_control_field_test * fix linter, translation and e2e tests * use pg ts types and dependencies for e2e types mocks * adjust switch mode persistance restriction * fix team settings style buttons * fail-closed guard for advanced expressions in merge-on-save, plus helper unit tests, and FF/test-helper cleanups * Refactor access control methods to use GetPropertyGroup for CPA group ID retrieval * fix styles * disable delete on masked policies in list view and remove dead modal warnings * fix unit tests * preserve hasAnyOf operator display for fully-masked multiselect conditions * address PR feedback: lock Actions on masked save, filter source/shared_only from /attributes, add unit tests and e2e tests * fix e2e tests * comment out e2e to isolate issue * completely remove the files to pass linter --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
5566604e03 |
MM-68838: Ping a restored plugin remote immediately on re-register (#36592)
* MM-68838: ping restored plugin remote immediately on re-register RegisterPluginForSharedChannels' restore branch updated the row but did not call PingNow, leaving the restored remote offline until the next pingLoop tick (up to PingFreq, default 1 minute). The new-connection branch already calls PingNow; the restore branch now mirrors it so sync attempts immediately after a plugin restart no longer fail with "offline remote cluster". * MM-68838: gob-encode error returns in apiRPCServer.ReceiveSharedChannelAttachmentSyncMsg The apiRPCServer wrapper for ReceiveSharedChannelAttachmentSyncMsg assigned the hook's error return directly to the gob-encoded response struct. When the framework's App.ReceiveSharedChannelAttachmentSyncMsg returned an error wrapped with %w (*fmt.wrapError, an unexported type), gob refused to encode it and the RPC server broke the connection with "type not registered for interface: fmt.wrapError". Every subsequent plugin/server RPC call then returned the zero-value response struct, causing plugins that dereferenced the nil returns to crash. Apply the existing encodableError() helper so the returned error becomes a gob-safe ErrorString, matching every other apiRPCServer method in this file. |
||
|
|
92f6870a2b |
Add "last used" field for incoming webhooks (#36416)
* Add "last used" field for incoming webhooks * Address feedback * Rename migrations * Fix web lint |
||
|
|
548183d748 |
Mm 68282 admin ephemeral mode (#36194)
* adds feature flag to enable mattermost ephemeral mode * add ephemeral mode config settings to system console When feature flag is set to true a new section for Mobile Ephemeral Mode settings shows under the Mobile Security section in case a valid Enterprise Advanced License is active. * adds Mobile Ephemeral Mode settings playwright tests * improve descriptions for settings * improves error messages and hints * move validation to common helper and add new tests * reverts package-lock.json changes * proper struct alignment * proper message sorting in json file * use generic doc url for MEM section while docs are not ready * Proper formatting for playwright tests * fixes test |
||
|
|
9bd77d3fc4 |
MM-68702: Reject demoting bot accounts to guest (#36487)
* MM-68702: Reject demoting bot accounts to guest Deny DemoteUserToGuest when the target is a bot so User Managers cannot degrade bot capabilities via guest conversion without bot administration permissions. Adds API error string and tests. Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com> * Fix TestDemoteUserToGuest bot subtest: enable bot creation in config Default test config disables bot accounts; enable ServiceSettings EnableBotAccountCreation for the subtest and restore afterward. Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com> |
||
|
|
d4471bece1 |
Mm 68503 be abac mask save path masking (#36513)
* MM-68501 - implement GetMaskedVisualAST and wire API handler Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * add missing test and fix style issues * fix styles * implement coderabbit feedback * MM-68501 - PR review: split masking file, model-level access mode, reject contradictory config Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68501 - apply shared_only filter to non-option field values (binary masking) * MM-68501 - consolidate masking flag check and log corrupt text value during masking * MM-68503 - add CEL utilities, write-path validation, and merge helpers Combined set of helpers consumed by BE-5's save path: CEL construction / serialization - extractStringValues, buildCELFromConditions, conditionToCEL, celStringLiteral, celValueLiteral. Used to rebuild a CEL string from a VisualExpression, including for GetMaskedExpression on the read-side of policy GET / search responses. Merge-on-save helpers - getHiddenValues (per-condition, with pre-fetched fields map for N+1 avoidance) — finds which stored values are not visible to the caller. - mergeConditionValues — re-injects the hidden values into a submitted condition without duplicates. - Together, these let BE-5 preserve attribute values the caller cannot see while still letting them edit the visible parts of a policy. Write-path value-hold validation - validatePolicyExpressionValues, invalidValueError, validateConditionValues. - Generic "Invalid value." error on every rejection — no signal about whether the value exists or is merely not held (prevents enumeration). - Rejects the masked-token sentinel "--------" if submitted as a literal. These all live in access_control_masking.go alongside the masking primitives that BE-2 introduced. i18n entries added for the two new error IDs (app.pap.save_policy.invalid_value, app.pap.validate_expression_values.app_error). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68503 - handle the masked-token sentinel in validation and merge When the GET /policies endpoint returns a policy via MaskPolicyExpressions, the raw expression contains the masked-token sentinel "--------" in place of hidden values. If the frontend round-trips that expression unchanged back to the server (e.g., the admin only modified channel assignment, not the rules), the sentinel reaches the save path. The previous code in validateConditionValues rejected the sentinel as "Invalid value." This blocks the legitimate round-trip case. Fix: - validateConditionValues: treat the sentinel as a placeholder and skip it during visibility / source-only / unknown-mode checks. Other values are still validated normally. - mergeConditionValues: strip the sentinel from submitted values before appending hidden values, so it never propagates to the stored result. Both array and single-value forms (string == "--------") are handled. TestMaskedTokenRejection (which asserted the old rejection behavior) is replaced by TestMaskedTokenConstant which only verifies the sentinel string itself. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68504 - integrate save-path masking: 403 block on delete, merge-on-save, response masking Save path (CreateOrUpdateAccessControlPolicy): * validatePolicyExpressionValues runs on the submitted expression before merge so re-injected hidden values are never validated against the caller's holdings. * mergeStoredPolicyExpressions re-injects hidden values from the stored policy and blocks (HTTP 403) any attempt to remove a condition that contained values the caller cannot see — closes the row-deletion gap in classified environments. * mergeExpressionWithMaskedValues unwraps single-element arrays for scalar operators after restoring the stored operator (avoids "attr == [val]" invalid CEL when the frontend submits "attr in []" as the masked-row placeholder for an originally-scalar condition). * checkSelfInclusion is bypassed for system admins (they may legitimately write conditions for values they do not hold); masking and value-hold validation still apply to system admins. Delete path (DeleteAccessControlPolicy): * Same masked-values 403 block — a caller with masked values cannot delete the policy at all (UI Delete button is also disabled in FE-3). Response masking: * createAccessControlPolicy and setAccessControlPolicyActiveStatus run MaskPolicyExpressions on the response so even a save reply doesn't leak the values the caller does not hold. GetMaskedExpression, maskConditionValuesWithToken, replaceHiddenValuesWithToken, MaskPolicyExpressions live alongside the rest of the masking helpers in access_control_masking.go. team_access_control.go: corrects ValidateChannelEligibilityForAccessControl call site (drops the spurious receiver and rctx; it's a package-level helper that only takes channel). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * MM-68503 - address PR review: batch field fetches, propagate errors, fail-closed write path * MM-68503 - restore team-admin api4 tests accidentally dropped during BE-5 rebuild * MM-68503 - address review and CodeRabbit feedback on save-path masking * add tests for delete masking, self-inclusion, GET mask * add assertions to strengten tests * fail-closed guard for advanced expressions in merge-on-save, plus helper unit tests, and FF/test-helper cleanups * Refactor access control methods to use GetPropertyGroup for CPA group ID retrieval --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
f0360a838a |
Data spillage report generation UI (#36340)
* Added base fr report generation * WIP * implemented UI flow * implemented UI flow * restructured the modal code into sub components * Refactoring and cleanup * lint fixes, added new tests * i18n fix * test fix * Updated test * CI * Several improvements * WIP * Added tests * Addressed some security enhancements * Created zip writer entery later * Improved a test to check for file content * Improved error handling * Made a geneeric function * Updated classes * accepting comment in report API * Added more tests * Integrated new API param * Removed an unnecessary check * Made a geneeric function * Made a geneeric function * Made the comment body not required and updated API docs * Updated report generation API call in download report button * Included decision in report and removed confirmation when keeping message * Updated test * Add explicit wait for removeWithoutReportButton visibility in test Prevent race condition by waiting for the button to be visible after UI transitions to skip-confirm step before clicking it. Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com> * PR Feedback * explicitelly added return statement * Included actor details in report * Updated tests --------- Co-authored-by: maria.nunez <maria.nunez@mattermost.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
9d06155540 |
Update bot checks (#36503)
* Fix bot permission checks in revokeSession, revokeAllSessionsForUser, and updatePassword MM-68701: Align permission checks with the bot-aware pattern used by updateUser, patchUser, deleteUser, and (via MM-68686) updateUserActive. Three handlers were missing the IsBot branch: - revokeSession / revokeAllSessionsForUser: both gated access through SessionHasPermissionToUser, which only requires EditOtherUsers (an ancillary permission granted to User Managers). Switching to SessionHasPermissionToUserOrBot routes bot targets through SessionHasPermissionToManageBot first and falls back to the user path only when the target is not a bot. - updatePassword: the permission flag canUpdatePassword was set by checking PermissionSysconsoleWriteUserManagementUsers (or PermissionManageSystem for system admins) with no IsBot branch. Adding an else-if user.IsBot guard routes bot targets through SessionHasPermissionToManageBot, consistent with every other handler in the file that touches bot accounts. Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> * Improve TestRevokeSessionBotPermissions: revoke a real bot session Seed a session directly via th.App.CreateSession instead of passing a fake ID and expecting a 400. The test now validates the full happy path: the session row is created, the privileged user revokes it, and the call returns 200 OK. Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> * Strengthen forbidden sub-test: revoke a real bot session with no perms Seed a real session for the bot before the unprivileged revoke call. The test now proves the permission gate blocks access even when the target session ID genuinely exists in the database. Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> * Address review feedback: add post-conditions to bot session revoke tests - TestRevokeSessionBotPermissions: after RevokeSession succeeds, assert GetSessionById returns an error to confirm the row is gone. - TestRevokeAllSessionsForUserBotPermissions: seed a real session before RevokeAllSessions so the call is not a no-op, then assert GetSessions returns an empty list afterwards. Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
669eb104c6 |
Fix webhook list ordering instability when paginating (MM-65732) (#36470)
* Fix webhook list ordering instability when paginating (MM-65732) The webhook list view reorders entries when navigating between pages. The first page initially shows webhooks in insertion order (from the server), but after loading additional pages the display settles into alphabetical order. Going back to page 1 then shows different items than were originally visible. Root causes: 1. Server: GetIncomingByTeamByUser, GetIncomingListByUser, GetOutgoingByTeamByUser, and GetOutgoingListByUser had no ORDER BY clause, so the database could return rows in any order. 2. Client (incoming webhooks): incomingWebhookCompare only resolved the channel-name fallback for the 'a' argument, not 'b', making the comparator asymmetric and producing an unstable sort. 3. Client: both installed_incoming_webhooks and installed_outgoing_webhooks called Array.prototype.sort() directly on the props array, mutating it. Fix: - Add ORDER BY DisplayName, Id to the four listing SQL queries so API pages always come back in alphabetical order. With a stable server order, the client sort over merged pages produces the same slice for each page number regardless of how many pages have been loaded. - Symmetrise incomingWebhookCompare by applying the same channel-name and 'Private Webhook' fallback to the 'b' argument. - Sort a copy ([...hooks].sort()) in both webhook list components so the original prop arrays are never mutated. Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> * Fix lint: remove space before JSX closing tag in webhook test Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> * Fold ordering tests into existing webhook store test functions Instead of four separate top-level test registrations (GetIncomingListByUserOrdering, etc.), each ordering assertion is now a t.Run sub-test inside its corresponding existing function: testWebhookStoreGetIncomingListByUser └─ "GetIncomingListByUser, ordered alphabetically by display name" TestWebhookStoreGetIncomingByTeamByUser └─ "GetIncomingByTeamByUser, ordered alphabetically by display name" testWebhookStoreGetOutgoingListByUser └─ "GetOutgoingListByUser, ordered alphabetically by display name" testWebhookStoreGetOutgoingByTeamByUser └─ "GetOutgoingByTeamByUser, ordered alphabetically by display name" Each sub-test creates fresh hooks (Charlie, Alpha, Bravo in insertion order) scoped to its own IDs so they do not interfere with the outer test's fixtures. Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> * Fix govet shadow and gofmt issues in webhook store tests - Rename the outer err variable to errSave in three functions (testWebhookStoreGetIncomingListByUser, TestWebhookStoreGetIncomingByTeamByUser, testWebhookStoreGetOutgoingByTeamByUser) so that hooks, err := declarations in sub-test closures no longer shadow it. - Change hookC, err = to hookC, err := in each ordering sub-test to declare a local err instead of capturing the outer one. - Remove a trailing blank line at the end of the file (gofmt). Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> * Remove jest.mock from installed_incoming_webhooks test The mock for delete_integration_link was copied from the outgoing webhooks list test but is not needed here: the real component renders fine within renderWithContext (as shown by installed_incoming_webhook.test.tsx which tests the individual item without any mocks). Since the ordering tests do not interact with delete functionality, drop the mock and align the action stub style to mockReturnValue(Promise.resolve()). Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> * Add ORDER BY to GetOutgoingByChannelByUser; add ordering sub-test GetOutgoingByChannelByUser was the last paginated webhook listing function without an ORDER BY clause. Add OrderBy("DisplayName", "Id") consistent with all other listing functions. Add the corresponding ordering sub-test inside testWebhookStoreGetOutgoingByChannelByUser, following the same errSave pattern established for the other functions to avoid govet shadow warnings. Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com> |
||
|
|
238867e247 |
MM-68732: Remove global mutex for login attempts in favour of database serialization (#36515)
* Add atomic login-attempt counter primitives to UserStore
Two new store methods back the upcoming switch from a global
per-node mutex to per-user atomic slot claiming:
TryIncrementFailedPasswordAttempts(userID, maxAttempts) (bool, error)
UPDATE Users SET FailedAttempts = FailedAttempts + 1
WHERE Id = ? AND FailedAttempts < maxAttempts
Returns true when a slot was claimed (rows affected == 1) and
false when the cap was already reached. The conditional UPDATE
serialises concurrent attempts on the same user via the row
lock, so the cap is enforced without any application-level
locking and without serialising attempts across users.
DecrementFailedPasswordAttempts(userID) error
UPDATE Users SET FailedAttempts = FailedAttempts - 1
WHERE Id = ? AND FailedAttempts > 0
Releases a slot previously claimed by TryIncrement when the
in-flight authentication turns out not to be a credential
failure. The conditional UPDATE means concurrent decrements
cannot underflow.
Storetest covers both primitives: claim-below-cap, reject-at-cap,
reject-above-cap, no-op for unknown user, and a 50-goroutine
concurrent test with a start barrier asserting exactly
maxAttempts slots are ever claimed and that decrement clamps at
zero under contention.
The testify mock is regenerated here so the storetest package
that returns *mocks.UserStore as a store.UserStore still satisfies
the interface; the wrapper layers are regenerated in the next
commit.
------
AI assisted commit
* Regenerate store layers for the new primitives
Pick up TryIncrementFailedPasswordAttempts and
DecrementFailedPasswordAttempts in every generated wrapper:
- retrylayer: retry on repeatable errors using the standard
three-attempt loop.
- timerlayer: record store-method duration metrics under
UserStore.TryIncrementFailedPasswordAttempts and
UserStore.DecrementFailedPasswordAttempts.
- localcachelayer: invalidate the profile cache only after the
underlying conditional UPDATE actually changes a row; an
at-cap no-op return on TryIncrement no longer produces
unnecessary cluster invalidation traffic.
------
AI assisted commit
* Drop login-attempt mutex; use per-user slot claiming
Replace the global per-node mutex that serialised every login
attempt with the database-side atomic slot machine added on the
Users row. Each of the three authentication entry points now
pre-claims a slot via TryIncrementFailedPasswordAttempts before
running the expensive password / LDAP / MFA check, and releases
the slot when the failure path is not a real credential mismatch:
- CheckPasswordAndAllCriteria (email/password): refunds the
slot on backend errors during the password check (malformed
stored hash, hasher misc failure, password-migration write
failure) so a transient infra issue cannot ratchet
FailedAttempts to a lockout for a user with valid credentials;
refunds on the MFA pre-flight probe (empty mfaToken on an
MFA-enabled user) so the probe is not counted as a real
attempt.
- DoubleCheckPassword: same backend-error refund predicate.
- checkLdapUserPasswordAndAllCriteria: pre-claims only for
existing users (first-time LDAP users have no local row to
claim against); refunds non-credential DoLogin errors (server
unreachable, transient) so an LDAP outage cannot lock out
everyone; refunds the MFA pre-flight probe; for first-time
users, explicitly bumps the counter via UpdateFailedPasswordAttempts
on a real bad-password or bad-MFA attempt, matching the
pre-refactor counting behaviour.
If the refund itself fails the underlying authentication error is
preserved and returned to the caller (the failure is logged); a
leaked slot is annoying, but masking the real failure with a
generic store 500 would be a clear observability regression.
Cluster-wide behaviour also changes: the previous design honoured
MaximumLoginAttempts per node, so an n-node cluster effectively
permitted n * MaximumLoginAttempts attempts. The cap is now
enforced globally.
------
AI assisted commit
* Cover app-layer behaviors of the new login slot machine
The store-layer tests already exercise TryIncrement and Decrement
under concurrency and at the cap boundary. The new behavioural
contracts at the app layer were not covered, so a regression that
flipped a refund predicate, a probe condition, or a first-time
LDAP path would have slipped through type checking and existing
unit tests.
Add tests around the three callers of the new path:
- CheckPasswordAndAllCriteria: an MFA pre-flight probe (empty
token) does not consume a slot; a real attempt with a wrong
non-empty token does; a backend error during the password
check (malformed stored hash) refunds the slot; the happy
path also asserts FailedAttempts resets to zero.
- DoubleCheckPassword: gets its first test coverage, covering
the happy path, rate-limit rejection once max attempts is
reached, and the backend-error refund path.
- checkLdapUserPasswordAndAllCriteria: covers paths the table
loop did not exercise, first-time LDAP user with a bad
password (uses GetUserByAuth to reach the freshly created
row), first-time LDAP user with a wrong MFA token, existing
LDAP user with a non-credential DoLogin error (slot
refunded), and the existing LDAP user MFA pre-flight probe
(slot refunded).
------
AI assisted commit
* Address coderabbit review
------
AI assisted commit
* Fix race in first-time LDAP failed-attempt counter
For first-time LDAP users we have no local row to pre-claim, so
the bad-password and bad-MFA branches fell back to an absolute
UpdateFailedPasswordAttempts(id, ldapUser.FailedAttempts+1) based
on a snapshot from GetUserByAuth. Concurrent first-attempt
requests for the same user could all read FailedAttempts == 0 and
all write 1, losing increments. As a secondary issue the absolute
set did not enforce MaximumLoginAttempts, so the counter could
also drift past the cap.
Switch both branches to TryIncrementFailedPasswordAttempts, the
atomic conditional UPDATE already used on every other path. The
row lock serialises concurrent increments and the predicate caps
at MaximumLoginAttempts.
A new concurrent storetest-style subtest runs
3 * maxFailedLoginAttempts goroutines through the first-time
bad-password path against the same fresh LDAP row and asserts
FailedAttempts lands at exactly maxFailedLoginAttempts. Against
the previous absolute-set implementation the test fails (observed
FailedAttempts = 4 with maxFailedLoginAttempts = 3, either a lost
increment or a cap overshoot).
The first-time bad-password branch also switches from a wrapped
500 return on store error to log-and-continue, matching the rest
of the file's refund/probe error handling: the underlying LDAP
authentication failure is the more useful error for the caller.
------
AI assisted commit
* Address review comments
------
AI assisted commit
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
|
||
|
|
02023f0328 | [MM-68463] New endpoint to GET user by auth_data (#36352) | ||
|
|
deafd88fd5 |
MM-68762: Discoverable Private Channels — Server data layer (#36539)
* MM-68762: Add Postgres migrations for discoverable private channels Three online-safe migrations introduce the schema that supports the Discoverable Private Channels feature (PRs 2-5 of MM-68430 will land behind it): - 000175 adds Channels.Discoverable BOOLEAN NOT NULL DEFAULT FALSE. Metadata-only on Postgres >= 11; no table rewrite. - 000176 creates a partial index on (TeamId) WHERE Discoverable AND Type='P' AND DeleteAt=0 using CREATE INDEX CONCURRENTLY (-- morph:nontransactional) so the build never blocks writes on the populated Channels table. - 000177 creates the ChannelJoinRequests table with three indexes, the important one being the partial unique index on (ChannelId, UserId) WHERE Status = 'pending'. That keeps the full audit history intact while still enforcing at-most-one active pending request per (channel, user). Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Add FeatureFlagDiscoverableChannels (default false) Gates the per-channel Discoverable toggle and the channel-join-request flow. Default-OFF so all PRs in the MM-68430 series can land on master without exposing partial UX. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Add Discoverable + ChannelJoinRequest models - Channel gains a Discoverable bool, ChannelPatch a *bool, both serialized as 'discoverable'. Patch() applies it, Auditable() logs it, and IsValid() rejects Discoverable=true on any non-private channel so a misconfigured patch can never produce a public discoverable channel. - New ChannelJoinRequest type captures the per-row state of a non-member's request: pending -> approved | denied | withdrawn. Rows are append-only with reviewer and timestamps so the table is also the audit trail. IsValid() enforces: * recognized status, * Message and DenialReason rune limits, * DenialReason only on denied rows (no orphan reasons), * reviewer + reviewed_at present for any terminal review (approved / denied) but not for self-service withdrawal. - Two new WebSocket event constants -- channel_join_request_created and channel_join_request_updated -- that later PRs broadcast on the admin queue and the requester's My Pending Requests panel. Unit tests cover Patch(), the new IsValid() rule on Discoverable, the PreSave/PreUpdate timestamp behavior on ChannelJoinRequest, and every IsValid branch including the reviewer-required-on-review invariant. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Add discoverable-channel permissions Two new channel-scoped permissions, each independently rebindable from the System Console: - manage_private_channel_discoverability gates the per-channel toggle so admins can restrict who can flip discoverability without also handing out manage_private_channel_properties. - manage_channel_join_requests gates the queue list / approve / deny / count endpoints (added in PR 2). Both are added to the channel_admin role bootstrap so new deployments get them by default, and a new permissions migration (add_discoverable_channel_permissions) grants them to channel_admin, team_admin and system_admin scheme roles on existing deployments. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Add ChannelJoinRequestStore and wire Discoverable into channel store - channelSliceColumns / channelToSlice / updateChannelT now include the new Discoverable column so Save() and Update() round-trip the field. Existing select paths inherit the column automatically because every read goes through channelSliceColumns. - New ChannelJoinRequestStore interface and SQL implementation: Save / Get / GetPendingForChannelAndUser / GetForChannel / GetForUser / Update / CountPending. Save translates the idx_channeljoinrequests_pending_unique partial unique index violation into store.ErrConflict so the app layer (PR 2) can return 409 without re-parsing pq errors. - Storetest suite at storetest/channel_join_request_store.go is invoked from sqlstore via the existing StoreTest harness; covers insert / partial-unique conflict / re-insert after withdrawal / NotFound / status filtering / pagination with TotalCount / Update / CountPending. - Mocks and retrylayer / timerlayer are regenerated via make store-mocks and go generate ./channels/store -- no hand-written generator output. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Add TS types for Discoverable channels + join requests webapp/platform/types: - Channel.discoverable?: boolean alongside existing policy_enforced / policy_is_active so the web client sees the same wire shape the server emits. - ChannelJoinRequest, ChannelJoinRequestStatus, ChannelJoinRequestList, GetChannelJoinRequestsOptions for the API contract surfaced in PR 2. webapp/platform/client: - WebSocketEvents enum gains ChannelJoinRequestCreated and ChannelJoinRequestUpdated so PR 3 can hang WS handlers off them without redeclaring constants. These are model-only updates with no UI consumer yet; PR 3 introduces the toggle, request flow, and admin queue surfaces. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Split ChannelJoinRequests indexes into concurrent migrations The mattermost-govet concurrentIndex lint check enforces CREATE INDEX CONCURRENTLY on every CREATE INDEX statement, even on an empty freshly-created table where it would be a no-op. The original 000177 file inlined three CREATE INDEX statements; that failed check-style. Mirror the convention used by 000166_create_views + 000167_create_views_channel_id_delete_at_index: keep the CREATE TABLE in its own (transactional) file, and move each index into a separate nontransactional file that runs CREATE INDEX CONCURRENTLY. Verified locally against Postgres 15 that all four new migrations apply in order and the storetest suite (partial unique constraint + paged list + count) still passes. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Wire new permission migration into test fixtures Two CI test surfaces missed when the channel_admin role and the permission-migration list gained the new manage_private_channel_discoverability and manage_channel_join_requests entries: - testlib/store.go: the shared mocked SystemStore used by SetupWithStoreMock / SetupEnterpriseWithStoreMock needs an explicit GetByName expectation for every migration key (because the mock panics on unexpected calls). Add the new MigrationKeyAddDiscoverableChannelPermissions key so TestCreateOrUpdateAccessControlPolicy, the elasticsearch aggregation_job_test, and every other mock-store test stop panicking on server bootstrap. - cmd/mmctl/commands/permissions_test.go: TestResetPermissionsCmd hard-codes the channel_admin default permission list and expects PatchRole to be called with exactly that slice. Extend the expected slice with the two new permission ids so the mmctl reset path stays in sync with the role bootstrap. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Register new idx_channels_discoverable_team in TestGetSchemaDefinition The schema-dump test asserts an exact index count and definition map for the channels table. Migration 000176 added idx_channels_discoverable_team — a partial btree on (teamid) gated by discoverable=true AND type='P' AND deleteat=0. Bump the expected count from 12 to 13 and add the index's CREATE INDEX definition as produced by pg_indexes (note: type is cast to channel_type, the existing domain). Verified locally against Postgres 15. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Fix golangci-lint findings in ChannelJoinRequest store Two golangci-lint findings on the freshly-added files: - sqlstore/channel_join_request_store.go:133 (modernize): collapse the 'if page < 0 { page = 0 }' clamp into max(opts.Page, 0). - storetest/channel_join_request_store.go:243 (govet shadow): the inner Save loop redeclared err with :=, shadowing the outer err captured from the first CountPending call. Switch to plain assignment so the same err is reused. Verified locally with golangci-lint v2.11.4 across public/..., channels/app/..., channels/store/..., channels/testlib/... and cmd/mmctl/commands/... — 0 issues. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Sync channel_admin bootstrap with TestDoAdvancedPermissionsMigration app_test.go pins the exact list of permissions the channel_admin role is expected to hold after DoAdvancedPermissionsMigration completes. The role bootstrap in role.go grew two entries (manage_private_channel_discoverability and manage_channel_join_requests), so the test's expected slice needs the same two entries appended in the same order, otherwise assert.Equal fails on slice ordering. This is the same class of fix as the mmctl/permissions_test.go change in a previous commit -- two parallel test fixtures encode the channel_admin defaults and have to be updated in lockstep with the bootstrap. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Add English translations for new model error keys 12 keys were emitted by the new Discoverable + ChannelJoinRequest validation paths but had no en.json entry, which trips i18n-check on CI. Add the missing entries with one-line English copy that mirrors adjacent model errors (Invalid <field>., Create at must be a valid time., etc.). The new entries are: - model.channel.is_valid.discoverable.app_error - model.channel_join_request.is_valid.channel_id.app_error - model.channel_join_request.is_valid.create_at.app_error - model.channel_join_request.is_valid.denial_reason.app_error - model.channel_join_request.is_valid.denial_reason_status.app_error - model.channel_join_request.is_valid.id.app_error - model.channel_join_request.is_valid.message.app_error - model.channel_join_request.is_valid.reviewed_by.app_error - model.channel_join_request.is_valid.reviewer.app_error - model.channel_join_request.is_valid.status.app_error - model.channel_join_request.is_valid.update_at.app_error - model.channel_join_request.is_valid.user_id.app_error Generated through 'make i18n-extract'; verified clean with 'make i18n-check'. Per the workspace rule, only en.json was modified -- no other locale files. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Address CodeRabbit review: stable pagination + redact denial reason from audit log Two production-code findings from CodeRabbit on the freshly-added ChannelJoinRequest server code: - sqlstore/channel_join_request_store.go (GetForChannel / GetForUser): OrderBy("CreateAt DESC") alone is unstable when two rows share a millisecond (NewId is monotonic-ish but CreateAt is millisecond resolution), so offset paging could duplicate or skip rows between pages. Add Id DESC as a deterministic tie-breaker on both list queries. - model/channel_join_request.Auditable: the denial reason is admin-typed free text and could carry sensitive content. Mirror the existing has_message pattern by emitting has_denial_reason as a boolean presence flag instead of the raw value. Reviewer id, review timestamp, and status are still logged, so the audit trail keeps every piece needed for compliance review. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Tighten model tests per CodeRabbit review Two test-only findings from CodeRabbit: - TestChannelJoinRequestPreUpdateAdvancesUpdateAt previously asserted GreaterOrEqual(r.UpdateAt, originalCreate). Because validRequest initialises UpdateAt to GetMillis() (same call site as CreateAt), a no-op PreUpdate would still pass that check. Seed r.UpdateAt = 1 before calling PreUpdate() and assert Greater(r.UpdateAt, int64(1)) so any regression that drops the GetMillis assignment fails the test. - TestChannelIsValidDiscoverable did not cover ChannelTypeGroup. Add the case alongside ChannelTypeOpen and ChannelTypeDirect so the contract that 'only ChannelTypePrivate accepts Discoverable=true' is fully pinned across all four channel types. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> * MM-68762: Mock ChannelJoinRequest accessor in retrylayer test retrylayer_test.go's genStore() helper mocks every Store() accessor because retrylayer.New() wraps the entire surface. The new ChannelJoinRequest() method I added on Store was missing from the mock, so TestRetry/on_regular_error_should_not_retry panicked with 'Unexpected Method Call ChannelJoinRequest()' on Postgres shard 0. Add the mock alongside the other accessors. No production code change. Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com> |
||
|
|
3f3d8408b2 |
Return descriptive errors from Role.IsValid and Role.IsValidWithoutId (#36582)
* Return descriptive errors from Role.IsValid and Role.IsValidWithoutId Previously both methods returned bool, leaving callers with no context about which validation check failed. Now both return error with a message identifying the specific constraint that was violated. * Add tests for Role.IsValid and Role.IsValidWithoutId * Log migration key on doPermissionsMigration failure --------- Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
fa1255f149 | Update Calls to v1.11.5 (#36574) | ||
|
|
54bee00622 |
MM-68332: consistently enforce query timeouts (#36522)
* Remove QueryRowx (no-timeout) from sqlxDBWrapper; migrate caller to QueryRowX QueryRowx forwarded to context.Background() with no timeout, while QueryRowX (uppercase) already enforces the wrapper timeout. Removing the no-timeout variant eliminates an accidental footgun and migrates the one sqlstore caller (plugin_store) to the timeout-enforcing method. * sqlxRow, with timeout cancel after Scan Introduce sqlxRow, which pairs *sqlx.Row with its context cancel function. QueryRowX (on both sqlxDBWrapper and sqlxTxWrapper) now returns *sqlxRow; Scan calls cancel immediately after the row is consumed, releasing the timeout context as soon as possible rather than waiting for the timer. * sqlxRows, with timeout cancel on Close Introduce sqlxRows, which embeds *sqlx.Rows and holds the timeout context's cancel function. Close() cancels the context immediately after the rows are done, releasing timeout resources as soon as iteration completes rather than waiting for the timer to fire. Introduce rowScanner interface (Next/Scan/Err) so the internal helpers scanRowsIntoMap and scanRetentionIdsForDeletion accept any row iterator rather than the concrete *sql.Rows, accommodating the new return types without threading *sqlxRows through every caller. * abolish the X suffix altogether * delete unused NamedQuery * add timeout tests for Query and QueryRow on db and tx wrappers * fix tx/timeout tests: handle pq driver.ErrBadConn on killed connection * fixup! fix tx/timeout tests: handle pq driver.ErrBadConn on killed connection * override Next() on sqlxRows to cancel on EOF * rm redundant row.Err(), handled by Scan * apply timeout to context unless deadline set * rebind consistently * address review feedback: rename, trace, and QueryContext fix - Rename withQueryTimeout → ensureQueryTimeout to better convey that it respects existing deadlines rather than overriding them. - Add missing w.trace blocks to QueryRowContext and ExecContext. - Change QueryContext to use ensureQueryTimeout and return *sqlxRows (instead of *sql.Rows) so the cancel is deferred to Close/Next rather than released prematurely. * fix Beginx → Begin after master merge * fix golangci-lint inline warning in sqlx_wrapper_test --------- Co-authored-by: Mattermost Build <build@mattermost.com> |