Coverage for seedboxsync/core/database/models/user.py: 100%
29 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-26 17:14 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-26 17:14 +0000
1#
2# Copyright (C) 2015-2026 Guillaume Kulakowski <guillaume@kulakowski.fr>
3#
4# For the full copyright and license information, please view the LICENSE
5# file that was distributed with this source code.
6#
7"""Peewee DAO model for User."""
9import datetime
10from typing import Self
11from flask_login import UserMixin
12from peewee import AutoField, CharField, DateTimeField
13from werkzeug.security import check_password_hash
14from seedboxsync.core.database.models import SeedboxSyncModel
17class User(SeedboxSyncModel, UserMixin): # type: ignore[misc]
18 """
19 Data Access Object (DAO) representing a user.
21 This model stores user account information, including authentication
22 credentials, email address, account creation date, and last login timestamp.
24 Attributes:
25 id (int): Auto-incremented primary key.
26 username (str): Unique username of the user.
27 password (str): Hashed password of the user.
28 email (str): Unique email address of the user.
29 created (datetime): Timestamp when the user account was created.
30 last_login (datetime): Timestamp when the user last logged in.
31 """
33 ORIGIN_LOCAL = "local"
34 ORIGIN_OIDC = "oidc"
35 ORIGIN_CHOICES = (
36 (ORIGIN_LOCAL, "Local"),
37 (ORIGIN_OIDC, "OIDC"),
38 )
40 id = AutoField(help_text="Unique identifier of the user")
41 username = CharField(unique=True, help_text="Username of the user")
42 password = CharField(help_text="Salted password of the user")
43 origin = CharField(choices=ORIGIN_CHOICES, default=ORIGIN_LOCAL, max_length=10, help_text="Origin of the user account (local or oidc)")
44 email = CharField(unique=True, help_text="Email address of the user")
45 created = DateTimeField(default=datetime.datetime.now, help_text="Timestamp when the user was created")
46 last_login = DateTimeField(null=True, help_text="Timestamp when the user last logged in")
48 @classmethod
49 def authenticate(cls, login: str, password: str) -> Self | None:
50 """
51 Authenticate a user using their username or email address.
53 Args:
54 login (str): Username or email address.
55 password (str): Plain-text password to verify.
57 Returns:
58 Self | None: The authenticated user, or None if authentication fails.
59 """
60 user = cls.get_or_none((cls.username == login) | (cls.email == login))
62 if user is None:
63 return None
65 if not check_password_hash(user.password, password):
66 return None
68 # Update last login timestamp
69 user.update_last_login()
71 return user
73 def update_last_login(self) -> None:
74 """
75 Update the last login timestamp for the user.
77 This method sets the `last_login` field to the current datetime and
78 saves the change to the database.
79 """
80 self.last_login = datetime.datetime.now()
81 self.save()