Exemplo n.º 1
0
async def process_join(conn: AsyncIOMotorClient, invitation_token: str,
                       user_current: UserTokenWrapper):
    token_db: TokenDB = await get_token(conn, invitation_token)
    if token_db.used_at:
        raise StarletteHTTPException(status_code=HTTP_403_FORBIDDEN,
                                     detail="Invitation token already used")

    group_db: GroupDB
    group_db_id: str
    group_db, group_db_id = await get_group_by_id(conn,
                                                  token_db.group_id,
                                                  get_id=True)

    group_update: GroupUpdate = GroupUpdate(member=UserBase(
        **user_current.dict()))
    if token_db.subject == TokenSubject.GROUP_INVITE_CO_OWNER:
        group_update: GroupUpdate = GroupUpdate(co_owner=UserBase(
            **user_current.dict()))

    group_id_wrapper: GroupIdWrapper = GroupIdWrapper(**group_db.dict(),
                                                      id=str(group_db_id))
    group_db_updated: GroupDB
    group_db_id_updated: ObjectId
    group_db_updated, group_db_id_updated = await update_group(
        conn, group_id_wrapper, group_update)

    await update_token(
        conn, TokenUpdate(token=token_db.token, used_at=datetime.utcnow()))

    return GroupResponse(group=GroupIdWrapper(**group_db_updated.dict(),
                                              id=str(group_db_id_updated)))
Exemplo n.º 2
0
async def register(
        background_tasks: BackgroundTasks,
        user_create: UserCreate = Body(..., embed=True),
        conn: AsyncIOMotorClient = Depends(get_database),
        smtp_conn: FastMail = Depends(get_smtp),
) -> UserResponse:
    await check_availability_username_and_email(conn, user_create.email,
                                                user_create.username)
    async with await conn.start_session(
    ) as session, session.start_transaction():
        user_db: UserDB = await create_user(conn, user_create)
        token: str = await TokenUtils.wrap_user_db_data_into_token(
            user_db, subject=TokenSubject.ACCESS)
        token_activation_expires_delta = timedelta(
            minutes=settings.ACTIVATE_TOKEN_EXPIRE_MINUTES)  # 7 days
        token_activation: str = await TokenUtils.wrap_user_db_data_into_token(
            user_db,
            subject=TokenSubject.ACTIVATE,
            token_expires_delta=token_activation_expires_delta,
        )

        action_link: str = f"{settings.FRONTEND_DNS}{settings.FRONTEND_ACTIVATION_PATH}?token={token_activation}"
        await background_send_new_account_email(smtp_conn, background_tasks,
                                                user_db.email, action_link)
        return UserResponse(
            user=UserTokenWrapper(**user_db.dict(), token=token))
Exemplo n.º 3
0
async def get_user_from_invitation(
        conn: AsyncIOMotorClient = Depends(get_database),
        token: str = Depends(get_invitation_token),
) -> UserTokenWrapper:
    try:
        payload: dict = decode(token,
                               str(settings.SECRET_KEY),
                               algorithms=[settings.ALGORITHM])
        if not payload.get("subject") in (
                TokenSubject.GROUP_INVITE_CO_OWNER,
                TokenSubject.GROUP_INVITE_MEMBER,
                TokenSubject.USER_INVITE,
        ):
            raise StarletteHTTPException(
                status_code=HTTP_403_FORBIDDEN,
                detail="This is not an invitation token")
        token_data: TokenPayload = TokenPayload(**payload)
    except PyJWTError:
        raise StarletteHTTPException(status_code=HTTP_403_FORBIDDEN,
                                     detail="Could not validate invitation")
    user_db: UserDB = await get_user_by_email(conn,
                                              token_data.user_email_invited)
    if not user_db:
        raise StarletteHTTPException(status_code=HTTP_404_NOT_FOUND,
                                     detail="This user doesn't exist")

    return UserTokenWrapper(**user_db.dict(), token=token)
Exemplo n.º 4
0
async def join_via_invitation(
    user_invitation: UserTokenWrapper = Depends(get_user_from_invitation),
    user_current: UserTokenWrapper = Depends(get_current_user),
    conn: AsyncIOMotorClient = Depends(get_database),
) -> UserResponse:
    # TODO: Create a user_friends entity in database and link them.
    # TODO: Create gamification to encourage users to become part of the community
    token_db: TokenDB = await get_token(conn, user_invitation.token)
    if not token_db.used_at:
        if user_current.email == token_db.user_email_invited:
            await update_token(
                conn, TokenUpdate(token=token_db.token, used_at=datetime.utcnow())
            )
            return UserResponse(user=UserTokenWrapper(**user_current.dict()))
        raise StarletteHTTPException(
            status_code=HTTP_403_FORBIDDEN, detail="This user was not invited"
        )
    raise StarletteHTTPException(
        status_code=HTTP_403_FORBIDDEN, detail="Invitation token already used"
    )
