~homeworkprod/byceps

ref: 4e95e590e725922047822aef3b5b4682dd5c75d2 byceps/byceps/util/user_session.py -rw-r--r-- 3.3 KiB
4e95e590 — Jochen Kupperschmidt Explicitly cast values to type aliases 2 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
"""
byceps.util.user_session
~~~~~~~~~~~~~~~~~~~~~~~~

:Copyright: 2006-2021 Jochen Kupperschmidt
:License: Revised BSD (see `LICENSE` file for details)
"""

from __future__ import annotations
from enum import Enum
from typing import Optional
from uuid import UUID

from flask import session

from ..services.authentication.exceptions import AuthenticationFailed
from ..services.authentication.session.models.current_user import CurrentUser
from ..services.authentication.session import service as session_service
from ..services.user import service as user_service
from ..services.user.transfer.models import User
from ..typing import PartyID, UserID

from .authorization import get_permissions_for_user


KEY_LOCALE = 'locale'
KEY_USER_ID = 'user_id'
KEY_USER_AUTH_TOKEN = 'user_auth_token'


def start(user_id: UserID, auth_token: str, *, permanent: bool = False) -> None:
    """Initialize the user's session by putting the relevant data
    into the session cookie.
    """
    session.clear()

    session[KEY_USER_ID] = str(user_id)
    session[KEY_USER_AUTH_TOKEN] = str(auth_token)
    session.permanent = permanent


def end() -> None:
    """End the user's session by deleting the session cookie."""
    session.pop(KEY_USER_ID, None)
    session.pop(KEY_USER_AUTH_TOKEN, None)
    session.permanent = False


def get_current_user(
    required_permissions: set[Enum],
    locale: Optional[str],
    *,
    party_id: Optional[PartyID] = None,
) -> CurrentUser:
    user = get_user(party_id=party_id)

    if user is None:
        return session_service.get_anonymous_current_user(locale=locale)

    permissions = get_permissions_for_user(user.id)

    if not required_permissions.issubset(permissions):
        return session_service.get_anonymous_current_user(locale=locale)

    return session_service.get_authenticated_current_user(
        user, permissions=permissions, locale=locale
    )


def get_user(*, party_id: Optional[PartyID] = None) -> Optional[User]:
    """Return the current user if authenticated, `None` if not."""
    user_id_str = session.get(KEY_USER_ID)
    auth_token = session.get(KEY_USER_AUTH_TOKEN)

    return _load_user(user_id_str, auth_token, party_id=party_id)


def _load_user(
    user_id_str: Optional[str],
    auth_token: Optional[str],
    *,
    party_id: Optional[PartyID] = None,
) -> Optional[User]:
    """Load the user with that ID.

    Return `None` if:
    - the ID is unknown.
    - the account is not enabled.
    - the auth token is invalid.
    """
    if user_id_str is None:
        return None

    user_id = UserID(UUID(user_id_str))

    user = user_service.find_active_user(
        user_id, include_avatar=True, include_orga_flag_for_party_id=party_id
    )

    if user is None:
        return None

    # Validate auth token.
    if (auth_token is None) or not _is_auth_token_valid(user.id, auth_token):
        # Bad auth token, not logging in.
        return None

    return user


def _is_auth_token_valid(user_id: UserID, auth_token: str) -> bool:
    try:
        session_service.authenticate_session(user_id, auth_token)
    except AuthenticationFailed:
        return False
    else:
        return True


def get_locale() -> Optional[str]:
    """Return the locale set in the session, if any."""
    return session.get(KEY_LOCALE)


def set_locale(locale: str) -> None:
    """Set locale for the session."""
    session[KEY_LOCALE] = locale