mirror of
https://github.com/hanzoai/team.git
synced 2026-09-30 01:57:35 +00:00
master
236
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8feb24ea1f |
One name for this fork, and it is ours
The server module named github.com/hanzoai/team/server/v8 — a path that resolves to the retired workspace's repository, not this one — while server/public still published github.com/mattermost/mattermost/server/public. Both are github.com/hanzoteam/server/... now, which is where this code actually lives. That second half is what made the binary unbootable. Upstream's public path stayed claimed by upstream, mattermost-plugin-ai imported it, and the build linked public/model twice; public/plugin's gob.Register then ran twice under one name and panicked at init, before main. Reverting the rename traded the brand for a working binary. Rebranding the plugin alongside it needs no such trade: the agents fork is github.com/hanzoteam/agents/v2 against our public module, so no module named mattermost/mattermost is in the graph at all. Measured: `go list -deps ./cmd/mattermost` names exactly one public/model, and it is ours. |
||
|
|
8e84c8d5d6 |
The plugin API keeps the import path plugins import
Renaming server/public to github.com/hanzoai/team/server/public left the old
path unclaimed, and mattermost-plugin-ai still imports it — so the binary
linked public/model TWICE, ours and upstream's, and public/plugin's
gob.Register calls ran twice under one name:
panic: gob: registering duplicate types for "[]*model.SlackAttachment":
[]*model.MessageAttachment != []*model.SlackAttachment
at init, before main. The server has not been able to start since that rename
landed, which is why the deployment is still answering from an older build.
server/v8 keeps its new name; nothing outside this repo imports it. public
does not, because its path is not a brand — it is the plugin API, and every
plugin written against Mattermost imports it by that name. Our own agents
fork is proof: it carries a replace pointing that exact path back here.
Reverting the public half is what makes the two copies one again. Measured
after: `go list -deps ./cmd/mattermost` names exactly one public/model, the
binary starts, and it serves.
|
||
|
|
316dcf770f |
The module is ours, so its import path says so
Every package in this fork announced itself as github.com/mattermost/mattermost — in 4,553 import lines, in both go.mod module declarations, and in the workspace. A fork whose own import path names another company is one nobody can read as theirs, and it is the first thing anyone sees at the top of every file. server/v8 and server/public are now github.com/hanzoai/team/server/…, rewritten mechanically and verified: `go build ./...` is clean for both modules and `go vet` passes. Three things are deliberately UNTOUCHED, because they are other people's names rather than our branding: mattermost-plugin-ai and mattermost-plugin-mscalendar are real dependencies we consume, github.com/mattermost/msgpack is a real dependency, and the github.com/mattermost/mattermost/pull/… links in comments point at real upstream discussions that renaming would turn into dead links. server/public needs a `replace` to resolve locally now: the require still names a version, and no proxy serves our path. The workspace alone does not satisfy a require, which is exactly how this failed the first time. Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
c85589eb27 |
serve the API at /v1/workspace
The base moves off /v1/team for two reasons that point the same way. It collided: /v1/team on hanzo.team is a carve-out routed to the older cloud service, so the moment this image shipped its own API would have been answered by something else -- a 404 on our own endpoints, on the host we serve from. And workspace is the better noun. It is what the product calls itself in its own words, and it does not read as though the API belongs to one team among several. No /api/ anywhere in a path we own. Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
c4c2c619ab |
Serve the HTTP API from /v1/team instead of /api/v4
The product namespace is team, and /api/ is not used as a path prefix here — API hosts carry the api.* name and paths carry the version. This moves the one constant every route mounts on, so the public router, the local router and the websocket endpoint all follow it. The two 404 catch-alls had the old prefix written out by hand, which meant the constant was not actually the single source of truth; they now derive from it, as do the emoji, image-proxy, remote-cluster and self-ping URLs that were each carrying their own copy of the literal. The remote-cluster path constants gain a leading slash from the shared constant, so the two call sites that joined them with an explicit separator would have produced a double slash and are adjusted. APIURLSuffixV4 was a bare alias of the value and APIURLSuffixV5 mounted a subrouter that no route ever registered on, so both are gone rather than carried forward under names that no longer describe anything. APIURLSuffixV1 is not ours at all — it addresses the external push notification service — so it keeps its value and is renamed PushURLSuffix to stop it reading as a version of this API. Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
5979de1824 |
sign in through Hanzo IAM, and place people by their org
The generic OAuth2 slot upstream calls GitLab is not GitLab-shaped: it is
scope, auth endpoint, token endpoint, user endpoint. Here it is Hanzo
IAM, so it is named that and defaults to hanzo.id's advertised endpoints.
An operator supplies a client id and secret; nothing else.
Three things had to move for that to actually work:
- The client config only emitted the SSO props under a license, so the
sign-in button never rendered without one. Hanzo IAM is this
server's identity provider, not a licensed add-on, so its props are
unconditional.
- getSSOProvider rerouted any openid-scoped service to an OpenID
provider this build does not register, and IAM's scope is
"openid email profile". One service, one provider.
- The provider reads sub as the identity. Email and username move;
sub does not.
Tenancy rides on the owner claim, which IAM treats as authoritative:
each org gets an invite-only team named for it, created on the first
sign-in from that org and joined on every one after. The claim is read
fresh each time, so someone moved between orgs in IAM lands in the right
team on their next login. A profile field cannot influence this -- only
the claim can.
Tests cover the claim mapping and the tenant boundary, including that a
user from one org does not land in another's team. The GitLab provider's
tests are gone with the provider; these replace them.
Co-authored-by: Hanzo Dev <[email protected]>
|
||
|
|
53c27715ff |
remove the licensing commerce: seat cap, cloud billing, trial requests
An unlicensed server hard-capped at 250 users (maxUsersHardLimit, no grace period) and blocked user creation and reactivation at that line. That is a commercial seat limit, not a safety limit, and a standalone deploy cannot carry it. GetServerLimits and its two enforcement sites in user.go are gone. Also gone: the cloud subsystem that talked to customers.mattermost.com and portal.internal.prod.cloud.mattermost.com -- billing, subscriptions, invoices, trial license requests, daily seat reporting, IP filtering, and the post/file history caps that expressed plan tiers. Two plugin API methods stay as stubs. GetCloudLimits and RequestTrialLicense are declared in plugin.API, and plugin binaries are compiled against those signatures; removing them from the interface would break a plugin that references either. They report no limits and no trial. The post and file accessibility filters stay. They are generic "hide everything before time T" machinery that reads a stored timestamp, and with nothing writing that timestamp they return at their first check. Rewriting 25 read paths to delete an unreachable branch buys nothing. Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
109a1c90f7 |
remove the source-available enterprise tree
server/enterprise/ ships under the Mattermost Source Available License, which permits production use only under an Enterprise subscription. A fork that runs standalone cannot carry it, so it goes -- along with the build plumbing that existed to find it: the enterprise/sourceavailable build tags, BUILD_ENTERPRISE_READY and its dead ifeq branches, the EE/Elasticsearch/OpenSearch test targets, and the ldap mock generator. What is left is the AGPL core, which upstream already calls the team build (BUILD_TYPE_NAME = team). Prometheus metrics, Elasticsearch and compliance export went with the tree; database search covers search. Prepackaged plugins narrow to the four we actually serve: agents, calls, playbooks, boards. Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
aa3f1cf9f7 |
fix(mmctl): lengthen sampledata passwords to meet FIPS minimum (#37867)
* fix(mmctl): lengthen sampledata passwords to meet FIPS minimum Zero-pad user index to 3 digits in guest and regular-user password format strings so all generated passwords are >= 14 characters, matching PasswordFIPSMinimumLength. Update inject-test-data login hint and add a regression test. Co-authored-by: Julien Tant <[email protected]> * Simplify sampledata password length test comment Co-authored-by: Julien Tant <[email protected]> * Use subtests in sampledata password length coverage So a single require failure reports only that case instead of aborting the rest of the matrix. Co-authored-by: Julien Tant <[email protected]> * Name default-user subtests as user for clarity Co-authored-by: Julien Tant <[email protected]> * Run sampledata password length subtests in parallel Co-authored-by: Julien Tant <[email protected]> * Empty commit to re-trigger CI --------- Co-authored-by: Julien Tant <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
71447391a2 |
[MM-69601] mmctl: support file attachments in post create (#37310)
Co-authored-by: mattermost-code <[email protected]> Co-authored-by: Doug Lauder <[email protected]> Co-authored-by: Mattermost Build <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
32d6add42f |
[MM-62445] Allow changing a team's name (slug) via mmctl team rename (#37169)
* Allow renaming a team's name (slug) via the team update API The team update API path is sanitized and intentionally ignores the Name field, so the previously orphaned RenameTeam app method (which no longer persisted the Name change) is now wired into the updateTeam handler and fixed to persist the new slug and emit a team update event. Co-authored-by: mattermost-code <[email protected]> * Add --name flag to mmctl team rename Allow changing a team's name (the URL slug) via mmctl team rename. The command now accepts --name and/or --display-name, and at least one must be provided. Co-authored-by: mattermost-code <[email protected]> * Polish team rename error message and fix err shadow Co-authored-by: mattermost-code <[email protected]> * Strengthen team rename tests Add regression coverage that the rename path cannot leak non-renamable fields (email/type), that invalid and duplicate renames leave the slug unchanged, and a unit case for renaming name and display name together. Co-authored-by: mattermost-code <[email protected]> * Address PR feedback: 1 answered, 1 resolved, 0 declined * Remove unused App.RenameTeam method The rename path now goes through App.UpdateTeam (via the team update API and mmctl), leaving App.RenameTeam orphaned. Remove it and update the name-occupied error source label to UpdateTeam. * Return non-not-found errors from team name lookup Only treat store.ErrNotFound from GetByName as an available slug; any other lookup failure is now returned instead of silently allowing the rename to proceed. Addresses CodeRabbit review feedback on the team update path. * Remove low-value TestUpdateTeamNameLookupError per review feedback --------- Co-authored-by: mattermost-code <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
c245713824 |
Add --active flag to mmctl user list (#37560)
Mirror the existing --inactive filter so admins can list only active users. Reject using --active and --inactive together, and update unit, e2e, and docs coverage. Co-authored-by: Sven Hüster <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
d9a3bfb51a |
MM-69751 Replace concurrent React user setting with feature flag and add to root.html (#37422)
* MM-69751 Replace concurrent React user setting with feature flag and add to root.html * Address Coderabbit feedback |
||
|
|
ae56750319 |
[MM-69561] Add ability to rotate (regenerate) Personal Access Tokens (#37295)
* MM-69561: Add ability to rotate (regenerate) Personal Access Tokens - Add UpdateTokenRotate to UserAccessTokenStore interface and implement in sqlstore: deletes sessions on the old secret, then updates the token row with the new secret and expiry in one transaction - Regenerate store retrylayer, timerlayer, and mocks - Add RotateUserAccessToken app method: validates expiry (bot-exempt), captures old session for cache eviction, generates new secret, and sends a notification email - Register POST /api/v4/users/tokens/rotate handler with full permission checks (create_user_access_token + edit_other_users + manage_system for sysadmin targets); rejects OAuth sessions and disabled tokens - Add RotateUserAccessToken to the Go client (client4.go) - Add storetest covering secret rotation and old-session cleanup - Add API4 tests: happy path, permission denials, OAuth rejection, and max-lifetime enforcement * MM-69561: Add dedicated rotate email and i18n strings - Add SendUserAccessTokenRotatedEmail (subject/body distinct from the 'added' email so users aren't confused by a rotation event) - Add SendUserAccessTokenRotatedEmail to ServiceInterface + mock - Add en.json strings for the rotate email and the two new error ids (rotate.app_error, disabled_token.app_error) - Switch RotateUserAccessToken to call SendUserAccessTokenRotatedEmail * MM-69561: Add mmctl token rotate subcommand - Add RotateUserAccessToken to the mmctl Client interface and mock - Add 'mmctl token rotate <token-id> [--expires-in <duration>]' command reusing the existing resolveTokenExpiry/parseExpiresIn helpers from 'generate'; prints the new secret once on success - Add unit tests: happy path, --expires-in passed through, server error, invalid --expires-in * MM-69561: Regenerate mmctl docs for token rotate * MM-69561: Add API docs for POST /users/tokens/rotate * MM-69561: Fix i18n string ordering after extract * MM-69561: Add missing API4 test cases for token rotate Cover the three untested access-control branches flagged by the test analysis bot: - Rotating a disabled token returns 400 - Non-system-admin rotating a sysadmin's token returns 403 - Rotating a remote user's token returns 403 * MM-69561: Address review comments - Fix handler authorization order: check SessionHasPermissionToUserOrBot and manage_system before IsRemote/IsActive to avoid leaking token state to unauthorized callers; matches revokeUserAccessToken/disableUserAccessToken - Fix API docs minimum server version: 10.8 -> 10.10 * MM-69561: Restore i18n strings accidentally deleted by extract The earlier i18n-extract run stripped ~164 unrelated translation keys (mostly enterprise-only strings like app.pap.* and api.ldap.*) because the enterprise codebase isn't present in this checkout, so the extractor treated them as unused. Restore them while keeping the 5 new keys added for token rotation. * comment * [MM-69561] Add webapp Regenerate option for Personal Access Tokens Adds a "Regenerate" link to Account Settings > Security > Personal Access Tokens that calls the POST /users/tokens/rotate endpoint added in the server-side rotate PAT work. Regenerating shows a confirmation modal naming the token, then reveals the new secret via the existing one-time-copy flow used for token creation. - webapp Client4.rotateUserAccessToken - mattermost-redux rotateUserAccessToken action - Regenerate link/confirm modal/reveal flow in user_access_token_section - i18n strings - Playwright e2e coverage * Fix regenerate PAT e2e test: confirm modal is not nested in the Profile dialog ConfirmModal renders via react-bootstrap's Modal, which portals to document.body as a sibling of the Profile dialog rather than a descendant, so it must be located via #confirmModal on the page instead of scoped to the Profile dialog locator. * Let users pick a new expiry when regenerating a Personal Access Token Previously, regenerating a token always called rotateUserAccessToken with no expiresAt, so the rotated secret never expired even if the original token did. The Regenerate confirmation modal now includes the same expiry picker used by token creation (extracted into a shared renderExpiryPicker helper), enforces MaximumPersonalAccessTokenLifetimeDays the same way, and disables the confirm button until a valid expiry is selected. * Scope the red background in the Regenerate modal to the warning text only The confirmation question, expiry picker, and its hints were sitting inside the same alert-danger box as the warning, making the whole modal read as an error. Only the warning paragraph keeps the red background now. * Move the regenerate confirmation question below the expiry picker * Align rotate-token wording with UI: use 'regenerate/regenerated' * [MM-69561] rename pat_expiry_notify job to notify_expiring_access_tokens Unifies naming with the sibling cleanup_expired_access_tokens job and fixes the "expiry" vs "expiring" ambiguity: this job warns about tokens approaching expiry, not ones that have already expired. Safe to rename outright since the job hasn't shipped yet. * [MM-69561] remove dead session lookup from EnableUserAccessToken The GetSessionContext call and its result were never used: both branches returned nil regardless. Leftover from mirroring DisableUserAccessToken's shape, which does use its session (to revoke it) unlike Enable. * [MM-69561] rename remaining PAT identifiers to match AccessToken convention The job-level rename (pat_expiry_notify -> notify_expiring_access_tokens) left the app-layer function and its helpers using the old PAT/ PersonalAccessToken naming. Rename them to match: - NotifyPersonalAccessTokensExpiring -> NotifyExpiringAccessTokens - patExpiryBucket -> accessTokenExpiryBucket - sendPATExpiryNotification -> sendAccessTokenExpiryNotification - patExpiryNotifyBatchLimit -> expiringAccessTokenBatchLimit - patExpiryThresholds -> expiringAccessTokenThresholds - maxPersonalAccessTokenExpiry -> maxUserAccessTokenExpiry Also reword the package doc on notify_expiring_access_tokens, which no longer needs to explain a PAT/UserAccessToken naming split now that the app-layer method matches the job name. Pure rename, no behavior change. --------- Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
d908576206 |
mmctl: list and optionally auto-add missing team members on channel move (#37199)
* mmctl: list and optionally auto-add channel members missing from target team on channel move When moving a channel via 'mmctl channel move', the server rejects the move if any channel member is not part of the destination team, returning only a generic error. This adds client-side detection that lists the specific users that need to be added, and a new --auto-add-users flag that adds those users to the destination team before moving the channel. Co-authored-by: mattermost-code <[email protected]> * mmctl: add unit coverage for --force bypass and member-fetch error paths on channel move Co-authored-by: mattermost-code <[email protected]> * mmctl: use membership APIs for channel move preflight Replace GetUsersInChannel/GetUsersInTeam with GetChannelMembers and GetTeamMembersByIds so the move preflight matches server MoveChannel membership checks, including deactivated channel members. Co-authored-by: mattermost-code <[email protected]> * mmctl: fix min() lint and expose GetTeamMembersByIds in local API - Use built-in min() for team member ID batching in channel move preflight - Register getTeamMembersByIds on local API so mmctl local-mode E2E tests pass Co-authored-by: mattermost-code <[email protected]> * mmctl: fix unusedwrite lint in channel move unit tests Co-authored-by: mattermost-code <[email protected]> --------- Co-authored-by: mattermost-code <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
41022c9202 |
[MM-69599] Add mmctl channel users list command to list channel members (#37311)
* [MM-69599] Add `mmctl channel users list` command Add a `list` subcommand under `channel users` that prints each channel member's id, username, email and roles, backed by Client4 GetChannelMembers (paginated) and GetUsersByIds. Mirrors the --page, --per-page and --all pagination flags used by other list commands. Co-authored-by: mattermost-code <[email protected]> * [MM-69599] Expand channel users list test coverage Add unit coverage for custom --page/--per-page plumbing, multi-page --all accumulation, the --all fetch error path, members whose user details are missing from GetUsersByIds, and plain-format template rendering. Strengthen the e2e --all case to assert the listed member's username, email and roles. Co-authored-by: mattermost-code <[email protected]> * Honor --per-page as chunk size when listing all channel users When --all is set, pass the --per-page flag value to getPages instead of hardcoding DefaultPageSize. This matches user list and job list behavior where --per-page controls pagination chunk size and only --page is ignored. Co-authored-by: mattermost-code <[email protected]> --------- Co-authored-by: mattermost-code <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
eb0e00db97 |
Add job create, show, and cancel commands to mmctl (#37280)
* Add create, show, and cancel subcommands to mmctl job The `mmctl job` command previously only supported `list` and `update`. This adds three subcommands backed by existing client methods: - `job create [type] [--data k=v]` triggers a new job (CreateJob) - `job show [job]` displays a single job (GetJob) - `job cancel [job]` requests cancellation via the dedicated cancel endpoint (CancelJob), which uses the create-job permission and emits its own audit event, unlike `update [job] cancel_requested` Also fixes the leftover "import jobs" wording in the `list` flag help and regenerates the mmctl docs. * Exercise --data flag parsing in mmctl job create test The create-job test passed the Data map as the flag's default value, so GetStringToString returned the default without ever exercising pflag's stringToString parser. Declare the flag empty and Set the value explicitly so the test validates the real parse path. * Don't gate mmctl job create on model.AllJobTypes model.AllJobTypes (which IsValidJobType checks against) is a curated subset that omits creatable job types such as access_control_sync, so validating the type client-side wrongly rejected jobs the server can schedule. Drop the client-side check and let the server validate the type per its create-job permissions; it returns a clear error for anything unschedulable. Tab-completion still suggests AllJobTypes as an advisory hint. * Remove explanatory AllJobTypes comments from mmctl job * Add e2e tests for mmctl job create, show, and cancel Cover the no-permission, not-found, and success paths for the new generic job subcommands against a running server, mirroring the existing export job e2e tests. --------- Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
a444cc3c29 |
[MM-69598] Add mmctl user status command to get and set a user's status (#37312)
* Add mmctl user status get/set commands Co-authored-by: mattermost-code <[email protected]> * Register local-mode status routes and add mmctl status tests Expose GET/PUT /users/{user_id}/status on the local socket API so mmctl --local can read and set statuses, and add unit + e2e coverage for the new user status commands. Co-authored-by: mattermost-code <[email protected]> * Improve mmctl status tests and validate dnd-end-time earlier Validate --dnd-end-time before resolving the target user, add command wiring/flag coverage, a GetMe failure case, and stronger e2e assertions from the test-quality review. Co-authored-by: mattermost-code <[email protected]> * Regenerate mmctl docs for user status command Co-authored-by: mattermost-code <[email protected]> * Accept RFC3339 UTC timestamps for mmctl status --dnd-end-time Use time.RFC3339 parsing so --dnd-end-time accepts Z-suffixed UTC timestamps in addition to numeric offsets, per CodeRabbit review feedback. Co-authored-by: mattermost-code <[email protected]> * Show @username in mmctl user status output --------- Co-authored-by: mattermost-code <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
59f0cdb3d4 |
[MM-69596] Show user roles in mmctl user search plain-text output (#37309)
Co-authored-by: mattermost-code <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
fd8c25db20 |
[MM-69600] Add --show-ids flag to mmctl channel list (#37313)
* Add --show-ids flag to mmctl channel list Mirror post list --show-ids so the plain-text channel list output includes channel IDs. Co-authored-by: mattermost-code <[email protected]> * Add unit tests for mmctl channel list --show-ids Co-authored-by: mattermost-code <[email protected]> * Assert show-ids flag registration in channel list test Co-authored-by: mattermost-code <[email protected]> * chore: re-trigger CodeRabbit review after rate limit Co-authored-by: mattermost-code <[email protected]> --------- Co-authored-by: mattermost-code <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
7e0307f65a | Fix flaky mmctl plugin e2e tests by using local HTTP server (#37211) | ||
|
|
fb7c9b9d7a |
Add direct message support to mmctl post create (#37159)
* Add direct message support to mmctl post create Co-authored-by: mattermost-code <[email protected]> * Regenerate mmctl docs for post create direct messages Co-authored-by: mattermost-code <[email protected]> * Strengthen mmctl post create direct message test coverage Co-authored-by: mattermost-code <[email protected]> * Document why local mmctl client cannot send direct messages Add a test comment explaining that local mode lacks the post-create, direct-channel, and current-user API endpoints required by post create. Co-authored-by: mattermost-code <[email protected]> * Add local mode API support for mmctl post create Implement local mode handlers for POST /posts and POST /channels/direct, and add --user flag to mmctl post create for specifying the acting user in local mode instead of relying on GET /users/me. Co-authored-by: mattermost-code <[email protected]> * Address CodeRabbit feedback and regenerate mmctl docs - Defer WriteHeader in localCreatePost until after burn-on-read handling - Preserve prior viper local state in post create tests - Regenerate mmctl docs for --user flag Co-authored-by: mattermost-code <[email protected]> * Address PR feedback: 2 answered, 3 resolved, 0 declined * Reset mmctl unit test config state * Address PR feedback: 3 answered, 1 resolved, 0 declined * Address PR feedback: 1 answered, 1 resolved, 0 declined * Retrigger CI after transient Enterprise FIPS proxy.golang.org failure Co-authored-by: mattermost-code <[email protected]> * Merge local-mode API tests into their parent test functions Fold TestLocalCreateDirectChannel into TestCreateDirectChannel and TestLocalCreatePost into TestCreatePost as local-mode subtests, per reviewer feedback to consolidate the local-mode coverage with the existing create tests. * Revert local mode support for mmctl post create Reverts 20a2b1c per reviewer feedback: remove the local-mode POST /posts and POST /channels/direct API handlers, the --user flag, and the associated audit events. mmctl post create now errors out when run with --local instead of attempting to create posts in local mode. * Retrigger CI after transient Enterprise FIPS npm cache failure The Enterprise CI/tests failure on 8d04b5c was caused by an npm EEXIST cache error during webapp node_modules install — unrelated to this PR's mmctl changes. Co-authored-by: mattermost-code <[email protected]> --------- Co-authored-by: mattermost-code <[email protected]> Co-authored-by: Mattermost Build <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
439a9c1825 |
Move mmctl user deleteall to mmctl system nuke users (#37197)
* Move mmctl user deleteall to system nuke users Relocate the destructive PermanentDeleteAllUsers command out of the user namespace into a new system nuke subcommand to reduce the risk of accidental invocation. Regenerate mmctl docs and move associated unit and e2e tests accordingly. Co-authored-by: mattermost-code <[email protected]> * Address CodeRabbit feedback for system nuke users E2E tests Exercise SystemNukeUsersCmd with localOnlyPrecheck and --confirm in E2E tests, verify non-local mode is blocked via subprocess, and update confirmation text to mention users, posts, and related data. Co-authored-by: mattermost-code <[email protected]> * Fix viper local flag pollution in nuke users E2E tests Avoid setting viper local directly so unit tests that bind the local flag are not affected when mmctl unit and e2e suites run together. Co-authored-by: mattermost-code <[email protected]> * Simplify nuke users E2E test to reuse existing deleteall pattern Replace the subprocess-based localOnlyPrecheck verification with the straightforward client-based approach from the original TestDeleteAllUserCmd. The local-only enforcement is already covered by the local-only PermanentDeleteAllUsers API endpoint, so the regular and system-admin clients fail naturally without invoking the precheck. This also removes the viper local flag manipulation that previously polluted other E2E tests. --------- Co-authored-by: mattermost-code <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
3e85247df8 |
[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.
* 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.
* 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.
* 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.
* 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.
* 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).
* 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.
* 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.
* 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.
* 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.
* Retrigger enterprise CI
Pick up enterprise fix 48db14db (golangci-lint findings in rank
access-control code) in the combined Enterprise CI/tests lane.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* Fix SCSS property order lint errors
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* Update webapp/channels/src/components/admin_console/system_properties/rank_badge.scss
Co-authored-by: abhijit-singh <[email protected]>
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* Fix consistent-type-imports lint errors in Playwright e2e specs
* 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.
* 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.
* 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).
* Empty commit to retrigger CI after enterprise branch update
Co-authored-by: Maria A Nunez <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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: abhijit-singh <[email protected]>
Co-authored-by: Maria A Nunez <[email protected]>
Co-authored-by: Mattermost Build <[email protected]>
Co-authored-by: Hanzo Dev <[email protected]>
|
||
|
|
97d5c39f9a |
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 |
||
|
|
5e11ed1cd9 |
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 |
||
|
|
bbc9b152e6 |
MM-68417, MM-68420: API support for PAT expiry and admin policy settings (#36706)
* MM-68417, MM-68420: API support for PAT expiry and admin policy settings
POST /users/{id}/tokens now accepts a client-supplied expires_at
(previously stripped per the TODO in api4/user.go), and the create app
method enforces two new ServiceSettings:
- EnforcePersonalAccessTokenExpiry (bool, default false): when on,
rejects creates with expires_at == 0
- MaximumPersonalAccessTokenLifetimeDays (int, default 0 = unlimited):
caps how far in the future expires_at may be
Rejections return distinct app error ids so clients can disambiguate:
expires_at_required, expires_at_in_past, expires_at_too_far.
GET /users/{id}/tokens already serializes expires_at via the model's
JSON tag added in MM-68419; clients derive token status (active /
expired / inactive) from is_active + expires_at without a separate
server-side field, keeping the response shape minimal.
The Client4 helper CreateUserAccessToken gained an optional variadic
expiresAt parameter (and the mmctl Client interface + mock match)
rather than introducing a parallel WithExpiry method.
Refs: MM-68417, MM-68420
* MM-68417: exempt bot accounts from PAT expiry enforcement
Mirrors the existing EnableUserAccessTokens bypass at session.go:453,
where bot tokens are allowed even when human PATs are disabled. Bots
are programmatic clients that typically need long-lived credentials,
and integrations that provision them would otherwise break the moment
an admin enables EnforcePersonalAccessTokenExpiry — turning a settings
toggle into a footgun. The expiry policy now applies only to human
users; bots can still be given a future expires_at by callers that
want it, but the server won't require one.
Locked in by a new TestCreateUserAccessToken/bot_tokens_are_exempt
subtest that enables enforcement plus a 30-day cap, creates a bot,
and asserts a non-expiring token is accepted.
Refs: MM-68417
* MM-68420: bound MaximumPersonalAccessTokenLifetimeDays in isValid
Negative values silently meant "unlimited" (the runtime check is `> 0`),
and very large values overflow int64 when computing
`now + days*86_400_000` at token-creation time, producing a wrap-around
that either rejects all reasonable expiries or accepts past timestamps
as valid.
Bound the setting in ServiceSettings.isValid to [0, MaxPersonalAccess
TokenLifetimeDays] where the cap is 36500 (100 years) — past any
realistic operational use and well clear of int64 overflow. Surfaces
as a config validation error rather than a silently-broken runtime
check. New TestServiceSettingsIsValid cases lock in zero, negative,
upper-bound, and above-upper-bound behavior.
Refs: MM-68420
* MM-68417: reject multiple expiresAt values in Client4 helper
CodeRabbit caught that the variadic CreateUserAccessToken silently used
expiresAt[0] when callers passed more than one value, masking a
mis-call instead of failing fast. Return an error in that case so the
misuse is visible at the call site rather than producing a token with
the wrong (or right-but-coincidental) expiry.
Refs: MM-68417
* MM-68417: make expiresAt a required parameter on Client4.CreateUserAccessToken
Drop the variadic in favor of a regular int64 parameter. The variadic
form silently dropped extra values and made a misuse undetectable at
the call site (per CodeRabbit review on PR 36706); the previous fix
guarded against >1 values at runtime, but a required parameter is
strictly better — the compiler now refuses the misuse and every caller
is forced to make a deliberate decision about expiry. Existing callers
that want the old behavior pass 0 (== never expires).
Refs: MM-68417
* MM-68417: add --expires-in flag to mmctl user token generate
Adds an --expires-in duration flag so operators can create expiring
PATs via the CLI. Accepts the standard Go duration syntax plus a
trailing 'd' for days (the common case for token lifetimes), e.g.
--expires-in 90d, --expires-in 12h, --expires-in 1h30m. Empty (the
default) means no expiry — matching prior behavior. Without this
flag the command was unusable once an admin enables
EnforcePersonalAccessTokenExpiry, since every create would fail with
app.user_access_token.expires_at_required.app_error.
Flag parsing now happens before the user-lookup API call so the
command fails fast on invalid input. Regenerated mmctl docs reflect
the new flag.
Refs: MM-68417
* MM-68417: cap --expires-in day count to prevent time.Duration overflow
parseExpiresIn returns time.Duration(days) * 24 * time.Hour, which is
int64 nanoseconds and overflows past ~106751 days (CodeRabbit caught
this). Cap at model.MaxPersonalAccessTokenLifetimeDays (36500) so the
CLI rejects values the server would reject anyway, well below the
int64-overflow point. Adds two test cases (at-cap and beyond-cap) to
TestParseExpiresIn.
Refs: MM-68417
* MM-68420: collapse PAT expiry settings into a single max-lifetime setting
Remove ServiceSettings.EnforcePersonalAccessTokenExpiry and fold the
policy onto MaximumPersonalAccessTokenLifetimeDays: 0 means no policy
(never-expiring tokens allowed, no cap), while a value > 0 requires every
new token to expire within that many days. The two-setting design let an
admin set a maximum but leave enforcement off, silently allowing
never-expiring tokens to sidestep the cap; the only combination the
boolean added (require expiry, no upper bound) has little practical
value. The removed field was introduced on this branch and never
released, so this is not a breaking change.
---------
Co-authored-by: Hanzo Dev <[email protected]>
|
||
|
|
8d20e78741 |
MM-68664: Microsoft Entra ID / Default Credential authentication for Azure Blob Storage (#36733)
* Add DefaultAzureCredential authentication for Azure Blob Storage Adds a second authentication mode for the Azure filestore backend alongside the existing shared-key path. The new "default credential" mode constructs azidentity.NewDefaultAzureCredential, which discovers managed identity, workload identity, service principal env vars, and az login in that order at runtime - the standard Microsoft pattern for host-provided identities. The credential type is configured via FileSettings.AzureAuthMode (and ExportAzureAuthMode for the dedicated export store). Both default to shared_key so existing deployments are unaffected. The access-key field is only required under shared_key; default_credential reads identity from the host environment and needs no per-mode config. ------ AI assisted commit * Add Azure authentication selector to the System Console Adds an "Azure Authentication" dropdown to both the primary file-storage panel and the dedicated export-store panel. Two options: "Shared key" (the existing default) and "Default credential (Microsoft Entra ID)". The Azure Storage Account Key field is hidden when default credential is selected; it has no role in that auth mode. The Cypress spec is extended to cover the new dropdown's visibility toggling. ------ AI assisted commit * Use fmt.Errorf instead of pkg/errors * Do not support empty AzureAuthModeSharedKey There is no need to support legacy settings when a feature is not yet released. * Bring in master's Azure Blob Storage Cypress spec and scroll the access key into view Two related changes: - The merge commit just before this one missed master's MM-68787 updates to the Azure Blob Storage Cypress spec (the AzureClouddropdown visibility, the disabled -> not.exist tightening when S3 driver is selected, and the new "shows the custom endpoint only for the Custom cloud" test). This commit pulls those in. - The new "hides the access key when the authentication mode is default credential" spec asserts the access key field is visible immediately after selecting the Azure driver. With the AzureAuthMode and (now landed) AzureCloud dropdowns above it, the field sits below the visible area of the System Console scroll container, and Cypress's strict be.visible check fails on overflow clipping. scrollIntoView mirrors what the Test Connection spec already does for the same reason. ------ AI assisted commit --------- Co-authored-by: Mattermost Build <[email protected]> |
||
|
|
71cad2e855 |
MM-68787: Support sovereign-cloud endpoints for Azure Blob Storage (#36732)
* Support sovereign-cloud endpoints in the Azure Blob Storage backend
Adds an AzureCloud enum to FileBackendSettings and the matching
FileSettings / ExportFileSettings entries, defaulting to commercial.
The supported values pick the service URL the SDK signs against:
- commercial: vhost-style against blob.core.windows.net, exactly
the existing default. Only the storage account name is needed.
- government: vhost-style against blob.core.usgovcloudapi.net, no
user-supplied endpoint. Only the storage account name is needed.
- custom: the admin-provided AzureEndpoint is the full service URL,
scheme and storage account included, and Mattermost passes it to
the SDK unchanged. Covers Azurite, reverse proxies, Azure China,
and any future Azure cloud Mattermost has not codified a preset
for.
Wires the new field through NewFileBackendSettingsFromConfig,
NewExportFileBackendSettingsFromConfig, ConfigToFileBackendSettings,
SetDefaults, the System Console (File Storage and Export Storage
panels) with matching i18n strings, and a dedicated Cypress spec.
Azurite test settings switch to AzureCloud=custom with the full
http://host:port/account/ URL so the existing integration suites
keep driving Azurite without any conditional URL plumbing inside the
driver. buildAzureServiceURL is now total: it returns an error for
custom-without-endpoint and for unknown enum values rather than
producing a malformed URL.
------
AI assisted commit
* Hide S3 File Storage fields when a different driver is selected
Match the existing Azure-field behavior: when the selected file storage
driver is not Amazon S3, hide the S3-only fields entirely instead of
just disabling them. Both driver groups already use this hide-when-
inactive pattern in the dedicated export storage section.
The Cypress assertion in azure_blob_storage_spec.js that codified the
old disable-only behavior is updated to expect not.exist.
------
AI assisted commit
* Address review feedback on AzureCloud validation and config-test endpoints
- Switch FileSettings.isValid AzureCloud / ExportAzureCloud value
checks from slices.Contains to a switch statement, matching the
closed-enum style used a few lines above for DriverName.
- Revert the io.EOF suppression in testFileStore and testEmail. The
suppression was bot-suggested log-noise polish; reviewer prefers
to keep the original error handling for those endpoints.
- Drop the redundant "http(s)" qualifier from the scheme check comment
in buildAzureServiceURL.
------
AI assisted commit
---------
Co-authored-by: Mattermost Build <[email protected]>
|
||
|
|
e4b86e02e4 |
MM-68316: add mattermost db ping subcommand (#36406)
* MM-68316: add `mattermost db ping` subcommand Adds a one-shot DB-readiness CLI that opens a connection to the configured PostgreSQL DSN and exits 0 when reachable. Honours --timeout (default 5m) and --retry-interval (default 2s); reads the DSN from --config / MM_CONFIG / MM_SQLSETTINGS_DATASOURCE matching the existing `db init`/`db migrate` semantics. Intended primarily as an init-container readiness check for the mattermost-operator, removing its dependency on a separate postgres:13 image (relevant for air-gapped deployments). * MM-68316: address PR feedback (sanitize ping retry log) Drop mlog.Err(err) from the per-attempt "Waiting for database" log line in pingWithRetry: lib/pq error strings can echo DSN fragments back at operators when DSN parsing fails. Replace with a non-sensitive status field. Also clarifies the TestDBPingInvalidDSN comment to reflect actual lib/pq behavior (parse error surfaces at PingContext, not Open, so the retry loop treats it as transient — desired for a readiness probe). * MM-68316 Preserve safe db ping DSN errors --------- Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
6d3fc14911 |
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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> --------- Co-authored-by: Ibrahim Serdar Acikgoz <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
77dd5cc0af |
MM-68150: Upgrade golangci-lint to v2.12.2 (#36554)
* 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 <[email protected]> |
||
|
|
0a981c295f |
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
|
||
|
|
969b6a21f2 | Migrate CPA to the v2 Property System (#36180) | ||
|
|
edf2075381 |
MM-68149: Upgrade to Go 1.26.2 (#36418)
* MM-68149: upgrade to Go 1.26.2 Update go directive in go.mod and .go-version. * MM-68149: replace pointer helpers with Go 1.26 new() Go 1.26 extends the built-in new() to accept an initial value expression, making typed-pointer helpers like model.NewPointer(x), bToP(x), and boolPtr(x) redundant. Replace every call site with new(x) and remove the now-unused helper functions and their //go:fix inline directives. * MM-68149: apply go fix for reflect API and format-string changes - reflect.Ptr → reflect.Pointer (renamed in Go 1.18, deprecated alias removed in 1.26) - reflect range-over-struct: for i := 0; i < t.NumField(); i++ → for field := range t.Fields() and the equivalent for Methods() and interface types - Fix format-string concatenation and variadic-arg mismatches flagged by go vet * MM-68149: update JPEG fixtures and test infrastructure for Go 1.26 encoder Go 1.26 ships a new image/jpeg encoder that produces slightly different output. Regenerate all JPEG fixture files and switch the comparison helpers from byte-equality to pixel-level comparison with a small per-channel tolerance, so minor encoder drift across patch versions is handled automatically. Add -update-fixtures flag to make it easy to regenerate fixtures after future major Go upgrades. Document the update procedure in tests/README.md. * MM-68149: CI check that go fix ./... produces no changes * Fix real bugs flagged by CodeRabbit review - group.go: set newGroup.MemberCount not group.MemberCount (member count was populated on the wrong variable and lost before publish/return) - file_test.go: guard compareImage(GetFilePreview) on the preview slice length, not the thumbnail slice length (copy-paste error) - config_test.go: remove duplicate MinimumLength assignment * fixup! Fix real bugs flagged by CodeRabbit review |
||
|
|
6cbbb10a5e |
ci: compile mmctl e2e tests with requirefips when FIPS_ENABLED=true (#36267)
* ci: compile mmctl e2e tests with requirefips when FIPS_ENABLED=true Without this, the mmctl test binary was compiled without the requirefips tag even in the FIPS container, leaving model.FIPSEnabled=false and PasswordSettings.MinimumLength=8. Short passwords like "somepass" passed validation and hashing silently succeeded, giving false confidence that the tests were FIPS-clean. * tests: fix short password in TestUserConvertCmdF for FIPS "Valid bot to user convert" reached ConvertBotToUser with "password" (8 chars), which fails MinimumLength=14 on FIPS builds. |
||
|
|
d4a058e754 |
[MM-68351] Fix nil pointer panic in mmctl websocket command on connection failure (#36138)
* fix(mmctl): prevent nil pointer panic in websocket command on connection failure When the WebSocket connection fails immediately, Listen() closes EventChannel via defer. Reading from a closed channel with a plain receive returns nil, causing a panic in ToJSON(). Switch to range so the loop exits cleanly, add a nil guard, and surface ListenError to the caller. Fixes MM-68351 * test(mmctl): add unit tests for websocket nil event and ListenError handling Extracts the event-processing loop into processWebSocketEvents to enable unit testing, and adds tests covering the nil-event skip and error surfacing. * test(mmctl): add happy-path subtest for processWebSocketEvents --------- Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
e03d609871 |
ci: enable fullyparallel mode for server tests (#35816)
* ci: enable fullyparallel mode for server tests Replace os.Setenv, os.Chdir, and global state mutations with parallel-safe alternatives (t.Setenv, t.Chdir, test hooks) across 37 files. Refactor GetLogRootPath and MM_INSTALL_TYPE to use package-level test hooks instead of environment variables. This enables gotestsum --fullparallel, allowing all test packages to run with maximum parallelism within each shard. * ci: split fullyparallel from continue-on-error in workflow template - Add new boolean input 'allow-failure' separate from 'fullyparallel' - Change continue-on-error to use allow-failure instead of fullyparallel - Update server-ci.yml to pass allow-failure: true for test coverage job - Allows independent control of parallel execution and failure tolerance * fix: protect TestOverrideLogRootPath with sync.Mutex for parallel tests - Replace global var TestOverrideLogRootPath with mutex-protected functions - Add SetTestOverrideLogRootPath() and getTestOverrideLogRootPath() functions - Update GetLogRootPath() to use thread-safe getter - Update all test files to use SetTestOverrideLogRootPath() with t.Cleanup() - Fixes race condition when running tests with t.Parallel() * fix: configure audit settings before server setup in tests - Move ExperimentalAuditSettings from UpdateConfig() to config defaults - Pass audit config via app.Config() option in SetupWithServerOptions() - Fixes audit test setup ordering to configure BEFORE server initialization - Resolves CodeRabbit's audit config timing issue in api4 tests * fix: implement SetTestOverrideLogRootPath mutex in logger.go The previous commit updated test callers to use SetTestOverrideLogRootPath() but didn't actually create the function in config/logger.go, causing build failures across all CI shards. This commit: - Replaces the exported var TestOverrideLogRootPath with mutex-protected unexported state (testOverrideLogRootPath + testOverrideLogRootMu) - Adds exported SetTestOverrideLogRootPath() setter - Adds unexported getTestOverrideLogRootPath() getter - Updates GetLogRootPath() to use the thread-safe getter - Fixes log_test.go callers that were missed in the previous commit * fix(test): use SetupConfig for access_control feature flag registration InitAccessControlPolicy() checks FeatureFlags.AttributeBasedAccessControl at route registration time during server startup. Setting the flag via UpdateConfig after Setup() is too late — routes are never registered and API calls return 404. Use SetupConfig() to pass the feature flag in the initial config before server startup, ensuring routes are properly registered. * fix(test): restore BurnOnRead flag state in TestRevealPost subtest The 'feature not enabled' subtest disables BurnOnRead without restoring it via t.Cleanup. Subsequent subtests inherit the disabled state, which can cause 501 errors when they expect the feature to be available. Add t.Cleanup to restore FeatureFlags.BurnOnRead = true after the subtest completes. * fix(test): restore EnableSharedChannelsMemberSync flag via t.Cleanup The test disables EnableSharedChannelsMemberSync without restoring it. If the subtest exits early (e.g., require failure), later sibling subtests inherit a disabled flag and become flaky. Add t.Cleanup to restore the flag after the subtest completes. * Fix test parallelism: use instance-scoped overrides and init-time audit config Replace package-level test globals (TestOverrideInstallType, SetTestOverrideLogRootPath) with fields on PlatformService so each test gets its own instance without process-wide mutation. Fix three audit tests (TestUserLoginAudit, TestLogoutAuditAuthStatus, TestUpdatePasswordAudit) that configured the audit logger after server init — the audit logger only reads config at startup, so pass audit settings via app.Config() at init time instead. Also revert the Go 1.24.13 downgrade and bump mattermost-govet to v2.0.2 for Go 1.25.8 compatibility. * Fix audit unit tests * Fix MMCLOUDURL unit tests * Fixed unit tests using MM_NOTIFY_ADMIN_COOL_OFF_DAYS * Make app migrations idempotent for parallel test safety Change System().Save() to System().SaveOrUpdate() in all migration completion markers. When two parallel tests share a database pool entry, both may race through the check-then-insert migration pattern. Save() causes a duplicate key fatal crash; SaveOrUpdate() makes the second write a harmless no-op. * test: address review feedback on fullyparallel PR - Use SetLogRootPathOverride() setter instead of direct field access in platform/support_packet_test.go and platform/log_test.go (pvev) - Restore TestGetLogRootPath in config/logger_test.go to keep MM_LOG_PATH env var coverage; test uses t.Setenv so it runs serially which is fine (pvev) - Fix misleading comment in config_test.go: code uses t.Setenv, not os.Setenv (jgheithcock) * fix: add missing os import in post_test.go The os import was dropped during a merge conflict resolution while burn-on-read shared channel tests from master still use os.Setenv. --------- Co-authored-by: wiggin77 <[email protected]> Co-authored-by: Mattermost Build <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
1bdd985b41 |
Generate instead of hard-coding test passwords, enforce new minimum for FIPS, shard CI, fix FIPS builds (#35905)
* 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 * 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: Hanzo Dev <[email protected]> |
||
|
|
fbb57f0e29 | [MM-67859] Update license renewal and expiry email branding (#35701) | ||
|
|
a7c122fc3b |
chore: fix typos in comments (#34960)
Co-authored-by: Mattermost Build <[email protected]> |
||
|
|
de986ae735 |
Add property system app layer architecture (#35157)
* Refactor property system with app layer routing and access control separation Establish the app layer as the primary entry point for property operations with intelligent routing based on group type. This architecture separates access-controlled operations (CPA groups) from standard operations, improving performance and code clarity. Architecture Changes: - App layer now routes operations based on group type: - CPA groups -> PropertyAccessService (enforces access control) - Non-CPA groups -> PropertyService (direct, no access control) - PropertyAccessService simplified to handle only CPA operations - Eliminated redundant group type checks throughout the codebase * Move access control routing into PropertyService This change makes the PropertyService the main entrypoint for property related operations, and adds a routing mechanism to decide if extra behaviors or checks should run for each operation, in this case, the property access service logic. To add specific payloads that pluggable checks and operations may need, we use the request context. When the request comes from the API, the endpoints are in charge of adding the caller ID to the payload, and in the case of the plugin API, on receiving a request, the server automatically tags the context with the plugin ID so the property service can react accordingly. Finally, the new design enforces all these checks migrating the actual property logic to internal, non-exposed methods, so any caller from the App layer needs to go through the service checks that decide if pluggable logic is needed, avoiding any possibility of a bypass. * Fix i18n * Fix bad error string * Added nil guards to property methods * Add check for multiple group IDs on value operations * Add nil guard to the plugin checker * Fix build error * Update value tests * Fix linter * Adds early return when content flaggin a thread with no replies * Fix mocks * Clean the state of plugin property tests before each run * Do not wrap appErr on API response and fix i18n * Fix create property field test * Remove the need to cache cpaGroupID as part of the property service * Split the property.go file into multiple * Not found group doesn't bypass access control check * Unexport SetPluginCheckerForTests * Rename plugin context getter to be more PSA specific --------- Co-authored-by: Miguel de la Cruz <[email protected]> |
||
|
|
8733a3e5ce |
ci: cache prepackaged plugins in mmctl tests (#35720)
* ci: cache prepackaged plugins in mmctl tests Cache the prepackaged_plugins directory between mmctl test runs using actions/cache. The cache key is derived from a SHA-256 hash of the PLUGIN_PACKAGES lines in server/Makefile, so the cache automatically invalidates whenever any plugin version is bumped — no manual key updates needed. Uses actions/cache output (cache-hit) to skip the download step entirely on cache hits. Saves ~1-2 minutes per mmctl test run by avoiding repeated plugin downloads. * fix: align plugin cache key with actual downloaded packages Address CodeRabbit review: cache key was derived from all 15 plugins in the Makefile, but the download only fetched jira plugin. Now both the hash and the download use the same MMCTL_PLUGIN_PACKAGES value, defined once, preventing stale cache or unnecessary invalidation. * Revert "fix: align plugin cache key with actual downloaded packages" This reverts commit 73303864bf1c066a49f649bcdab5c2201b38dd1f. * Revert "ci: cache prepackaged plugins in mmctl tests" This reverts commit acd1ec757afda3e6f5e4907c3dc92aa7765563f1. * prepackage the existing test plugin to speed up tests * fix: use public key for plugin signature verification in test --------- Co-authored-by: Jesse Hallam <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
71fcf94c5e |
[MM-67744] Add -buildvcs=false to default GOFLAGS (#35587)
* Add -buildvcs=false to default GOFLAGS This prevents Go from embedding VCS information into binaries, which avoids false positives in container vulnerability scanners like Trivy when using Go workspaces with enterprise dependencies. Also updates mmctl-build target to use $(GO) and $(GOFLAGS) for consistency with other build targets. Made-with: Cursor * Update comment wording Trigger PR sync to test Enterprise CI Made-with: Cursor * Trigger CI to test Enterprise CI fix Made-with: Cursor * Test Enterprise CI Made-with: Cursor * replace buildvcs metadata in mmctl * rm redundant -buildvcs=false in GitHub actions * update mmctl-docs to $(GO) * simplify getVersionInfo signature * use GOOS/GOARCH convention * export GOFLAGS for common use * Clarify version.go var block comment --------- Co-authored-by: Jesse Hallam <[email protected]> |
||
|
|
8b11064356 |
Fix import failures for Japanese filenames with dakuten on macOS (#35204)
* 🐛 fix: normalize Unicode filenames in import attachment lookup Fix import failures for files with Japanese dakuten/handakuten characters (e.g., ガ, パ, べ) on macOS. macOS stores filenames in NFD (decomposed) form while Linux/Windows use NFC (composed) form. This mismatch caused attachment lookup failures when zip filenames and JSONL paths used different normalization forms. Changes: - Add NormalizeFilename utility function using golang.org/x/text/unicode/norm - Normalize filenames when building attachment maps from zip files - Normalize paths when looking up attachments in maps - Apply fixes to both server (import.go) and mmctl (validate.go) * avoid duplicating normalizeFilename * add coverage for Korean filenames --------- Co-authored-by: Jesse Hallam <[email protected]> Co-authored-by: Mattermost Build <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
d03688add9 |
mmctl: Add support for listing user roles through mmctl (#34064)
Co-authored-by: Indusha Semba <[email protected]> Co-authored-by: Mattermost Build <[email protected]> Co-authored-by: Ben Schumacher <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
0f81a30c31 |
[MM-29973] Adds E2E tests for mmctl plugin disable (#35464)
|
||
|
|
489a62a555 |
Add --workers flag to mmctl import process to control concurrency (#35582)
* Add --workers flag to mmctl import process to control concurrency The bulk import worker count was hardcoded to runtime.NumCPU(), causing high database load on the master during imports on live systems. This is particularly impactful for incremental Slack imports where all users are re-imported each time, generating 8-15 DB operations per user against the master (due to LockToMaster). The new --workers flag allows administrators to reduce concurrency (e.g., --workers 1) to minimize impact on live users at the cost of longer import duration. Defaults to 0 which preserves the existing runtime.NumCPU() behavior. * Add max workers limit, capped at CPU Count * 4 |
||
|
|
29b13b946a |
fix: only match root-level JSONL files when importing a zip (#35481)
* fix: only match root-level JSONL files when importing a zip When importing a Mattermost export zip, the code iterated over all files to find the first .jsonl by extension. Exported attachments under data/ could themselves be .jsonl files, causing the import to pick an attachment as the manifest instead of the actual root-level JSONL file. Extract an IsRootJsonlFile helper in the imports package and use it in the import process worker, mmctl validator, and bulk import test to restrict the search to files with no directory component. * test: add integration test for import with nested JSONL decoy file Verify that BulkImportWithPath correctly picks the root-level JSONL manifest and ignores a decoy .jsonl inside a subdirectory, covering the fix from ad7f230f06. |
||
|
|
cdb370c39b |
MM-29974 Adds e2e tests to the plugin list command (#34866)
* Adds e2e tests to the plugin list command Adds end-to-end tests to the pluginListCmdF function. These tests verify scenarios where appropriate permissions are not available, plugins are disabled, and success cases. * Updates plugin list command E2E tests Removes the `pluginArg` from `pluginListCmdF` calls within end-to-end tests. Ensures test cases accurately evaluate the `pluginListCmdF` behavior when no specific plugin name is provided as an argument, improving test coverage for default listing scenarios. Relates to MM-29974 * Simplifies plugin test defer cleanup Refactors defer statements in plugin E2E tests to directly call `s.th.App.UpdateConfig`. Removes redundant anonymous function wrappers, making the test cleanup code cleaner and more concise. Relates to MM-29974 --------- Co-authored-by: Mattermost Build <[email protected]> |