Commit Graph
3233 Commits
Author SHA1 Message Date
Miguel de la CruzandMiguel de la Cruz 1eb4c62cf9 PSAv2 endpoint improvements (#36782)
* Updates search opts and cursor for searching property fields

* Adds store changes and tests to accomodate the new search options

* Add an index for improving delta query efficiency

* Enhances the get property fields endpoint to support deltas and hierarchical searches

* Adds new fields search endpoint

* Adds since to the property values endpoints

* Adds property value endpoint documentation

* Fix linter

* Address coderabbit comments

* Complete i18n

* Minor improvements

* Consistently apply DWIM semantics around object_type system

* Add support for DM/GMs

* Move auditRec to the top of the searchPropertyFieldsCore method

* Update since mechanics to use greater or equal

* Update API docs

* Updated tests to filter after the semantics changes

---------

Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es>
2026-06-22 17:27:02 +02:00
Matthew BirtchandJesse Hallam a4ecc008fa MM-68232: Remove external Google Fonts from email templates (#36011)
* remove google font dependency from emails

* fix malformed anchor closing tag in cloud_welcome_email.html

---------

Co-authored-by: Jesse Hallam <jesse@mattermost.com>
2026-06-22 14:41:36 +00:00
Alejandro García MontoroandMattermost Build 489d08c0c9 Bring back Level check in testlib.hasMsg (#37121)
* Bring back Level check in testlib.hasMsg

* Fix tests broken by restored level check

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
2026-06-22 07:21:13 +00:00
3972ae0b4e Read pending scheduled posts from master to avoid replica lag gaps (#37104)
* Fix scheduled post job to fetch pending posts from master

The scheduled post job fetches a page of pending posts, deletes the
processed ones, then fetches the next page. Reading the page from a
read replica can return stale data, causing already-processed posts to
reappear on later pages. Read from master to avoid this gap.

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

* Add regression test for scheduled post master read

Verifies GetPendingScheduledPosts reads pending posts from the master
database by pointing the replica at a separate empty database; a read
from the replica would return no rows.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
2026-06-22 12:36:18 +05:30
Alejandro García Montoro ee04f28e87 MM-69311: Add a new ClusterReliableFallbackLength metric (#37122)
* Add a new ClusterReliableFallbackLength metric

This metric tracks the length of the cluster messages that are meant to
be sent via UDP but that result in a UDP datagram larger than the
maximum length allowed.

* make mocks

* Use 8 exponential buckets: from 32KiB to 4MiB
2026-06-19 20:30:49 +02:00
Andre Vasconcelos 0bdd22bdab Bumping prepackaged Gitlab version to 1.13.0 (#37124) 2026-06-19 16:17:44 +03:00
017a7102f8 [MM-69055] Add rank property field type (#36809)
* Add rank property field type and migrate classification field to use it

Introduces a new 'rank' property field type that behaves identically to
'select' across validation, access-control masking, options handling, and
rendering. The classification markings admin panel now creates its
template, system, and channel fields as 'rank' instead of 'select', with
a paired DB migration to flip any existing classification rows.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Enforce rank validation on ranked property field options

For PropertyFieldTypeRank fields, every option must carry a non-negative,
unique rank. Adds the Rank field to CustomProfileAttributesSelectOption
and strips stray Rank values from options on non-rank field types so
they cannot drift into persisted attrs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Materialize rank options in AttributeView and signal field updates to access control

Extend the AttributeView materialized view so rank-typed property values
are exposed as {"name", "rank"} objects, enabling downstream ABAC SQL and
CEL machinery to compare a user's rank against a named option without
baking rank integers into policy expressions.

Add OnPropertyFieldOptionsChanged on the access control service interface
and call it after every property field update so the service can drop any
per-field metadata it caches (such as the rank-by-name map) and invalidate
compiled-policy cache entries that reference the field.

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

* Show rank-and-lower options/values for shared_only rank fields

shared_only masking on a rank field previously required an exact option
match, the same as select/multiselect. For rank fields the intended
semantics are clearance-style: a caller sees every option and every
target value at or below their own rank.

filterSharedOnlyFieldOptions and filterSharedOnlyValue now branch rank
fields to filterSharedOnlyRankFieldOptions / filterSharedOnlyRankValue,
which compare against the caller's rank instead of intersecting option
IDs. Select, multiselect, and scalar (text/date/user) masking are
unchanged. A caller with no value of their own has no rank and sees
nothing; the source plugin still sees everything.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Render rank property field type like select in webapp

Treat the new 'rank' custom profile attribute type as a single-select
everywhere it is editable, and make it fully usable from the system
console:

- user_settings/general: render rank fields as a single-select dropdown
  (resolving option IDs to names) instead of a free-text input.
- system_user_detail: resolve rank option names and render the value
  input as a native single-select.
- user_properties_type_menu: add a selectable "Rank" field type
  (reusing the Select icon) so existing rank fields display correctly
  and admins can create new rank fields from the UI.
- en.json: add the "Rank" type label string.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Make rank cache invalidation cluster-aware and cover field deletion

OnPropertyFieldOptionsChanged previously dropped the access control
service's per-field rank cache only on the node handling the property
field update, so peer nodes kept serving stale name->rank maps until
restart. It was also wired into the update path only, leaving deleted
rank fields cached indefinitely.

Call OnPropertyFieldOptionsChanged from DeletePropertyField so a removed
rank field's cached options are dropped too. The access control service
now broadcasts the invalidation cluster-wide (see companion enterprise
change).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Backfill option ranks when migrating classification fields to rank

The select->rank conversion previously only flipped the field type. On
upgrades the existing classification options carry no rank (master's
select option struct has no rank field, so the UI-sent rank was dropped
on save), which left an invalid rank field: the matview projected null
ranks, shared_only masking hid every value, and the validation hook
rejected any later edit.

Backfill a rank onto each option from its 1-based array position. The
classification UI keeps levels in severity order and rewrites the full
options array on every save, so position is the authoritative ordering;
1-based matches both the UI's `opt.rank ?? (i+1)` fallback and the
presets' ranks, so a configured preset is still recognized after upgrade.
The flip and backfill share one migration (one transaction), so the field
is never observable in the invalid (rank, null-rank) state, and the down
migration strips the rank key to restore the prior select shape.

Only the three known classification fields with a non-empty options array
are touched; the updates match at most three rows and add no meaningful
lock footprint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Add System Console UI for ranked property fields

Implements the admin UI for the ranked property field type across three
System Console surfaces:

- User Attributes: the "Ranked" attribute type, numbered value chips in
  ascending rank order, a per-chip popover (rename / change rank / remove),
  and an "Edit ranking" modal with drag-reorder, arrow steppers, numeric
  rank inputs, auto-assigned next rank, and inline duplicate rejection.
- Membership Policy editor: ranked comparison operators (is exactly,
  is not, is at least, is greater than, is at most, is less than) shown for
  ranked attributes in place of the standard set, with CEL build/parse and
  default-operator wiring.
- User detail: a ranked-value picker rendering options highest-rank-first
  with numbered badges and a checkmark on the assigned value.

Adds Playwright e2e coverage for all three surfaces (plus page-object
helpers) and webapp unit tests for the operator wiring and rank utilities.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Fix enterprise CI: regenerate access-control mocks and add rank i18n key

The enterprise CI lane fails on two pre-existing backend gaps on this
branch:

- go vet (check-style): OnPropertyFieldOptionsChanged was added to
  PolicyAdministrationPointInterface but the generated einterfaces mocks
  were never regenerated, so AccessControlServiceInterface /
  PolicyAdministrationPointInterface mocks no longer satisfy the
  interface (access_control_test.go, access_control_masking_test.go).
  Add the missing method to both mocks.

- Check i18n: enterprise access_control/administration.go references the
  app.pap.rehydrate_rank.app_error key, which was never added to
  server/i18n/en.json. Add it so `make i18n-extract` produces a clean
  diff.

Verified: both affected test packages compile, `make i18n-extract`
yields no diff, and `make i18n-check` passes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Retrigger enterprise CI

Pick up enterprise fix 15914c11 (require.NoError in rank cel_utils tests)
in the combined Enterprise CI/tests lane, which pins the enterprise SHA at
mattermost-side dispatch time.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Retrigger enterprise CI

Pick up enterprise fix 48db14db (golangci-lint findings in rank
access-control code) in the combined Enterprise CI/tests lane.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Clamp shared_only rank values to the caller's rank instead of hiding

For a shared_only rank field, filterSharedOnlyRankValue previously hid a
target's value entirely when the target outranked the caller, returning
nil. That answered "is the target at or below me?" but not the question
the field exists for: "what can we talk about, and at what level?" A
higher-ranked target simply disappeared.

Now the value is clamped to the highest rank the caller shares with the
target — the target's own value when it is at or below the caller's rank,
otherwise the option at the caller's own rank. The caller always learns
their shared ceiling and never sees a rank above their own. Ranks are
unique per field, so the clamp target is unambiguous.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Renumber rank migrations to 194-197 (after property_groups 193)

000193_add_property_groups_schema_version was already on master, so
our rank migrations should follow it: 194-197.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix e2e classification helper field names after rename migration

Migration 000196 renamed channel_classification → classification and
system_classification → classification. Update the e2e helpers to
create and clean up fields with the new canonical names so the frontend
(which looks for 'classification') can find them.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Remove unused i18n key and fix e2e prettier formatting

app.pap.save_policy.advanced_expression_blocked was added to en.json
but never used as an AppError key in Go code — remove it so i18n-check
passes. Also fix prettier formatting in ranked_operators.spec.ts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix 000194 down migration: drop AttributeView before enum rebuild

TestUpAndDownMigrations/Should_be_reversible_for_postgres failed because
the down migration rebuilds the property_field_type enum (Postgres can't
drop an enum value in place), which requires ALTER COLUMN on
PropertyFields.Type. The AttributeView materialized view reads that
column, so Postgres rejects the alter: "cannot alter type of a column
used by a view or rule".

The up path is unaffected because it uses ADD VALUE (no column rewrite);
only the down rebuild trips the dependency. Mirror the canonical
drop-view / alter-column / recreate-view pattern: drop AttributeView,
rebuild the enum, then recreate the no-rank view (the same definition
000197's down restores, which 000177's down later replaces).

Verified end-to-end against PostgreSQL 16: full up-then-down sequence
now passes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Add unit tests for rank validation and field-change signalling

Fills two gaps flagged by PR test analysis:
- access_control_attribute_validation_test.go: rank option validation
  (valid ranks persist; missing/negative/duplicate rank rejected; zero
  rank allowed; non-rank fields strip stray rank values).
- property_field_test.go: UpdatePropertyFields/DeletePropertyField
  signal the access control service via OnPropertyFieldOptionsChanged
  for each affected field, and stay nil-safe when no AC service exists.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Add api4 tests for rank-field permission branching

Fills the API gap flagged by PR test analysis: rank fields participate
in the isOptionsOnly permission branch alongside select/multiselect.
- properties_test.go: an options-only PATCH on a rank field uses the
  narrower manage-options permission (member succeeds), while a
  structural PATCH (name change) requires the full edit-field
  permission (member forbidden, admin succeeds).
- custom_profile_attributes_test.go: an options-only PATCH on a rank
  CPA field routes through the options path and round-trips ranks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Add mmctl unit tests for rank attribute resolution

Fills the mmctl gap flagged by PR test analysis. Both resolution
directions for rank-typed attributes were untested:
- user_attributes_test.go (TestResolveDisplayValue): a stored rank
  option ID resolves to its option name for display; unknown IDs and
  option-less fields fall back to the raw value.
- user_attributes_value_test.go (TestResolveOptionNamesToIDs): setting
  a rank value by option name resolves to the option ID; already-an-ID
  and unknown names pass through.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Refine ranked attribute editing UI

Edit ranking modal:
- Derive ranks from row position so reordering or removing a value always
  keeps them contiguous (1..N) with no gaps or duplicates
- Drop the arrow steppers in favor of drag-only reordering; render the drag
  clone through a body portal so it isn't offset by the modal dialog transform
- Show values as read-only chips (lowest-first) with Lowest/Highest labels
- "Add value" toggles into an inline row (fake handle + borderless field);
  Enter commits the value instead of closing the modal
- Title shows the field name plus "Ranked attribute"

Inline rank values:
- Rank badge fills the left of the chip; add an inline remove (X) that reuses
  react-select's CrossIcon so it matches select fields

Also give the rank attribute type its own SortAscendingIcon and trim a couple
of stale comments.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Fix SCSS property order lint errors

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix e2e rank modal tests to match drag-and-drop UX

The ranked schema modal uses position-based ranks (drag to reorder) — there
are no editable numeric rank inputs per row and no per-row name inputs.  The
two failing tests were written against an earlier design that had those inputs:

- "rejects a duplicate rank inline" expected .ranked-schema-modal__rank-input
  and .ranked-schema-modal__error (neither exists).  Replaced with a test that
  covers the actual duplicate-label guard on the add-value inline input.

- "adds a value via the Edit ranking modal" expected .ranked-schema-modal__name-input
  (doesn't exist) and that save is disabled while the add input is open (it
  isn't — save is disabled only when rows is empty).  Updated to use the real
  add-value flow: click "Add value", fill .ranked-schema-modal__add-input, blur
  to commit.

Also removes the three broken page-object helpers (rankedModalRankInputs,
rankedModalError, rankedModalNameInputs) whose CSS selectors never existed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Enforce positive ranks and backfill them on select-to-rank conversion

Rank values must be positive integers (>= 1), matching the webapp's
isValidRank helper and rankForIndex which both require rank >= 1. The
server was accepting rank = 0 (rank < 0 check), allowing invalid data
that would cause filtering bugs: a caller with a rank-0 option would
see nothing in a shared-only field since no option would satisfy
rank <= 0. Change the guard to rank <= 0 and update the error message
and tests accordingly.

When converting a field from select/multiselect to rank via the type
dropdown, the existing options had no ranks. The server's option
validation then rejected the save (or the UI showed stale local state
appearing valid). Auto-assign contiguous 1-based ranks in array order
at the point of type change so the conversion is immediately valid and
the user does not need to open the rank-ordering modal just to commit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Show an inline error when a ranked option is renamed to a duplicate

Renaming a ranked option to a name already used by another option was
silently ignored: the draft reverted with no feedback, leaving the user
unsure why their edit didn't stick. The add-value flow already warns on
duplicates; the rename path now matches it.

The chip popover surfaces "Values must be unique." beneath the label
input while the typed name collides, and the rename stays blocked so a
duplicate is never committed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Scroll the ranked schema list so the modal footer stays in view

A ranked attribute with many values (e.g. 30) grew the edit modal taller
than the viewport, pushing the Save/Cancel footer off the bottom of the
screen where it couldn't be reached.

Cap the value list at 50vh and scroll it internally, keeping the header
and footer anchored. The list is the droppable's own scroll container, so
react-beautiful-dnd auto-scrolls it while a row is being dragged, and a
small right padding keeps the scrollbar clear of the remove buttons.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Add a shared allow-list for option-bearing property field types

The "is this a select/multiselect/rank field?" check was open-coded as a
three-way type-negation chain in the model, both PSAv2 patch handlers, and
the access-control shared-options filter. Each copy had to be kept in sync
by hand as field types were added.

Introduce an optionFieldTypes allow-list in the model with a
PropertyFieldType.SupportsOptions() helper (mirroring the webapp's
supportsOptions) and route every call site through it, so adding a future
option-bearing type is a one-line change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Guard the rank options filter before its store lookup

filterSharedOnlyRankFieldOptions built the option-rank map and ran the
caller-rank store lookup before checking whether the field had a usable
options array at all. A field with no options (or a malformed attrs blob)
has nothing to filter, yet still paid for a database query.

Move the cheap nil/shape guards to the top so an optionless field returns
immediately, and reuse the extracted options slice in the filter loop
instead of pulling it out of attrs a second time.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Memoize the ranked attribute editors' event handlers

The rank schema modal and the inline rank-values cell recreated every
handler (drag-end, move, remove, confirm, rename, add) on each render.
These are passed down to the modal footer, the drag-and-drop context, and
per-chip popovers, so the fresh identities defeated memoization downstream.

Wrap them in useCallback with explicit dependencies. No behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Add tests for the classification select-to-rank migration

The 000195 conversion is non-trivial: it flips specific (Name, ObjectType)
fields in the access_control group from select to rank, then backfills a
1-based rank onto each option from its array position, guarding against
empty or absent options arrays.

Cover the behavior end to end against a real Postgres schema: the type
flip with position-derived ranks in order, the empty- and absent-options
guards (type flips without fabricating options), the name/object-type/group
mismatches that must stay select, and the down round-trip that strips the
ranks and reverts the type.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Gate the rank property field type behind a feature flag

Add a PropertyFieldRank feature flag (off by default) that gates the
"rank" custom profile attribute type.

The enforcement is a single app-layer gate, rankPropertyFieldGate, shared
by both CreatePropertyField (blocks creating a rank field) and
UpdatePropertyFields (blocks converting an existing field to rank). When
the flag is off it returns app.property_field.rank_disabled.app_error.

The admin console CPA type menu hides the rank option unless the flag is
on, read via useGetFeatureFlagValue.

Existing app/api4 tests that exercise rank fields now enable the flag in
their setup so they continue to pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Scope the rank feature-flag gate to user-object fields

The PropertyFieldRank gate rejected any rank-typed field create/update
when the flag was off. But classification markings now uses the rank
type internally (template/system/channel object types) and ships GA
behind the separate ClassificationMarkings flag, which is on by default
while PropertyFieldRank is off by default. The migration also converts
existing classification fields to rank unconditionally. Together this
broke the classification admin panel in the default configuration: both
creating new classification fields and editing existing (migrated) ones
returned app.property_field.rank_disabled.app_error.

Scope the gate to ObjectType == user, which is the only origin of the
user-facing rank CPA type (createCPAField forces ObjectType=user). Rank
fields on other object types are exempt, so classification keeps working
regardless of the flag while the user-facing CPA rank type stays gated.

Add regression cases proving a non-user (classification-style) rank
field is creatable and convertible-to with the flag off, and enable
PropertyFieldRank in the e2e default config so the user-facing rank
specs pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Update webapp/channels/src/components/admin_console/system_properties/rank_badge.scss

Co-authored-by: abhijit-singh <abhijitsingh0702@gmail.com>

* Enable the rank feature flag in the e2e CI server env

The new rank e2e tests failed in playwright-full because the CI docker
server gets feature flags from MM_FEATUREFLAGS_* env vars (config patches
don't stick without a SplitKey), and PropertyFieldRank was only set in the
local default_config.ts. Add the matching env var so the flag is on in CI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Fix stale migration-number references in classification rank down migrations

The 000195 and 000196 down migrations carried comment references to their
development-era numbers (000191/000192). Correct them to the current numbering
(000195 reverses itself; 000196's down restores distinct names before 000195's
down matches on them). Addresses review feedback from @mgdelacroix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Use PropertyFieldType.SupportsOptions() for option-field type checks

Replace the repeated `Select || Multiselect || Rank` comparisons with the
existing SupportsOptions() helper across the access-control masking, validation,
and option-filtering paths, and in mmctl value resolution. Behavior is unchanged
(SupportsOptions covers exactly those three types). Addresses review feedback
from @mgdelacroix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* webapp: use supportsOptions() helper for option-field type checks

Replace the repeated `type === 'select' || 'multiselect' || 'rank'` checks with
the existing supportsOptions() helper from @mattermost/types/properties, matching
the server-side SupportsOptions() usage. Addresses review feedback from @mgdelacroix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Include rank in extractOptionIDsFromValue error message

The function handles select, multiselect and rank, but the error message only
listed select and multiselect. Addresses review feedback from @mgdelacroix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Validate option rank in CustomProfileAttributesSelectOption.IsValid

When an option carries a rank, enforce that it is a positive integer at the
model layer, mirroring the field-level option validation. Rank stays optional
so select/multiselect options (which carry none) remain valid. Addresses review
feedback from @mgdelacroix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Invalidate access-control cache before broadcasting field update

In UpdatePropertyFields, move the OnPropertyFieldOptionsChanged notification
above the websocket broadcast so a client reacting to the update event never
re-reads stale cached field metadata. This matches the ordering already used in
DeletePropertyField. Addresses review feedback from @mgdelacroix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Assert stored options in options-only patch permission tests

The select/multiselect/rank options-only update tests only checked that the
request succeeded; also assert the returned field's options (id, name, and rank
for the rank case) so the tests actually verify the change was persisted.
Addresses review feedback from @mgdelacroix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Add FeatureFlagPropertyFieldRank to webapp ClientConfig type

The server-side PropertyFieldRank feature flag and user_properties_type_menu.test.tsx
reference FeatureFlagPropertyFieldRank, but it was never added to the webapp
ClientConfig FeatureFlags type, leaving tsc -b red. Add the missing field.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Tighten the ranked option popover spacing

Drop MUI's default 8px MuiList top/bottom padding on the per-option popover and
reduce the label input's bottom padding to 4px, so the popover reads tighter
without jamming the label input against the top edge. Addresses design feedback
from @abhijit-singh.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Add header and footer dividers to the Edit ranking modal

Enable GenericModal's bodyDivider and footerDivider so the ranked schema modal
shows a divider under the header and above the Save/Cancel footer, which also
delineates the scroll area when the value list is long. Addresses design
feedback from @abhijit-singh.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Match ranked 'Add values…' affordance to select/multiselect

The ranked values cell now mirrors the select/multiselect CreatableSelect
cell for consistency across field types:

- Tab commits a pending value (keeping focus in the input for the next
  one) when it's non-empty and not a duplicate, matching the select cell;
  a blank/duplicate input lets Tab move focus away normally.
- The empty-state placeholder uses the shared 'Add values… (required)'
  text and matches react-select's placeholder color (full-opacity
  neutral50) and 10px content inset, so size, color, and left alignment
  line up with the select cell.
- The placeholder is hidden once values exist, mirroring react-select.
- The chips well gets the same hover/focus background tint and text
  cursor as the select control.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Auto-size the ranked 'Add values…' input like react-select

The bare <input> scrolled its overflow in a fixed-width box, while the
select/multiselect cell grows its input and wraps to a new row as you
type. Replicate react-select's sizer technique: wrap the input in an
inline-grid whose hidden ::after mirrors the live text (content:
attr(data-value), white-space: pre, font: inherit) and sizes the grid
column the input fills. The input now grows with its content and, as a
flex child of the values well, wraps to a new row when it no longer fits.

Both the input and the sizer use 'font: inherit' (matching react-select)
so the measured and rendered text line up and the font matches the select
cell. Since the auto-sized input no longer spans the whole cell, a
mousedown handler on the well forwards focus to the input, mirroring
react-select's clickable control.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Fix consistent-type-imports lint errors in Playwright e2e specs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Enable PropertyFieldRank feature flag in rank e2e test setup

The Rank type option in the attribute type menu is gated behind the
PropertyFieldRank feature flag. initSetup resets config, so the flag
must be explicitly re-enabled before the test exercises the UI selector.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Remove broken feature-flag patchConfig from rank e2e setup

setupTest called patchConfig({FeatureFlags: {PropertyFieldRank: 'true'}})
with the string 'true'. PropertyFieldRank is a Go bool, so the server's
patchConfig handler (which does json.Decode(&cfg) into *model.Config before
any filtering) failed to unmarshal the string into a bool and returned 400
"Invalid or missing config in request body." This hard-failed setupTest and
all four specs (CI run 27387310039: 4/4 failed at spec line 36).

The call was both broken and unnecessary:

- Without a SplitKey the config store marks FeatureFlags read-only
  (PlatformService.SetupFeatureFlags -> SetReadOnlyFF(!splitConfigured)), so
  no patchConfig can toggle a flag. The flag is enabled at the server level:
  the MM_FEATUREFLAGS_PROPERTYFIELDRANK env var in CI (added to
  e2e-tests/.ci/server.generate.sh) and the server config locally.
- With the env var in place, all four specs passed at commit 8fca57bf3b; the
  later commit that added this line is what regressed them.

Remove the call and document where the flag actually comes from.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Locate ranked add-value input by class, not placeholder

The "creates a ranked attribute" spec timed out at addRankValuesToLast
waiting for getByPlaceholder('Add value…') on the inline add-value input.

Commit 3c12f30008 ("Match ranked 'Add values…' affordance to
select/multiselect") changed that input in two ways the page object never
caught up with:

- the placeholder text became 'Add values… (required)' (plural, suffixed),
  so 'Add value…' no longer matches; and
- the placeholder now renders only in the empty state
  (placeholder={showPlaceholder ? ... : undefined}), so it disappears after
  the first value is added — a placeholder lookup can never add 3 values.

Locate the input by its stable class .user-property-rank-values__add-input
instead, which is present regardless of options count or placeholder text.

This is the real failure the reverted patchConfig change had misdiagnosed as
a feature-flag issue. The spec passed at 8fca57bf3b because that predates
3c12f30008 (placeholder was then 'Add value…' and always shown).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Empty commit to retrigger CI after enterprise branch update

Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>

* Fix migration test after renumbering 000194 to 000198

During conflict resolution, migration 000194_add_type_id_index_to_access_control_policies
was renumbered to 000198 to maintain sequential ordering after the new rank migrations.
This commit updates the corresponding test file to match:
- Renamed migration_000194_test.go to migration_000198_test.go
- Updated test function name from TestMigration000194 to TestMigration000198
- Updated migration file references to point to 000198 instead of 000194

Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>

* Renumber migrations: restore 000194 from master, move rank migrations to 000196-000199

The 000194_add_type_id_index_to_access_control_policies migration came from master and should keep its original number. Our rank migrations have been renumbered:
- 000194 -> 000196: add_rank_to_property_field_type
- 000195 -> 000197: convert_classification_fields_to_rank
- 000196 -> 000198: rename_classification_linked_fields
- 000197 -> 000199: add_rank_to_attribute_view

This leaves 000195 available for the threadmemberships_cleanup_v2 migration from master.

Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>

* Rename migration test files to match renumbered migrations

After renumbering rank migrations from 000194-000197 to 000196-000199, the test file migration_000195_test.go (which tests the classification->rank conversion) needed to be renamed to migration_000197_test.go to match the new migration number. Updated the test function name and migration file references accordingly.

Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>

* Filter policy field autocomplete to user-type fields only

Add ObjectType: user filter to GetAccessControlFieldsAutocomplete so
the attribute picker in the Membership Policies editor no longer
surfaces template, system, and channel classification-marking fields
as ghost entries alongside real user CPAs.

MM-69366

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

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: abhijit-singh <abhijitsingh0702@gmail.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-06-18 16:43:51 -04:00
cursor[bot] f825da5772 Fix flaky TestUserHasJoinedChannel (#37110)
Automatic Merge
2026-06-18 15:05:13 +02:00
Maria A NunezandCursor e1188034ff Fix migration 184 number collision between master and release-11.8 (#37100)
release-11.8 shipped 000184_add_admin_to_permission_level as its last
migration (v11.8.0 GA). On master the same migration landed as 000189
after 000184 had already been assigned to add_lastused_to_incoming_webhooks,
creating a number collision that would cause the webhooks migration to be
silently skipped when upgrading from 11.8.

Renumber on master to resolve:
- 000189 → 000184 (add_admin_to_permission_level): aligns with the GA number
- 000184 → 000196 (add_lastused_to_incoming_webhooks): fresh unapplied number

Regenerate migrations.list accordingly.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-18 07:10:39 -04:00
Nick Misasi b3e2e8b8e4 Update prepackaged Agents, Boards, and Playbooks plugins for release-11.9 (#37092)
Automatic Merge
2026-06-18 07:35:12 +02:00
Christopher Poile 29025d478d [MM-69343] Add MessagesWillBeConsumedWithContext hook (#37091) 2026-06-17 21:21:46 +00:00
Bill Gardner 7581f5ca60 update Calls to v1.12.0 (#37077) 2026-06-17 15:16:28 -04:00
Ben SchumacherandMattermost Build 99ea984ead Remove unused streaming port 8075 from cluster configuration (#37024)
* Remove legacy streaming port 8075 from Dockerfile EXPOSE

* Remove legacy streaming port 8075 from Dockerfile.fips EXPOSE

* Remove legacy SteamingPort (streaming port) from cluster settings tests

* Remove legacy ports 8064 and 8075 from cluster docker-compose

* Restore original datasource escape sequence in docker-compose

* Revert MM_SQLSETTINGS_DATASOURCE to original & escape sequences

* Restore \\u0026 escape sequences in MM_SQLSETTINGS_DATASOURCE

* Restore \\u0026 escape in MM_SQLSETTINGS_DATASOURCE, keep port cleanup

* Restore \\u0026 escape in MM_SQLSETTINGS_DATASOURCE (match master)

* Restore literal backslash-u0026 in MM_SQLSETTINGS_DATASOURCE

* Restore \\u0026 escape sequences to match master

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
2026-06-17 19:02:58 +02:00
Arya KhochareandMattermost Build ffb86ddcae Mattermost emoji reaction fix (#34903)
* reaction case fix

* emojiName usage

* updated make-emojis script and createEmoji API

* added test coverage

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
2026-06-17 13:38:11 +00:00
Christopher Poile 56291ddd1d [MM-68620] MBE Phase 13: channel-guard enforcement for scheduled posts & drafts (#36961)
* add guards to ScheduledPost* and Draft*; update version availability

* make i18n strings extractable

* PR comments, much simplification
2026-06-16 20:09:39 -04:00
ab7a536435 Tighten thread membership cleanup on team membership changes (#36764)
* Tighten thread membership cleanup on team membership changes

Ensure ThreadMembership rows are cleaned up when a user is removed
from or leaves a team, add a defense-in-depth filter on the thread
retrieval path so memberships referencing channels the user is no
longer a member of are excluded, and add a one-time migration to
clean up stale records on existing deployments.

https://mattermost.atlassian.net/browse/MM-69008

* Centralize per-channel membership removal and tighten tests

Extract the combined channel-member and thread-membership removal into
a shared helper used by both removeUserFromChannel and LeaveTeam, so
future code paths cannot revoke channel access without dropping the
dependent thread state. The channel-leave event is now logged after
the combined removal completes.

Also drop verbose test header comments and rename a test to a
behavior-focused name.

* Backfill channel members in thread storetest setups

The new ChannelMembers predicate on thread read queries filters out
ThreadMembership rows whose user has no ChannelMembers row for the
thread's channel. Several existing storetest setups bypassed the
normal write path and inserted threads/memberships without channel
members. Add the missing channel-member rows so the test data matches
the real-world invariant.

* Retrigger enterprise CI

Pick up enterprise merge e6953d4 (master into
MM-69008-thread-membership-team-leave) in the combined Enterprise
CI/tests lane, which pins the enterprise SHA at mattermost-side
dispatch time.

Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-06-16 14:29:30 -04:00
Pablo Vélez 6a3b21eb8f Mm 68846 masking from visual to canonical walker (#36772)
* MM-68846 - add canonical CEL AST masking walker and model resolver interface

* add canonical masking methods to PAP einterface and update mock

* Migrate app-layer masking to canonical CEL AST walker

* Remove Visual AST masking dead code and obsolete i18n key

* Update simulation masking tests to mock MaskExpressionForCaller

* reject persisted tokens and enforce merge shape match and add corresponding tests

* MM-68900 - abac masking add e2e back

* add missing config values and split the tests

* implement coderabbit feedback

* adjust timeout and disable button logic

* enhance masking logic and tests for access control policies

* refactor masking tests and database setup for improved field deletion handling

* Enhance error handling in TestMergeStoredPolicyExpressions to verify error ID and status code

* Refactor error handling in access control policy methods for clarity

* Refactor masking logic and improve clarity in access control methods

* Allow deny-all 'false' policies by dropping the redundant sentinel check in rejectMaskedTokens; add tests

* Add masking-related error messages and update test descriptions with tags

* Add error handling for uninitialized Policy Administration Point in access control
2026-06-16 17:09:11 +02:00
c48a6df910 Fix flaky TestStartServerPortUnavailable (#37047)
* Fix flaky TestStartServerPortUnavailable

The test blocked a port via localhost:0 and later configured the server
to bind the same address. On dual-stack hosts Start() could still
succeed by binding [::1] while the test listener only held 127.0.0.1.
Configure the blocked address up front and hold both IPv4/IPv6 when
available so the port-unavailable path is exercised deterministically.

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

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

* Remove unused newServer helper after port-unavailable test refactor

The flaky-test fix switched TestStartServerPortUnavailable to
newServerWithConfig, leaving newServer unused and failing check-style.

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

* Remove redundant IPv6 test listener

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com>
2026-06-16 15:35:02 +02:00
Felipe Martin fbb05c584f [MM-69126] Fix custom emoji upload size and GIF frame limits (#36984)
* [MM-69126] Fix custom emoji upload size and GIF frame limits

* Assert 413 status and error ID in oversized emoji test

* Raise max emoji GIF frames to 70

* Enforce emoji GIF frame limit on the direct-write path
2026-06-16 11:52:23 +02:00
d067d35503 Fix flaky TestGetLinkMetadataFromCache (#37050)
* Fix flaky TestGetLinkMetadataFromCache

The link metadata LRU cache is a package-global singleton. Under fully
parallel CI, TestGetLinkMetadataFromCache and TestGetLinkMetadata both
call PurgeLinkCache and assert on cache contents concurrently, so one
test can purge or overwrite entries while another is mid-assertion.

Serialize link-cache test setup and purge operations with a package-level
mutex so parallel tests no longer stomp on each other's cache state.

Tests-only change. Verified with `go test -short -run
'^TestGetLinkMetadataFromCache$' -count=5` locally.

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

* Combine link metadata cache tests into single non-parallel function

Per reviewer feedback, replace the package-level mutex with merging
TestGetLinkMetadataFromCache into TestGetLinkMetadata as a subtest group.
This serializes cache access without a global lock.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
2026-06-15 12:43:40 -03:00
cursor[bot] c4b82e53e4 Fix flaky TestUserHasJoinedChannel (#37052)
Automatic Merge
2026-06-15 16:35:14 +02:00
Jesse Hallam c6f89ed71b Prevent plugins from changing the push notification transport type (#37059)
* Prevent plugins from changing the push notification transport type

* fixup! Prevent plugins from changing the push notification transport type
2026-06-15 14:11:48 +00:00
Alejandro García Montoro c621c9f7e0 MM-69213: Validate Azure storage account names and use httpservice for custom endpoints (#37014)
* Validate Azure storage account name format

For the commercial and government clouds the storage account name
becomes part of the service hostname, so enforce Azure's documented
format (3 to 24 lowercase letters and digits) both in config
validation and when building the service URL. Also treat an empty
AzureCloud as commercial, consistent with the URL builder.

* Route Azure custom endpoints through httpservice

The Azure custom-cloud backend now issues requests through
httpservice's transport rather than the SDK default, so they honor
ServiceSettings.AllowedUntrustedInternalConnections like other
outbound connections. The commercial and government clouds keep the
SDK default transport, which Private Link deployments rely on.

The allowlist value is plumbed into FileBackendSettings, and the
host/IP checks are factored into a shared httpservice helper that
MakeTransport now uses too.
2026-06-15 15:46:42 +02:00
Felipe Martin c45a675553 MM-68976 Preserve PluginSettings.SignaturePublicKeyFiles on config patch endpoint (#36868)
* MM-68976 Preserve PluginSettings.SignaturePublicKeyFiles on config patch endpoint

The full PUT /api/v4/config endpoint silently preserves
PluginSettings.SignaturePublicKeyFiles (added in #13682), but the sparse
PUT /api/v4/config/patch endpoint had no equivalent guard, so a session
with sysconsole_write_plugins could modify the field through it.

Mirror the full update endpoint's behavior by silently preserving the
existing value in patchConfig, and add a regression test equivalent to
the one in TestUpdateConfig.

* MM-68976 Document SignaturePublicKeyFiles as non-modifiable in config API spec

Both the update and patch config endpoints preserve
PluginSettings.SignaturePublicKeyFiles; note this in the OpenAPI
descriptions alongside the existing PluginSettings.EnableUploads note.
2026-06-15 15:09:05 +02:00
0a738be909 Fix S3 MoveFile/CopyFile failing on files larger than 5GiB (#37035)
* Fix S3 MoveFile/CopyFile failing on files larger than 5GiB

S3FileBackend.MoveFile, CopyFile and DecodeFilePathIfNeeded used the
minio-go CopyObject call, which performs a single server-side PUT-copy.
S3 rejects copy sources larger than 5GiB, so any move/copy of a file
over 5GiB fails with:

  The specified copy source is larger than the maximum allowable size
  for a copy source: 5368709120

This breaks, among other things, finalizing an `mmctl import upload` of
an import archive larger than 5GiB on S3-backed installations (the
upload streams fine, then the .tmp -> final rename fails).

Extract the shared copy logic into a copyObject helper that picks the
right S3 operation by source size: CopyObject for sources up to 5GiB,
and ComposeObject (server-side multipart copy via UploadPartCopy) for
larger sources, supporting objects up to 5TiB. Small files keep using a
single CopyObject; only files over 5GiB pay for multipart. The size is
checked explicitly rather than relying on ComposeObject's own single-copy
fast path, which is only taken when the source range Start is -1, a value
its input validation rejects (Start must be non-negative), so it is
unreachable here.

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

* Add unit tests for S3 copyObject copy-vs-compose routing

Extract the copy-routing logic into copyObjectWithClient, which takes a
small s3CopyClient interface (StatObject/CopyObject/ComposeObject) so it
can be tested with a mock. The exported behavior of copyObject (and its
callers MoveFile/CopyFile) is unchanged; *minio.Client satisfies the
interface.

Tests cover the three branches:
- source > 5GiB     -> ComposeObject (multipart copy), not CopyObject
- source <= 5GiB     -> CopyObject, not ComposeObject (incl. the boundary)
- StatObject error  -> propagated, neither copy is attempted

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

* make mocks

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: wiggin77 <wiggin77@warpmail.net>
2026-06-15 08:34:36 -04:00
Ibrahim Serdar Acikgoz f83ca8b9fa Fix Permission Policy tab 403 for channel admins when no policy exists (#36980) 2026-06-15 10:00:13 +02:00
Pablo Vélez d081ae0c9e fix the mocks and the store layer (#37049) 2026-06-13 18:10:23 -04:00
d418653717 Allow syncing any User Attribute field with LDAP/SAML and disable the editable toggle when synced (#37018)
* Allow syncing any CPA field with LDAP/SAML and disable editable toggle when synced

A custom profile attribute field could only be linked to LDAP/SAML sync
when it was user-editable, and the editable toggle stayed enabled for
synced fields. Toggling editable off silently stripped the link on save.

Allow admin-managed fields to be synced (sync and admin-managed are no
longer mutually exclusive on the server) and disable the editable toggle
in the dot menu while a field is synced, since synced values come from
the IdP and are never user-editable.

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

* Add tests for syncable admin-managed CPA fields and disabled editable toggle

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

* Strengthen sync test coverage: combined admin-managed+synced and SAML update path

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

* ci: re-trigger Enterprise CI after transient npm network failure

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

* Address PR feedback: 1 answered, 1 resolved, 0 declined

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-06-12 16:13:41 -07:00
Pablo VélezandMattermost Build 4641761122 MM - 69063 - team abac backend and security gate (#36903)
* MM-69063 - Add team ABAC model and constants foundation

* Add team ABAC store EXISTS, channel Type retrofit, policy count split, and index migration

* Add team ABAC app layer: access gate, hydrators, assign/unassign, cleanup, and  GetTeamMembersToRemove store

* Enforce team membership ABAC on join and hide policy governed teams from  non-qualifying users in the directory

* Add team_ids to access policy assign/unassign, expose per-team policy GET,  and support abac_match_only for not_in_team user listing

* Add team ABAC client methods, websocket handler, per-team System Console policy UI, and hide policy-governed teams from non-qualifying users

* Make team ABAC mode-aware: advisory on public teams, strict on private, and surface governed private teams to qualifying users in directory listings

* Flag-gate team ABAC mutation/read APIs and fix policy-save error handling, member-removal limit, team-id  validation, export, and audit cleanup

* coderabbit feedback; Broadcast team policy enforcement updates on policy create/update and activation, not only on delete

* Update team access control policy schema to allow nullable policies and enhance test cases with channel counts

* Enhance access control policy tests to include team policy search alongside channel policy search

* Add team membership access control feature flag to docker-compose generation

* Implement team access control policy checks and refactor related components

* Audit-log team ABAC policy removal on team archive and delete

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
2026-06-12 23:39:16 +02:00
Maria A NunezandCursor Agent 20c4cc42b2 Seed display names for session attribute fields (#37033)
* Seed display names for session attribute fields

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Extract session attribute display names into named constants

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-06-12 11:45:21 -04:00
Jesse Hallam aad6c8afe8 [MM-69228] Default the CJKSearch feature flag to true (#37032) 2026-06-12 17:03:42 +02:00
2db07164df Fix flaky TestGetMattermostLog (#36927)
* Fix flaky TestGetMattermostLog

ReconfigureLogger can create mattermost.log as soon as file logging is
enabled, racing the test's missing-file assertion. Flush and remove any
auto-created log before that check. Apply the same MM-62438 teardown
pattern to the path-validation subtest (disable file target, flush, then
remove temp dirs) and flush after pointing FileLocation outside the root.

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

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

* Fix govet shadow in TestGetMattermostLog

Reuse the existing err variable when removing the auto-created log file
so golangci-lint passes.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
2026-06-11 16:17:36 -04:00
Jesse Hallam ebf41be704 Stop shared channel sync error spam for deleted remote clusters (#36931)
Filter GetUsersForUser against live remote clusters so orphaned user rows
are no longer synced, and skip not-found remotes in processTask instead of
retrying and logging an error.
2026-06-11 16:12:17 -04:00
Elias NahumandJesse Hallam 93d0e6198d Route Calls pushes through a VoIP token if present (#36726)
* Route Calls pushes through a VoIP token if present

Add VoIP device id support so calls notifications can be delivered as iOS
PushKit pushes, while standard pushes continue using the regular device
token.

* New SessionPropVoIPDeviceId + IsValidVoIPDeviceID validator
* PushNotifyAppleReactNativeVoIP platform constant
* voip_device_id accepted on PUT /users/sessions/device and on login
* DoLogin refactored to accept LoginOptions struct; all four call sites
  (api4/user, channels/web/{saml,magic_link,oauth}) converted
* sendPushNotificationToAllSessions: when SubType=calls, prefer
  Session.Props[voip_device_id]; fall back to standard DeviceId

Tests cover the validator, the device-props endpoint (accept/reject), and
the SubType=calls routing matrix.

* Skip session per Calls plugin "answered_elsewhere" convention

When the Calls plugin fans out a cancel-ring push to a user's other
VoIP devices after they answer a call from one of them, it can't tell
core which session to skip because the public plugin API doesn't expose
a skip-session-id parameter. It encodes the auth-session-id on SenderId
together with Category="answered_elsewhere". Core honors the convention
and clears SenderId on the per-session copy before forwarding so the
session id never reaches the proxy or device. Category is preserved on
the wire so the mobile client can pick CXCallEndedReason.answeredElsewhere.

The matching side lives in mattermost-plugin-calls' push_notifications.go;
both sides cross-reference the constant.

* delay voip token audit until session props are set

* capture voip audit failed case

* Promote VoIP token to a column; dispatch by Transport; harden revocation

* ai feedback review

* feedback review

* feedback review

* fix tests

* update openAPI

* update serialized session model

---------

Co-authored-by: Jesse Hallam <jesse@mattermost.com>
2026-06-11 16:34:53 -03:00
Devin Binnie 80eb49802d Fixes for the session attributes manifest (#37012)
* Fixes for the session attributes manifest

* Test fix

* Fix test
2026-06-11 13:02:27 -04:00
cursor[bot] d5415c0a0d Fix flaky TestPreparePostForClient/files (#36992)
Automatic Merge
2026-06-11 16:59:44 +02:00
Harshil Sharma d562481f82 Added pre-migration infra and a pre-migration for fixing schema ID migration numbers (#36870)
* Added automated fix for https://github.com/mattermost/mattermost/pull/36866

* Added log:

* demoved debugging markers

* Added tests

* Review fixes

* Updated tableExists function to only look in the current schema

* Review fixes

* Broken down a long commne tline into shorter ones

* Added premigration in CLI upgrade command

* Code simplicity

* CI

* CI

* CI
2026-06-11 17:09:43 +05:30
471fd8d1dd Bound document content extraction time and decouple it from uploads (MM-69098) (#36856)
* Bound document content extraction to prevent upload DoS (MM-69098)

Extraction of uploaded Office documents ran with no time or size bound and
acquired a shared worker-pool slot on the request goroutine, so a low-privilege
user could keep cheap-to-upload but expensive-to-extract documents in flight and
degrade file uploads for every user on the server.

- Enforce a configurable per-extraction timeout (FileSettings.ExtractContentTimeout,
  default 10s) and honor the previously-ignored size argument in the document and
  PDF extractors to bound the work performed.
- Run extraction on a dedicated, bounded worker pool with non-blocking submit
  (GoExtraction) so saturating it can never block the request goroutines that
  dispatch uploads; overflow is skipped and backfilled by the ExtractContent job.
- Route the file and resumable-upload extraction dispatch sites through the new
  pool instead of the shared GoBuffered/Go pools.

Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>

* Use require.Fail instead of t.Fatal in extraction pool test

The mattermost-govet tFatal analyzer (run by check-style) forbids t.Fatal
in tests in favor of testify assertions.

Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>

* ci: retrigger workflows (transient container init failure)

Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>

* Use sync.WaitGroup.Go for extraction workers

Satisfies the golangci-lint waitgroupgo/modernize analyzer flagged by
check-style at goroutines.go:58.

Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>

* Fix file close race with detached extraction goroutine

On timeout, extractWithTimeout returns while the converter may still be
reading the input on a detached goroutine. ExtractContentFromFileInfo
previously closed the file via defer as soon as Extract returned, racing
with (and closing the file out from under) that goroutine.

Transfer close ownership to docextractor via ExtractSettings.ReaderCloser:
the reader is now closed only after extraction actually finishes - in the
detached goroutine on the timeout path, or after Extract returns on the
synchronous path. ExtractContentFromFileInfo no longer closes the file
itself.

Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>

* Document that extraction timeout bounds wait time, not CPU

Clarify in ExtractSettings.Timeout and extractWithTimeout that on timeout
the docconv converter keeps running on a detached goroutine (docconv is
not context-aware), so the timeout bounds how long an extraction occupies
a worker slot, not the CPU it consumes. MaxFileSize is the primary bound
on per-extraction work. Note load-shedding as a possible future
improvement.

Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>

* Add tests for PDF size cap and ExtractContentTimeout validation

- TestPdfMaxFileSize exercises the LimitedReaderWithError branch in
  pdf.go: a tight MaxFileSize errors out before extraction, while zero
  and generous limits extract normally.
- TestFileSettingsExtractContentTimeout asserts ExtractContentTimeout=-1
  fails IsValid while 0 (disabled) and 10 (default) pass.

Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>

* Harden extraction shutdown, panic safety, and timeout input validation

- stopExtractionWorkers now drains the queue after signaling stop so a
  worker cannot dequeue and run buffered tasks during shutdown.
- The detached extraction goroutine recovers panics and converts them to
  errors, so a panic in an extractor cannot crash the server.
- Admin console enforces a client-side minimum of 0 for
  ExtractContentTimeout (validators.minValue).
- Test hardening: bounded wait for the detached extractor to start, plus
  a test that a panic is surfaced as an error.

Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>

* ci: retrigger workflows (transient Docker Hub image pull timeout)

Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>

* Correct misleading extraction-skip logs and comments

The queue-full warning logs and GoExtraction doc comments claimed skipped
content would be backfilled by a 'periodic ExtractContent job'. That job is
registered with a nil scheduler, so it never runs automatically -- it only
runs when an admin manually triggers a content extraction job (mmctl extract).

Reword the logs and comments to state that skipped files stay unsearchable
until an admin runs a content extraction job, so we don't imply automatic
recovery that doesn't exist.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 23:12:51 +00:00
3535473d96 Fix flaky TestBasicAPIPlugins/test_send_mail_plugin (#36834)
The send-mail plugin test calls RetryInbucket only when GetMailBox
returns an error. When mail delivery is slow, Inbucket returns an empty
mailbox with no error, so the hook fails immediately instead of waiting.
Wrap the hook invocation in require.Eventually so the test retries until
SendMail delivery and mailbox assertions succeed under CI load.

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

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
2026-06-10 15:40:07 -04:00
99dd99b8e6 Fix flaky TestThreadStore Get unread reply counts for thread (#36926)
The MM-41797 timestamp-spacing fix used +1000ms offsets for replies,
which could place their CreateAt after MaintainMembership's LastViewed
(now). That made the first unread-count assertion expect 0 but observe 2.

Anchor thread posts in the past and use millisecond offsets so replies
stay strictly after the root while remaining before LastViewed.

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

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
2026-06-10 15:39:50 -04:00
Jesse HallamandAlejandro García Montoro 8a267c8aba MM-69104: Elide plugin_statuses_changed payload (#36966)
* MM-69104: signal plugin_statuses_changed without a payload, refetch on demand

The plugin_statuses_changed websocket event broadcast the entire cluster-wide
plugin status slice over the best-effort (UDP) cluster transport. With enough
installed plugins the serialized payload exceeded the gossip datagram size and
was silently dropped with EMSGSIZE ("message too long"), leaving admin clients
on peer nodes with stale plugin state during plugin reinstall/restart churn.

The event is admin-only and only feeds the System Console plugin views, so
rather than push the whole slice, publish it as a payload-free signal sent
reliably over the cluster. The Plugin Management and Marketplace views subscribe
via the useWebSocket hook and debounce a refetch of a full, cluster-wide status
from the existing REST endpoint, pulling the data on demand only while a relevant
view is mounted.

* MM-69104: keep plugin_statuses as an empty array for client compatibility

Some clients call array methods on plugin_statuses (e.g. the Boards/Focalboard
plugin does data.plugin_statuses.find(...)). Omitting the field entirely makes that
throw, so send a non-nil empty slice that serializes as [] rather than null. The
payload stays effectively empty, so the cluster-broadcast size fix is preserved.

* fixup! MM-69104: signal plugin_statuses_changed without a payload, refetch on demand

* Apply suggestions from code review

Co-authored-by: Alejandro García Montoro <alejandro.garciamontoro@gmail.com>

* fixup! MM-69104: signal plugin_statuses_changed without a payload, refetch on demand

use shared useDebounce hook in usePluginStatusesSync

* fixup! MM-69104: signal plugin_statuses_changed without a payload, refetch on demand

trim docstring

* fixup! MM-69104: signal plugin_statuses_changed without a payload, refetch on demand

return pluginStatuses from hook

* fixup! MM-69104: signal plugin_statuses_changed without a payload, refetch on demand

cover hook return value

---------

Co-authored-by: Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
2026-06-10 13:07:53 -03:00
cursor[bot] 58022aad54 Fix flaky TestUserHasJoinedChannel (#36985)
Automatic Merge
2026-06-10 14:29:44 +02:00
Alejandro García Montoro 2e3c0ff278 MM-69175: Fix broken CI steps (#36989)
* Fix broken migration/codegen CI self-checks

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

* Regenerate stale migrations.list

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

* make mocks

* make gen-serialized

* make mmctl-docs
2026-06-10 13:54:10 +02:00
de0779d1bf Update latest minor version to 11.9.0 (#36976)
* Update latest minor version to 11.9.0

* Update update-versions script to include components package

* Update components package and its dependencies to 11.9.0

---------

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

* PR feedback

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

* Fix types

* Adjust the test to test the license first

* PR feedback

* Coderabbit feedback

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

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

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

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

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

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

* MM-68830: address review feedback

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

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

* MM-68830: add tests for Role.Clone

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

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

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

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

* Additional fix

* PR feedback
2026-06-04 13:22:59 +00:00