auths.py 6.64 KB
Newer Older
1
from fastapi import Response, Request
2
3
4
5
from fastapi import Depends, FastAPI, HTTPException, status
from datetime import datetime, timedelta
from typing import List, Union

6
from fastapi import APIRouter, status
7
8
9
from pydantic import BaseModel
import time
import uuid
Timothy J. Baek's avatar
Timothy J. Baek committed
10
import re
11
12
13
14

from apps.web.models.auths import (
    SigninForm,
    SignupForm,
15
    UpdateProfileForm,
16
    UpdatePasswordForm,
17
18
19
20
21
22
    UserResponse,
    SigninResponse,
    Auths,
)
from apps.web.models.users import Users

Timothy J. Baek's avatar
Timothy J. Baek committed
23
24
25
26
27
28
from utils.utils import (
    get_password_hash,
    get_current_user,
    get_admin_user,
    create_token,
)
Timothy J. Baek's avatar
Timothy J. Baek committed
29
from utils.misc import parse_duration, validate_email_format
Timothy J. Baek's avatar
Timothy J. Baek committed
30
from constants import ERROR_MESSAGES
31

Timothy J. Baek's avatar
Timothy J. Baek committed
32
33
router = APIRouter()

34
35
36
37
38
############################
# GetSessionUser
############################


39
@router.get("/", response_model=UserResponse)
40
41
42
43
44
45
46
47
async def get_session_user(user=Depends(get_current_user)):
    return {
        "id": user.id,
        "email": user.email,
        "name": user.name,
        "role": user.role,
        "profile_image_url": user.profile_image_url,
    }
48
49


50
############################
51
# Update Profile
52
53
54
55
############################


@router.post("/update/profile", response_model=UserResponse)
56
57
async def update_profile(
    form_data: UpdateProfileForm, session_user=Depends(get_current_user)
58
59
):
    if session_user:
60
61
62
        user = Users.update_user_by_id(
            session_user.id,
            {"profile_image_url": form_data.profile_image_url, "name": form_data.name},
63
64
65
66
67
68
69
70
71
        )
        if user:
            return user
        else:
            raise HTTPException(400, detail=ERROR_MESSAGES.DEFAULT())
    else:
        raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)


72
73
74
75
76
############################
# Update Password
############################


77
@router.post("/update/password", response_model=bool)
78
79
80
async def update_password(
    form_data: UpdatePasswordForm, session_user=Depends(get_current_user)
):
81
82
    if session_user:
        user = Auths.authenticate_user(session_user.email, form_data.password)
83

84
85
        if user:
            hashed = get_password_hash(form_data.new_password)
Timothy J. Baek's avatar
Timothy J. Baek committed
86
            return Auths.update_user_password_by_id(user.id, hashed)
87
88
        else:
            raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_PASSWORD)
89
90
91
92
    else:
        raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)


93
94
95
96
97
98
############################
# SignIn
############################


@router.post("/signin", response_model=SigninResponse)
Timothy J. Baek's avatar
Timothy J. Baek committed
99
async def signin(request: Request, form_data: SigninForm):
100
101
    user = Auths.authenticate_user(form_data.email.lower(), form_data.password)
    if user:
Timothy J. Baek's avatar
Timothy J. Baek committed
102
103
104
105
        token = create_token(
            data={"id": user.id},
            expires_delta=parse_duration(request.app.state.JWT_EXPIRES_IN),
        )
106
107
108
109
110
111
112
113

        return {
            "token": token,
            "token_type": "Bearer",
            "id": user.id,
            "email": user.email,
            "name": user.name,
            "role": user.role,
Timothy J. Baek's avatar
Timothy J. Baek committed
114
            "profile_image_url": user.profile_image_url,
115
116
        }
    else:
Timothy J. Baek's avatar
Timothy J. Baek committed
117
        raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
118
119
120
121
122
123
124
125


############################
# SignUp
############################


@router.post("/signup", response_model=SigninResponse)
126
async def signup(request: Request, form_data: SignupForm):
127
    if not request.app.state.ENABLE_SIGNUP:
