roc-fnb-server/roc_fnb/website/models/user.py
2025-06-01 11:55:03 -04:00

97 lines
2.3 KiB
Python

from base64 import b64decode, b64encode
from dataclasses import dataclass
import json
from random import randbytes
from typing import Optional, Any, Self
from bson.objectid import ObjectId
import scrypt
import jwt
from roc_fnb.util.base64 import base64_encode, base64_decode
with open('private-key.pem') as file:
PRIVATE_KEY = file.read()
with open('public-key.pem') as file:
PUBLIC_KEY = file.read()
@dataclass
class JwtUser:
_id: ObjectId
email: str
name: str
moderator: bool
admin: bool
@classmethod
def from_json(cls, data: dict) -> Self:
_id = ObjectId(base64_decode(data.pop('_id')))
return cls(_id=_id, **data)
@dataclass
class User:
_id: Optional[ObjectId]
email: str
name: str
password_hash: bytes
salt: bytes
moderator: bool
admin: bool
@classmethod
def create(
cls,
email: str,
name: str,
password: str | bytes,
moderator: bool = False,
admin: bool = False,
):
"""Alternate constructor which hashes a given password"""
salt = randbytes(32)
password_hash = scrypt.hash(password, salt)
return cls(
_id=None,
email=email,
name=name,
password_hash=password_hash,
salt=salt,
moderator=moderator,
admin=admin,
)
@property
def document(self):
doc = {
"email": self.email,
"name": self.name,
"password_hash": self.password_hash,
"salt": self.salt,
"moderator": self.moderator,
"admin": self.admin,
}
if self._id is not None:
doc['_id'] = self._id
return doc
@property
def public_fields(self):
"""
Session data is visible to client scripts.
This is a feature, not a bug; client scripts may need to gather login info.
"""
return {
'_id': base64_encode(self._id.binary),
"email": self.email,
"name": self.name,
"moderator": self.moderator,
"admin": self.admin,
}
def check_password(self, password: str) -> bool:
return self.password_hash == scrypt.hash(password, self.salt)