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]