Timothy J. Baek's avatar
Timothy J. Baek committed
128
129
130
        raise HTTPException(
            status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.ACCESS_PROHIBITED
        )
131

132
    if not validate_email_format(form_data.email.lower()):
Timothy J. Baek's avatar
Timothy J. Baek committed
133
134
135
        raise HTTPException(
            status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.INVALID_EMAIL_FORMAT
        )
136

137
138
    if Users.get_user_by_email(form_data.email.lower()):
        raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN)
139

140
    try:
Timothy J. Baek's avatar
Timothy J. Baek committed
141
142
143
144
145
        role = (
            "admin"
            if Users.get_num_users() == 0
            else request.app.state.DEFAULT_USER_ROLE
        )
146
        hashed = get_password_hash(form_data.password)
147
148
149
        user = Auths.insert_new_auth(
            form_data.email.lower(), hashed, form_data.name, role
        )
150

151
        if user:
Timothy J. Baek's avatar
Timothy J. Baek committed
152
153
154
155
            token = create_token(
                data={"id": user.id},
                expires_delta=parse_duration(request.app.state.JWT_EXPIRES_IN),
            )
156
157
158
159
160
161
162
163
164
165
166
167
            # response.set_cookie(key='token', value=token, httponly=True)

            return {
                "token": token,
                "token_type": "Bearer",
                "id": user.id,
                "email": user.email,
                "name": user.name,
                "role": user.role,
                "profile_image_url": user.profile_image_url,
            }
        else:
168
            raise HTTPException(500, detail=ERROR_MESSAGES.CREATE_USER_ERROR)
169
    except Exception as err:
170
171
        raise HTTPException(500, detail=ERROR_MESSAGES.DEFAULT(err))

172
173
174
175
176
177
178

############################
# ToggleSignUp
############################


@router.get("/signup/enabled", response_model=bool)
179
180
async def get_sign_up_status(request: Request, user=Depends(get_admin_user)):
    return request.app.state.ENABLE_SIGNUP
181
182
183


@router.get("/signup/enabled/toggle", response_model=bool)
184
185
186
async def toggle_sign_up(request: Request, user=Depends(get_admin_user)):
    request.app.state.ENABLE_SIGNUP = not request.app.state.ENABLE_SIGNUP
    return request.app.state.ENABLE_SIGNUP
Timothy J. Baek's avatar
Timothy J. Baek committed
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209


############################
# Default User Role
############################


@router.get("/signup/user/role")
async def get_default_user_role(request: Request, user=Depends(get_admin_user)):
    return request.app.state.DEFAULT_USER_ROLE


class UpdateRoleForm(BaseModel):
    role: str


@router.post("/signup/user/role")
async def update_default_user_role(
    request: Request, form_data: UpdateRoleForm, user=Depends(get_admin_user)
):
    if form_data.role in ["pending", "user", "admin"]:
        request.app.state.DEFAULT_USER_ROLE = form_data.role
    return request.app.state.DEFAULT_USER_ROLE
Timothy J. Baek's avatar
Timothy J. Baek committed
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239


############################
# JWT Expiration
############################


@router.get("/token/expires")
async def get_token_expires_duration(request: Request, user=Depends(get_admin_user)):
    return request.app.state.JWT_EXPIRES_IN


class UpdateJWTExpiresDurationForm(BaseModel):
    duration: str


@router.post("/token/expires/update")
async def update_token_expires_duration(
    request: Request,
    form_data: UpdateJWTExpiresDurationForm,
    user=Depends(get_admin_user),
):
    pattern = r"^(-1|0|(-?\d+(\.\d+)?)(ms|s|m|h|d|w))$"

    # Check if the input string matches the pattern
    if re.match(pattern, form_data.duration):
        request.app.state.JWT_EXPIRES_IN = form_data.duration
        return request.app.state.JWT_EXPIRES_IN
    else:
        return request.app.state.JWT_EXPIRES_IN