|
1 |
| -"""An application user |
| 1 | +""" An application user |
| 2 | +
|
| 3 | + The aim is to provide a set of fields that are common to most |
| 4 | + applications with a good know set of practices around authentication |
| 5 | + both via password and one time passwords. |
| 6 | +
|
2 | 7 | """
|
3 | 8 |
|
4 | 9 | from typing import Optional
|
@@ -62,18 +67,38 @@ class User(
|
62 | 67 | def check_password(self, plain_text_pass):
|
63 | 68 | return verify_password(plain_text_pass, self.password)
|
64 | 69 |
|
65 |
| - def get_otp(self, digits: int, timeout: int): |
66 |
| - """ |
| 70 | + def get_otp( |
| 71 | + self, |
| 72 | + digits: int = 6, |
| 73 | + timeout: int = 30 |
| 74 | + ): |
| 75 | + """ Get the current OTP for the user |
| 76 | +
|
| 77 | + This is sent to the user via email or SMS and is used to |
| 78 | + authenticate the user. This should be different based |
| 79 | + on the timeout and the digits. |
67 | 80 | """
|
68 | 81 | otp = TOTP(self.secret, digits=digits, interval=timeout)
|
69 | 82 | return otp.now()
|
70 | 83 |
|
71 |
| - def verify_otp(self, timeout: int, window: int, token: str): |
| 84 | + def verify_otp( |
| 85 | + self, |
| 86 | + token: str, |
| 87 | + timeout: int = 30, |
| 88 | + window: int = 30 |
| 89 | + ): |
| 90 | + """ |
| 91 | + """ |
72 | 92 | otp = TOTP(self.secret, interval=timeout)
|
73 | 93 | return otp.verify(token, valid_window=window)
|
74 | 94 |
|
75 | 95 | @classmethod
|
76 | 96 | async def get_by_email(cls, session, email):
|
| 97 | + """ A custom getter where the user is found via email |
| 98 | +
|
| 99 | + The aim is to assist with finding the user by email |
| 100 | + which is handy when authenticating via passwords |
| 101 | + """ |
77 | 102 | query = cls._base_get_query().where(cls.email == email)
|
78 | 103 | try:
|
79 | 104 | results = await session.execute(query)
|
@@ -113,6 +138,19 @@ def receive_init(target, args, kwargs):
|
113 | 138 | target.otp_secret = random_base32()
|
114 | 139 |
|
115 | 140 | def encrypt_password(target, value, oldvalue, initiator):
|
| 141 | + """ Encrypt the password when it is set |
| 142 | +
|
| 143 | + This enables the application logic to simply set the plain |
| 144 | + text password and the model encrypts it on the way in. |
| 145 | +
|
| 146 | + The idea is to abstract this from the duties of the application. |
| 147 | + """ |
116 | 148 | return hash_password(value)
|
117 | 149 |
|
118 |
| -event.listen(User.password, 'set', encrypt_password, retval=True) |
| 150 | +# Support for the above method to run when the password is set |
| 151 | +event.listen( |
| 152 | + User.password, |
| 153 | + 'set', |
| 154 | + encrypt_password, |
| 155 | + retval=True |
| 156 | +) |
0 commit comments