EnxDev opened a new issue, #37927:
URL: https://github.com/apache/superset/issues/37927

   <html>
   <body>
   <!--StartFragment--><html><head></head><body>
   <h1> Motivation </h1>
   
   Apache Superset currently lacks essential, modern password management 
capabilities when `AUTH_TYPE = AUTH_DB` is configured. The existing user 
experience is fragmented, insecure in certain areas, and heavily reliant on 
server-side CLI access for basic account recovery operations. This creates a 
significant operational burden for administrators and a frustrating experience 
for end users.
   
   **Current state of affairs:**
   
   1. **Authenticated password change** — A logged-in user can change their own 
password via Profile → Change Password, but the form does **not** require the 
current password, making it vulnerable to session hijacking attacks. This was 
reported in [[Issue 
#37700](https://github.com/apache/superset/issues/37700)](https://github.com/apache/superset/issues/37700)
 and partially addressed by [[PR 
#37773](https://github.com/apache/superset/pull/37773)](https://github.com/apache/superset/pull/37773)
 which gates legacy FAB routes behind a config flag, but the underlying UX and 
security gaps remain.
   
   2. **Forgotten password (self-service reset)** — There is **no** "Forgot 
Password" flow out of the box. When a user loses access to their account, the 
only remediation paths are:
      - An administrator runs `superset fab reset-password --username <user> 
--password <new_password>` on the server (requires shell access).
      - The administrator deactivates the user and recreates the account, 
losing ownership references and audit history.
      - Direct SQL manipulation of the `ab_user` table (highly discouraged).
   
      This gap has been a recurring community request since at least 2017 
([[Issue 
#2731](https://github.com/apache/incubator-superset/issues/2731)](https://github.com/apache/incubator-superset/issues/2731)),
 with dedicated discussions 
([[#18427](https://github.com/apache/superset/discussions/18427)](https://github.com/apache/superset/discussions/18427),
 
[[#34241](https://github.com/apache/superset/discussions/34241)](https://github.com/apache/superset/discussions/34241))
 and feature requests 
([[#19498](https://github.com/apache/superset/issues/19498)](https://github.com/apache/superset/issues/19498),
 
[[#36223](https://github.com/apache/superset/issues/36223)](https://github.com/apache/superset/issues/36223)).
   
   3. **Admin-initiated password reset via UI** — Administrators cannot reset 
another user's password from the web interface. [[PR 
#36764](https://github.com/apache/superset/pull/36764)](https://github.com/apache/superset/pull/36764)
 adds password change functionality to the user management modal, but a 
holistic approach covering security policies, audit logging, and session 
invalidation is still missing.
   
   4. **Email change** — There is no CLI or UI mechanism to update a user's 
email address. If a user loses access to their email, the administrator has no 
supported path to update it without direct database edits.
   
   5. **Critical operational scenarios** — In on-premise and self-hosted 
deployments, three scenarios cause the most pain:
      - **Total admin lockout** — If the sole admin forgets their password and 
has no shell access, recovery is extremely difficult.
      - **Missing automated deprovisioning** — No built-in mechanism to disable 
or expire stale accounts.
      - **Ungoverned server access** — CLI password resets require direct 
server access, which may not be available to the logical "Superset admin" in 
many organizations.
   
   This SIP proposes a unified, security-conscious overhaul of password and 
credential management in Superset, scoped exclusively to `AUTH_TYPE = AUTH_DB` 
deployments.
   
   ### Proposed Change
   
   The proposal is organized into four pillars, each addressing a distinct area 
of the password lifecycle.
   
   ---
   
   #### Pillar 1 — Secure Authenticated Password Change
   
   When a logged-in user changes their own password:
   
   - **Require current password** — The change password form must prompt for 
the existing password before accepting a new one. This prevents unauthorized 
password changes in case of session hijacking.
   - **Password policy enforcement** — Introduce configurable validation rules 
via `superset_config.py`:
     - `PASSWORD_MIN_LENGTH` (default: 12)
     - `PASSWORD_REQUIRE_UPPERCASE` (default: `True`)
     - `PASSWORD_REQUIRE_LOWERCASE` (default: `True`)
     - `PASSWORD_REQUIRE_DIGIT` (default: `True`)
     - `PASSWORD_REQUIRE_SPECIAL` (default: `True`)
     - `PASSWORD_COMMON_LIST_CHECK` (default: `True`) — reject passwords from a 
known common-passwords list
   - **Session invalidation** — After a successful password change, all 
existing sessions for the user must be invalidated. The session cookie is 
regenerated, and any persistent tokens are revoked. This mitigates session 
hijacking.
   - **Hashing** — Passwords are hashed using `bcrypt` (or `argon2` as a 
configurable alternative). Passwords must never be stored in plaintext or 
logged.
   - **Audit logging** — Every password change event is recorded with 
timestamp, user ID, source IP, and whether it was self-initiated or 
admin-initiated.
   
   **Frontend changes:** Migrate the password change form from the legacy FAB 
SSR view to a React SPA component consistent with the post-6.0.0 architecture. 
The new form includes fields for current password, new password, and password 
confirmation with real-time client-side validation feedback.
   
   ---
   
   #### Pillar 2 — Self-Service Forgotten Password Reset (Email-Based)
   
   A new "Forgot Password" flow accessible from the login page:
   
   ```
   User clicks "Forgot Password" on login page
           ↓
   User enters their email address
           ↓
   System generates a cryptographically secure, single-use token
           ↓
   Email sent via configured SMTP with an HTTPS reset link
           ↓
   Token has a configurable expiry (default: 30 minutes, range: 15–60 min)
           ↓
   User clicks link → lands on password reset page
           ↓
   User enters new password (subject to password policy)
           ↓
   Token is invalidated immediately after use
           ↓
   All existing sessions invalidated
           ↓
   User is redirected to login page
   ```
   
   **Security requirements:**
   
   - Tokens must be cryptographically random (at least 256 bits of entropy, 
e.g. `secrets.token_urlsafe(48)`).
   - Tokens are single-use and invalidated on consumption or expiry.
   - The system must **not** reveal whether an email address exists in the 
database. Regardless of whether the email is found, the user sees the same 
confirmation message: _"If an account with that email exists, a reset link has 
been sent."_
   - Rate limiting on the reset endpoint: configurable via 
`PASSWORD_RESET_RATE_LIMIT` (default: 5 requests per 15 minutes per IP).
   - The reset link must use HTTPS.
   - Token storage in the database uses hashed values (never store the raw 
token).
   
   **Configuration:**
   
   ```python
   # superset_config.py
   PASSWORD_RESET_ENABLED = True  # default: False (opt-in)
   PASSWORD_RESET_TOKEN_EXPIRY_MINUTES = 30
   PASSWORD_RESET_RATE_LIMIT = "5 per 15 minutes"
   ```
   
   **Prerequisite:** A working SMTP configuration (`EMAIL_NOTIFICATIONS = True` 
with valid `SMTP_*` settings). When SMTP is not configured and 
`PASSWORD_RESET_ENABLED = True`, Superset should log a warning at startup.
   
   ---
   
   #### Pillar 3 — Admin-Initiated Password Reset and Email Change (UI + CLI)
   
   **Web UI — Admin password reset:**
   
   In Security → List Users, administrators (users with the `Admin` role) can 
trigger a password reset for any user. Two modes are supported:
   
   1. **Set password directly** — Admin enters a new password for the user via 
a modal dialog. The user's existing sessions are invalidated. _Whether this 
mode should be supported is open for discussion: while it offers a quick 
recovery path (especially when SMTP is unavailable), it means the admin has 
momentary knowledge of the user's password, which conflicts with zero-knowledge 
security principles. Community input on whether to include, exclude, or gate 
this behind a separate config flag (e.g., `ADMIN_DIRECT_PASSWORD_SET = False`) 
is welcome._
   2. **Send reset link** — Admin triggers a reset email to the user's 
registered email (requires SMTP). This is the preferred approach as the admin 
never sees the new password.
   
   Both actions are gated behind the existing `can_write` permission on the 
`User` resource and are recorded in the audit log.
   
   **CLI — Password reset (existing):**
   
   The existing `superset fab reset-password` command is preserved and 
documented:
   
   ```bash
   superset fab reset-password --username <username> --password <new_password>
   ```
   
   **CLI — Email change (new):**
   
   A new CLI command to update a user's email address:
   
   ```bash
   superset change-email --username <username> --new-email <new_email>
   ```
   
   This command:
   
   - Validates email format.
   - Checks for uniqueness against existing users.
   - Updates the `ab_user.email` field.
   - Logs the change in the audit log.
   - Is only available when `AUTH_TYPE = AUTH_DB`.
   
   This addresses the scenario where a user loses access to their email and 
cannot use the self-service reset flow.
   
   **CLI — Account deactivation (new):**
   
   ```bash
   superset deactivate-user --username <username>
   ```
   
   Deactivates the user (`active = False`), invalidates all sessions, and logs 
the event.
   
   ---
   
   #### Pillar 4 — Security Hardening and Operational Safety
   
   **Login rate limiting and account lockout:**
   
   - `LOGIN_RATE_LIMIT` — Configurable rate limit on the login endpoint 
(default: 10 attempts per 5 minutes per IP).
   - `LOGIN_MAX_FAILURES` — After N consecutive failed login attempts for a 
given username, the account is temporarily locked (default: 5 failures → 15 
minute lockout).
   - Lockout events are logged and optionally trigger an email notification to 
the user.
   
   **Audit logging:**
   
   All authentication-related events are logged to a new `auth_audit_log` table:
   
   - Successful and failed logins (with IP and user-agent)
   - Password changes (self and admin-initiated)
   - Password resets (request and completion)
   - Email changes
   - Account lockouts and unlocks
   - Account activations and deactivations
   
   **Session management hardening:**
   
   After any password or credential change:
   
   - Invalidate all existing `session` records for the user.
   - Regenerate the session ID for the current session.
   - Revoke any persistent or remember-me tokens.
   
   ---
   
   #### Future Considerations (Out of Scope for This SIP)
   
   The following topics are acknowledged as important but are intentionally 
excluded from this SIP to keep scope manageable. They may be addressed in 
subsequent SIPs:
   
   - **Multi-Factor Authentication (MFA)** — Strongly recommended for 
production deployments. Could be introduced via TOTP (e.g. `pyotp`) as a 
follow-up.
   - **Password expiration policies** — Configurable password rotation (e.g. 
every 90 days). Depends on organizational policy and is controversial in modern 
security guidance (NIST SP 800-63B discourages mandatory rotation).
   - **OIDC / SAML integration improvements** — When `AUTH_TYPE` is set to 
`AUTH_OID`, `AUTH_OAUTH`, or `AUTH_REMOTE_USER`, password management is 
delegated to the identity provider. This SIP does not modify those flows. 
However, a future SIP could improve the OIDC onboarding experience and 
documentation.
   - **WebAuthn / Passkeys** — Modern passwordless authentication. Requires 
significant frontend and backend work.
   
   ### New or Changed Public Interfaces
   
   **REST API — New endpoints:**
   
   | Method | Endpoint                            | Description                 
                                        |
   | ------ | ----------------------------------- | 
------------------------------------------------------------------- |
   | `PUT`  | `/api/v1/me/password`               | Authenticated user changes 
own password (requires current password) |
   | `POST` | `/api/v1/auth/forgot-password`      | Request a password reset 
email (unauthenticated)                    |
   | `POST` | `/api/v1/auth/reset-password`       | Complete password reset 
with token (unauthenticated)                |
   | `PUT`  | `/api/v1/user/{id}/password`        | Admin resets a user's 
password (Admin role required)                |
   | `POST` | `/api/v1/user/{id}/send-reset-link` | Admin triggers a reset 
email for a user (Admin role required)       |
   
   **REST API — Modified endpoints:**
   
   | Method | Endpoint            | Change                                      
|
   | ------ | ------------------- | ------------------------------------------- 
|
   | `PUT`  | `/api/v1/user/{id}` | Email field becomes updatable by Admin role 
|
   
   **CLI — New commands:**
   
   | Command                                                       | 
Description                   |
   | ------------------------------------------------------------- | 
----------------------------- |
   | `superset change-email --username <user> --new-email <email>` | Update a 
user's email address |
   | `superset deactivate-user --username <user>`                  | Deactivate 
a user account     |
   
   **CLI — Existing commands (unchanged):**
   
   | Command                                                         | 
Description                              |
   | --------------------------------------------------------------- | 
---------------------------------------- |
   | `superset fab reset-password --username <user> --password <pw>` | Reset 
password from CLI (already exists) |
   | `superset fab create-admin`                                     | Create 
admin user (already exists)       |
   
   **Frontend — React components:**
   
   - New `ChangePasswordModal` component (replaces legacy FAB SSR view)
   - New `ForgotPasswordPage` component (login page addition)
   - New `ResetPasswordPage` component (token-based reset)
   - Modified `UserListModal` to include admin password reset action
   - Password strength indicator component with real-time policy feedback
   
   **Configuration — New `superset_config.py` keys:**
   
   ```python
   # Password policy
   PASSWORD_MIN_LENGTH = 12
   PASSWORD_REQUIRE_UPPERCASE = True
   PASSWORD_REQUIRE_LOWERCASE = True
   PASSWORD_REQUIRE_DIGIT = True
   PASSWORD_REQUIRE_SPECIAL = True
   PASSWORD_COMMON_LIST_CHECK = True
   
   # Self-service reset
   PASSWORD_RESET_ENABLED = False
   PASSWORD_RESET_TOKEN_EXPIRY_MINUTES = 30
   PASSWORD_RESET_RATE_LIMIT = "5 per 15 minutes"
   
   # Login security
   LOGIN_RATE_LIMIT = "10 per 5 minutes"
   LOGIN_MAX_FAILURES = 5
   LOGIN_LOCKOUT_DURATION_MINUTES = 15
   ```
   
   **Important:** All features in this SIP apply exclusively when `AUTH_TYPE = 
AUTH_DB`. When external authentication providers are configured (OAuth, LDAP, 
REMOTE_USER), the password-related endpoints and UI elements are hidden.
   
   ### New Dependencies
   
   | Package                  | Purpose                                     | 
License | Maintained   |
   | ------------------------ | ------------------------------------------- | 
------- | ------------ |
   | `flask-limiter`          | Rate limiting for login and reset endpoints | 
MIT     | Yes (active) |
   | `argon2-cffi` (optional) | Alternative password hashing to bcrypt      | 
MIT     | Yes (active) |
   
   No new npm dependencies are anticipated. The frontend components will use 
existing Ant Design form components and Superset's design system.
   
   Note: `bcrypt` is already a transitive dependency via Flask-AppBuilder. 
`flask-limiter` may already be available depending on the deployment; if not, 
it is lightweight and actively maintained.
   
   ### Migration Plan and Compatibility
   
   **Database migrations:**
   
   1. New table `password_reset_token`:
      - `id` (PK)
      - `user_id` (FK → `ab_user.id`)
      - `token_hash` (VARCHAR, indexed)
      - `created_at` (TIMESTAMP)
      - `expires_at` (TIMESTAMP)
      - `used_at` (TIMESTAMP, nullable)
   
   2. New table `auth_audit_log`:
      - `id` (PK)
      - `user_id` (FK → `ab_user.id`, nullable for failed logins with unknown 
user)
      - `event_type` (VARCHAR — e.g. `login_success`, `login_failure`, 
`password_change`, `password_reset_request`, `password_reset_complete`, 
`email_change`, `account_lockout`, `account_deactivated`)
      - `ip_address` (VARCHAR)
      - `user_agent` (TEXT)
      - `metadata` (JSON, nullable — additional context)
      - `created_at` (TIMESTAMP)
   
   3. New table `account_lockout`:
      - `id` (PK)
      - `user_id` (FK → `ab_user.id`)
      - `failed_attempts` (INTEGER)
      - `locked_until` (TIMESTAMP, nullable)
      - `last_failed_at` (TIMESTAMP)
   
      Note: on FAB schema ownership: The ab\_\* tables (e.g., ab_user, ab_role, 
ab_permission_view) are owned and managed by Flask-AppBuilder's own migration 
chain. Adding custom columns to these tables introduces a maintenance risk: FAB 
upstream may alter the same tables in future releases, causing Alembic 
migration conflicts, failed upgrades, or silent schema drift. To preserve clean 
upgrade paths and avoid coupling Superset's auth enhancements to FAB's release 
cycle, this SIP introduces new, Superset-managed tables (password_reset_token, 
auth_audit_log, account_lockout) that reference ab_user.id via foreign key 
without modifying the upstream schema. This approach also simplifies a 
potential future migration away from FAB's built-in user model.
   
   **Backward compatibility:**
   
   - All new features are opt-in via configuration flags. Existing deployments 
are unaffected by default.
   - The existing `superset fab reset-password` CLI command remains unchanged.
   - The legacy FAB password change view is deprecated in favor of the new 
SPA-based component. During a transition period, the legacy route can be 
re-enabled via the config flag introduced in [[PR 
#37773](https://github.com/apache/superset/pull/37773)](https://github.com/apache/superset/pull/37773).
   - No breaking changes to existing stored URLs or bookmarks.
   
   **Rollout strategy:**
   
   - Phase 1: Secure authenticated password change (Pillar 1) + audit logging 
foundation
   - Phase 2: Self-service forgot password flow (Pillar 2) + CLI email change
   - Phase 3: Admin UI password reset (Pillar 3)
   - Phase 4: Rate limiting and lockout (Pillar 4)
   
   ### Rejected Alternatives
   
   1. **Relying entirely on Flask-AppBuilder upstream** — A PR 
([[dpgaspar/Flask-AppBuilder#1566](https://github.com/dpgaspar/Flask-AppBuilder/pull/1566)](https://github.com/dpgaspar/Flask-AppBuilder/pull/1566))
 was opened to add forgot-password to FAB itself, but has been stalled for 
years with no progress. Waiting for upstream is not viable given the community 
demand and the Superset project's migration to SPA architecture.
   
   2. **Requiring all deployments to use OIDC/SAML** — While external identity 
providers solve password management entirely, many small-to-medium deployments 
and development environments rely on `AUTH_DB`. Forcing migration to an 
external IdP would be a significant barrier to adoption.
   
   3. **Direct database SQL for password resets** — Some community workarounds 
suggest running `UPDATE ab_user SET password = '...'` directly. This is 
fragile, error-prone, bypasses all security controls, and should never be 
recommended as a supported workflow.
   
   4. **Admin-only password resets (no self-service)** — While simpler to 
implement, this still requires admin intervention for every forgotten password 
and does not scale for larger deployments. The self-service flow (Pillar 2) is 
essential for operational efficiency.
   
   5. **Implementing a full identity management system** — Building user 
lifecycle management, SSO, and identity federation within Superset itself would 
be overengineering. This SIP scopes the work to the minimum necessary for 
secure password management when using database authentication, while 
acknowledging that organizations with advanced requirements should use external 
identity providers.
   


-- 
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