Exemplo n.º 5
0
async def group_by_id(
        group_id: str,
        user_current: UserTokenWrapper = Depends(get_current_user),
        conn: AsyncIOMotorClient = Depends(get_database),
) -> GroupResponse:
    group_db: GroupDB = await get_group_by_id(conn, group_id)
    if group_db.user_in_group(UserBase(**user_current.dict())):
        return GroupResponse(
            group=GroupIdWrapper(**group_db.dict(), id=group_id))

    raise StarletteHTTPException(status_code=HTTP_403_FORBIDDEN,
                                 detail="User is not in the group")
Exemplo n.º 6
0
async def login(
        user_login: UserLogin = Body(..., embed=True),
        conn: AsyncIOMotorClient = Depends(get_database),
) -> UserResponse:
    user_db: UserDB = await get_user_by_email(conn,
                                              user_login.email,
                                              raise_bad_request=True)
    if not user_db.check_password(user_login.password):
        raise StarletteHTTPException(status_code=HTTP_400_BAD_REQUEST,
                                     detail="Invalid credentials")
    token: str = await TokenUtils.wrap_user_db_data_into_token(
        user_db, subject=TokenSubject.ACCESS)
    return UserResponse(user=UserTokenWrapper(**user_db.dict(), token=token))
Exemplo n.º 7
0
async def create(
        group_create: GroupCreate = Body(..., embed=True),
        user_current: UserTokenWrapper = Depends(get_current_user),
        conn: AsyncIOMotorClient = Depends(get_database),
) -> GroupResponse:
    async with await conn.start_session(
    ) as session, session.start_transaction():
        group_db: GroupDB
        group_db_id: ObjectId
        group_db, group_db_id = await create_group(
            conn, group_create, UserBase(**user_current.dict()))
        return GroupResponse(
            group=GroupIdWrapper(**group_db.dict(), id=str(group_db_id)))
Exemplo n.º 8
0
async def groups(
        user_current: UserTokenWrapper = Depends(get_current_user),
        conn: AsyncIOMotorClient = Depends(get_database),
) -> GroupsResponse:
    groups_db: List[Tuple[GroupDB, ObjectId]] = await get_groups_by_user(
        conn, UserBase(**user_current.dict()))

    groups_id_wrapper: List[GroupIdWrapper] = []
    for group_db, group_db_id in groups_db:
        groups_id_wrapper.append(
            GroupIdWrapper(**group_db.dict(), id=str(group_db_id)))

    return GroupsResponse(groups=groups_id_wrapper)
Exemplo n.º 9
0
async def update_current(
    user_update: UserUpdate = Body(..., embed=True),
    user_current: UserTokenWrapper = Depends(get_current_user),
    conn: AsyncIOMotorClient = Depends(get_database),
) -> UserResponse:
    user_update.username = (
        None if user_update.username == user_current.username else user_update.username
    )
    user_update.email = (
        None if user_update.email == user_current.email else user_update.email
    )
    await check_availability_username_and_email(
        conn, user_update.email, user_update.username
    )
    user_db: UserDB = await update_user(conn, user_current, user_update)
    return UserResponse(user=UserTokenWrapper(**user_db.dict()))
Exemplo n.º 10
0
async def leave(
        group_id: str,
        user_current: UserTokenWrapper = Depends(get_current_user),
        conn: AsyncIOMotorClient = Depends(get_database),
) -> GenericResponse:
    group_db: GroupDB = await get_group_by_id(conn, group_id)
    user_base: UserBase = UserBase(**user_current.dict())
    if group_db.user_in_group(user_base):
        group_id_wrapper: GroupIdWrapper = GroupIdWrapper(**group_db.dict(),
                                                          id=str(group_id))
        await leave_group(conn, group_id_wrapper, user_base)
        return GenericResponse(
            status=GenericStatus.COMPLETED,
            message="User left the group",
        )

    raise StarletteHTTPException(status_code=HTTP_403_FORBIDDEN,
                                 detail="User is not in the group")
Exemplo n.º 11
0
 async def _delete_inactive_users():
     conn = await get_database()
     tokens_db: List[TokenDB] = await get_tokens_by_subject_and_lt_datetime(
         conn,
         subject=TokenSubject.ACTIVATE,
         needle_datetime=datetime.utcnow(),
         used=False,
     )
     for token_db in tokens_db:
         user_db: UserDB = await get_user_by_email(conn, token_db.email)
         await delete_user(conn, UserTokenWrapper(**user_db.dict()))
         tokens_db_current_email: List[TokenDB] = await get_tokens_by_email(
             conn, token_db.email)
         for token_db_current_email in tokens_db_current_email:
             await update_token(
                 conn,
                 TokenUpdate(token=token_db_current_email.token,
                             deleted=True))
