async def edit_paste( request: Request, paste_id: str, payload: payloads.PastePatch, ) -> Union[UJSONResponse, Dict[str, Optional[Union[str, int, datetime.datetime]]]]: """Edit a paste on MystBin. * Requires authentication. """ author = request.state.user if not author: return UJSONResponse({"error": "Forbidden"}, status_code=403) paste: Union[Record, int] = await request.app.state.db.edit_paste( paste_id, author["id"], payload.new_content, payload.new_expires, payload.new_nick, ) if not paste or paste == 404: return UJSONResponse( {"error": "Paste was not found or you are not it's author."}, status_code=404, ) return UJSONResponse(dict(paste[0]))
async def delete_pastes( request: Request, payload: payloads.PasteDelete, ) -> Union[UJSONResponse, Dict[str, List[str]]]: """Deletes pastes on MystBin. * Requires authentication. """ # We will filter out the pastes that are authorized and unauthorized, and return a clear response response = {"succeeded": [], "failed": []} author: Record = request.state.user if not author: return UJSONResponse({"error": "Unauthorized"}, status_code=401) for paste in payload.pastes: if await request.app.state.db.ensure_author(paste, author["id"]): response["succeeded"].append(paste) else: response["failed"].append(paste) for paste in response["succeeded"]: await request.app.state.db.delete_paste(paste, author["id"], admin=False) return UJSONResponse(response, status_code=200)
async def get_bookmarks(request: Request, authorization: str = Depends(auth_model)): """Fetches all of the authorized users bookmarks * Requires authentication """ if not request.state.user: return UJSONResponse({"error": "Unauthorized"}, status_code=401) data = await request.app.state.db.get_bookmark(request.state.user['id']) return UJSONResponse({"bookmarks": data})
async def get_admin_userlist(request: Request): """ Returns a count of how many users there are * Requires admin authentication. """ if not request.state.user or not request.state.user["admin"]: return UJSONResponse({"error": "Unauthorized"}, status_code=401) count = await request.app.state.db.get_admin_usercount() return UJSONResponse({"count": count})
async def unsubscribe_user(request: Request, user_id: int) -> UJSONResponse: """ Revokes a users subscriber access * Requires admin authentication """ if not request.state.user or not request.state.user["admin"]: return UJSONResponse({"error": "Unauthorized"}, status_code=401) success = await request.app.state.db.toggle_subscription(user_id, False) return UJSONResponse({"success": success})
async def remove_ban(request: Request, ip: str = None, userid: int = None): if not ip and not userid: return UJSONResponse({"error": "Bad Request"}, status_code=400) if not request.state.user or not request.state.user["admin"]: return UJSONResponse({"error": "Unauthorized"}, status_code=401) if await request.app.state.db.unban_user(userid, ip): return Response(status_code=204) return Response(status_code=400)
async def post_ban(request: Request, reason: str, ip: str = None, userid: int = None): if not request.state.user or not request.state.user["admin"]: return UJSONResponse({"error": "Unauthorized"}, status_code=401) data = await request.app.state.db.ban_user(ip=ip, userid=userid, reason=reason) return UJSONResponse({"success": data})
async def delete_bookmark(request: Request, bookmark: payloads.BookmarkPutDelete, authorization: str = Depends(auth_model)) -> Response: """Deletes a bookmark on the authorized user's account * Requires authentication. """ if not request.state.user: return UJSONResponse({"error": "Unauthorized"}, status_code=401) if not await request.app.state.db.delete_bookmark(request.state.user['id'], bookmark.paste_id): return UJSONResponse({"error": "Bookmark does not exist"}, status_code=400) else: return Response(status_code=204)
async def auth_from_discord( request: Request) -> Union[Dict[str, str], UJSONResponse]: """Allows user to authenticate from Discord OAuth.""" try: data = await request.json() code = data.get("code", None) except: return UJSONResponse({"error": "Bad JSON body passed"}, status_code=421) if not code: return UJSONResponse({"error": "Missing code query"}, status_code=400) client_id = request.app.config["apps"]["discord_application_id"] client_secret = request.app.config["apps"]["discord_application_secret"] url = yarl.URL( request.app.config["site"]["frontend_site"]).with_path("/discord_auth") data = { "client_id": client_id, "client_secret": client_secret, "grant_type": "authorization_code", "code": code, "redirect_uri": url, "scope": "identify email", } headers = {"Content-Type": "application/x-www-form-urlencoded"} async with request.app.state.client.post( "https://discord.com/api/v8/oauth2/token", data=data, headers=headers) as resp: data = await resp.json() token = data["access_token"] async with request.app.state.client.get( "https://discord.com/api/v8/users/@me", headers={"Authorization": f"Bearer {token}"}, ) as resp: resp.raise_for_status() data = await resp.json() userid = data["id"] email = [data["email"]] username = f"{data['username']}#{data['discriminator']}" if request.state.user is not None: token = await request.app.state.db.update_user( request.state.user["id"], discord_id=userid, emails=email) return {"token": token} elif _id := await request.app.state.db.check_email(email): token = await request.app.state.db.update_user(_id, discord_id=userid, emails=email) return UJSONResponse({"token": token})
async def disconnect_app(request: Request, app: str): if app not in ("github", "discord", "google"): return Response(status_code=404) if not request.state.user: return UJSONResponse({"error": "Unauthorized"}, status_code=401) if not await request.app.state.db.unlink_account(request.state.user["id"], app): return UJSONResponse({"error": "Account not linked"}, status_code=400) return Response(status_code=204)
async def search_bans(request: Request, search: str = None, page: int = 1): if not request.state.user or not request.state.user["admin"]: return UJSONResponse({"error": "Unauthorized"}, status_code=401) if search is not None: data = await request.app.state.db.search_bans(search=search) if isinstance(data, str): return UJSONResponse({"reason": data, "searches": []}) return UJSONResponse({"reason": None, "searches": data}) else: return UJSONResponse(await request.app.state.db.get_bans(page))
async def get_all_pastes( request: Request, count: int, page: Optional[int] = 0, oldest_first: bool = False ): if not request.state.user or not request.state.user["admin"]: return UJSONResponse({"error": "Unauthorized"}, status_code=401) if page < 0: return UJSONResponse({"error": "Page must be greater than 0"}, status_code=421) elif count < 1: return UJSONResponse({"error": "Count must be greater than 1"}, status_code=421) return UJSONResponse({"pastes": await request.app.state.db.get_all_pastes(page, count, reverse=oldest_first)})
async def auth_from_google( request: Request) -> Union[Dict[str, str], UJSONResponse]: """Allows user to authenticate from Google OAuth.""" try: data = await request.json() code = data.get("code", None) except: return UJSONResponse({"error": "Bad JSON body passed"}, status_code=421) if not code: return UJSONResponse({"error": "Missing code query"}, status_code=400) client_id = request.app.config["apps"]["google_application_id"] client_secret = request.app.config["apps"]["google_application_secret"] url = yarl.URL( request.app.config["site"]["frontend_site"]).with_path("/google_auth") data = { "client_id": client_id, "client_secret": client_secret, "redirect_uri": url, "grant_type": "authorization_code", "code": code, } headers = {"Content-Type": "application/x-www-form-urlencoded"} async with request.app.state.client.post( "https://oauth2.googleapis.com/token", data=data, headers=headers) as resp: resp.raise_for_status() data = await resp.json() token = data["access_token"] async with request.app.state.client.get( "https://www.googleapis.com/userinfo/v2/me", headers={"Authorization": f"Bearer {token}"}, ) as resp: resp.raise_for_status() data = await resp.json() email = [data["email"]] userid = data["id"] if request.state.user is not None: token = await request.app.state.db.update_user( request.state.user["id"], google_id=userid, emails=email) return UJSONResponse({"token": token}) elif _id := await request.app.state.db.check_email(email): token = await request.app.state.db.update_user(_id, google_id=userid, emails=email) return UJSONResponse({"token": token})
async def subscribe_user( request: Request, user_id: int, authorization: str = Depends(auth_model) ) -> UJSONResponse: """ Gives a user subscriber access * Requires admin authentication """ if not request.state.user or not request.state.user["admin"]: return UJSONResponse({"error": "Unauthorized"}, status_code=401) success = await request.app.state.db.toggle_subscription(user_id, True) return UJSONResponse({"success": success})
async def get_things(request: Request) -> UJSONResponse: """ Handle a request to / when the server manages multiple things. Handle a GET request. :param request -- the request :return UJSONResponse """ if request.app.state.mode == "gateway": things = request.app.state.things descriptions = [] for idx, thing in tuple(things.get_things()): description = thing.as_thing_description() description["links"].append({ "rel": "alternate", "href": f"{get_ws_href(request)}{thing.href}", }) description["base"] = f"{get_http_href(request)}{thing.href}" description["securityDefinitions"] = { "nosec_sc": { "scheme": "nosec", }, } description["security"] = "nosec_sc" bak = copy.deepcopy(description) descriptions.append(bak) return UJSONResponse(descriptions) else: thing = request.app.state.thing.get_thing() description = thing.as_thing_description() description["links"].append({ "rel": "alternate", "href": f"{get_ws_href(request)}{thing.href}", }) description["base"] = f"{get_http_href(request)}{thing.href}" description["securityDefinitions"] = { "nosec_sc": { "scheme": "nosec", }, } description["security"] = "nosec_sc" return UJSONResponse(description)
async def create_bookmark(request: Request, bookmark: payloads.BookmarkPutDelete, authorization: str = Depends(auth_model)) -> Response: """Creates a bookmark on the authorized user's account * Requires authentication. """ if not request.state.user: return UJSONResponse({"error": "Unauthorized"}, status_code=401) try: await request.app.state.db.create_bookmark(request.state.user['id'], bookmark.paste_id) return Response(status_code=201) except ValueError as e: return UJSONResponse({"error": e.args[0]}, status_code=400)
async def get_self( request: Request, authorization: str = Depends(auth_model) ) -> Union[UJSONResponse, Dict[str, Union[str, int, bool]]]: """Gets the User object of the currently logged in user. * Requires authentication. """ user = request.state.user if not user: return UJSONResponse({"error": "Unauthorized"}, status_code=401) return UJSONResponse(responses.User(**user).dict())
async def create_rule(rule: RuleInput): rule_data = rule.dict() rule_data.update({"id": str(uuid.uuid4())}) try: logger.debug(rule_data) rule = Rule(**rule_data) table.insert(rule_data) await re.load_rule(rule) except ValidationError as e: logger.error(str(e)) return UJSONResponse(e.json(), status_code=422) return UJSONResponse(rule_data)
async def get_admin_userlist(request: Request, page: int = 1): """ Returns a list of smaller user objects * Requires admin authentication. """ if page < 1: return UJSONResponse({"error": "page must be >= 1"}, status_code=400) if not request.state.user or not request.state.user["admin"]: return UJSONResponse({"error": "Unauthorized"}, status_code=401) data = await request.app.state.db.get_admin_userlist(page) return UJSONResponse(data)
async def get_paste(request: Request, paste_id: str, password: Optional[str] = None) -> Response: """Get a paste from MystBin.""" if not request.state.user or not request.state.user["admin"]: return UJSONResponse({"error": "Unauthorized"}, status_code=401) paste = await request.app.state.db.get_paste(paste_id, password) if paste is None: return Response(status_code=404) resp = responses.PasteGetResponse(**paste) return UJSONResponse(recursive_hook(resp.dict()))
async def get_paste(request: Request, paste_id: str, password: Optional[str] = None) -> UJSONResponse: """Get a paste from MystBin.""" paste = await request.app.state.db.get_paste(paste_id, password) if paste is None: return UJSONResponse({"error": "Not Found"}, status_code=404) if paste["has_password"] and not paste["password_ok"]: return UJSONResponse({"error": "Unauthorized"}, status_code=401) resp = responses.PasteGetResponse(**paste) return UJSONResponse(recursive_hook(resp.dict()))
async def get_any_user(request: Request, user_id: int) -> Union[UJSONResponse, Dict[str, str]]: """Returns the User object of the passed user_id. * Requires admin authentication. """ if not request.state.user or not request.state.user["admin"]: return UJSONResponse({"error": "Unauthorized"}, status_code=401) data: Optional[Union[Record, int]] = await request.app.state.db.get_user( user_id=user_id) if data: return UJSONResponse(dict(data)) return UJSONResponse({"error": "The given user was not found"}, status_code=400)
async def create_lead(lead: LeadModel, request: Request, token: str): logger.info( f"""request: {request.json()}, headers: {dict(request.headers)}, token: {token}, ip: {request.client.host}""") result, processed = await LeadService.execute(str(token), lead) logger.info(f"response: {result}, processed: {processed}") if processed: return UJSONResponse(status_code=status.HTTP_201_CREATED, content={ "status": "ok", "message": "Batch Accepted!" }) return UJSONResponse(status_code=status.HTTP_400_BAD_REQUEST, content=result)
async def unban_user( request: Request, user_id: int, ip: str = None, ) -> UJSONResponse: """ Unbans a user from the service * Requires admin authentication """ if not request.state.user or not request.state.user["admin"]: return UJSONResponse({"error": "Unauthorized"}, status_code=401) success = await request.app.state.db.unban_user(user_id, ip) return UJSONResponse({"success": success})
async def get_server_stats(request: Request): if not request.state.user or not request.state.user["admin"]: return UJSONResponse({"error": "Unauthorized"}, status_code=401) data = { "memory": PROC.memory_full_info().uss / 1024 ** 2, "memory_percent": PROC.memory_percent(memtype="uss"), "cpu_percent": PROC.cpu_percent(), "uptime": (datetime.datetime.utcnow() - START_TIME).total_seconds(), "total_pastes": await request.app.state.db.get_paste_count(), "requests": request.app.state.request_stats["total"], "latest_request": request.app.state.request_stats["latest"].isoformat(), } return UJSONResponse(data, status_code=200)
async def get_all_pastes( request: Request, limit: Optional[int] = None, ) -> Union[UJSONResponse, Dict[str, List[Dict[str, str]]]]: """Get all pastes for a specified author. * Requires authentication. """ user = request.state.user if not user: return UJSONResponse({"error": "Forbidden"}, status_code=403) pastes = await request.app.state.db.get_all_user_pastes(user["id"], limit) pastes = [dict(entry) for entry in pastes] return UJSONResponse({"pastes": pastes})
async def extract(data: BodyExtract): """ Face extraction/embeddings endpoint accept json with parameters in following format: - **images**: dict containing either links or data lists. (*required*) - **max_size**: Resize all images to this proportions. Default: [640,480] (*optional*) - **threshold**: Detection threshold. Default: 0.6 (*optional*) - **return_face_data**: Return face crops encoded in base64. Default: False (*optional*) - **extract_embedding**: Extract face embeddings (otherwise only detect faces). Default: True (*optional*) - **extract_ga**: Extract gender/age. Default: False (*optional*) - **api_ver**: Output data serialization format. Currently only version "1" is supported (*optional*) \f :return: List[List[dict]] """ images = jsonable_encoder(data.images) output = await processing.embed(images, max_size=data.max_size, return_face_data=data.return_face_data, extract_embedding=data.extract_embedding, threshold=data.threshold, extract_ga=data.extract_ga, api_ver=data.api_ver) return UJSONResponse(output)
async def get_properties(thing: Thing = Depends(get_thing)) -> UJSONResponse: """ Handle a request to /properties. :param thing -- the thing this request is for" :return: UJSONResponse """ return UJSONResponse(await thing.get_properties())
async def get_actions(thing: Thing = Depends(get_thing)) -> UJSONResponse: """ Handle a request to /actions. :param thing-- the thing this request is for :return UJSONResponse """ return UJSONResponse(await thing.get_action_descriptions())
async def invoke_action( action_name: str, message: typing.Dict[str, typing.Any], thing: Thing = Depends(get_thing) ) -> UJSONResponse: """ Handle a POST request. :param thing -- the thing this request is for :param action_name -- name of the action from the URL path :param message -- the request body :return UJSONResponse """ response = {} for name, action_params in message.items(): if name != action_name: continue input_ = None if "input" in action_params: input_ = action_params["input"] action = await thing.perform_action(name, input_) if action: response.update(await action.as_action_description()) # Start the action asyncio.create_task(perform_action(action)) return UJSONResponse(response, status_code=201)