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

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, 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 created (datetime): Timestamp when the user account was created. 

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

31 """ 

32 

33 ORIGIN_LOCAL = "local" 

34 ORIGIN_OIDC = "oidc" 

35 ORIGIN_CHOICES = ( 

36 (ORIGIN_LOCAL, "Local"), 

37 (ORIGIN_OIDC, "OIDC"), 

38 ) 

39 

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") 

47 

48 @classmethod 

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

50 """ 

51 Authenticate a user using their username or email address. 

52 

53 Args: 

54 login (str): Username or email address. 

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

56 

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)) 

61 

62 if user is None: 

63 return None 

64 

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

66 return None 

67 

68 # Update last login timestamp 

69 user.update_last_login() 

70 

71 return user 

72 

73 def update_last_login(self) -> None: 

74 """ 

75 Update the last login timestamp for the user. 

76 

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()