- Add POST /api/auth/change-username (password + OTP when 2FA is on, format/reserved-name checks, uniqueness) and a Change Username modal in Account.vue; use the real username for the 2FA provisioning URI. - Stop creating the built-in admin/password user; the initial admin is seeded only from OVPMON_INITIAL_ADMIN_USER/PASSWORD. - Reject 2FA-pending temporary tokens on all protected routes (Flask token_required, Profiler verify_token); only /api/auth/verify-2fa accepts them. - Stop logging the OTP and TOTP secret in enable_2fa. - Profiler: validate profile username (pattern + realpath checks in schema, router, pki and generator) to prevent path traversal. - Restrict CORS to the panel origin in Profiler and Flask APIs. - UI: header username no longer sticks to the hardcoded Admin fallback; it is synced from /user/me and updated after a rename. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
87 lines
2.5 KiB
Python
87 lines
2.5 KiB
Python
from pydantic import BaseModel, Field
|
|
from typing import List, Optional, Literal
|
|
from datetime import datetime
|
|
|
|
# --- PKI Settings Schemas ---
|
|
class PKISettingBase(BaseModel):
|
|
fqdn_ca: str = "ovpn-ca"
|
|
fqdn_server: str = "ovpn-srv"
|
|
easyrsa_dn: str = "cn_only"
|
|
easyrsa_req_country: str = "RU"
|
|
easyrsa_req_province: str = "Moscow"
|
|
easyrsa_req_city: str = "Moscow"
|
|
easyrsa_req_org: str = "SomeORG"
|
|
easyrsa_req_email: str = "info@someorg.local"
|
|
easyrsa_req_ou: str = "IT"
|
|
easyrsa_key_size: int = 2048
|
|
easyrsa_ca_expire: int = 3650
|
|
easyrsa_cert_expire: int = 3649
|
|
easyrsa_cert_renew: int = 30
|
|
easyrsa_crl_days: int = 3649
|
|
easyrsa_batch: bool = True
|
|
|
|
class PKISettingUpdate(PKISettingBase):
|
|
pass
|
|
|
|
class PKISetting(PKISettingBase):
|
|
id: int
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
# --- System Settings Schemas ---
|
|
class SystemSettingsBase(BaseModel):
|
|
protocol: Literal['tcp', 'udp'] = "udp"
|
|
port: int = 1194
|
|
vpn_network: str = "172.20.1.0"
|
|
vpn_netmask: str = "255.255.255.0"
|
|
tunnel_type: Literal['FULL', 'SPLIT'] = "FULL"
|
|
split_routes: List[str] = Field(default_factory=list)
|
|
duplicate_cn: bool = False
|
|
crl_verify: bool = False
|
|
client_to_client: bool = False
|
|
user_defined_dns: bool = False
|
|
dns_servers: List[str] = Field(default_factory=list)
|
|
user_defined_cdscripts: bool = False
|
|
connect_script: str = ""
|
|
disconnect_script: str = ""
|
|
management_interface: bool = False
|
|
management_interface_address: str = "127.0.0.1"
|
|
management_interface_address: str = "127.0.0.1"
|
|
management_port: int = 7505
|
|
public_ip: Optional[str] = None
|
|
tun_mtu: Optional[int] = None
|
|
mssfix: Optional[int] = None
|
|
|
|
class SystemSettingsUpdate(SystemSettingsBase):
|
|
pass
|
|
|
|
class SystemSettings(SystemSettingsBase):
|
|
id: int
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
class ConfigResponse(BaseModel):
|
|
server: Optional[SystemSettings] = None
|
|
pki: Optional[PKISetting] = None
|
|
|
|
# --- User Profile Schemas ---
|
|
class UserProfileBase(BaseModel):
|
|
username: str = Field(..., pattern=r"^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$")
|
|
|
|
class UserProfileCreate(UserProfileBase):
|
|
pass
|
|
|
|
class UserProfile(UserProfileBase):
|
|
id: int
|
|
status: str
|
|
created_at: datetime
|
|
revoked_at: Optional[datetime] = None
|
|
expiration_date: Optional[datetime] = None
|
|
days_remaining: Optional[int] = None
|
|
is_revoked: bool = False
|
|
is_expired: bool = False
|
|
file_path: Optional[str] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|