Skip to content

[FIX] v3.x: Match synced MySQL users by (username, host) to avoid collapsing multi-host accounts#1075

Merged
saeedvaziry merged 1 commit intovitodeploy:3.xfrom
erhanurgun:fix/sync-database-users-composite-match
May 2, 2026
Merged

[FIX] v3.x: Match synced MySQL users by (username, host) to avoid collapsing multi-host accounts#1075
saeedvaziry merged 1 commit intovitodeploy:3.xfrom
erhanurgun:fix/sync-database-users-composite-match

Conversation

@erhanurgun
Copy link
Copy Markdown
Contributor

@erhanurgun erhanurgun commented Apr 22, 2026

Closes #1074

SyncDatabaseUsers was looking up existing rows by username only, so on a server with both 'app'@'localhost' and 'app'@'127.0.0.1' one row kept being overwritten on every sync and the second one was never inserted. The Vito UI then ended up showing a databases list that didn't match the host above it.

Fix is one extra where('host', ...) when the handler returns a non-empty host. Postgres is left on the username-only path because its get-users-list view returns '' for host (roles are host-agnostic) and an unconditional composite filter would insert a duplicate (rolname, '') row on every sync.

  /** @var ?DatabaseUser $databaseUser */
- $databaseUser = $server->databaseUsers()
-     ->where('username', $user[0])
-     ->first();
+ $query = $server->databaseUsers()->where('username', $user[0]);
+ if ($user[1] !== '') {
+     $query->where('host', $user[1]);
+ }
+ $databaseUser = $query->first();

vendor/bin/phpunit tests/Feature/DatabaseUserTest.php -> 15/15 green; three new tests cover the multi-host case, idempotent re-sync, and the Postgres no-duplicate regression. CreateDatabaseUser, UpdateDatabaseUser, and DeleteDatabaseUser already scope by (username, host), so this just brings sync in line.

No migration. A composite unique index on (server_id, username, host) would be a natural follow-up but felt out of scope.

…g multi-host accounts

SyncDatabaseUsers previously matched existing database_users rows by username
only. MySQL and MariaDB treat 'user'@'localhost' and 'user'@'127.0.0.1' as
independent accounts with potentially different passwords and grants, so
collapsing them onto a single row caused silent data corruption: the databases
column was overwritten in an order-dependent way, additional host variants
were never inserted, and the stored host no longer matched the grants shown
under it.

The lookup now scopes by (username, host) whenever the handler returns a
non-empty host, aligning sync with CreateDatabaseUser, UpdateDatabaseUser and
DeleteDatabaseUser. PostgreSQL keeps the original username-only match because
its get-users-list view returns an empty host (roles are host-agnostic); an
unconditional composite filter would miss the existing UI-created row and
insert a duplicate (rolname, '') record on every sync.

Adds regression tests covering MySQL multi-host sync, MySQL sync idempotency
and the PostgreSQL no-duplicate-row invariant.
@erhanurgun erhanurgun marked this pull request as ready for review April 22, 2026 23:06
@saeedvaziry
Copy link
Copy Markdown
Member

@erhanurgun I appreceate the efforts on the issues and PRs however I rather to skip the long reads with AI generated too much details.

Lets keep the issues and PR descriptions for us humans with our own words.

Thanks

@erhanurgun
Copy link
Copy Markdown
Contributor Author

erhanurgun commented Apr 25, 2026

Sorry for the wall of text, you're right. Trimmed both the PR description and the issue.

@erhanurgun erhanurgun changed the title [FIX] Match synced MySQL users by (username, host) to avoid collapsing multi-host accounts [FIX] v3.x: Match synced MySQL users by (username, host) to avoid collapsing multi-host accounts Apr 25, 2026
@saeedvaziry saeedvaziry merged commit a20f5b9 into vitodeploy:3.x May 2, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] v3.x: SyncDatabaseUsers corrupts databases column when multiple MySQL users share a username across different hosts

2 participants