Coverage for seedboxsync/core/database/models/user.py: 100%

30 statements  

« prev     ^ index     » next       coverage.py v7.17.0, created at 2026-10-31 17:10 +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.""" 

8 

9import datetime 

10from typing import Self 

11from flask_login import UserMixin 

12from peewee import AutoField, BooleanField, CharField, DateTimeField 

13from werkzeug.security import check_password_hash 

14from seedboxsync.core.database.models import SeedboxSyncModel 

15 

16 

17class User(SeedboxSyncModel, UserMixin): # type: ignore[misc] 

18 """ 

19 Data Access Object (DAO) representing a user. 

20 

21 This model stores user account information, including authentication 

22 credentials, email address, account creation date, and last login timestamp. 

23 

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 is_admin (bool): Indicates if the user has administrator privileges. 

30 created (datetime): Timestamp when the user account was created. 

31 last_login (datetime): Timestamp when the user last logged in. 

32 """ 

33 

34 ORIGIN_LOCAL = "local" 

35 ORIGIN_OIDC = "oidc" 

36 ORIGIN_CHOICES = ( 

37 (ORIGIN_LOCAL, "Local"), 

38 (ORIGIN_OIDC, "OIDC"), 

39 ) 

40 

41 id = AutoField(help_text="Unique identifier of the user") 

42 username = CharField(unique=True, help_text="Username of the user") 

43 password = CharField(help_text="Salted password of the user") 

44 origin = CharField(choices=ORIGIN_CHOICES, default=ORIGIN_LOCAL, max_length=10, help_text="Origin of the user account (local or oidc)") 

45 email = CharField(unique=True, help_text="Email address of the user") 

46 is_admin = BooleanField(default=False, help_text="Indicates if the user has administrator privileges") 

47 created = DateTimeField(default=datetime.datetime.now, help_text="Timestamp when the user was created") 

48 last_login = DateTimeField(null=True, help_text="Timestamp when the user last logged in") 

49 

50 @classmethod 

51 def authenticate(cls, login: str, password: str) -> Self | None: 

52 """ 

53 Authenticate a user using their username or email address. 

54 

55 Args: 

56 login (str): Username or email address. 

57 password (str): Plain-text password to verify. 

58 

59 Returns: 

60 Self | None: The authenticated user, or None if authentication fails. 

61 """ 

62 user = cls.get_or_none((cls.username == login) | (cls.email == login)) 

63 

64 if user is None: 

65 return None 

66 

67 if not check_password_hash(user.password, password): 

68 return None 

69 

70 # Update last login timestamp 

71 user.update_last_login() 

72 

73 return user 

74 

75 def update_last_login(self) -> None: 

76 """ 

77 Update the last login timestamp for the user. 

78 

79 This method sets the `last_login` field to the current datetime and 

80 saves the change to the database. 

81 """ 

82 self.last_login = datetime.datetime.now() 

83 self.save()