04/16 Upload codebase
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
from datetime import datetime
|
||||
from flask_login import UserMixin
|
||||
from argon2 import PasswordHasher
|
||||
from argon2.exceptions import VerifyMismatchError, VerificationError, InvalidHashError
|
||||
from sqlalchemy.dialects.mysql import INTEGER
|
||||
|
||||
from app import db
|
||||
|
||||
|
||||
class User(db.Model, UserMixin):
|
||||
__tablename__ = 'users'
|
||||
|
||||
id = db.Column(INTEGER(unsigned=True), autoincrement=True, primary_key=True)
|
||||
email = db.Column(db.String(255), unique=True, nullable=False, index=True)
|
||||
# master_hash: Argon2id hash of the client-derived PBKDF2 auth_hash
|
||||
# The raw master password is NEVER sent to or stored on the server.
|
||||
master_hash = db.Column(db.String(255), nullable=False)
|
||||
# enc_key_salt: random 16-byte salt (base64) generated at registration.
|
||||
# Returned to the client on login so it can re-derive the AES-256-GCM vault key.
|
||||
# The server never uses this for decryption — it is opaque to us.
|
||||
enc_key_salt = db.Column(db.String(64), nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
|
||||
last_login = db.Column(db.DateTime, nullable=True)
|
||||
# TOTP / MFA
|
||||
totp_secret = db.Column(db.String(64), nullable=True)
|
||||
totp_enabled = db.Column(db.Boolean, default=False, nullable=False)
|
||||
# ECDH P-256 sharing keypair
|
||||
# Public key: raw uncompressed point (65 bytes), base64-encoded, stored plaintext
|
||||
sharing_public_key = db.Column(db.Text, nullable=True)
|
||||
# Private key: JWK, AES-256-GCM encrypted with the user's vault key
|
||||
sharing_private_key_enc = db.Column(db.Text, nullable=True)
|
||||
sharing_private_key_iv = db.Column(db.String(64), nullable=True)
|
||||
|
||||
folders = db.relationship('Folder', backref='owner', lazy='dynamic', cascade='all, delete-orphan')
|
||||
vault_items = db.relationship('VaultItem', backref='owner', lazy='dynamic', cascade='all, delete-orphan')
|
||||
|
||||
def check_password(self, auth_hash: str) -> bool:
|
||||
ph = PasswordHasher()
|
||||
try:
|
||||
return ph.verify(self.master_hash, auth_hash)
|
||||
except (VerifyMismatchError, VerificationError, InvalidHashError):
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
return f'<User {self.email}>'
|
||||
Reference in New Issue
Block a user