Beispiel #1
0
def get_token(
        authorization: Optional[str] = Header(None),
        activation: Optional[str] = Header(None),
        recovery: Optional[str] = Header(None),
) -> str:
    token: str
    if authorization:
        prefix, token = authorization.split(" ")
        if settings.JWT_TOKEN_PREFIX != prefix:
            raise StarletteHTTPException(status_code=HTTP_403_FORBIDDEN,
                                         detail="Invalid authorization")
        return token
    if activation:
        prefix, token = activation.split(" ")
        if settings.JWT_TOKEN_PREFIX != prefix:
            raise StarletteHTTPException(status_code=HTTP_403_FORBIDDEN,
                                         detail="Invalid activation")
        return token
    if recovery:
        prefix, token = recovery.split(" ")
        if settings.JWT_TOKEN_PREFIX != prefix:
            raise StarletteHTTPException(status_code=HTTP_403_FORBIDDEN,
                                         detail="Invalid recover")
        return token

    raise StarletteHTTPException(status_code=HTTP_403_FORBIDDEN,
                                 detail="Invalid header")
Beispiel #2
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)
Beispiel #3
0
def get_single_entry(  # pylint: disable=too-many-arguments
    backend: orm.implementation.Backend,
    collection: AiidaCollection,
    entry_id: str,
    response: EntryResponseOne,
    request: Request,
    params: SingleEntryQueryParams,
) -> EntryResponseOne:
    params.filter = f"id={entry_id}"
    results, data_returned, more_data_available, data_available, fields = collection.find(
        backend, params
    )

    if more_data_available:
        raise StarletteHTTPException(
            status_code=500,
            detail=f"more_data_available MUST be False for single entry response, however it is {more_data_available}",
        )

    links = ToplevelLinks(next=None)

    if fields and results is not None:
        results = handle_response_fields(results, fields)[0]

    return response(
        links=links,
        data=results,
        meta=meta_values(
            str(request.url), data_returned, data_available, more_data_available
        ),
    )
Beispiel #4
0
async def recover(
    background_tasks: BackgroundTasks,
    user_agent: Optional[str] = Header(None),
    user_recover: UserRecover = Body(..., embed=True),
    conn: AsyncIOMotorClient = Depends(get_database),
    smtp_conn: FastMail = Depends(get_smtp),
) -> GenericResponse:
    user_db: UserDB = await get_user_by_email(conn, user_recover.email)
    if user_db.username == user_recover.username:
        os: str
        browser: str
        os, browser = simple_detect(user_agent)
        token_recovery_expires_delta = timedelta(minutes=60 * 24 * 1)  # 24 hours
        token_recovery: str = await TokenUtils.wrap_user_db_data_into_token(
            user_db,
            subject=TokenSubject.RECOVER,
            token_expires_delta=token_recovery_expires_delta,
        )
        action_link: str = f"{settings.FRONTEND_DNS}{settings.FRONTEND_RECOVERY_PATH}?token={token_recovery}"
        await background_send_recovery_email(
            smtp_conn, background_tasks, user_db.email, action_link, os, browser
        )
        return GenericResponse(
            status=GenericStatus.RUNNING,
            message="Recovery account email has been processed",
        )
    raise StarletteHTTPException(
        status_code=HTTP_404_NOT_FOUND, detail="This user doesn't exist"
    )
Beispiel #5
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)))
Beispiel #6
0
def get_entry_info(request: Request, entry: str):
    from optimade.models import EntryInfoResource

    valid_entry_info_endpoints = ENTRY_INFO_SCHEMAS.keys()
    if entry not in valid_entry_info_endpoints:
        raise StarletteHTTPException(
            status_code=404,
            detail=
            f"Entry info not found for {entry}, valid entry info endpoints are: {valid_entry_info_endpoints}",
        )

    schema = ENTRY_INFO_SCHEMAS[entry]()
    queryable_properties = {"id", "type", "attributes"}
    properties = retrieve_queryable_properties(schema, queryable_properties)

    output_fields_by_format = {"json": list(properties.keys())}

    return EntryInfoResponse(
        meta=meta_values(str(request.url), 1, 1, more_data_available=False),
        data=EntryInfoResource(
            formats=list(output_fields_by_format.keys()),
            description=schema.get("description", "Entry Resources"),
            properties=properties,
            output_fields_by_format=output_fields_by_format,
        ),
    )
