Files
timemaster/backend/app/schemas/auth.py
T
patrickandClaude Opus 4.8 98cb2f867a
Security Audit / Python Dependency Audit (push) Has been cancelled
Security Audit / Node.js Dependency Audit (push) Has been cancelled
feat: Mandant/Reseller ohne E-Mail anlegbar (Temp-Passwort statt Einladung)
- admin_email bzw. reseller email optional; ohne E-Mail wird interne Login-
  Kennung (<name>@<slug>.local) + einmaliges Temp-Passwort erzeugt, Account
  sofort aktiv, kein Mailversand
- TenantOut/ResellerOut: initial_password (einmalig) ergänzt
- LoginRequest.email: str statt EmailStr (Kennung muss kein zustellbares
  Postfach sein; .local-Domains sind sonst nicht einloggbar)
- Frontend: E-Mail-Felder optional, CredDialog zeigt Login + Temp-Passwort einmalig
- Test: Anlage ohne E-Mail + Login mit generierten Zugangsdaten; 173/173 grün

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 01:34:23 +02:00

85 lines
2.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from pydantic import BaseModel, EmailStr, Field, model_validator
class RegisterRequest(BaseModel):
company_name: str = Field(min_length=2, max_length=255)
first_name: str = Field(min_length=1, max_length=100)
last_name: str = Field(min_length=1, max_length=100)
email: EmailStr
password: str = Field(min_length=8, max_length=128)
@model_validator(mode="after")
def password_strength(self):
pw = self.password
if not any(c.isupper() for c in pw):
raise ValueError("Password must contain at least one uppercase letter")
if not any(c.isdigit() for c in pw):
raise ValueError("Password must contain at least one digit")
return self
class LoginRequest(BaseModel):
# Login-Kennung: i.d.R. eine E-Mail, kann aber auch eine intern erzeugte
# Kennung sein (z.B. Mandanten-Admin ohne E-Mail → '<name>@<slug>.local').
# Daher kein EmailStr-Zwang das Passwort ist das eigentliche Gate.
email: str = Field(min_length=1, max_length=255)
password: str
class RefreshRequest(BaseModel):
refresh_token: str | None = None
class PasswordResetRequest(BaseModel):
email: EmailStr
class PasswordResetConfirm(BaseModel):
token: str
new_password: str = Field(min_length=8, max_length=128)
@model_validator(mode="after")
def password_strength(self):
pw = self.new_password
if not any(c.isupper() for c in pw):
raise ValueError("Password must contain at least one uppercase letter")
if not any(c.isdigit() for c in pw):
raise ValueError("Password must contain at least one digit")
return self
class TokenResponse(BaseModel):
access_token: str
refresh_token: str | None = None # Nur für API-Clients; Browser nutzt HttpOnly-Cookie
token_type: str = "bearer"
totp_required: bool = False
partial_token: str | None = None
class TotpSetupResponse(BaseModel):
secret: str # base32 secret for manual entry
otpauth_uri: str # otpauth://totp/... für QR-Code
class TotpConfirmRequest(BaseModel):
code: str = Field(min_length=6, max_length=6)
class TotpLoginRequest(BaseModel):
partial_token: str
code: str = Field(min_length=6, max_length=6)
class TotpDisableRequest(BaseModel):
password: str
code: str = Field(min_length=6, max_length=6)
class AccessTokenResponse(BaseModel):
access_token: str
token_type: str = "bearer"
class MessageResponse(BaseModel):
message: str