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")
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)
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 ), )
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" )
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)))
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, ), )
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")
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")
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")
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)
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")
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" )
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)
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" )
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_})
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" )
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" )
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")
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")
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}
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))
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)
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, )
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", )
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
async def starlette_exception(): raise StarletteHTTPException(status_code=500, detail="500 response")
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]}
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")