Post work PR #933

This commit is contained in:
maziggy
2026-04-13 13:53:30 +02:00
parent ba1c97c808
commit 2f0cca186b
7 changed files with 506 additions and 470 deletions
+2
View File
@@ -249,6 +249,8 @@ Perfect for remote print farms, traveling makers, or accessing your home printer
- Admin creates users with email — system sends secure random password automatically
- Users can reset their own password from the login screen (no admin needed)
- Customizable email templates (welcome email, password reset)
- **Two-Factor Authentication (TOTP + Email OTP)** — Per-user opt-in 2FA compatible with Google Authenticator, Authy, 2FAS and any standard TOTP app, or a 6-digit code delivered by email. Each user gets 10 single-use backup codes. Brute-force-protected (per-user + per-IP rate limits), replay-protected (same code cannot be accepted twice in the same 30 s window), and the pre-auth token is a single-use DB-backed challenge bound to the browser session via an HttpOnly cookie.
- **Single Sign-On (OIDC / SSO)** — Log in via PocketID, Authentik, Keycloak, or any standards-compliant OIDC provider. PKCE (S256) for public clients, `email_verified` gating, issuer & `aud`/`nonce` validation, opt-in account linking via verified email, optional auto-provisioning of new BamBuddy accounts, and strict SSRF hardening on every URL pulled from the OIDC discovery document (scheme + private/loopback/link-local IP checks).
- **Per-user email notifications** — Users receive email alerts for their own print jobs (start, complete, failed, stopped) with individual toggle controls
</td>
+5 -1
View File
@@ -1471,7 +1471,11 @@ async def run_migrations(conn):
# Migration: Add password_changed_at to users (M-R7-B)
# Tracks the last time a user's password was changed/reset. JWTs whose iat
# predates this timestamp are rejected in all six auth validation paths.
await _safe_execute(conn, "ALTER TABLE users ADD COLUMN password_changed_at DATETIME")
# R4 fix: TIMESTAMP is accepted by both SQLite and PostgreSQL; DATETIME
# is rejected by Postgres ("type 'datetime' does not exist"), which made
# _safe_execute swallow the error and leave existing Postgres installs
# without the column — causing UndefinedColumnError on every User query.
await _safe_execute(conn, "ALTER TABLE users ADD COLUMN password_changed_at TIMESTAMP")
# Migration: Back-fill password_changed_at = created_at for existing users (I2).
# Users who never changed their password would have NULL here, meaning old
+26
View File
@@ -437,6 +437,18 @@ export function SettingsPage() {
queryFn: () => api.getLDAPStatus(),
});
// Tab-indicator queries: green bullet when 2FA is enabled for the current
// user, or when at least one OIDC provider is configured and enabled.
const { data: twoFAStatus } = useQuery({
queryKey: ['twoFAStatus'],
queryFn: () => api.get2FAStatus(),
});
const { data: oidcProvidersAll = [] } = useQuery({
queryKey: ['oidcProvidersAll'],
queryFn: () => api.getOIDCProvidersAll(),
enabled: isAdmin,
});
// User management queries and mutations
const { data: usersData = [], isLoading: usersLoading } = useQuery({
queryKey: ['users'],
@@ -4415,6 +4427,13 @@ export function SettingsPage() {
>
<Shield className="w-4 h-4" />
{t('settings.tabs.twoFa')}
<span
className={`w-2 h-2 rounded-full ${
twoFAStatus?.totp_enabled || twoFAStatus?.email_otp_enabled
? 'bg-green-400'
: 'bg-bambu-gray/40'
}`}
/>
</button>
{isAdmin && (
<button
@@ -4427,6 +4446,13 @@ export function SettingsPage() {
>
<Globe className="w-4 h-4" />
{t('settings.tabs.oidc')}
<span
className={`w-2 h-2 rounded-full ${
oidcProvidersAll.some((p) => p.is_enabled)
? 'bg-green-400'
: 'bg-bambu-gray/40'
}`}
/>
</button>
)}
</div>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+5 -2
View File
@@ -3,6 +3,9 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<!-- L-4: Restrict Referer header to origin-only on cross-origin navigation so
sensitive tokens in query parameters are not leaked to third-party servers. -->
<meta name="referrer" content="strict-origin-when-cross-origin" />
<title>Bambuddy</title>
<!-- PWA Meta Tags -->
@@ -23,8 +26,8 @@
<!-- Splash screens for iOS -->
<link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
<script type="module" crossorigin src="/assets/index-D7ghDKX2.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-B1-InwHl.css">
<script type="module" crossorigin src="/assets/index-BFeV6ocI.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-B7mnhxng.css">
</head>
<body>
<div id="root"></div>