♻️ Refactor ohmyapi_auth
- remove middlewares (should come from user) - save symmetrically hased e-mail instead of raw
This commit is contained in:
parent
cc3872cf74
commit
33a9c94042
2 changed files with 32 additions and 5 deletions
|
|
@ -1 +1 @@
|
||||||
from . import middlewares, models, permissions, routes
|
from . import models, permissions, routes
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,40 @@
|
||||||
|
import hmac
|
||||||
|
import hashlib
|
||||||
|
import base64
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
from secrets import token_bytes
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from passlib.context import CryptContext
|
from passlib.context import CryptContext
|
||||||
from tortoise.contrib.pydantic import pydantic_queryset_creator
|
from tortoise.contrib.pydantic import pydantic_queryset_creator
|
||||||
|
|
||||||
from ohmyapi.db import Model, field, pre_delete, pre_save
|
from ohmyapi.db import Model, field, Q
|
||||||
from ohmyapi.router import HTTPException
|
from ohmyapi.router import HTTPException
|
||||||
|
|
||||||
|
import settings
|
||||||
|
|
||||||
pwd_context = CryptContext(schemes=["argon2"], deprecated="auto")
|
pwd_context = CryptContext(schemes=["argon2"], deprecated="auto")
|
||||||
|
SECRET_KEY = getattr(settings, "SECRET_KEY", "OhMyAPI Secret Key")
|
||||||
|
|
||||||
|
|
||||||
|
def hmac_hash(data: str) -> str:
|
||||||
|
digest = hmac.new(SECRET_KEY.encode("UTF-8"), data.encode("utf-8"), hashlib.sha256).digest()
|
||||||
|
return base64.urlsafe_b64encode(digest).decode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
class Group(Model):
|
class Group(Model):
|
||||||
id: UUID = field.data.UUIDField(pk=True)
|
id: UUID = field.data.UUIDField(pk=True)
|
||||||
name: str = field.CharField(max_length=42, index=True)
|
name: str = field.CharField(max_length=42, index=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name if self.name else ""
|
||||||
|
|
||||||
|
|
||||||
class User(Model):
|
class User(Model):
|
||||||
id: UUID = field.data.UUIDField(pk=True)
|
id: UUID = field.data.UUIDField(pk=True)
|
||||||
email: str = field.CharField(max_length=255, unique=True, index=True)
|
|
||||||
username: str = field.CharField(max_length=150, unique=True)
|
username: str = field.CharField(max_length=150, unique=True)
|
||||||
|
email_hash: Optional[str] = field.CharField(max_length=255, unique=True, index=True)
|
||||||
password_hash: str = field.CharField(max_length=128)
|
password_hash: str = field.CharField(max_length=128)
|
||||||
is_admin: bool = field.BooleanField(default=False)
|
is_admin: bool = field.BooleanField(default=False)
|
||||||
is_staff: bool = field.BooleanField(default=False)
|
is_staff: bool = field.BooleanField(default=False)
|
||||||
|
|
@ -28,18 +43,30 @@ class User(Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
class Schema:
|
class Schema:
|
||||||
exclude = ("password_hash",)
|
exclude = ["password_hash", "email_hash"]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
fields = {
|
||||||
|
'username': self.username if self.username else "-",
|
||||||
|
'is_admin': 'y' if self.is_admin else 'n',
|
||||||
|
'is_staff': 'y' if self.is_staff else 'n',
|
||||||
|
}
|
||||||
|
return ' '.join([f"{k}:{v}" for k, v in fields.items()])
|
||||||
|
|
||||||
def set_password(self, raw_password: str) -> None:
|
def set_password(self, raw_password: str) -> None:
|
||||||
"""Hash and store the password."""
|
"""Hash and store the password."""
|
||||||
self.password_hash = pwd_context.hash(raw_password)
|
self.password_hash = pwd_context.hash(raw_password)
|
||||||
|
|
||||||
|
def set_email(self, new_email: str) -> None:
|
||||||
|
"""Hash and set the e-mail address."""
|
||||||
|
self.email_hash = hmac_hash(email)
|
||||||
|
|
||||||
def verify_password(self, raw_password: str) -> bool:
|
def verify_password(self, raw_password: str) -> bool:
|
||||||
"""Verify a plaintext password against the stored hash."""
|
"""Verify a plaintext password against the stored hash."""
|
||||||
return pwd_context.verify(raw_password, self.password_hash)
|
return pwd_context.verify(raw_password, self.password_hash)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def authenticate(cls, username: str, password: str) -> Optional["User"]:
|
async def authenticate_username(cls, username: str, password: str) -> Optional["User"]:
|
||||||
"""Authenticate a user by username and password."""
|
"""Authenticate a user by username and password."""
|
||||||
user = await cls.filter(username=username).first()
|
user = await cls.filter(username=username).first()
|
||||||
if user and user.verify_password(password):
|
if user and user.verify_password(password):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue