mirror of
https://github.com/mattermost/mattermost.git
synced 2026-05-28 04:35:04 -04:00
827 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8f6a7f5093 | i18n fix | ||
|
|
94b52978eb | fix test | ||
|
|
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 |
||
|
|
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>
|
||
|
|
b0d2f83620
|
Translations update from Mattermost Weblate (#36695)
Some checks are pending
Server CI / Check mmctl docs (push) Blocked by required conditions
Server CI / Postgres (shard 0) (push) Blocked by required conditions
Server CI / Postgres (shard 1) (push) Blocked by required conditions
Server CI / Postgres (shard 2) (push) Blocked by required conditions
Server CI / Postgres (shard 3) (push) Blocked by required conditions
Server CI / Merge Postgres Test Results (push) Blocked by required conditions
Server CI / Elasticsearch v8 Compatibility (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 0) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 1) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 2) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 3) (push) Blocked by required conditions
Server CI / Merge Postgres FIPS Test Results (push) Blocked by required conditions
Server CI / Run mmctl tests (push) Blocked by required conditions
Server CI / Run mmctl tests (FIPS) (push) Blocked by required conditions
Server CI / Build mattermost server app (push) Blocked by required conditions
Tools CI / check-style (mattermost-govet) (push) Waiting to run
Tools CI / Test (mattermost-govet) (push) Waiting to run
Web App CI / check-lint (push) Waiting to run
Web App CI / check-i18n (push) Blocked by required conditions
Web App CI / check-external-links (push) Blocked by required conditions
Web App CI / check-types (push) Blocked by required conditions
Web App CI / test (platform) (push) Blocked by required conditions
Web App CI / test (mattermost-redux) (push) Blocked by required conditions
Web App CI / test (channels shard 1/4) (push) Blocked by required conditions
Web App CI / test (channels shard 2/4) (push) Blocked by required conditions
Web App CI / test (channels shard 3/4) (push) Blocked by required conditions
Web App CI / test (channels shard 4/4) (push) Blocked by required conditions
Web App CI / upload-coverage (push) Blocked by required conditions
Web App CI / build (push) Blocked by required conditions
YAML Lint / yamllint (push) Waiting to run
* 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> |
||
|
|
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> |
||
|
|
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) | ||
|
|
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>
|
||
|
|
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)
Some checks are pending
Server CI / Check mmctl docs (push) Blocked by required conditions
Server CI / Postgres (shard 0) (push) Blocked by required conditions
Server CI / Postgres (shard 1) (push) Blocked by required conditions
Server CI / Postgres (shard 2) (push) Blocked by required conditions
Server CI / Postgres (shard 3) (push) Blocked by required conditions
Server CI / Merge Postgres Test Results (push) Blocked by required conditions
Server CI / Elasticsearch v8 Compatibility (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 0) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 1) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 2) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 3) (push) Blocked by required conditions
Server CI / Merge Postgres FIPS Test Results (push) Blocked by required conditions
Server CI / Run mmctl tests (push) Blocked by required conditions
Server CI / Run mmctl tests (FIPS) (push) Blocked by required conditions
Server CI / Build mattermost server app (push) Blocked by required conditions
Tools CI / check-style (mattermost-govet) (push) Waiting to run
Tools CI / Test (mattermost-govet) (push) Waiting to run
Web App CI / check-lint (push) Waiting to run
Web App CI / check-i18n (push) Blocked by required conditions
Web App CI / check-external-links (push) Blocked by required conditions
Web App CI / check-types (push) Blocked by required conditions
Web App CI / test (platform) (push) Blocked by required conditions
Web App CI / test (mattermost-redux) (push) Blocked by required conditions
Web App CI / test (channels shard 1/4) (push) Blocked by required conditions
Web App CI / test (channels shard 2/4) (push) Blocked by required conditions
Web App CI / test (channels shard 3/4) (push) Blocked by required conditions
Web App CI / test (channels shard 4/4) (push) Blocked by required conditions
Web App CI / upload-coverage (push) Blocked by required conditions
Web App CI / build (push) Blocked by required conditions
YAML Lint / yamllint (push) Waiting to run
* 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>
|
||
|
|
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>
|
||
|
|
d4fc0ecb1c
|
MM-68150: Upgrade golangci-lint to v2.12.2 (#36554)
Some checks are pending
Server CI / Check mmctl docs (push) Blocked by required conditions
Server CI / Postgres (shard 0) (push) Blocked by required conditions
Server CI / Postgres (shard 1) (push) Blocked by required conditions
Server CI / Postgres (shard 2) (push) Blocked by required conditions
Server CI / Postgres (shard 3) (push) Blocked by required conditions
Server CI / Merge Postgres Test Results (push) Blocked by required conditions
Server CI / Elasticsearch v8 Compatibility (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 0) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 1) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 2) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 3) (push) Blocked by required conditions
Server CI / Merge Postgres FIPS Test Results (push) Blocked by required conditions
Server CI / Run mmctl tests (push) Blocked by required conditions
Server CI / Run mmctl tests (FIPS) (push) Blocked by required conditions
Server CI / Build mattermost server app (push) Blocked by required conditions
Tools CI / check-style (mattermost-govet) (push) Waiting to run
Tools CI / Test (mattermost-govet) (push) Waiting to run
Web App CI / check-lint (push) Waiting to run
Web App CI / check-i18n (push) Blocked by required conditions
Web App CI / check-external-links (push) Blocked by required conditions
Web App CI / check-types (push) Blocked by required conditions
Web App CI / test (platform) (push) Blocked by required conditions
Web App CI / test (mattermost-redux) (push) Blocked by required conditions
Web App CI / test (channels shard 1/4) (push) Blocked by required conditions
Web App CI / test (channels shard 2/4) (push) Blocked by required conditions
Web App CI / test (channels shard 3/4) (push) Blocked by required conditions
Web App CI / test (channels shard 4/4) (push) Blocked by required conditions
Web App CI / upload-coverage (push) Blocked by required conditions
Web App CI / build (push) Blocked by required conditions
YAML Lint / yamllint (push) Waiting to run
* Simplify invite_people email parsing Replace backwards in-place mutation loop with a straightforward forward filter into a new slice. Extract into parseEmailList so the logic can be unit tested directly. * MM-68150: Upgrade golangci-lint to v2.12.2 Remove //go:fix inline from NewPointer, which is a generic function not yet supported by the inline analyzer, and fix 11 slicesbackward modernize issues flagged by the new version. * MM-68150: Enable all linters by default; disable those with >20 existing issues Switch from opt-in (default: none) to opt-out (default: all) so new linters added to golangci-lint are evaluated automatically. Explicitly disable every linter that has more than 20 pre-existing violations, deferring those for later cleanup. Also disable a handful of linters whose violations are intentional patterns in this codebase (nilerr, dogsled, sqlclosecheck, iotamixing, predeclared, containedctx, iface, gocheckcompilerdirectives, promlinter, goprintffuncname, gomoddirectives). * MM-68150: Fix mirror linter issues Replace Write([]byte(s)) with WriteString(s), and FindIndex([]byte(s)) with FindStringIndex(s), to avoid unnecessary allocations. * MM-68150: Fix nosprintfhostport linter issue Use net.JoinHostPort to construct host:port strings instead of fmt.Sprintf with a manually formatted pattern. * MM-68150: Fix rowserrcheck and sqlclosecheck linter issues Check rows.Err() after iteration loops in schema_dump.go. In the sqlx_wrapper test, defer rows.Close() rather than closing inline. * MM-68150: Fix nilnesserr linter issues — wrong variable in error handlers In 11 places, a stale variable (often the outer err from a prior assignment) was used instead of the freshly-checked error variable (appErr, rowErr, jsonErr, writeErr, esErr). Each produces a typed-nil wrapped in a non-nil interface, silently discarding the real error. * MM-68150: Add i18n string for app.compile_csv_chunks.write_error --------- Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
f604ec7a5c
|
MM-68662: Add Azure Blob Storage filestore backend (#36498)
* Generalize file backend error types
Replace S3FileBackendAuthError and S3FileBackendNoBucketError with
backend-agnostic FileBackendAuthError and FileBackendNoBucketError so
non-S3 drivers can return them and the admin "Test Connection" flow
keeps surfacing useful messages.
The old S3-prefixed names are kept as type aliases of the generic
types so external code (plugins, historical consumers) continues to
compile, and so existing S3 construction sites stay untouched.
The type switch in connectionTestErrorToAppError now matches the
generic types, with new i18n keys (test_connection_auth.app_error
and test_connection_no_bucket.app_error) whose wording does not name
S3. The old S3-specific i18n keys are dropped via `make i18n-extract`
since they are no longer referenced from code; the api4 test that
asserted on those keys is updated, and the Cypress
`MM-T996 Amazon S3 connection error messaging` spec that asserted
on the old user-facing string is updated to the new wording.
------
AI assisted commit
* Pull in Azure SDK and uuid dependencies
Bring in github.com/Azure/azure-sdk-for-go/sdk/azcore and
.../sdk/storage/azblob (with .../sdk/internal as their indirect
dependency). The two are needed by the upcoming Azure Blob Storage
filestore backend and its lazy-Range-backed reader. The bump of
golang.org/x/{crypto,net,sys,term,text} comes transitively from
azblob's minimum versions.
Also promotes github.com/google/uuid from indirect to direct,
since the Azure backend uses it to generate block IDs that share
the same wire format the SDK itself produces in UploadStream.
------
AI assisted commit
* Add azureRangeReader, a seekable Range-backed blob reader
A small standalone type that satisfies the FileBackend interface's
ReadCloseSeeker + the broader io.ReaderAt contract on top of Azure
Blob Storage HTTP Range requests. Lands as its own commit because
the upcoming Azure FileBackend driver builds on it, and the reader
itself is independently useful — and independently testable against
a fake downloader without standing up an Azure client.
Design notes:
* Read opens an HTTP Range stream lazily at the current offset and
reuses it for sequential reads. Seek to a different offset closes
the open stream; the next Read re-opens it.
* Seek to the same offset is a no-op and does not close the open
stream, so callers like zip.NewReader that probe with redundant
seeks don't kick off a fresh download.
* ReadAt issues a dedicated ranged DownloadStream per call and does
not touch the streaming cursor — matches the io.ReaderAt contract
the bulk-import worker's zip.NewReader path relies on.
* Close cancels the context (which any in-flight Azure call will
observe and abort), stops the deadline timer, and closes the
current body if any. It is safe to call when no body was ever
opened.
* CancelTimeout lets long-running consumers like the import worker
opt out of the per-operation deadline that would otherwise kill
multi-minute downloads partway through.
The implementation talks to a small blobDownloader interface rather
than *blob.Client directly so the unit tests can substitute a fake
downloader that records every requested Range and tracks Close
calls on the bodies it hands out.
------
AI assisted commit
* Add Azure Blob Storage filestore driver
Implements the FileBackend interface against Azure Blob Storage in
a new azurestore.go (~520 LOC). The driver is not yet selectable
via NewFileBackend's switch — that wiring lands in the next commit
together with the admin config surface — but the driver itself is
complete and self-contained behind the FileBackendSettings struct.
Filesstore.go grows three pieces of supporting infrastructure that
the driver consumes:
* a `driverAzure = "azureblob"` constant alongside the existing
driverS3 and driverLocal,
* an Azure-specific block on FileBackendSettings (storage account,
access key, container, path prefix, endpoint, SSL flag, request
timeout),
* a CheckMandatoryAzureFields validator that mirrors
CheckMandatoryS3Fields.
Behavioural notes that warrant calling out:
* Reader returns the previously-added azureRangeReader, so reads
stream lazily over HTTP Range and ReadAt is available for the
bulk-import worker's zip.NewReader path. The deadline timer is
armed before the initial GetProperties call so the HEAD itself
is bounded.
* WriteFile and AppendFile both go through StageBlock +
CommitBlockList via a shared stageBlocks helper, never the SDK's
UploadStream. UploadStream's small-payload fast path falls back
to single-shot PutBlob, which leaves the resulting blob with no
committed block list; a subsequent AppendFile that calls
CommitBlockList on that blob would then clobber its content.
Routing every write through the block-list mechanism keeps
AppendFile correct regardless of payload size.
* AppendFile stages the new chunk as one or more blocks and commits
the existing committed block list plus the newly staged IDs.
The new bytes go up exactly once — no re-download, no
re-concatenate, no re-upload of the prior contents.
* WriteFileContext does not wrap the caller-supplied context with
its own timeout — that timeout is applied in WriteFile only,
matching the S3 driver, so long-running TryWriteFileContext
callers (like message-export bulk writes) opt out of the
per-operation timeout the way the abstraction documents.
Authentication is shared-key only for this drop; Microsoft Entra
ID / managed identity is deferred to a follow-up. The endpoint is
configurable so the same code targets the production Azure host
(vhost style — {account}.blob.core.windows.net) or Azurite /
Azure Government / sovereign clouds (path style —
host[:port]/{account}).
------
AI assisted commit
* Wire Azure backend into config, validation, and driver selection
This commit registers the previously-added AzureFileBackend driver
with the rest of the system. Until now the driver was usable only
via direct construction; after this commit, `DriverName: "azureblob"`
in config.json is a fully-supported deployment configuration.
Five integration sites are touched:
* `newFileBackend` in filesstore.go now dispatches `driverAzure` to
NewAzureFileBackend, alongside the existing s3 and local cases.
NewFileBackendSettingsFromConfig (and its export counterpart) gain
an Azure branch that maps the model.FileSettings fields onto the
Azure-specific FileBackendSettings fields.
* `model.FileSettings` grows the user-facing Azure config schema:
storage account, access key, container, path prefix, endpoint,
SSL flag, request timeout, plus matching Export* fields for the
dedicated export store. SetDefaults populates them so deployments
that never opted into Azure don't carry nil pointers. `isValid`
accepts the new ImageDriverAzure constant.
* `Config.Sanitize()` masks AzureAccessKey and ExportAzureAccessKey
the same way it masks AmazonS3SecretAccessKey, so the shared key
never reaches an API consumer in plain text.
* `desanitize()` restores the masked keys on a config write so a
PATCH that doesn't touch the key doesn't clobber it with the
FakeSetting placeholder.
* `configSensitivePaths` covers both Azure key paths so audit
diffs don't include them either.
* `ConfigToFileBackendSettings` in the `mattermost db` CLI helper
gets the Azure branch its production counterpart already has —
without it, `mattermost db migrate` / `db downgrade` would fail
on Azure-configured deployments with "missing azure storage
account setting".
Finally, the shared FileBackendTestSuite is now wired against
Azurite via TestAzureFileBackendTestSuite, which skips when
CI_AZURITE_HOST is unreachable. The test-infra wiring (the docker
service, the env vars, the start_dependencies entry) landed in a
previous PR; this commit is what makes the suite actually exercise
the Azure driver end to end.
------
AI assisted commit
* Validate Azure timeout and path prefix in Config.IsValid
Parity with the S3-side checks that already cover
AmazonS3RequestTimeoutMilliseconds and AmazonS3PathPrefix. Without
these, a zero/negative AzureRequestTimeoutMilliseconds passes
validation and later creates immediately-expired request contexts,
and leading/trailing whitespace in AzurePathPrefix produces blob
keys that don't match what the admin configured.
Same checks added for the Export* counterparts. The
file_driver.app_error translation is updated to mention the new
'azureblob' option alongside 'local' and 'amazons3'.
------
AI assisted commit
* Stream zip entries from the Azure backend
writeZipEntry was calling ReadFile, which loads the entire blob
into memory before writing it to the archive. For large blobs or
deep directories this spikes RSS or OOMs the goroutine. Switch to
Reader (the streaming azureRangeReader) and io.Copy into the zip
entry so memory stays bounded regardless of blob size.
------
AI assisted commit
* Use a backend-agnostic fallback for FileBackendNoBucketError
The fallback Error() message was "no such bucket", which leaks S3
terminology when an Azure caller returns the type with no wrapped
Err. Use "no such bucket or container" so logs and external error
handling stay neutral across backends.
------
AI assisted commit
* Defend Azure path prefix against directory traversal
Reject ".." in AzurePathPrefix and ExportAzurePathPrefix at config
validation time, since path.Join collapses traversal segments and a
prefix like "../other-tenant" would otherwise escape the configured
isolation boundary.
Harden the prefix helper as a second line of defense: if the joined
path no longer sits inside pathPrefix, fall back to joining the prefix
with the base name of the caller-supplied path. That preserves the
prefix invariant for plugin and import paths that the upload code does
not sanitize uniformly.
------
AI assisted commit
* Honor SkipVerify when constructing the Azure client
FileBackendSettings.SkipVerify is plumbed through from the System Console
the same way it is for S3, so admins toggling the flag for self-signed
endpoints (Azurite, sovereign clouds) get the behavior they expect
without having to drop SSL entirely and send the shared key in clear
text.
------
AI assisted commit
* Warn when the Azure request timeout falls back to its default
Config.IsValid already rejects non-positive AzureRequestTimeoutMilliseconds
for any path that goes through config validation, so this warn only fires
for direct callers that bypass validation (tests, helpers). Logging the
substitution turns a silent coercion into something an operator can
correlate against unexpected request behavior.
------
AI assisted commit
* Cap Azure request timeout at 10 minutes
Reject AzureRequestTimeoutMilliseconds values above the ceiling so an
operator (or someone who has admin access) cannot effectively disable
timeouts by setting the value to math.MaxInt64. A hung Azure call then
holds a goroutine open until the OS gives up.
Applies the same bound to ExportAzureRequestTimeoutMilliseconds. S3 has
the same gap; treating it is out of scope here but worth a follow-up.
------
AI assisted commit
* Refuse AppendFile on blobs without a committed block list
A blob written by another tool (Azure portal, azcopy, a migration script,
a plugin using Put Blob) has its content in the blob but an empty
committed-block list. Committing a new block list against such a blob
silently replaces the existing content with only the appended bytes.
Check the blob's properties before staging when the committed-block list
is empty, and refuse with a clear error if the blob has content. Same
hazard for an admin pointing the backend at an existing container with
pre-existing files.
Adds an integration test against Azurite to lock the behavior in.
------
AI assisted commit
* Surface truncated reads from azureRangeReader
Read closed the body cleanly and returned io.EOF even when the remote
stream terminated before the blob's content length. Callers (and any
retry layer above) then accepted a partial blob as complete.
ReadAt unconditionally rewrote io.ErrUnexpectedEOF to io.EOF, which made
truncated downloads indistinguishable from clean reads. That is exactly
what zip.NewReader consumes for archive readers, so the bulk-import
worker would silently import partial archives.
Read now closes the body, nils it, and returns io.ErrUnexpectedEOF when
EOF arrives before offset reaches size. ReadAt only collapses
ErrUnexpectedEOF to EOF when the full count was delivered and the stream
was consumed to the end of the blob. Otherwise the truncation
propagates with context.
Both code paths are exercised by new fakeDownloader-backed tests.
------
AI assisted commit
* Move container provisioning out of Azure TestConnection
Auto-creating the container inside TestConnection meant a typo in the
System Console (mattermosst instead of mattermost) silently provisioned
an unwanted container in the admin's Azure subscription, with no audit
log and no warning. They'd discover it later when uploads landed
somewhere unexpected.
TestConnection now returns FileBackendNoBucketError when the container
is missing, mirroring the S3 contract. A new MakeContainer method
mirrors S3FileBackend.MakeBucket, and Server.Start dispatches via two
capability interfaces (bucketMaker / containerMaker) instead of a hard
S3 type assertion — so the NoBucket error is no longer silently
swallowed for backends Server.Start has not been taught about.
------
AI assisted commit
* Carry file backend auth detail through to AppError
The Test Connection button collapsed every typed backend failure into
the same generic i18n message. Operators trying to debug bad credentials
or a missing bucket only saw "Unable to authenticate against the file
storage backend" with no SDK code to grep for in their logs.
Use errors.As so the typed checks survive future wrapping, and pass the
underlying error string through the NewAppError details argument. The
AppError serializer surfaces that detail to the admin console alongside
the translated message, so a bad S3 InvalidAccessKeyId or an Azure
AuthenticationFailed shows up in the toast without an i18n schema
change.
------
AI assisted commit
* Remove non-ascii characters from comments
------
AI assisted commit
* Make linter happy
------
AI assisted commit
* Harden Azure prefix boundary check
strings.HasPrefix on the joined path is a string-level check, not a
path-level one, so a configured prefix of "mattermost" accepts a joined
result of "mattermost-evil/...". A crafted caller path like
"../mattermost-evil/secrets" would collapse via path.Join to that exact
sibling and slip through the boundary check, escaping the configured
prefix scope.
Require the joined path to be the cleaned prefix itself or to start with
the prefix followed by a path separator. The fallback path.Join uses the
same cleaned prefix for consistency.
------
AI assisted commit
* Provision Azurite container in standalone test setup
The shared FileBackendTestSuite's SetupTest already handles a missing
container by detecting FileBackendNoBucketError from TestConnection and
calling MakeContainer, but TestAzureFileBackendAppendRefusesNonBlockBlob
bypasses SetupTest and calls TestConnection directly. On a fresh Azurite
instance the test would fail before exercising the append-refusal logic.
Extract a newAzuriteBackend(t) helper alongside azuriteSettings(t) that
builds the backend and ensures the container exists, mirroring the
suite's setup. Use errors.As for forward compatibility with future
wrapping.
------
AI assisted commit
* Fix grammar in email-settings i18n string
"Email settings has unset values." -> "Email settings have unset values."
------
AI assisted commit
* Make Azure MakeContainer idempotent
Treat a ContainerAlreadyExists response as success so that two nodes
racing through TestConnection plus MakeContainer at boot both converge
instead of having the loser fail. Mirrors how the S3 backend handles
the equivalent BucketAlreadyOwnedByYou case.
------
AI assisted commit
* Narrow AzureEndpoint comment to path-style only
The setting only builds path-style URLs, so it cannot reach sovereign
clouds like Azure Government or Azure China, which require vhost-style
endpoints. Update the comment to reflect what the code actually does
and document that sovereign-cloud support is out of scope.
------
AI assisted commit
|
||
|
|
9f1fe90b69
|
Migrate CPA to the v2 Property System (#36180) | ||
|
|
323841e9c5
|
Add board channel types (BO/BP) for Integrated Boards (#35887)
Some checks are pending
Server CI / Check mmctl docs (push) Blocked by required conditions
Server CI / Postgres (shard 0) (push) Blocked by required conditions
Server CI / Postgres (shard 1) (push) Blocked by required conditions
Server CI / Postgres (shard 2) (push) Blocked by required conditions
Server CI / Postgres (shard 3) (push) Blocked by required conditions
Server CI / Merge Postgres Test Results (push) Blocked by required conditions
Server CI / Elasticsearch v8 Compatibility (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 0) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 1) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 2) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 3) (push) Blocked by required conditions
Server CI / Merge Postgres FIPS Test Results (push) Blocked by required conditions
Server CI / Run mmctl tests (push) Blocked by required conditions
Server CI / Run mmctl tests (FIPS) (push) Blocked by required conditions
Server CI / Build mattermost server app (push) Blocked by required conditions
Tools CI / check-style (mattermost-govet) (push) Waiting to run
Tools CI / Test (mattermost-govet) (push) Waiting to run
Web App CI / check-lint (push) Waiting to run
Web App CI / check-i18n (push) Blocked by required conditions
Web App CI / check-external-links (push) Blocked by required conditions
Web App CI / check-types (push) Blocked by required conditions
Web App CI / test (platform) (push) Blocked by required conditions
Web App CI / test (mattermost-redux) (push) Blocked by required conditions
Web App CI / test (channels shard 1/4) (push) Blocked by required conditions
Web App CI / test (channels shard 2/4) (push) Blocked by required conditions
Web App CI / test (channels shard 3/4) (push) Blocked by required conditions
Web App CI / test (channels shard 4/4) (push) Blocked by required conditions
Web App CI / upload-coverage (push) Blocked by required conditions
Web App CI / build (push) Blocked by required conditions
YAML Lint / yamllint (push) Waiting to run
* Add board channel types (BO/BP) with POST /boards API
Introduces board channel types as a new channel variant that reuses the
Channels table but is fully isolated from all /channels endpoints.
Model:
- Add ChannelTypeOpenBoard ("BO") and ChannelTypePrivateBoard ("BP")
- Add IsBoard(), IsOpenBoard(), IsPrivateBoard() helpers
- Add board-specific websocket events (board_created/updated/deleted/restored)
Store:
- SaveBoardChannel: atomic channel + view creation in a single transaction
- Save() rejects board types (forces use of SaveBoardChannel)
- Exclude boards from all channel listing/search queries (GetTeamChannels,
GetAll, GetChannels, GetChannelsByUser, GetDeleted, autocomplete, search)
API:
- POST /boards: create board channel (feature-flagged behind IntegratedBoards)
- All /channels write endpoints reject board types with 400
- All /channels read endpoints reject or exclude board types
- Open boards get same public-read semantics as open channels
Tests:
- 15 rejection tests covering every /channels write + read endpoint
- 9 exclusion tests covering every listing/search endpoint
- 8 store tests for SaveBoardChannel + Save rejection
- 4 board creation API tests (create, private, flag off, sidebar exclusion)
- 3 authorization tests for board permission semantics
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Update generated files: i18n, go.mod, migrations list
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add i18n translations for board channel error strings
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix board guard ordering in getChannelMembers and getChannelStats
Move the board rejection check after the permission check so that
nonexistent channel IDs still return 403 (not 404) matching the
original behavior expected by TestGetChannelMembers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Filter boards at store level instead of API guards
Store.Get() now excludes board types via WHERE clause, making boards
invisible to all /channels endpoints. Added GetBoardChannel() for
/boards endpoints. Removed redundant API-level rejectBoardChannel
guards from 10 handlers that already call GetChannel(). Kept explicit
guards only on 3 handlers that don't fetch the channel.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix empty i18n translation for app.channel.save_member.app_error
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add board system properties, kanban column config, and audit logging
Migration:
- Register "boards" property group with system-wide Assignee (user) and
Status (select: Todo/In Progress/Complete) fields, both protected
- Idempotent migration following content flagging pattern
Board creation:
- Look up boards fields by name, set board:linked_properties on channel
- Build kanban view props with group_by mapping status options to columns
- Add typed KanbanProps/KanbanColumn/KanbanGroupBy structs with
ToProps()/KanbanPropsFromProps() for round-tripping
- Add audit record logging for POST /boards
- Add early team_id validation in API handler
- Error on missing status options instead of silent empty columns
Tests:
- Migration test: field creation + idempotent re-run
- Board creation test: verify kanban props + linked_properties
- Fix updateChannelMemberRoles test to use valid role string
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add boards migration mock to testlib store setup
The boards property migration calls System().GetByName() which needs
a matching mock expectation, same pattern as content_flagging_setup_done.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add kanban view props validation and tests
Validate kanban View.Props in IsValid(): group_by required with valid
field_id, 1-100 columns, each column needs id, name, and at least one
option_id. Update all test helpers to produce valid kanban props.
11 dedicated validation tests + round-trip test for KanbanProps.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Validate board display name is not empty
Add early DisplayName validation in CreateBoardChannel with a clear
error. Add tests for empty and whitespace-only display names.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Exclude boards from GetMany and getChannelsMemberCount
Add board type exclusion to Store.GetMany() and use filtered channel
IDs in getChannelsMemberCount handler so board channels don't leak
into member count results. Add test covering the endpoint.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix review issues: drop search indexing for boards, use request context
- Remove search layer indexing of board channels so they stay invisible
to Elasticsearch/Bleve-powered search and autocomplete
- Replace context.Background() with rctx.Context() for proper
cancellation and tracing in CreateBoardChannel
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix gofmt alignment in websocket_message.go after merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Regenerate server i18n after merge
* Restore translation for permission_policy.app_error
* Filter board channels in name lookups, autocomplete, and indexing
The store-layer board exclusion filter was missing from getByName,
getByNames, GetDeletedByName, the global Autocomplete, and
GetChannelsBatchForIndexing — leaving boards reachable via name
lookups, the no-team-filter search path, and admin reindex jobs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Reject boards in id-batch lookups, unread, and member-mutation endpoints
- GetChannelsByIds, GetChannelsWithTeamDataByIds, and GetChannelUnread
now exclude BO/BP at the store layer so boards can't slip through if
callers stop filtering first.
- updateChannelMemberNotifyProps, updateChannelMemberAutotranslation,
and viewChannel now reject board IDs explicitly via the existing
rejectBoardChannelByID helper, matching the other write endpoints.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Gate boards properties setup on the IntegratedBoards feature flag
doSetupBoardsProperties registered the boards property group and
fields at every server boot regardless of the IntegratedBoards
feature flag. Skip the migration when the flag is disabled so the
property metadata only appears once boards are actually enabled.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Use App accessors for boards property lookups
CreateBoardChannel reached into a.Srv().PropertyService() directly
instead of going through the App-level GetPropertyGroup and
GetPropertyFieldByName methods that already wrap the service. Switch
to the standard App accessors so the calls match the rest of the
codebase.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Log full channel input on createBoard audit record
createBoard only captured team_id and type on the audit record, so
failed creations lost most of the request payload. Use
AddEventParameterAuditableToAuditRec with the full channel struct,
matching createChannel.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Use allow-list of message channel types in store filters
Inverted every sq.NotEq{[BO, BP]} filter into sq.Eq{messageChannelTypes}
(or teamMessageChannelTypes for queries that also exclude direct
channels) so that any future non-message channel type — wikis, etc. —
is excluded by default rather than requiring every existing call site
to be updated. Also rewrote GetChannelUnread on top of the squirrel
builder so the same allow-list slice can be reused.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* go.mod: promote prometheus/common to direct after merge
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Use model.NewPointer for boards property permission field
Drops the local permNone variable in doSetupBoardsProperties and uses
model.NewPointer(model.PermissionLevelNone) inline, matching the
surrounding ContentFlagging/ManagedCategory code.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Extract saveViewT to share Views insert between Save and SaveBoardChannel
ViewStore.Save and SaveBoardChannel both built the same INSERT INTO
Views statement, so a future column addition would need updates in two
places. Extract the insert (plus PreSave/IsValid) into a private
saveViewT method that accepts any sqlxExecutor — the regular master
handle for ViewStore.Save, and the channel transaction for
SaveBoardChannel.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add IsMessageChannel helper on model.Channel
Mirrors IsBoard for the positive case: returns true for Open, Private,
Direct, and Group channel types. Lets future filtering code be
expressed against the allow-list rather than enumerating board types,
so newly introduced non-message channel types are excluded by default.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Move board input validation into Channel.IsValidBoard
CreateBoardChannel inlined four guards for type, team, and display
name. Move the type/team_id/display_name checks into a new
Channel.IsValidBoard method so the rules live with the model and
return the AppError directly. The TrimSpace on DisplayName stays at
the call site to match how CreateChannel sanitizes before validating.
Drops the now-unused app.channel.create_board_channel.{invalid_type,
no_team,no_display_name} translations and adds matching
model.channel.is_valid_board.* keys.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Extract buildBoardKanbanView from CreateBoardChannel
The kanban view construction (read status options, build columns,
serialize props, assemble *model.View) only depends on the status
property field and the creator id. Pulling it into its own helper
shrinks CreateBoardChannel and makes the column-building logic
testable in isolation.
Adds board_test.go with coverage for the empty-options error path,
the standard happy path, and the option-skipping branches.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Test Channel.IsValidBoard
Cover the four reject cases (wrong type, missing team_id, empty
display name) plus the open and private board accept cases.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* gofmt board_test.go
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Document POST /api/v4/boards in OpenAPI spec
* Add valid kanban props to api4 makeTestViewForAPI helper
* Assert kanban.ToProps error in makeTestViewForAPI helper
The helper used to swallow the error from kanban.ToProps. Take *testing.T
and require.NoError so a serialization failure surfaces immediately at the
call site instead of producing a malformed view.
* Run boards properties setup unconditionally
The feature-flag gate added in
|
||
|
|
d8612e378f
|
[MM-2541] Shortcut to mark all channels as read for a team (#34012)
Some checks are pending
Server CI / Check mmctl docs (push) Blocked by required conditions
Server CI / Postgres (shard 0) (push) Blocked by required conditions
Server CI / Postgres (shard 1) (push) Blocked by required conditions
Server CI / Postgres (shard 2) (push) Blocked by required conditions
Server CI / Postgres (shard 3) (push) Blocked by required conditions
Server CI / Merge Postgres Test Results (push) Blocked by required conditions
Server CI / Elasticsearch v8 Compatibility (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 0) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 1) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 2) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 3) (push) Blocked by required conditions
Server CI / Merge Postgres FIPS Test Results (push) Blocked by required conditions
Server CI / Run mmctl tests (push) Blocked by required conditions
Server CI / Run mmctl tests (FIPS) (push) Blocked by required conditions
Server CI / Build mattermost server app (push) Blocked by required conditions
Tools CI / check-style (mattermost-govet) (push) Waiting to run
Tools CI / Test (mattermost-govet) (push) Waiting to run
Web App CI / check-lint (push) Waiting to run
Web App CI / check-i18n (push) Blocked by required conditions
Web App CI / check-external-links (push) Blocked by required conditions
Web App CI / check-types (push) Blocked by required conditions
Web App CI / test (platform) (push) Blocked by required conditions
Web App CI / test (mattermost-redux) (push) Blocked by required conditions
Web App CI / test (channels shard 1/4) (push) Blocked by required conditions
Web App CI / test (channels shard 2/4) (push) Blocked by required conditions
Web App CI / test (channels shard 3/4) (push) Blocked by required conditions
Web App CI / test (channels shard 4/4) (push) Blocked by required conditions
Web App CI / upload-coverage (push) Blocked by required conditions
Web App CI / build (push) Blocked by required conditions
YAML Lint / yamllint (push) Waiting to run
* feat(webapp): added keyboard shortcut for Mark All As Read (MM-2541)
- Added shortcut (within sidebar) for Shift+ESC to mark _all_ messages, teams as read
- Desktop only
- Added feature toasts for new features and localStorage support
- Added feature toast for mark-all-as-read feature
- Should decide when/how people want this shown, I just followed designs
- Will only show if the user has not clicked 'Got it' before, and is not on mobile
- Added confirmation modal for mark all as read shortcut
- Contains option to not show again, saved in localStorage
- Added English translations for read shortcut
- Will need i18n aid on other languages
This is a draft version of this feature update that still needs testing and i18n support, along with a11y validation.
* feat(webapp): feature flags and fixes for mark all as read shortcut
- Added feature flags surrounding rollout of mark-all-as-read shortcut
- Added shortcut to list of shortcuts in help section
- Extended tests for new components
- Updated snapshot for sidebar_list, keyboard_shortcuts_modal
- Fixed styling and CSS issues
Still in draft, needs documentation and e2e support.
* fix(webapp): fixed some issues with new mark-all-read feature
- Scoped persistent storage to current user ID
so that subsequent new logins also get the notification
- Replaced LocalStorage calls with useGlobalState calls, sad
that I missed that this updated call was being used.
- Fixed an issue that would have caused the new shortcut to
show up in the Help menu's shortcuts without being enabled.
* Fixed a snapshot test and a missing i18n member
* Replaced useGlobalState with backend-ready usePreference. Previous version was just a mistake as we didnt know about the supported API
* fix(server): fix lint issue with gofmt
* feat(server,webapp): added cleaner and more effective method with which to mark-all-read
- Added 2 new routes to the API (need to find docs to update those):
- `PUT /api/v4/channels/members/<userId>/direct/read` will mark a user's non-team DMs and GMs as read
- `PUT /api/v4/users/<userId>/teams/<teamId>/read` will do a similar action as the multi-channel mark_read action, but with a teamId signifier. Because this is using a teamId, it will _not_ handle DMs or GMs.
- Updated sidebar_list.tsx to use these new routes for the new shortcut
- Added extensive testing, including feature flag assurance.
* fix from upstream changes
* fix: eslint errors in teams actions
* document new API endpoints
* fix i18n
* fix err id
* remove unused localhost methods
* use ShortcutKey and ShortcutSequence
* feature_enhancements, mark as read toast enchancements
* read all modal mount point, use openModal
* use handler
* fix style
* fix: fix refactoring typo
* Merge fix: realign branch with upstream changes
Upstream MM-67319/MM-67320 (#36037) moved ShortcutKey and
WithTooltip into the shared package and rewrote the keyboard
shortcuts test to snapshot real DOM instead of a
react-test-renderer tree. The merge resolution missed several
follow-on consequences; clean them up so the branch builds, type
checks, lints, passes i18n-extract-check and runs without
throwing at mount.
- Port the inline-content variant from the deleted channels-side
shortcut_key.scss to the new shared shortcut_key.css.
- Refresh the keyboard_shortcuts_sequence snapshot so it matches
Testing Library's container output (DOM only, no component
nodes, class= not className=).
- Repoint mark_all_as_read_modal and mark_all_as_read_toast at
components/shortcut_key for ShortcutKeys and use
ShortcutKeys.escape; the channels-side with_tooltip is now a
thin re-export and the field was renamed in the shared keys
map. Without this both consumers threw "Cannot read properties
of undefined" at mount.
- Switch mark_all_as_read_toast's UserAgent import to
@mattermost/shared/utils/user_agent; the channels-local
utils/user_agent path no longer resolves.
- Drop the orphan mark_all_threads_as_read_modal.cancel string
from en.json so formatjs extraction is in sync.
* Clean up TestReadAllInTeam
Drop four lines left from debugging and replace them with a real
assertion: LastViewedAtTimes must contain the test channel with a
value at or after the most recent post.
Update three client.GetChannel calls to the (ctx, id) signature;
the prior etag argument no longer compiles after upstream removed
it.
* Use SelectBuilder for team channels query
GetTeamChannelsWithUnreadAndMentions built a squirrel query and
then manually called ToSql before handing the string+args to
GetReplica().Select. SelectBuilder accepts the builder directly
and removes the intermediate dance, matching the pattern used
elsewhere in this store.
* Mark all team-channel threads on team read
MarkTeamChannelsAndThreadsViewed used Thread().MarkAllAsReadByTeam
unconditionally, writing every thread membership in the team for
the user even when nothing was stale. Scoping the call to
channelsToView (channels with unread channel-level messages) would
have closed the perf concern but introduced a regression: in CRT
mode a thread reply does not bump the channel's TotalMsgCount, so
a channel can be read at the channel level while still having
unread thread replies, and those would have been silently skipped.
Build the channel-id list from the keys of the times map instead.
GetTeamChannelsWithUnreadAndMentions already populates that map
for every team channel the user belongs to, so no extra query is
needed. MarkAllAsReadByChannels then filters the actual UPDATE
through its LastReplyAt > LastViewed clause, keeping writes
bounded to genuinely stale rows.
Gate the channel-level work (UpdateLastViewedAt, push clearing,
the MultipleChannelsViewed event) on channelsToView being
non-empty, but always run the thread mark and broadcast
ThreadReadChanged for every team channel so CRT clients refresh
thread state in channels that had no channel-level change.
* Mark mark-read audit records as success
The handlers for mark all DM/GM and mark team read created an
audit record with status Fail and never updated it on success,
so successful calls were always logged as failures.
* Mark all DM/GM threads on full read
MarkAllDirectAndGroupMessagesViewed early-returned when no
channel had unreads, so followed threads in DMs/GMs whose
channel-level counters were already current stayed unread under
CRT. Mirror MarkTeamChannelsAndThreadsViewed and call
MarkAllAsReadByChannels for every DM/GM in times.
* Polish DM/GM channels-with-unreads query
Use model.ChannelTypeDirect/Group constants instead of bare
"D"/"G" literals, and update the error wrap to mention DM/GM
channels (it was copied from the team variant).
* Fix stale ReadAllMessages godoc
* Type last_viewed_at_times as int64 map in OpenAPI
The response field was declared as a generic object. Add
additionalProperties so generated clients see it as a
channelId -> int64 timestamp map.
* Gate MarkAllAsReadToast mount on feature flag
The toast was mounted unconditionally, so its async chunk loaded
even when EnableShiftEscapeToMarkAllRead was off. Gate the mount
with the flag so the chunk only loads when the feature is on.
* Return data from markAllInTeamAsRead thunk
Match the {data: response} shape used by adjacent thunks instead
of returning {}, so callers can read the API payload.
* Coerce undefined suffix in createStoredKey
createStoredKey('foo') returned 'fooundefined' when the suffix
arg was omitted. Coerce a missing suffix to ''.
* Refactor mark-read websocket events
* Polish DM/GM channels-with-unreads query
* Fix import order in shortcut_key consumers
* Fix CI
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
Co-authored-by: Jesse Hallam <jesse@mattermost.com>
Co-authored-by: Caleb Roseland <caleb@calebroseland.com>
Co-authored-by: Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
|
||
|
|
5661fe1941
|
MM-68501 - implement GetMaskedVisualAST and wire API handler (#36413)
* 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 --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
56089922e3
|
Data spillage report generation (#36339)
* Added base fr report generation * WIP * Refactoring and cleanup * lint fixes, added new tests * test fix * Several improvements * Addressed some security enhancements * Created zip writer entery later * Improved a test to check for file content * Improved error handling * Made a geneeric function * accepting comment in report API * Removed an unnecessary check * Made a geneeric function * Made the comment body not required and updated API docs |
||
|
|
69fbaeced9
|
[MM-68496] Feature flag Managed Categories, expose Default Category Name to UI for channel creation and settings (#36289)
* [MM-68496] Feature flag Managed Categories, expose Default Category Name to UI for Channels * PR feedback * PR feedback * Fix i18n * Fix test * Fix E2E * Merge'd * Add tests * Re-add old tests (skipped) * Add IncrementVersion to PropertyGroup store, increment version on managed category group * Fix lint * Fix mock * Fix prettier * Add tests * Fixed issue when moving from existing category to existing category * Fix e2e --------- Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
6293e354c9
|
Translations update from Mattermost Weblate (#36404)
* Translated using Weblate (Polish) Currently translated at 100.0% (3191 of 3191 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/pl/ * Translated using Weblate (Polish) Currently translated at 100.0% (7243 of 7243 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pl/ * Translated using Weblate (Turkish) Currently translated at 91.5% (6634 of 7243 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/tr/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 99.9% (7239 of 7243 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 100.0% (3191 of 3191 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 100.0% (7243 of 7243 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 100.0% (7243 of 7243 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 100.0% (3191 of 3191 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 100.0% (7243 of 7243 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 100.0% (3191 of 3191 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 100.0% (7243 of 7243 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/zh_Hans/ * Translated using Weblate (Turkish) Currently translated at 93.3% (6763 of 7243 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/tr/ * Translated using Weblate (Polish) Currently translated at 100.0% (7243 of 7243 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pl/ * Translated using Weblate (Polish) Currently translated at 100.0% (7243 of 7243 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pl/ * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/ * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ * Translated using Weblate (Norwegian Bokmål) Currently translated at 76.9% (5617 of 7303 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/nb_NO/ --------- Co-authored-by: master7 <marcin.karkosz@rajska.info> Co-authored-by: Kaya Zeren <kayazeren@gmail.com> Co-authored-by: Sharuru <mave@foxmail.com> Co-authored-by: ThrRip <coding@thrrip.space> Co-authored-by: Frank Paul Silye <frankps@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
ecf8a741ac
|
Add unread badge to Recaps sidebar link (#36246)
* Add unread badge to Recaps sidebar link
Shows the count of unread finished recaps (completed or failed) on the
LHS Recaps link. Pending and processing recaps are excluded so the badge
only reflects work the user can actually read. When any unread recap has
failed, the badge is colored as an error to surface the failure.
The badge updates live through the existing recap_updated WebSocket
event, which refreshes the recap in the Redux store.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Fix Recaps failed-badge color losing to active sidebar rule
The failed-badge modifier selector had the same specificity (0,4,0) as
`.channel-view .sidebar--left .active .badge` in _badge.scss, so when
the Recaps link was the active route the global mention background
color won on cascade order. Scope the rule with `#SidebarContainer` so
it wins on specificity (1 id + 4 classes) regardless of active state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Fix Recaps badge selector memoization
getUnreadFinishedRecapsBadge was keyed off getAllRecaps, which is not
memoized and returns a new array on every call. That broke reselect's
reference-equality input check, so the selector recomputed and returned
a fresh {count, hasFailed} object on every store dispatch — forcing
RecapsLink (always mounted when the feature flag is on) to re-render
on every action. Key the selector off state.entities.recaps directly
and iterate ids in the result function so memoization holds when the
recaps slice is unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Address PR feedback on Recaps sidebar badge
- Pass shallowEqual to the useSelector consuming
getUnreadFinishedRecapsBadge. The selector returns a plain
{count, hasFailed} object, so recap updates that change a recap
but leave the badge values the same (e.g. marking a read recap)
would otherwise force RecapsLink to re-render.
- Scope the "no badge" negative assertion to the render container so
it only asserts on the badge element, not any '1' or '.badge'
elsewhere in the DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Address UX feedback on Recaps sidebar badge
- Add `unread` class to the sidebar item and `unread-title` to the
link when there are unread recaps so the label goes bold and the
icon goes full-opacity, matching how channels and the threads link
indicate unread state.
- Keep the badge (and the new failed icon) visible on hover so it
doesn't disappear under the cursor -- same override the threads
link uses.
- Replace the red failed-badge modifier with an amber alert icon
rendered in place of the count badge when any unread recap has
failed. Red mention badges are reserved for urgent priority
messages and caused confusion here.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Keep Recaps badge in place on hover
The global sidebar hover rule shrinks padding-right from 16px to 5px
to make room for the per-channel menu button, which shifted the badge
right since it stays visible. Restore padding-right: 16px on hover for
the Recaps link, matching what the threads link already does.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Align Recaps failed-icon aria-label with tooltip
The aria-label on the .RecapsFailedIcon span was a hardcoded English
string ("Recap failed") that differed from the tooltip shown to
sighted users ("One or more recaps failed"). Derive the aria-label
from the same intl message used by the tooltip so screen readers and
sighted users get the same wording and the label is localized.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Stop Recaps link from overriding global unread label styling
The combined `.active .SidebarLink, .SidebarLink.unread-title` rule
pushed font-weight: 400 onto .SidebarChannelLinkLabel with specificity
(0,4,0), overriding the global `.SidebarChannel.unread` rule that sets
font-weight: 600 and --sidebar-unread-text at (0,3,0). As a result the
Recaps label rendered at normal weight when unread, inconsistent with
channels and the threads link. Split the rules: keep the active-state
overrides as they were, and limit the unread-title rule to the
icon-specific styling Recaps actually needs, letting the global unread
styling apply to the label.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add i18n entry for Recaps failed-tooltip
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* change size of alert icon
* fix the right icon
* Add ViewedAt to recaps and POST /recaps/mark_viewed endpoint
Introduce a new ViewedAt field on Recap, separate from ReadAt, that
tracks whether the user has at least seen a finished recap on the
recaps page. ReadAt keeps its existing per-recap "Mark read" semantics.
- New Postgres migration 000172 adds the ViewedAt column (default 0)
and an idx_recaps_user_id_viewed_at index mirroring the existing
ReadAt index.
- New store method MarkRecapsAsViewed(userId, statuses) does a single
UPDATE ... WHERE ViewedAt = 0 AND Status IN (...) RETURNING Id so
the app layer can fan out one WS event per affected recap.
- New App.MarkRecapsAsViewed(rctx) marks the user's not-yet-viewed
completed/failed recaps and broadcasts WebsocketEventRecapUpdated
per affected id.
- New POST /recaps/mark_viewed handler. Registered before the
{recap_id} regex routes so mark_viewed isn't captured as an id.
- RegenerateRecap now resets ViewedAt = 0 so a regenerated recap is
surfaced again in the badge once it completes. As a related fix,
UpdateRecap now persists ReadAt and ViewedAt -- previously it
silently dropped the ReadAt = 0 reset that RegenerateRecap was
setting in memory.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Mark recaps as viewed when the recaps page mounts
Wire the new server endpoint into the webapp:
- Recap type now includes viewed_at: number.
- Client4.markRecapsAsViewed posts to /recaps/mark_viewed.
- New markRecapsAsViewed redux action, fired alongside getRecaps and
getAgents in the recaps page mount effect. The server broadcasts
recap_updated per affected recap so other tabs/devices receive the
update through the existing handleRecapUpdated WS handler -- no new
client-side handler needed.
- getUnreadFinishedRecapsBadge now filters on viewed_at === 0 instead
of read_at === 0, so the sidebar badge clears on page open instead
of requiring per-recap "Mark read" clicks. Selector tests updated to
match.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Address review feedback on Recaps viewed_at change
- Defer markRecapsAsViewed until after getRecaps resolves on the
recaps page mount. Previously they ran in parallel, so getRecaps
could land last and overwrite the viewed_at: <now> timestamps the
WS-driven refresh had just written, briefly re-showing the badge.
- Switch the markRecapsAsViewed audit log to LevelContent and record
the affected ids as result state, matching the pattern of every
other mutating recap handler (markRecapAsRead, deleteRecap, etc).
recap_count meta is now recorded unconditionally.
- Add an app-layer test that asserts MarkRecapsAsViewed publishes a
recap_updated websocket event for each affected recap. The fan-out
is the entire reason this lives in the app layer, so a regression
removing the publish loop should fail loudly.
- Add a store-layer regression test that UpdateRecap actually
persists ReadAt = 0 / ViewedAt = 0 resets, guarding the regenerate
flow against a future change that drops those columns from the
update map.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Update migrations.list for 000172_add_recaps_viewed_at
Regenerated via `make migrations-extract` so the autogenerated
sequence list includes the new recaps ViewedAt migration files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Use AddMeta for Recaps mark_viewed audit ids
AddEventResultState takes a model.Auditable, not a plain map[string]any,
so the previous attempt to record the affected ids did not compile.
Record them as audit metadata instead, matching the pattern used by
getRecaps which similarly returns a slice and uses AddMeta only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Split Recaps ViewedAt index into a CONCURRENTLY migration
The lint check rejects bare CREATE/DROP INDEX in migrations because
they take an ACCESS EXCLUSIVE lock and block DML. Split the index off
into 000173 with CONCURRENTLY + the morph:nontransactional directive,
matching the pattern used by 000168/000169 (LinkedFieldID column +
its index). 000172 keeps just the ALTER TABLE ADD COLUMN, which can
stay transactional.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add viewed_at to existing Recap test fixtures
The Recap type now requires viewed_at, so the fixtures in
recap_item.test.tsx, recap_processing.test.tsx, and recaps_list.test.tsx
need it too. CI was rejecting them with TS2741.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Mock markRecapsAsViewed in recaps.test.tsx
The mount effect now also dispatches markRecapsAsViewed, but the
manual jest.mock for 'mattermost-redux/actions/recaps' only exposed
getRecaps, so the runtime call resolved to undefined and crashed
with "markRecapsAsViewed is not a function". Add the missing entry
to the mock.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add /recaps/mark_viewed and Recap.viewed_at to OpenAPI spec
The recap-spec validator rejected the new POST /api/v4/recaps/mark_viewed
handler because it had no documented operation. Add the path with its
MarkRecapsAsViewed operationId, response shape, and behavior, and add
the new viewed_at timestamp field to the Recap schema in definitions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Fill in app.recap.mark_viewed.app_error translation
The new MarkRecapsAsViewed app method references this i18n key but the
en.json entry was added with an empty translation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Skip markRecapsAsViewed when getRecaps fails
Marking recaps as viewed implies the user just looked at them. If
getRecaps fails the user is staring at an error/empty state, so we
shouldn't ack them on the server. Gate the dispatch on the thunk's
result.error -- the codebase's bindClientFunc swallows errors and
returns {error}, so the conventional try/catch pattern doesn't apply
here.
Update the recaps.test.tsx dispatch mock to return a resolved promise
so the new awaited result has the expected shape.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Clear and assert markRecapsAsViewed mock in recaps.test.tsx
Reset the new mock in beforeEach so it doesn't carry state across
tests, and assert that the mount effect dispatches markRecapsAsViewed
after getRecaps resolves. Awaiting via waitFor since the mark fires
inside an async fetchData chain.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
2b753c49f2
|
Remove unused GetChannelCounts store and app methods (#36351)
GetChannelCounts was only reachable from App and had no callers. Remove SqlChannelStore implementation, store interface, timer/retry layers, tests, mock, model.ChannelCounts, and the orphaned i18n key. Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com> |
||
|
|
ebc066e7fd
|
[MM-68273] Add system messages for share / unshare events (#36032)
* [MM-68273] Add system messages for share / unshare events * Fix CI * Address feedback * Fix CI * Remove unknown prop * Add missing tests |
||
|
|
724c5b7191
|
CPA Display Name Support (#36247)
* Phase 1: CPA display_name + CEL-safe name validation (server) - Add typed DisplayName field to CPAAttrs + display_name attr key constant. - Add ValidateCPAFieldName helper enforcing CEL IDENTIFIER + reserved-word blacklist. - Wire validation into App.CreateCPAField (always) and App.PatchCPAField (lenient grandfather: skip when Name unchanged). - Trim + 255-rune cap DisplayName in CPAField.SanitizeAndValidate. - Developer-facing godoc note documenting rule, sources of truth, and Option C scoping. - Asserting test for documented Option C plugin-API bypass (closed by PR #36173). Spec: planner/projects/property-display-name/ideas/001-cpa-display-name/spec.md Plan: .planning/phase-1/PLAN.md Made-with: Cursor * Phase 1 (review): address Reza's Major + Minor findings - Rename misleading subtest "empty DisplayName is omitted from attrs" to "empty DisplayName round-trips as empty string" (Major #1). - Add TestCPAAttrs_JSONOmitEmpty pinning the omitempty wire-format contract that PR #36173's typed-attrs strategy relies on (Major #1). - Extend TestValidateCPAFieldName: case-sensitivity (IN/In ok), single-character names (a/_/A ok), missing "as" reserved word (Minor #2). Add whitespace-only DisplayName case (Minor #2). - Document PropertyFieldNameMaxRunes reuse in SanitizeAndValidate to prevent drift (Minor #3). - Replace broken PLAN-server.md reference in bypass-test docstring with in-tree CPAAttrs godoc reference (Minor #4). - Document omitempty semantics on CPAAttrs.DisplayName field to prevent the same misreading caught in review (Minor #5). - Document grouping intent above CPAFieldNameReservedWords (Minor #8). Review: .planning/phase-1/REVIEW.md Made-with: Cursor * Phase 2: in-app backfill migration for CPA display_name - Add cpaDisplayNameBackfillKey + cpaDisplayNameBackfillVersion constants. - Implement (*Server).doSetupCPADisplayNameBackfill: idempotent, cursor-paged scan over CPA group fields; backfill attrs.display_name = name when empty. - Register in m1 migration slice in doAppMigrations (mlog.Fatal on error, matching existing convention). - Three migration tests: NoExistingFields, BackfillsMissing, Idempotent. System-key idempotency + per-field DisplayName-empty check together provide HA-safe behavior on rolling deploys (last-write-wins on the System key; data-level idempotency from the per-field check). Spec: planner/projects/property-display-name/ideas/001-cpa-display-name/spec.md Plan: .planning/phase-2/PLAN.md Made-with: Cursor * Phase 2 (review): document race + harden idempotency test - Document SearchPropertyFields→UpdatePropertyFields rolling-deploy race: stale snapshot can revert concurrent admin CPA rename. Pre- existing systemic shape (no UpdateAt optimistic-lock); narrow window; bounded blast radius (admin re-rename, ABAC ID-keyed). Accepted limitation per spec Out of Scope (Major #1, Option C). - Tighten TestCPADisplayNameBackfill_Idempotent: snapshot UpdateAt before second run; assert no DB write on the System key or the field row (Major #2). - Extract clearCPABackfillMarker helper with explanatory godoc to centralize the 3x-repeated test precondition (Minor #1). - Comment fieldA seed as the "key-present-as-empty-string" idempotency boundary case (Minor #6). - Add godoc to doSetupCPADisplayNameBackfill (Minor #10). Review: .planning/phase-2/REVIEW.md Made-with: Cursor * Linting * Removing unnecessary comments * Clean up tests * Linting * Fix tests * Updated API doc * Fix tests * PR Feedback * Move migration to PropertyService * Linting * Linting * Removed pagination --------- Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
d4f147e2da
|
Data spillage deletion summary (#36018)
* Report POC * Including more error logs * Added localisationj for each reviewer * Optimisations * Minor tweaks * restored go module files * lint fixes * Added back transslations * Added translations * linter and test fixes * restored go module files * e2e lint fix * lint fixes * AI fixes * fixed typo * fixed nil pointer error * Added more tests * Publish report even if deletion fails * Fixed the e2e test * Distinguished between no data and deleted data * lint fixes * fixed tests * e2e test fix * Updated test to also upload actual file * Removed file name tracking * Text updates * fixed e2e test * lint fix |
||
|
|
4da11e81af
|
[MM-68497] Enables membership policies on public channels with advisory semantics (#36275) | ||
|
|
6c0e0fee4a
|
[MM-68464] Introduce system object type for property fields and values (#36250) | ||
|
|
81d4fe3793
|
MM-68339: Add XML struct tags and multi-remote registration for shared channels plugin API (#36126)
* Add XML struct tags and multi-remote registration for shared channels plugin API Phase 1: Add xml struct tags to model types used in SyncMsg (Post, User, Reaction, Status, PostAcknowledgement, FileInfo, SyncResponse, MembershipChangeMsg). Add custom MarshalXML/UnmarshalXML for SyncMsg (Users map, MentionTransforms map), StringMap, and StringInterface. Exclude Post.Metadata, PrevStatus, and server-internal FileInfo fields from XML. JSON serialization is unaffected. Phase 2: Lift the one-remote-per-plugin constraint so plugins can register multiple remotes with different SiteURLs. Add SiteURL field to RegisterPluginOpts (defaults to "plugin_<PluginID>" for backward compatibility). Add GetAllByPluginID and GetBySiteURL store methods. Rewrite registration to dedup by SiteURL instead of PluginID. Add UnregisterPluginRemoteForSharedChannels for single-remote removal with plugin ownership validation. Validate SiteURL is non-empty in RemoteCluster.IsValid. Simplify IsPlugin() to check PluginID only. |
||
|
|
95f1f32a42
|
Fixing weblate (#36283) | ||
|
|
9c684e6313
|
Property System v2 Generic APIs blacklist (#36171)
Some checks are pending
Server CI / Check mmctl docs (push) Blocked by required conditions
Server CI / Postgres (shard 0) (push) Blocked by required conditions
Server CI / Postgres (shard 1) (push) Blocked by required conditions
Server CI / Postgres (shard 2) (push) Blocked by required conditions
Server CI / Postgres (shard 3) (push) Blocked by required conditions
Server CI / Merge Postgres Test Results (push) Blocked by required conditions
Server CI / Elasticsearch v8 Compatibility (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 0) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 1) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 2) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 3) (push) Blocked by required conditions
Server CI / Merge Postgres FIPS Test Results (push) Blocked by required conditions
Server CI / Run mmctl tests (push) Blocked by required conditions
Server CI / Run mmctl tests (FIPS) (push) Blocked by required conditions
Server CI / Build mattermost server app (push) Blocked by required conditions
Tools CI / check-style (mattermost-govet) (push) Waiting to run
Tools CI / Test (mattermost-govet) (push) Waiting to run
Web App CI / check-lint (push) Waiting to run
Web App CI / check-i18n (push) Blocked by required conditions
Web App CI / check-external-links (push) Blocked by required conditions
Web App CI / check-types (push) Blocked by required conditions
Web App CI / test (platform) (push) Blocked by required conditions
Web App CI / test (mattermost-redux) (push) Blocked by required conditions
Web App CI / test (channels shard 1/4) (push) Blocked by required conditions
Web App CI / test (channels shard 2/4) (push) Blocked by required conditions
Web App CI / test (channels shard 3/4) (push) Blocked by required conditions
Web App CI / test (channels shard 4/4) (push) Blocked by required conditions
Web App CI / upload-coverage (push) Blocked by required conditions
Web App CI / build (push) Blocked by required conditions
YAML Lint / yamllint (push) Waiting to run
* Adds version to the property group model * Ensures that the REST API rejects v1 group calls * Ensures field version and group version match * Simplify property groups on app layer tests * Add GetByID to PropertyGroupStore and enforce field/group version match on update * Simplify bits of the code * Fix i18n and add generic errors * Fix PropertyGroupStore mock to return stable IDs and default zero version to V1 * Fix tests that were using nonexistent group IDs * Fix rigidness on valid group names * Update group not found slug * Temporary allow to use tempaltes with v1 * Explicitly including tempaltes in the IsPSAv1 check for conflict check * Return 404 on group not found and template explicit inclusion on patch API endpoint * Fix CPA test that would use fields from unregistered groups --------- Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> |
||
|
|
c021eeaff8
|
MM-68439 Centralize filename handling for FileInfo (#36223)
Some checks are pending
Server CI / Check mmctl docs (push) Blocked by required conditions
Server CI / Postgres (shard 0) (push) Blocked by required conditions
Server CI / Postgres (shard 1) (push) Blocked by required conditions
Server CI / Postgres (shard 2) (push) Blocked by required conditions
Server CI / Postgres (shard 3) (push) Blocked by required conditions
Server CI / Merge Postgres Test Results (push) Blocked by required conditions
Server CI / Elasticsearch v8 Compatibility (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 0) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 1) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 2) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 3) (push) Blocked by required conditions
Server CI / Merge Postgres FIPS Test Results (push) Blocked by required conditions
Server CI / Run mmctl tests (push) Blocked by required conditions
Server CI / Run mmctl tests (FIPS) (push) Blocked by required conditions
Server CI / Build mattermost server app (push) Blocked by required conditions
Tools CI / check-style (mattermost-govet) (push) Waiting to run
Tools CI / Test (mattermost-govet) (push) Waiting to run
Web App CI / check-lint (push) Waiting to run
Web App CI / check-i18n (push) Blocked by required conditions
Web App CI / check-external-links (push) Blocked by required conditions
Web App CI / check-types (push) Blocked by required conditions
Web App CI / test (platform) (push) Blocked by required conditions
Web App CI / test (mattermost-redux) (push) Blocked by required conditions
Web App CI / test (channels shard 1/4) (push) Blocked by required conditions
Web App CI / test (channels shard 2/4) (push) Blocked by required conditions
Web App CI / test (channels shard 3/4) (push) Blocked by required conditions
Web App CI / test (channels shard 4/4) (push) Blocked by required conditions
Web App CI / upload-coverage (push) Blocked by required conditions
Web App CI / build (push) Blocked by required conditions
YAML Lint / yamllint (push) Waiting to run
* Introduce model.SanitizeFilename and model.IsValidFilename, and apply them in genFileInfoFromReader and FileInfo.IsValid. The sanitizer uses filepath.Base, NFC-normalizes Unicode, strips ASCII control characters, collapses backslashes to forward slashes, and truncates to the VARCHAR(256) fileinfo.name column width. |
||
|
|
694e6f40dc
|
fix: bot import panic when user exists without bot record (#36072)
* Fix bot import panic when user exists without bot record The original importBot error handler re-declared `var appErr *model.AppError` inside the CreateBot failure block, shadowing the outer appErr with a typed nil pointer. When errors.As received this (*model.AppError)(nil), it was a non-nil interface — so instead of returning false, it called AppError.Unwrap() on the nil receiver, causing a panic. This commit: - Fixes the variable shadowing that caused the panic - Adds recovery logic: when CreateBot fails because the username is taken, look up the existing user and create/update just the bot record - Fixes pre-existing silent error swallowing in Bot().GetByUsername — now distinguishes store.ErrNotFound from real database errors - Fixes pre-existing bug where DisplayName changes on re-import were lost because DisplayName is stored in Users.FirstName, not the Bots table - Adds logging at every step of the recovery path (Info for normal flow, Warn for fallback/error paths) - Uses distinct variable names (saveErr/updateErr) in the Save→Update fallback to avoid the same class of variable-reuse hazard - Adds comprehensive test suite (11 subtests) covering dry-run, apply, re-import, recovery, regression/panic guard, idempotency, DisplayName update, and plugin-owner edge cases * Address review findings: tighten assertions, fix error ID, add coverage - Use require.ErrorAs for store.ErrNotFound instead of generic require.Error in the dry-run test, so it catches only not-found rather than any store error - Add time.Sleep before idempotent re-import to ensure any real write would produce a different UpdateAt timestamp at millisecond resolution - Fix misleading error ID "app.bot.createbot.internal_error" to "app.bot.update.internal_error" in the Update fallback path - Add test for non-username CreateBot failure (email conflict) to cover the error passthrough at line 907-908 * Add missing i18n translations for bot import error strings Add translation entries for app.bot.update.internal_error, app.import.import_bot.lookup_error, and app.import.import_bot.user_not_found.error to fix enterprise CI i18n check failure. |
||
|
|
3fa8776095
|
[MM-68100] Implement Linked Properties for the Property System (#35808) | ||
|
|
67a645d2c6
|
MM-68378: Fix silent bulk failures in OpenSearch/Elasticsearch indexers (#36189)
- Register per-item OnFailure callback to log each rejected document with its error type and reason - Restructure DoJob to close the bulk processor before marking a job successful; check Stats().NumFailed after close and fail the job when any documents were not indexed - Use a sync.Once-guarded doClose helper so the processor is flushed exactly once across all exit paths; non-success paths return and let the defer handle the close - Close function owns all its logging: warn with error on failure, info on success; callers no longer need to log close errors themselves - Fix ResponseBodyEnabled() hardcoded false → return l.trace so per-item error JSON is visible when Trace=all is set - Set job to error (rather than silently return) when createBulkProcessor fails after the job has been claimed - Add integration tests for both backends that apply a write block to verify bulk failures are surfaced as job errors - Add i18n strings for the two new error codes |
||
|
|
8cd48d4651
|
[MM-67880] Add /mobile-logs slash command (#35658)
* [MM-67880] Add /mobile-logs slash command with E2E tests Add a new /mobile-logs slash command that allows users to manage the attach_app_logs preference for themselves or other users (admin-only). Includes unit tests for all code paths and Playwright E2E tests covering self-management, admin cross-user management, permission denial, and error handling. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix i18n error key suffixes and format E2E tests Rename error i18n keys to use .app_error suffix matching upstream convention (no_permission, update_error, user_not_found). Run prettier on the E2E test file to fix formatting. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix E2E test type error: use getUserPreferences instead of getMyPreferences The getAttachLogsPreference helper was using getMyPreferences() which returns PreferenceType (not an array), causing TS2345 errors. Switch to getUserPreferences(userId) which returns the expected array type. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * [MM-67880] Move unreachable usage fallback into switch default case The return after the switch was unreachable because action is validated earlier to be "on", "off", or "status". Move it into an explicit default case with a defensive comment. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * allow up to 2 arguments * Add audit logging for mobile logs slash command actions Implement a new function to log audit records when users enable or disable the attach_app_logs preference via the /mobile-logs command. This includes capturing relevant metadata such as user IDs, session information, and the action taken. The logging occurs in both the enable and disable command paths, enhancing traceability and accountability for user preference changes. * Enhance mobile logs command to handle cross-user permission checks Add a new response function for cases where a regular user attempts to access mobile log settings for another user, ensuring they receive a neutral error message instead of specific user information. Update the command logic to incorporate this response for both nonexistent users and deactivated accounts. Additionally, modify related tests and internationalization keys to reflect these changes, improving security and user experience. * Update E2E test for /mobile-logs command to verify permission denial for nonexistent users Enhance the existing E2E test for the /mobile-logs command by adding assertions to check that users receive a permission denial message when attempting to change mobile log settings for a nonexistent user. This improves test coverage and ensures proper error handling in the application. * update i18n strings --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
faf1118b6a
|
Update en.json (#36139)
Automatic Merge |
||
|
|
beb96185cd
|
[MM-68183] Permission policies (#36003)
Some checks failed
Server CI / Postgres with binary parameters (push) Has been cancelled
Server CI / Postgres (shard 0) (push) Has been cancelled
Server CI / Postgres (shard 1) (push) Has been cancelled
Server CI / Postgres (shard 2) (push) Has been cancelled
Server CI / Postgres (shard 3) (push) Has been cancelled
Server CI / Merge Postgres Test Results (push) Has been cancelled
Server CI / Elasticsearch v8 Compatibility (push) Has been cancelled
Server CI / Postgres FIPS (shard 0) (push) Has been cancelled
Server CI / Postgres FIPS (shard 1) (push) Has been cancelled
Server CI / Postgres FIPS (shard 2) (push) Has been cancelled
Server CI / Postgres FIPS (shard 3) (push) Has been cancelled
Server CI / Merge Postgres FIPS Test Results (push) Has been cancelled
Server CI / Coverage (shard 0) (push) Has been cancelled
Server CI / Coverage (shard 1) (push) Has been cancelled
Server CI / Coverage (shard 2) (push) Has been cancelled
Server CI / Coverage (shard 3) (push) Has been cancelled
Server CI / Run mmctl tests (push) Has been cancelled
Server CI / Run mmctl tests (FIPS) (push) Has been cancelled
Server CI / Build mattermost server app (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
--------- Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> |
||
|
|
80b977807a
|
Feature mm 64509 team admin abac channels (#36061)
* MM-67592 - be changes for team admin abac channels (#35353) * MM-67592 - be changes for team admin abac channels * Revert team-scoped API routes, keep app layer business logic * move from config to permission; Add cluster-aware LRU cache for policy team scope lookup * remove unnecessary references to config value * local/remote cache invalidation consistency for policy scope * Replace policy scope cache with store-level team scope query * rename functions and add comments to query --------- Co-authored-by: Mattermost Build <build@mattermost.com> * MM 67594 - policies CUD operations to team settings modal channels ABAC (#35590) * MM-67592 - be changes for team admin abac channels * Revert team-scoped API routes, keep app layer business logic * move from config to permission; Add cluster-aware LRU cache for policy team scope lookup * remove unnecessary references to config value * local/remote cache invalidation consistency for policy scope * Replace policy scope cache with store-level team scope query * format files correctly * fix mock expectations for store-query approach in tests * rename functions and add comments to query * revert error ids to original to prevent break tests * adjust translations * MM-67669 - add tab to team settings modal and basic listing * adjust tests and fix linter * use existing search api logic * fix style and adjust flaky test to clean up and restore orinals * address ai corabbit feedback and fix linter * fix unit tests * MM-67592 - be changes for team admin abac channels (#35353) * MM-67592 - be changes for team admin abac channels * fix linter * fix ts linter for playwright * Revert team-scoped API routes, keep app layer business logic * move from config to permission; Add cluster-aware LRU cache for policy team scope lookup * remove unnecessary references to config value * local/remote cache invalidation consistency for policy scope * Replace policy scope cache with store-level team scope query * format files correctly * fix mock expectations for store-query approach in tests * rename functions and add comments to query * revert error ids to original to prevent break tests * adjust translations --------- Co-authored-by: Mattermost Build <build@mattermost.com> * MM-67594 - support cud operations for team abac BE changes * create the team settings policy edit section, reuse most components, add basic e2e * move optional refresh policy list button to list component * temp get team admins cud policies and sync job * enhance validation and adjust e2e * Fix testExpression permission; fix pagination of team policies; add isValidId validation * adjust styles, handling renaming and add permission migrations * update the permissions names, use the simple confirmation modal, define the delete modal * fix policy deletion flow * fix some linter issues and adjust helper tests * remove delete from list and fix e2e * code comments clean up * remove CEL editor for now, clean styles, enhance e2e * fix linter, adjust unit test * fix linter and add missing translation * fix policy deletion ownership and sanitize test expression * fixed e2e tests * rollback orphaned policy on failed channel assignment * enforce channelless check before last_team_id fallback * enforce channelless guard on assign fallback too * add translations missing * add teamId to audit payload when present * fix refresh button pagination reset * fix null safety in channel selector loadChannels * use responsive width cap for team settings modal and adjust header size * remove redundant raw term from channel search URL, add showRefreshButton prop to PolicyList component * handle error when stamping last team ID on channelless policy * replace Props-based ownership with in-memory LRU cache, disable save on zero channels * make e2e tests more reliable in CI * test skip if no license valid found * add childCount guard to cache-hit paths and reduce TTL to 5s * fix e2e, adjust translation * address review feedback: flatten permission checks and separate error types - Flatten nested permission branching in deleteAccessControlPolicy using early returns to reduce indentation (review: isacikgoz) - Validate teamID as input (400) before using it for permission checks (403) in testExpression and validateExpressionAgainstRequester handlers - Remove redundant hasSystemPermission check in searchAccessControlPolicies since system_admin role already includes manage_team_access_rules - Refactor ValidateTeamAdminPolicyOwnership to return (bool, *model.AppError) separating "not owned" from "internal error" across all 8 call sites - Update tests to assert on both return values Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * add persistent team scope to access control policies, replace in-memory cache * fix translation * fix case-insensitive policy search and sanitize search term input * make policies tests have a unique name * decouple scope/scopeID filter from TeamID in policy store * Fix authZ bypass searchChannelsForAccessControlPolicy by forcing TeamIds to authorized team * show unsaved changes on navigator back, and list all private channels on load * filter already applied channels to a policy * adjust the styles to dark mode; do not show added channels to the policy in the add channels modal * fix linter * MM-67967 add sync status footer to team settings (#35729) * MM-67967 add sync status footer to team settings * remove magic numbers and strings and polish the code * fix linter * fix linter: replace interface{} with any per gofmt rewrite rule Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refine getJobsByType team-scoped filtering and permissions * fix sync footer stuck in syncing state on job creation error * fix team-scoped job pagination in getJobsByType * Fix authZ bypass searchChannelsForAccessControlPolicy by forcing TeamIds to authorized team * implement ux feedback, change titles font, fix marging and scroll view jump * MM-68135 - migrate add channels to policy modal to generic modal (#35907) * MM-67920 unify e2e team settings tests (#35867) * MM-67920 - extract duplicated policy editor helpers * remove duplicate team icon test file * rename Access Control to Membership Policies in e2e * replace networkidle with explicit element waits * fix attribute loading issue --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix playwright feedback issues and persist filters to the store layer in the no systemconsole path * Improve policy scope validation and team admin security checks * Renamed public channels to "AAA Public Channel %03d" and private ones to "ZZZ Private..." so the 55 public channels now fill the 50-result cap * fix e2e tests and add new unit tests to improve coverage * Improve e2e test stability: race condition handling and timeout adjustments * Improve team-scoped ABAC policies: scope preservation, input validation, shared exclusion * Add comprehensive ABAC test coverage: team admin ops and security validation to reduce flakyness * Fix team policy editor back button: preserve navigation intent through Undo * style: format import statements for better readability * Enhance access control policy creation for team admins: enforce scope stamping from query parameters to prevent unauthorized team assignments --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
c66bb0ecdb
|
[MM-68109] Introduce new policy version v0.3 (#35904) | ||
|
|
9fa8c8c0c8
|
Add bulk set (replace) channel memberships API endpoint (#36031)
* Add bulk set (replace) channel memberships API
PUT /api/v4/channels/{channel_id}/members accepts a complete desired
membership list and reconciles it against the current state, adding
missing users and removing extras while leaving existing members
untouched. Results stream back as NDJSON with configurable batch size
and delay to manage server load. Sysadmin only. Private channels
cannot be emptied entirely.
|
||
|
|
c3ab0f7f78
|
MM-68191: Add plugin Receive APIs for shared channel sync (#35962)
* Add plugin APIs for plugin to sync data into shared channels |
||
|
|
01219efbf4
|
[MM-68037] Managed Sidebar Categories (MVF) (#35935)
* [MM-68037] Managed Sidebar Categories (MVF) * PR feedback * PR feedback * Fix test issue again * Fixed a few things * Fix again * PR feedback * Update server/i18n/en.json Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update server/i18n/en.json Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update webapp/channels/src/packages/mattermost-redux/src/actions/channel_categories.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * PR feedback * PR feedback * More PR feedback * Test fixes * This one too * PR feedback * more * More feedback * More * more * Yup * More * PR feedback * Update webapp/channels/src/components/channel_settings_modal/managed_category_selector.scss Co-authored-by: Matthew Birtch <mattbirtch@gmail.com> * Block setting behind Enterprise license * Update webapp/channels/src/packages/mattermost-redux/src/selectors/entities/channel_categories.ts Co-authored-by: Harrison Healey <harrisonmhealey@gmail.com> * Update webapp/channels/src/packages/mattermost-redux/src/actions/channel_categories.ts Co-authored-by: Harrison Healey <harrisonmhealey@gmail.com> * PR feedback * Don't await for the initial managed category check * Turn into its own action --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Matthew Birtch <mattbirtch@gmail.com> Co-authored-by: Harrison Healey <harrisonmhealey@gmail.com> |
||
|
|
28dffaa574
|
MM-68235: Rename user-visible "Custom Profile Attributes" to "User Attributes" (#36046)
Some checks are pending
Server CI / Postgres (shard 2) (push) Blocked by required conditions
Server CI / Postgres (shard 3) (push) Blocked by required conditions
Server CI / Merge Postgres Test Results (push) Blocked by required conditions
Server CI / Elasticsearch v8 Compatibility (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 0) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 1) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 2) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 3) (push) Blocked by required conditions
Server CI / Merge Postgres FIPS Test Results (push) Blocked by required conditions
Server CI / Coverage (shard 0) (push) Blocked by required conditions
Server CI / Coverage (shard 1) (push) Blocked by required conditions
Server CI / Coverage (shard 2) (push) Blocked by required conditions
Server CI / Coverage (shard 3) (push) Blocked by required conditions
Server CI / Run mmctl tests (push) Blocked by required conditions
Server CI / Run mmctl tests (FIPS) (push) Blocked by required conditions
Server CI / Build mattermost server app (push) Blocked by required conditions
Tools CI / check-style (mattermost-govet) (push) Waiting to run
Tools CI / Test (mattermost-govet) (push) Waiting to run
Web App CI / check-lint (push) Waiting to run
Web App CI / check-i18n (push) Blocked by required conditions
Web App CI / check-external-links (push) Blocked by required conditions
Web App CI / check-types (push) Blocked by required conditions
Web App CI / test (platform) (push) Blocked by required conditions
Web App CI / test (mattermost-redux) (push) Blocked by required conditions
Web App CI / test (channels shard 1/4) (push) Blocked by required conditions
Web App CI / test (channels shard 2/4) (push) Blocked by required conditions
Web App CI / test (channels shard 3/4) (push) Blocked by required conditions
Web App CI / test (channels shard 4/4) (push) Blocked by required conditions
Web App CI / upload-coverage (push) Blocked by required conditions
Web App CI / build (push) Blocked by required conditions
* MM-68235: Rename user-visible "Custom Profile Attributes" to "User Attributes" Update all English i18n translation values and inline defaultMessage strings to use the current product name "User Attributes" instead of the old "Custom Profile Attributes" / CPA naming. Add naming-history comments to key CPA source files (model, app, api4, admin component) explaining that internal identifiers retain the old naming for backward compatibility with REST APIs, WebSocket events, and the Property System Architecture group name. This helps future developers understand the mapping without needing to track down the rename history. * Fix missed lowercase "custom profile attribute" strings |
||
|
|
8e14c8ed58
|
MM-67505 Add AnalyticsQueryTimeout setting and use when refreshing materialized views (#35906)
* MM-67505 Add AnalyticsQueryTimeout setting and use when refreshing materialized views * Fix last minute i18n change * Disallow 0 values for AnalyticsQueryTimeout * Fix E2E test config * Fix post store tests crashing * Update snapshot and revert accidental changes to it |
||
|
|
a2a896a5de
|
MM-67433: Elasticsearch health monitor (#35747)
* Add HealthCheck to search engine interface The watcher (next commit) needs a way to probe whether ES/OS is still reachable. Each backend does a cluster health request with a 5 s timeout, snapshotting the client under the read lock so the network call doesn't block Stop()/Start(). * Add search engine background watcher The old fire-and-forget ps.Go() calls for ES startup had no retry, no health monitoring, and no backoff. If Start() failed at boot the engine stayed down until the next config change or server restart. Replace with a single watcher goroutine that: - retries Start() with exponential backoff (15 s–5 m) - runs periodic health checks once active - stops the engine after N consecutive failures - reacts immediately to config/license changes - shuts down cleanly via context cancellation * Add tests for search engine watcher Covers retry-then-health transition, exponential backoff capping, disable/enable park-unpark via notify, intermittent vs threshold health failures, and edge cases (Start ok but not active, rapid config changes, failure counter reset). * Address review comments * make i18n-extract * Make Stop() a no-op if already stopped Both Elasticsearch and OpenSearch Stop() methods returned an error when the engine was already stopped. This forced every caller to handle or ignore a non-error condition. Returning nil instead is more idiomatic and simplifies all call sites. * Simplify config listener branches Merge the connectionChanged and startingES/stoppingES branches into one. Since Stop() is now a no-op when already stopped, we can always call Stop() then notify the watcher regardless of which condition triggered the change. * Restore license listener conditional order Keep the original order (license-added first, license-removed second) to reduce diff noise. The conditions are mutually exclusive so the order has no semantic effect. * Log consecutive failures in retry phase Track and log consecutiveFailures when Start() fails or returns nil but the engine is not active, so operators can see how many attempts have been made alongside the backoff duration. * Clarify health check timer reset placement Add a comment explaining why the timer reset lives outside the if/else: both success and below-threshold failure share the same health interval, while the critical-failure path continues before reaching this point. * Remove nested select in disabled-engine path Replace the nested select block with timer.Stop() + continue. The main loop's select already handles ctx.Done() and notify, so stopping the timer is enough to park until one of those fires. * Add context.Context to Start() Accept a context in SearchEngineInterface.Start() and propagate it to all network calls (checkMaxVersion, fetchServerInfo, index template creation). This lets the watcher's cancellable context flow through to the HTTP client, so a stuck Start() call returns promptly on shutdown instead of blocking until TCP timeout. * Simplify startSearchEngineWatcher comment Focus on the actual reason (long-lived, owns its lifecycle) rather than the shutdown-blocking detail, which is less relevant now that Start() accepts a cancellable context. * Make the comment on the goroutine more accurate * Fix log * Revert the branches merge This was causing a race condition in the TestElasticsearchAggregation test. * Own Start/Stop by the engine watcher The config listener simply notifies now, so it's easier to follow the logic of the calls. * Refactor the engine watcher into its own type Simplify the code by: 1. Moving the whole watcher into its own type, so that the PlatformService contains an instance of it, instead of all the locks and channels 2. Splitting the main loop into functions. John Carmack may not like this, but it's way easier to read and follow. * make i18n-extract * Rename notify > reevaluate * Park the watcher if there is no license * Call reevaluate from within requestRestart * Use RequestTimeoutSeconds instead of hardcoded 5s * Use atomic.Int32 in the tests as well * Add defensive code ta watcher exit Stop the engine if it was still running when the watcher exits, having a safety net for scenarios like race conditions between Start and a cancelled context. --------- Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
4d028d557b
|
Support Elasticsearch v9 alongside v8 (#35781) | ||
|
|
c96d215ff1
|
Translations update from Mattermost Weblate (#35966)
* Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/ * Translated using Weblate (Polish) Currently translated at 96.8% (3011 of 3108 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/pl/ * Translated using Weblate (Polish) Currently translated at 98.3% (6981 of 7095 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pl/ --------- Co-authored-by: master7 <marcin.karkosz@rajska.info> |
||
|
|
71ca373de7
|
Generate instead of hard-coding test passwords, enforce new minimum for FIPS, shard CI, fix FIPS builds (#35905)
Some checks are pending
Server CI / Check mmctl docs (push) Blocked by required conditions
Server CI / Postgres with binary parameters (push) Blocked by required conditions
Server CI / Postgres (shard 0) (push) Blocked by required conditions
Server CI / Postgres (shard 1) (push) Blocked by required conditions
Server CI / Postgres (shard 2) (push) Blocked by required conditions
Server CI / Postgres (shard 3) (push) Blocked by required conditions
Server CI / Merge Postgres Test Results (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 0) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 1) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 2) (push) Blocked by required conditions
Server CI / Postgres FIPS (shard 3) (push) Blocked by required conditions
Server CI / Merge Postgres FIPS Test Results (push) Blocked by required conditions
Server CI / Generate Test Coverage (push) Blocked by required conditions
Server CI / Run mmctl tests (push) Blocked by required conditions
Server CI / Run mmctl tests (FIPS) (push) Blocked by required conditions
Server CI / Build mattermost server app (push) Blocked by required conditions
Tools CI / check-style (mattermost-govet) (push) Waiting to run
Tools CI / Test (mattermost-govet) (push) Waiting to run
Web App CI / check-lint (push) Waiting to run
Web App CI / check-i18n (push) Blocked by required conditions
Web App CI / check-external-links (push) Blocked by required conditions
Web App CI / check-types (push) Blocked by required conditions
Web App CI / test (platform) (push) Blocked by required conditions
Web App CI / test (mattermost-redux) (push) Blocked by required conditions
Web App CI / test (channels shard 1/4) (push) Blocked by required conditions
Web App CI / test (channels shard 2/4) (push) Blocked by required conditions
Web App CI / test (channels shard 3/4) (push) Blocked by required conditions
Web App CI / test (channels shard 4/4) (push) Blocked by required conditions
Web App CI / upload-coverage (push) Blocked by required conditions
Web App CI / build (push) Blocked by required conditions
* Replace hardcoded test passwords with model.NewTestPassword() Add model.NewTestPassword() utility that generates 14+ character passwords meeting complexity requirements for FIPS compliance. Replace all short hardcoded test passwords across the test suite with calls to this function. * Enforce FIPS compliance for passwords and HMAC keys FIPS OpenSSL requires HMAC keys to be at least 14 bytes. PBKDF2 uses the password as the HMAC key internally, so short passwords cause PKCS5_PBKDF2_HMAC to fail. - Add FIPSEnabled and PasswordFIPSMinimumLength build-tag constants - Raise the password minimum length floor to 14 when compiled with requirefips, applied in SetDefaults only when unset and validated independently in IsValid - Return ErrMismatchedHashAndPassword for too-short passwords in PBKDF2 CompareHashAndPassword rather than a cryptic OpenSSL error - Validate atmos/camo HMAC key length under FIPS and lengthen test keys accordingly - Adjust password validation tests to use PasswordFIPSMinimumLength so they work under both FIPS and non-FIPS builds * CI: shard FIPS test suite and extract merge template Run FIPS tests on PRs that touch go.mod or have 'fips' in the branch name. Shard FIPS tests across 4 runners matching the normal Postgres suite. Extract the test result merge logic into a reusable workflow template to deduplicate the normal and FIPS merge jobs. * more * Fix email test helper to respect FIPS minimum password length * Fix test helpers to respect FIPS minimum password length * Remove unnecessary "disable strict password requirements" blocks from test helpers * Fix CodeRabbit review comments on PR #35905 - Add server-test-merge-template.yml to server-ci.yml pull_request.paths so changes to the reusable merge workflow trigger Server CI validation - Skip merge-postgres-fips-test-results job when test-postgres-normal-fips was skipped, preventing failures due to missing artifacts - Set guest.Password on returned guest in CreateGuestAndClient helper to keep contract consistent with CreateUserWithClient - Use shared LowercaseLetters/UppercaseLetters/NUMBERS/PasswordFIPSMinimumLength constants in NewTestPassword() to avoid drift if FIPS floor changes https://claude.ai/code/session_01HmE9QkZM3cAoXn2J7XrK2f * Rename FIPS test artifact to match server-ci-report pattern The server-ci-report job searches for artifacts matching "*-test-logs", so rename from postgres-server-test-logs-fips to postgres-server-fips-test-logs to be included in the report. --------- Co-authored-by: Claude <noreply@anthropic.com> |