Beispiel #7
0
async def get_group_invitation(
        token: str = Depends(get_invitation_token), ) -> str:
    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")
        return token
    except PyJWTError:
        raise StarletteHTTPException(status_code=HTTP_403_FORBIDDEN,
                                     detail="Could not validate invitation")
Beispiel #8
0
async def join(
        user_invitation: UserTokenWrapper = Depends(get_user_from_invitation),
        user_current: UserTokenWrapper = Depends(get_current_user),
        conn: AsyncIOMotorClient = Depends(get_database),
) -> GroupResponse:
    if user_current.email == user_invitation.email:
        return await process_join(conn, user_invitation.token, user_current)

    raise StarletteHTTPException(status_code=HTTP_403_FORBIDDEN,
                                 detail="This user was not invited")
Beispiel #9
0
def check_token_object(
        token_object: dict,
        get_id: bool) -> Union[Tuple[TokenDB, ObjectId], TokenDB]:
    if token_object:
        if get_id:
            return TokenDB(**token_object), token_object.get("_id")
        return TokenDB(**token_object)

    raise StarletteHTTPException(status_code=HTTP_404_NOT_FOUND,
                                 detail="This token doesn't exist")
Beispiel #10
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)
Beispiel #11
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")
Beispiel #12
0
def check_group_object(
    group_object: dict, get_id: bool
) -> Union[Tuple[GroupDB, ObjectId], GroupDB]:
    if group_object:
        if get_id:
            return GroupDB(**group_object), group_object.get("_id")
        return GroupDB(**group_object)

    raise StarletteHTTPException(
        status_code=HTTP_404_NOT_FOUND, detail="This group doesn't exist"
    )
Beispiel #13
0
async def single_leaderboard(request: Request, board: str):
    user = None
    if 'id' in request.session:
        user = await db.users.find_one({"_id": ObjectId(SERIALIZER.loads(request.session.get('id')))})

    if board in BOARDS:
        users = await db.users.find(sort=[(board, DESCENDING)]).to_list(50)

        return templates.TemplateResponse('board.html', {'request': request, 'user': user, 'board': board, 'users': enumerate(users)})

    raise StarletteHTTPException(status_code=404)
Beispiel #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"
    )
Beispiel #15
0
async def user_profile(request: Request, n: str):
    user = None
    if 'id' in request.session:
        user = await db.users.find_one({"_id": ObjectId(SERIALIZER.loads(request.session.get('id')))})

    profile_ = await db.users.find_one({'name': str(n)})

    if not profile_:
        raise StarletteHTTPException(status_code=404)

    return templates.TemplateResponse('user.html', {'request': request, 'user': user, 'profile': profile_})
Beispiel #16
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"
    )
Beispiel #17
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"
    )
Beispiel #18
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")
Beispiel #19
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")
Beispiel #20
0
async def datetime_parameters(interval_time: int = Query(None,
                                                         description="时间间隔",
                                                         gt=0),
                              start_time: typing.Optional[datetime] = Query(
                                  None, description="开始时间"),
                              end_time: typing.Optional[datetime] = Query(
                                  None, description="结束时间")) -> typing.Dict:
    if interval_time:
        end_time = arrow.now()
        start_time = end_time.shift(days=0 - interval_time)

        end_time = end_time.datetime
        start_time = start_time.datetime

    if interval_time is None:
        if start_time is None or end_time is None:
            raise StarletteHTTPException(400, "开始时间和结束时间是必填项")

    if start_time >= end_time:
        raise StarletteHTTPException(400, "开始时间不能小于结束时间")

    return {"start_time": start_time, "end_time": end_time}
