raghav-reglobe opened a new pull request, #66115:
URL: https://github.com/apache/doris/pull/66115

   ### What problem does this PR solve?
   
   Issue: #66113
   
   `ALTER USER ... IDENTIFIED BY` is a hard cutover — the old password stops 
working the instant the statement commits, so every client embedding that 
account's password must be updated simultaneously or fail to authenticate. This 
makes online credential rotation impossible without a maintenance window.
   
   This adds MySQL 8.0-compatible **dual password** support so a password can 
be rotated while the previous one keeps working, letting consumers migrate on 
their own schedule:
   
   ```sql
   ALTER USER u IDENTIFIED BY 'new' RETAIN CURRENT PASSWORD;  -- old password 
stays valid
   ALTER USER u IDENTIFIED BY 'new';                          -- secondary 
unchanged
   ALTER USER u DISCARD OLD PASSWORD;                         -- evict the 
retained one
   ```
   
   Doris already mirrors MySQL account management (`PASSWORD_HISTORY`, 
`PASSWORD_EXPIRE`, `FAILED_LOGIN_ATTEMPTS`, `PASSWORD_LOCK_TIME`); this fills 
the remaining gap — `PASSWORD_EXPIRE` can force a rotation, but there was no 
way to comply with it online.
   
   ### Semantics (mirrors the MySQL 8.0 manual)
   
   | Rule | MySQL manual |
   |---|---|
   | `RETAIN CURRENT PASSWORD` saves the current primary as the secondary; both 
authenticate | "the current password ... is retained as a secondary password" |
   | Exactly one secondary per user; the next `RETAIN` replaces it | dual = 
primary + one secondary |
   | A password change **without** `RETAIN` leaves an existing secondary 
**unchanged** | "the secondary password remains unchanged" |
   | An **empty** new password empties the secondary too, even with `RETAIN` | 
"the secondary password becomes empty as well" |
   | `RETAIN` on an account with an **empty primary** fails | "the statement 
fails" |
   | `DISCARD OLD PASSWORD` drops the secondary if one exists (no-op 
otherwise); cannot combine with a password change | "discards the secondary 
password, if one exists" |
   
   ### Design
   
   - **Storage**: `Password` gains a nullable secondary slot (GSON field 
`pwd2`). Absent in images/journals written before this change; ignored by 
binaries without it — tolerant in both directions, so no `FeMetaVersion` bump 
and rolling upgrade / downgrade is safe.
   - **Authentication**: primary is tried first, then the secondary, in one 
shared helper behind **both** existing check paths (`checkPasswordInternal` and 
`checkPasswordInternalForUser`), so every protocol lane (MySQL scramble, 
cleartext/LDAP fallback, Arrow Flight, HTTP, stream load) behaves identically. 
Failed-login policy fires only when both miss.
   - **Replay**: the retain flag rides `PrivInfo` on the existing 
`OP_SET_PASSWORD`; `DISCARD` is a new `AlterUserOpType` on `OP_ALTER_USER`. No 
new operation types.
   - **Observability**: authenticating with the retained secondary logs an INFO 
line and increments a new FE metric `doris_fe_secondary_password_auth_total`, 
so operators can confirm every consumer has converged on the new password 
before the next rotation evicts the old one.
   - **Grammar**: `RETAIN`/`CURRENT`/`PASSWORD` tokens already existed; 
`DISCARD` and `OLD` are added as **nonReserved** keywords (still usable as 
identifiers).
   
   ### Deliberate differences from MySQL (called out for review)
   
   - **Privileges**: Doris has no dynamic-privilege framework, so the clauses 
require the same privilege as any other `ALTER USER` password change (MySQL 
gates them behind `APPLICATION_PASSWORD_ADMIN` / `CREATE USER`). Happy to add a 
privilege check if the maintainers prefer.
   - **No per-user authentication plugins** in Doris, so MySQL's "changing the 
authentication plugin discards the secondary" rule has no analog.
   
   ### Out of scope (follow-ups)
   
   - `SET PASSWORD ... RETAIN CURRENT PASSWORD` statement form (this PR covers 
`ALTER USER`).
   - Introspection of whether a user currently holds a retained password.
   
   ### Rolling upgrade
   
   All FEs should run this version before the new clauses are first used 
(masters last, the usual order). Mixed-version behavior is safe: a binary 
without the change ignores the `pwd2` field and the retain flag 
(single-password behavior).
   
   ### Tests
   
   `DualPasswordTest` (7 cases): retain / evict / discard chain; secondary 
preserved on a plain change; empty-new empties the secondary; 
retain-on-empty-primary fails; replay (retain via `PrivInfo`, discard via 
`AlterUserOperationLog`, and legacy-journal compatibility); GSON 
forward/backward compatibility; parser (clauses parse, retain-without-password 
rejected, `DISCARD`/`OLD` still valid as identifiers).
   
   ### Checklist
   
   - [x] Regression test: `fe/fe-core/.../mysql/privilege/DualPasswordTest.java`
   - [x] Compatible with rolling upgrade (GSON-tolerant field, no meta-version 
bump)
   
   ### Behavior change
   
   `ALTER USER` gains optional `RETAIN CURRENT PASSWORD` / `DISCARD OLD 
PASSWORD` clauses. No behavior change for statements that don't use them.
   
   ### Release note
   
   Support MySQL-compatible dual password on `ALTER USER` (`RETAIN CURRENT 
PASSWORD` / `DISCARD OLD PASSWORD`) for zero-downtime credential rotation.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to