PhotosAPI/extensions/users.py

146 lines
4.8 KiB
Python
Raw Normal View History

2023-06-23 12:25:27 +03:00
import logging
2023-01-09 16:01:30 +02:00
from datetime import datetime, timedelta
2023-06-22 14:17:53 +03:00
from uuid import uuid1
from fastapi import Depends, Form
from fastapi.responses import Response, UJSONResponse
from starlette.status import HTTP_204_NO_CONTENT
2023-03-12 15:59:13 +02:00
from classes.exceptions import (
UserAlreadyExists,
UserCredentialsInvalid,
UserEmailCodeInvalid,
)
2023-01-09 16:01:30 +02:00
from modules.app import app
2023-06-22 14:17:53 +03:00
from modules.database import col_albums, col_emails, col_photos, col_users, col_videos
2023-01-09 16:01:30 +02:00
from modules.mailer import mail_sender
2023-06-22 14:17:53 +03:00
from modules.scheduler import scheduler
2023-01-09 16:01:30 +02:00
from modules.security import (
User,
get_current_active_user,
get_password_hash,
get_user,
2023-03-12 15:59:13 +02:00
verify_password,
2023-01-09 16:01:30 +02:00
)
2023-06-23 12:25:27 +03:00
from modules.utils import configGet
logger = logging.getLogger(__name__)
2023-01-09 16:01:30 +02:00
async def send_confirmation(user: str, email: str):
confirmation_code = str(uuid1())
try:
mail_sender.sendmail(
from_addr=configGet("sender", "mailer", "smtp"),
to_addrs=email,
2023-03-12 15:59:13 +02:00
msg=f'From: {configGet("sender", "mailer", "smtp")}\nSubject: Email confirmation\n\n'
+ configGet(
"message", "mailer", "messages", "registration_confirmation"
).format(
configGet("external_address")
+ f"/users/{user}/confirm?code={confirmation_code}"
),
)
2023-08-14 14:44:07 +03:00
await col_emails.insert_one(
2023-03-12 15:59:13 +02:00
{"user": user, "email": email, "used": False, "code": confirmation_code}
2023-01-09 16:01:30 +02:00
)
2023-06-23 12:25:27 +03:00
logger.info(
"Sent confirmation email to '%s' with code %s", email, confirmation_code
)
2023-08-14 14:44:07 +03:00
except Exception as exc:
logger.error("Could not send confirmation email to '%s' due to: %s", email, exc)
2023-01-09 16:01:30 +02:00
@app.get("/users/me/", response_model=User)
async def user_me(current_user: User = Depends(get_current_active_user)):
return current_user
2023-02-16 15:55:03 +02:00
user_confirm_responses = {
2023-02-16 16:04:28 +02:00
200: {
"description": "Successful Response",
"content": {
"application/json": {
2023-03-12 15:59:13 +02:00
"example": {"detail": configGet("email_confirmed", "messages")}
2023-02-16 16:04:28 +02:00
}
2023-03-12 15:59:13 +02:00
},
2023-02-16 16:04:28 +02:00
},
2023-03-12 15:59:13 +02:00
400: UserEmailCodeInvalid().openapi,
2023-02-16 15:55:03 +02:00
}
2023-01-09 16:01:30 +02:00
if configGet("registration_requires_confirmation") is True:
2023-03-12 15:59:13 +02:00
@app.get(
"/users/{user}/confirm",
response_class=UJSONResponse,
responses=user_confirm_responses,
)
@app.patch(
"/users/{user}/confirm",
response_class=UJSONResponse,
responses=user_confirm_responses,
)
2023-01-09 16:01:30 +02:00
async def user_confirm(user: str, code: str):
2023-08-14 14:44:07 +03:00
confirm_record = await col_emails.find_one(
2023-03-12 15:59:13 +02:00
{"user": user, "code": code, "used": False}
)
2023-01-09 16:01:30 +02:00
if confirm_record is None:
2023-02-16 15:55:03 +02:00
raise UserEmailCodeInvalid()
2023-08-14 14:44:07 +03:00
await col_emails.find_one_and_update(
2023-03-12 15:59:13 +02:00
{"_id": confirm_record["_id"]}, {"$set": {"used": True}}
)
2023-08-14 14:44:07 +03:00
await col_users.find_one_and_update(
2023-03-12 15:59:13 +02:00
{"user": confirm_record["user"]}, {"$set": {"disabled": False}}
)
return UJSONResponse({"detail": configGet("email_confirmed", "messages")})
2023-01-09 16:01:30 +02:00
2023-03-12 15:59:13 +02:00
user_create_responses = {409: UserAlreadyExists().openapi}
2023-01-09 16:01:30 +02:00
if configGet("registration_enabled") is True:
2023-03-12 15:59:13 +02:00
@app.post(
"/users", status_code=HTTP_204_NO_CONTENT, responses=user_create_responses
)
async def user_create(
user: str = Form(), email: str = Form(), password: str = Form()
):
2023-08-14 14:44:07 +03:00
if (await col_users.find_one({"user": user})) is not None:
2023-02-16 15:55:03 +02:00
raise UserAlreadyExists()
2023-08-14 14:44:07 +03:00
await col_users.insert_one(
2023-03-12 15:59:13 +02:00
{
"user": user,
"email": email,
"hash": get_password_hash(password),
"disabled": configGet("registration_requires_confirmation"),
}
)
2023-01-09 16:01:30 +02:00
if configGet("registration_requires_confirmation") is True:
2023-03-12 15:59:13 +02:00
scheduler.add_job(
send_confirmation,
trigger="date",
run_date=datetime.now() + timedelta(seconds=1),
kwargs={"user": user, "email": email},
)
2023-01-09 16:01:30 +02:00
return Response(status_code=HTTP_204_NO_CONTENT)
2023-03-12 15:59:13 +02:00
user_delete_responses = {401: UserCredentialsInvalid().openapi}
@app.delete(
"/users/me/", status_code=HTTP_204_NO_CONTENT, responses=user_delete_responses
)
async def user_delete(
password: str = Form(), current_user: User = Depends(get_current_active_user)
):
2023-08-14 14:44:07 +03:00
user = await get_user(current_user.user)
2023-01-09 16:01:30 +02:00
if not user:
return False
if not verify_password(password, user.hash):
2023-02-16 15:55:03 +02:00
raise UserCredentialsInvalid()
2023-08-14 14:44:07 +03:00
await col_users.delete_many({"user": current_user.user})
await col_emails.delete_many({"user": current_user.user})
await col_photos.delete_many({"user": current_user.user})
await col_videos.delete_many({"user": current_user.user})
await col_albums.delete_many({"user": current_user.user})
2023-03-12 15:59:13 +02:00
return Response(status_code=HTTP_204_NO_CONTENT)