Exemplo n.º 12
0
async def get_current_user(
        conn: AsyncIOMotorClient = Depends(get_database),
        token: str = Depends(get_token),
) -> UserTokenWrapper:
    try:
        payload: dict = decode(token,
                               str(settings.SECRET_KEY),
                               algorithms=[settings.ALGORITHM])
        token_data: TokenPayload = TokenPayload(**payload)
    except PyJWTError:
        raise StarletteHTTPException(status_code=HTTP_403_FORBIDDEN,
                                     detail="Could not validate credentials")

    user_db: UserDB = await get_user_by_email(conn, token_data.email)
    if not user_db:
        raise StarletteHTTPException(status_code=HTTP_404_NOT_FOUND,
                                     detail="This user doesn't exist")

    return UserTokenWrapper(**user_db.dict(), token=token)
Exemplo n.º 13
0
async def kick(
        group_kick: GroupKick = Body(..., embed=True),
        user_current: UserTokenWrapper = Depends(get_current_user),
        conn: AsyncIOMotorClient = Depends(get_database),
) -> GenericResponse:
    group_db: GroupDB = await get_group_by_id(conn, group_kick.id)
    user_base: UserBase = UserBase(**user_current.dict())
    if group_db.user_in_group(user_base):
        user_base_is_owner: bool = group_db.user_is_owner(user_base)
        user_base_is_co_owner: bool = group_db.user_is_co_owner(user_base)
        if user_base_is_owner or user_base_is_co_owner:
            user_kick_db: UserDB = await get_user_by_email(
                conn, group_kick.email)
            user_kick: UserBase = UserBase(**user_kick_db.dict())
            if group_db.user_in_group(user_kick):
                if group_db.user_is_owner(user_kick):
                    raise StarletteHTTPException(
                        status_code=HTTP_403_FORBIDDEN,
                        detail="Owner of the group can't be kicked",
                    )
                if group_db.user_is_co_owner(
                        user_kick) and user_base_is_co_owner:
                    raise StarletteHTTPException(
                        status_code=HTTP_403_FORBIDDEN,
                        detail="Co-owner is not allowed to kick other co-owner",
                    )
                group_id_wrapper: GroupIdWrapper = GroupIdWrapper(
                    **group_db.dict(), id=str(group_kick.id))
                await leave_group(conn, group_id_wrapper, user_kick)
                return GenericResponse(
                    status=GenericStatus.COMPLETED,
                    message="User kick out of the group",
                )
            raise StarletteHTTPException(status_code=HTTP_403_FORBIDDEN,
                                         detail="User is not in the group")
        raise StarletteHTTPException(
            status_code=HTTP_403_FORBIDDEN,
            detail="User is not allowed to kick other members",
        )
    raise StarletteHTTPException(
        status_code=HTTP_403_FORBIDDEN,
        detail="Current user is not part of the group")
Exemplo n.º 14
0
async def activate(
    user_current: UserTokenWrapper = Depends(get_current_user),
    conn: AsyncIOMotorClient = Depends(get_database),
) -> UserResponse:
    token_db: TokenDB = await get_token(conn, user_current.token)
    if token_db.subject == TokenSubject.ACTIVATE:
        if not token_db.used_at:
            user_db: UserDB = await update_user(
                conn, user_current, UserUpdate(is_active=True)
            )
            await update_token(
                conn, TokenUpdate(token=token_db.token, used_at=datetime.utcnow())
            )
            return UserResponse(user=UserTokenWrapper(**user_db.dict()))
        raise StarletteHTTPException(
            status_code=HTTP_403_FORBIDDEN, detail="Token has expired"
        )
    raise StarletteHTTPException(
        status_code=HTTP_403_FORBIDDEN, detail="Invalid activation"
    )
Exemplo n.º 15
0
async def change_password(
    user_current: UserTokenWrapper = Depends(get_current_user),
    password: AnyStr = Body(..., embed=True),
    conn: AsyncIOMotorClient = Depends(get_database),
) -> UserResponse:
    token_db: TokenDB = await get_token(conn, user_current.token)
    if token_db.subject == TokenSubject.RECOVER:
        if not token_db.used_at:
            user_db: UserDB = await update_user(
                conn, user_current, UserUpdate(password=password)
            )
            await update_token(
                conn, TokenUpdate(token=token_db.token, used_at=datetime.utcnow())
            )
            return UserResponse(user=UserTokenWrapper(**user_db.dict()))
        raise StarletteHTTPException(
            status_code=HTTP_403_FORBIDDEN, detail="Token has expired"
        )
    raise StarletteHTTPException(
        status_code=HTTP_403_FORBIDDEN, detail="Invalid recovery"
    )