Beispiel #21
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))
Beispiel #22
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")
async def log_exception_handler(
        request: Request, exc: Union[StarletteHTTPException,
                                     Exception]) -> Response:
    """Add log of exception if an exception occur."""
    # add the request_id to the logger to be able to understand what request
    # caused the exception
    log = logger.bind(request_id=create_request_id(request))

    if isinstance(exc, StarletteHTTPException):
        # do not log exception for wrong endpoints
        if exc.status_code != 404:
            log.error('following error occurred: {detail}. {error}'.format(
                detail=exc.detail, error=repr(exc)))
        return await http_exception_handler(request, exc)

    # if not already a StarletteHTTPException, convert the exception to it
    _exception = StarletteHTTPException(
        status_code=500, detail=traceback.format_exc().split('\n'))
    log.error(f'{_exception.detail}\n '
              f'request from {request.client} to url {request.url}')
    return await http_exception_handler(request, _exception)
Beispiel #24
0
def get_single_entry(
    collection: EntryCollection,
    entry_id: str,
    response: EntryResponseOne,
    request: Request,
    params: SingleEntryQueryParams,
) -> EntryResponseOne:
    from optimade.server.routers import ENTRY_COLLECTIONS

    params.filter = f'id="{entry_id}"'
    results, data_returned, more_data_available, fields = collection.find(
        params)

    included = get_included_relationships(results, ENTRY_COLLECTIONS)

    if more_data_available:
        raise StarletteHTTPException(
            status_code=500,
            detail=
            f"more_data_available MUST be False for single entry response, however it is {more_data_available}",
        )

    links = ToplevelLinks(next=None)

    if fields and results is not None:
        results = handle_response_fields(results, fields)[0]

    return response(
        links=links,
        data=results,
        meta=meta_values(
            url=str(request.url),
            data_returned=data_returned,
            data_available=len(collection),
            more_data_available=more_data_available,
        ),
        included=included,
    )
Beispiel #25
0
async def invite_user(
    background_tasks: BackgroundTasks,
    email_invited: EmailStr = Body(..., embed=True),
    user_current: UserTokenWrapper = Depends(get_current_user),
    conn: AsyncIOMotorClient = Depends(get_database),
    smtp_conn: FastMail = Depends(get_smtp),
) -> GenericResponse:
    try:
        await get_user_by_email(conn, email_invited)
        raise StarletteHTTPException(
            status_code=HTTP_403_FORBIDDEN, detail="This user already exist"
        )
    except StarletteHTTPException as exc:
        if exc.status_code == 403 and exc.detail == "This user already exist":
            raise exc

        token_user_invite_expires_delta = timedelta(
            minutes=settings.USER_INVITE_TOKEN_EXPIRE_MINUTES
        )
        token_user_invite: str = await TokenUtils.wrap_user_db_data_into_token(
            user_current,
            user_email_invited=email_invited,
            subject=TokenSubject.USER_INVITE,
            token_expires_delta=token_user_invite_expires_delta,
        )
        action_link: str = f"{settings.FRONTEND_DNS}{settings.FRONTEND_GROUP_INVITE}?token={token_user_invite}"
        await background_send_user_invite_email(
            smtp_conn,
            background_tasks,
            email_invited,
            action_link,
        )
        return GenericResponse(
            status=GenericStatus.RUNNING,
            message="Group invite email has been processed",
        )
Beispiel #26
0
async def get_invitation_token(invitation: str = Header(None)) -> str:
    prefix, token = invitation.split(" ")
    if settings.JWT_TOKEN_PREFIX != prefix:
        raise StarletteHTTPException(status_code=HTTP_403_FORBIDDEN,
                                     detail="Invalid invitation")
    return token
Beispiel #27
0
async def starlette_exception():
    raise StarletteHTTPException(status_code=500, detail="500 response")
Beispiel #28
0
def abort(code: int) -> StarletteHTTPException:
    raise StarletteHTTPException(status_code=code)
async def create_item(item_id: str):
    if item_id not in items:
        raise StarletteHTTPException(status_code=404, detail="Item not found")
    return {"item": items[item_id]}
Beispiel #30
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")