diff options
author | pacien | 2021-07-25 21:53:59 +0200 |
---|---|---|
committer | pacien | 2021-07-25 21:53:59 +0200 |
commit | 7f11aa00673b0f77523db44969699c54289ace5b (patch) | |
tree | 77ed092b04460e263532d76843945a05a1229d58 /app/app_account.py | |
parent | 1a74f19cd68e3b7264621e7231475c6b25505e6d (diff) | |
download | uge_l2_rdbms_python_proto-7f11aa00673b0f77523db44969699c54289ace5b.tar.gz |
app: working web prototype
Diffstat (limited to 'app/app_account.py')
-rw-r--r-- | app/app_account.py | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/app/app_account.py b/app/app_account.py new file mode 100644 index 0000000..3f4869d --- /dev/null +++ b/app/app_account.py | |||
@@ -0,0 +1,87 @@ | |||
1 | # UGE / L2 / Intro to relational databases / Python project prototype | ||
2 | # Author: Pacien TRAN-GIRARD | ||
3 | # Licence: EUPL-1.2 | ||
4 | |||
5 | from fastapi import APIRouter, Depends, Request, Form, status | ||
6 | |||
7 | from passlib.context import CryptContext | ||
8 | import re | ||
9 | |||
10 | from embrace.exceptions import IntegrityError | ||
11 | from psycopg2.errors import UniqueViolation | ||
12 | |||
13 | from app_sessions import UserSession | ||
14 | from app_database import db_transaction | ||
15 | |||
16 | |||
17 | # Password hashing context. | ||
18 | # Handles proper salting and migration automatically. | ||
19 | password_ctx = CryptContext(schemes=['bcrypt'], deprecated='auto') | ||
20 | |||
21 | username_pattern = re.compile(r'^[a-zA-Z0-9-_]{4,16}$') | ||
22 | |||
23 | router = APIRouter() | ||
24 | |||
25 | |||
26 | @router.get('/') | ||
27 | def homepage( | ||
28 | session: UserSession=Depends(UserSession), | ||
29 | ): | ||
30 | if session.is_logged_in(): | ||
31 | return 'Welcome!' | ||
32 | |||
33 | return 'Homepage here.' | ||
34 | |||
35 | |||
36 | @router.post('/account/register') | ||
37 | def account_register( | ||
38 | session: UserSession=Depends(UserSession), | ||
39 | username: str=Form(...), | ||
40 | password: str=Form(...), | ||
41 | ): | ||
42 | try: | ||
43 | if username_pattern.match(username) is None: | ||
44 | return 'error: Invalid username format.' | ||
45 | |||
46 | if not 4 <= len(password) <= 32: | ||
47 | return 'error: Invalid password length.' | ||
48 | |||
49 | hash = password_ctx.hash(password) | ||
50 | with db_transaction() as tx: | ||
51 | user = tx.create_account(username=username, password_hash=hash) | ||
52 | |||
53 | session.login(user.id) | ||
54 | return 'Account succesfully created. Welcome!' | ||
55 | |||
56 | except IntegrityError as exception: | ||
57 | if isinstance(exception.__cause__, UniqueViolation): | ||
58 | return 'error: This username is already taken.' | ||
59 | else: | ||
60 | raise exception | ||
61 | |||
62 | |||
63 | @router.post('/account/login') | ||
64 | def session_login( | ||
65 | session: UserSession=Depends(UserSession), | ||
66 | username: str=Form(...), | ||
67 | password: str=Form(...), | ||
68 | ): | ||
69 | with db_transaction() as tx: | ||
70 | user = tx.fetch_account_username(username=username) | ||
71 | |||
72 | if user is not None and password_ctx.verify(password, user.password_hash): | ||
73 | session.login(user.id) | ||
74 | return 'Welcome back!' | ||
75 | else: | ||
76 | return 'error: Invalid credentials.' | ||
77 | |||
78 | |||
79 | @router.post('/account/logout') | ||
80 | def session_logout( | ||
81 | session: UserSession=Depends(UserSession), | ||
82 | ): | ||
83 | if session.is_logged_in(): | ||
84 | session.logout() | ||
85 | return 'You have been successfully logged out.' | ||
86 | |||
87 | return 'Nothing to do' | ||