Exemplo n.º 16
0
async def invite_qrcode(
    user_current: UserTokenWrapper = Depends(get_current_user),
    group_invite: GroupInvite = Body(..., embed=True),
    conn: AsyncIOMotorClient = Depends(get_database),
) -> GroupInviteQRCodeResponse:
    group_db: GroupDB = await get_group_by_id(conn, group_invite.group_id)
    user_host: UserBase = UserBase(**user_current.dict())

    if group_db.user_is_owner(user_host) or group_db.user_is_co_owner(
            user_host):
        if group_invite.role == GroupRole.OWNER:
            raise StarletteHTTPException(status_code=HTTP_403_FORBIDDEN,
                                         detail="Owner role is unique")

        if group_invite.role == GroupRole.CO_OWNER:
            if group_db.user_is_co_owner(user_host):
                raise StarletteHTTPException(
                    status_code=HTTP_403_FORBIDDEN,
                    detail="User is not allowed to invite another co-owner",
                )
            return GroupInviteQRCodeResponse(
                invite_link=await process_invitation_qrcode(
                    group_invite,
                    user_host,
                    TokenSubject.GROUP_INVITE_CO_OWNER,
                    settings.GROUP_INVITE_CO_OWNER_TOKEN_EXPIRE_MINUTES,
                ))

        if group_invite.role == GroupRole.MEMBER:
            return GroupInviteQRCodeResponse(
                invite_link=await process_invitation_qrcode(
                    group_invite,
                    user_host,
                    TokenSubject.GROUP_INVITE_MEMBER,
                    settings.GROUP_INVITE_MEMBER_TOKEN_EXPIRE_MINUTES,
                ))

    raise StarletteHTTPException(status_code=HTTP_403_FORBIDDEN,
                                 detail="User is not allowed to invite")
Exemplo n.º 17
0
async def invite(
        background_tasks: BackgroundTasks,
        group_invite: GroupInvite = Body(..., embed=True),
        user_current: UserTokenWrapper = Depends(get_current_user),
        conn: AsyncIOMotorClient = Depends(get_database),
        smtp_conn: FastMail = Depends(get_smtp),
) -> GenericResponse:
    group_db: GroupDB = await get_group_by_id(conn, group_invite.group_id)
    user_host: UserBase = UserBase(**user_current.dict())

    if group_db.user_is_owner(user_host) or group_db.user_is_co_owner(
            user_host):
        if group_invite.role == GroupRole.OWNER:
            raise StarletteHTTPException(status_code=HTTP_403_FORBIDDEN,
                                         detail="Owner role is unique")

        # This is a mock that will be replace if there is a user with the desired email
        # TODO: FRONTEND will take care to register/login a user before joining a group
        user_invited: UserDB = UserDB(email=group_invite.email,
                                      first_name="",
                                      last_name="",
                                      username="")
        try:
            user_invited: UserDB = await get_user_by_email(
                conn, group_invite.email)
        except StarletteHTTPException as exc:
            if not (exc.status_code == 404
                    and exc.detail == "This user doesn't exist"):
                raise exc

        # There will be no problem with mocked user_invited, because you can't be part
        # of any group if you are not registered.
        if group_db.user_in_group(user_invited):
            raise StarletteHTTPException(status_code=HTTP_403_FORBIDDEN,
                                         detail="User already in group")

        if group_invite.role == GroupRole.CO_OWNER:
            if group_db.user_is_co_owner(user_host):
                raise StarletteHTTPException(
                    status_code=HTTP_403_FORBIDDEN,
                    detail="User is not allowed to invite another co-owner",
                )
            await process_invitation(
                background_tasks,
                smtp_conn,
                group_db,
                group_invite,
                user_host,
                user_invited,
                TokenSubject.GROUP_INVITE_CO_OWNER,
                settings.GROUP_INVITE_CO_OWNER_TOKEN_EXPIRE_MINUTES,
            )

        if group_invite.role == GroupRole.MEMBER:
            await process_invitation(
                background_tasks,
                smtp_conn,
                group_db,
                group_invite,
                user_host,
                user_invited,
                TokenSubject.GROUP_INVITE_MEMBER,
                settings.GROUP_INVITE_MEMBER_TOKEN_EXPIRE_MINUTES,
            )

        return GenericResponse(
            status=GenericStatus.RUNNING,
            message="Group invite email has been processed",
        )

    raise StarletteHTTPException(status_code=HTTP_403_FORBIDDEN,
                                 detail="User is not allowed to invite")