diff --git a/docker-compose.yml b/docker-compose.yml index 23fe779..f3b8455 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,6 +24,8 @@ services: DB_PASS: gestao DB_BASE: gestao RELOAD: "True" + EMAIL_ADDRESS: "YOUR EMAIL ADDRESS" + EMAIL_PASSWORD: "YOUR-EMAIL-PASSWORD" networks: - sindpol_network diff --git a/gestao/db/migrations/versions/2023-11-25-07-13_89f33fa986f8.py b/gestao/db/migrations/versions/2023-11-25-07-13_89f33fa986f8.py new file mode 100644 index 0000000..84fc517 --- /dev/null +++ b/gestao/db/migrations/versions/2023-11-25-07-13_89f33fa986f8.py @@ -0,0 +1,42 @@ +"""rename column 'mother_date' to 'mother_name' and add columns 'password and 'religion' + +Revision ID: 89f33fa986f8 +Revises: 0084e7dffc7c +Create Date: 2023-11-25 07:13:16.617587 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "89f33fa986f8" +down_revision = "0084e7dffc7c" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + with op.batch_alter_table("user") as batch_op: + batch_op.alter_column( + "mother_date", + new_column_name="mother_name", + existing_type=sa.String(length=200), + ) + batch_op.add_column( + sa.Column("password", sa.String(200), nullable=True), + ) + batch_op.add_column( + sa.Column("religion", sa.String(200), nullable=True), + ) + + +def downgrade() -> None: + with op.batch_alter_table("user") as batch_op: + batch_op.alter_column( + "mother_name", + new_column_name="mother_date", + existing_type=sa.String(length=200), + ) + batch_op.drop_column("password") + batch_op.drop_column("religion") diff --git a/gestao/db/models/user.py b/gestao/db/models/user.py index 65559e5..b552353 100644 --- a/gestao/db/models/user.py +++ b/gestao/db/models/user.py @@ -24,7 +24,7 @@ class Meta(BaseMeta): blood_type: str = ormar.String(max_length=200) gender: str = ormar.String(max_length=200) father_name: str = ormar.String(max_length=200) - mother_date: str = ormar.String(max_length=200) + mother_name: str = ormar.String(max_length=200) position: str = ormar.String(max_length=200) occupancy: str = ormar.String(max_length=200) admission_date: date = ormar.Date() @@ -40,6 +40,8 @@ class Meta(BaseMeta): status: str = ormar.String(max_length=200, default="active") workstation: Optional[str] = ormar.String(max_length=200, nullable=True) nickname: Optional[str] = ormar.String(max_length=200, unique=True, nullable=True) + password: str = ormar.String(max_length=200, nullable=True) + religion: Optional[str] = ormar.String(max_length=200, nullable=True) dispatcher: str = ormar.String(max_length=200) dispatched_date: date = ormar.Date() created_at: datetime = ormar.DateTime(timezone=True, default=datetime.now) diff --git a/gestao/web/api/login/__init__.py b/gestao/web/api/login/__init__.py new file mode 100644 index 0000000..b0bd37b --- /dev/null +++ b/gestao/web/api/login/__init__.py @@ -0,0 +1,3 @@ +from gestao.web.api.login.views import router + +__all__ = ["router"] diff --git a/gestao/web/api/login/schemas.py b/gestao/web/api/login/schemas.py new file mode 100644 index 0000000..aadb2fc --- /dev/null +++ b/gestao/web/api/login/schemas.py @@ -0,0 +1,10 @@ +from pydantic import BaseModel + + +class AuthUserDTO(BaseModel): + registration: str + password: str + + +class RecoverPasswordDTO(BaseModel): + email: str diff --git a/gestao/web/api/login/utils.py b/gestao/web/api/login/utils.py new file mode 100644 index 0000000..30ce3c9 --- /dev/null +++ b/gestao/web/api/login/utils.py @@ -0,0 +1,37 @@ +import os +import smtplib +import email.message +import secrets +import string + + +def generate_password(length=8): + characters = string.ascii_letters + string.digits + password = "".join(secrets.choice(characters) for _ in range(length)) + return password + + +def send_email(user_name: str, user_email: str, user_message: str) -> None: + + email_data = { + "email_address": os.getenv("EMAIL_ADDRESS"), + "email_password": os.getenv("EMAIL_PASSWORD"), + "email_subject": "recover-password SINDPOL-DF", + "email_body": """ +
recover-password
+
name: {}
+
new-password: {}
+ """, + } + + msg = email.message.Message() + msg["Subject"] = email_data["email_subject"] + msg["From"] = email_data["email_address"] + msg["To"] = user_email + msg.add_header("Content-Type", "text/html") + msg.set_payload(email_data["email_body"].format(user_name, user_message)) + + s = smtplib.SMTP("smtp.gmail.com: 587") + s.starttls() + s.login(msg["From"], email_data["email_password"]) + s.sendmail(msg["From"], [msg["To"]], msg.as_string().encode("utf-8")) diff --git a/gestao/web/api/login/views.py b/gestao/web/api/login/views.py new file mode 100644 index 0000000..511fead --- /dev/null +++ b/gestao/web/api/login/views.py @@ -0,0 +1,35 @@ +import logging + +from fastapi import APIRouter, HTTPException + +from gestao.db.models.user import User +from gestao.web.api.login.schemas import AuthUserDTO, RecoverPasswordDTO +from gestao.web.api.login.utils import generate_password, send_email + +router = APIRouter() + + +@router.post("/user") +async def login_user(login_data: AuthUserDTO) -> User: + try: + return await User.objects.select_related(User.dependents).get( + **login_data.dict() + ) + except Exception: + logging.error("User not found", exc_info=True) + raise HTTPException( + status_code=404, + detail="User not found", + ) + + +@router.post("/recover_password") +async def recover_password(recover_data: RecoverPasswordDTO) -> None: + try: + user = await User.objects.get(**recover_data.dict()) + new_password = generate_password() + send_email(user.name, user.email, new_password) + await user.update(password=new_password) + except Exception: + logging.error("User not found", exc_info=True) + raise HTTPException(status_code=404, detail="User not found") diff --git a/gestao/web/api/router.py b/gestao/web/api/router.py index 3788ffd..31c7c19 100644 --- a/gestao/web/api/router.py +++ b/gestao/web/api/router.py @@ -1,8 +1,9 @@ from fastapi.routing import APIRouter -from gestao.web.api import echo, monitoring, user +from gestao.web.api import echo, monitoring, user, login api_router = APIRouter() api_router.include_router(monitoring.router) api_router.include_router(echo.router, prefix="/echo", tags=["echo"]) api_router.include_router(user.router, prefix="/users", tags=["users"]) +api_router.include_router(login.router, prefix="/login", tags=["login"]) diff --git a/gestao/web/api/user/schemas.py b/gestao/web/api/user/schemas.py index 00f1acb..2d0599c 100644 --- a/gestao/web/api/user/schemas.py +++ b/gestao/web/api/user/schemas.py @@ -28,7 +28,7 @@ class CreateUserDTO(BaseModel): blood_type: str gender: str father_name: str - mother_date: str + mother_name: str position: str occupancy: str admission_date: date @@ -46,6 +46,8 @@ class CreateUserDTO(BaseModel): dependents: Optional[List[CreateUserDependentDTO]] workstation: Optional[str] nickname: Optional[str] + password: str + religion: Optional[str] class UpdateUserDTO(BaseModel): @@ -62,7 +64,7 @@ class UpdateUserDTO(BaseModel): blood_type: Optional[str] gender: Optional[str] father_name: Optional[str] - mother_date: Optional[str] + mother_name: Optional[str] position: Optional[str] occupancy: Optional[str] admission_date: Optional[date] @@ -80,3 +82,5 @@ class UpdateUserDTO(BaseModel): workstation: Optional[str] nickname: Optional[str] status: Optional[str] + password: Optional[str] + religion: Optional[str]