def lfg_post( *, ls: LoginSession = f.Depends(dep_loginsession), session: so.Session = f.Depends(database.DatabaseSession), user: t.Optional[str] = f.Query(None, description="The user on behalf of which you are acting."), data: models.AnnouncementEditable = f.Body(..., description="The data of the LFG you are creating."), ): """ Create a new LFG with the passed data. Requires the `create:lfg` scope, or the `create:lfg_sudo` scope if you're creating a LFG on behalf of another user. """ if "create:lfg" not in ls.cu.permissions: raise f.HTTPException(403, "Missing `create:lfg` scope.") if user is None: user = ls.cu.sub if "create:lfg_sudo" not in ls.cu.permissions and user != ls.cu.sub: raise f.HTTPException(403, "Missing `create:lfg_sudo` scope.") # noinspection PyArgumentList lfg = database.Announcement(**data.dict(), creator_id=user) session.add(lfg) session.commit() planned_event.set() send_message(session, models.EventAnnouncement( type="create", announcement=models.AnnouncementFull.from_orm(lfg), ).json()) return lfg
async def api_all(index: str, req: fastapi.Request, fmt: str = 'row'): """ Query the database and return ALL records for a given index. """ try: i = INDEXES[index] # discover what the user doesn't have access to see restricted, auth_s = profile(restricted_keywords, portal, req) # lookup the schema for this index and perform the query reader, query_s = profile( query.fetch_all, CONFIG.s3_bucket, i.s3_prefix, restricted=restricted, ) # fetch records from the reader return _fetch_records(reader, index, None, fmt, query_s=auth_s + query_s) except KeyError: raise fastapi.HTTPException(status_code=400, detail=f'Invalid index: {index}') except ValueError as e: raise fastapi.HTTPException(status_code=400, detail=str(e))
async def api_match(index: str, req: fastapi.Request, q: str, limit: int = None): """ Return all the unique keys for a value-indexed table. """ try: i = INDEXES[index] qs = _parse_query(q) # execute the query keys, query_s = profile(query.match, engine, i, qs) # allow an upper limit on the total number of keys returned if limit is not None: keys = itertools.islice(keys, limit) # read the matched keys return _match_keys(keys, index, qs, limit, query_s=query_s) except KeyError: raise fastapi.HTTPException(status_code=400, detail=f'Invalid index: {index}') except ValueError as e: raise fastapi.HTTPException(status_code=400, detail=str(e))
async def api_count_index(index: str, req: fastapi.Request, q: str = None): """ Query the database and estimate how many records will be returned. """ try: i = INDEXES[index] qs = _parse_query(q) # lookup the schema for this index and perform the query count, query_s = profile(query.count, engine, CONFIG.s3_bucket, i, qs) return { 'profile': { 'query': query_s, }, 'index': index, 'q': qs, 'count': count, 'nonce': nonce(), } except KeyError: raise fastapi.HTTPException(status_code=400, detail=f'Invalid index: {index}') except ValueError as e: raise fastapi.HTTPException(status_code=400, detail=str(e))
def verify_webhook_origin_twitter( config, request: fastapi.Request, x_twitter_webhooks_signature: typing.Optional[str] = fastapi.Header(None), ) -> None: """Verify the origin of a request is Twitter based. Twitter will send a header `x-twitter-webhooks-signature` which contains the HMAC SHA256 hash of the resource payload signed using our `CONSUMER_SECRET`. This dependency will raise a `400 BAD REQUEST` exception if it does not find that header.""" if "=" not in x_twitter_webhooks_signature: raise fastapi.HTTPException( status_code=fastapi.status.HTTP_400_BAD_REQUEST, message="Twitter signature did not match", ) # get the digests from the request expected_digest = hmac.new( config.twitter_auth.CONSUMER_KEY_SECRET.encode("utf8"), msg=request.body(), digestmod=hashlib.sha256, ).digest() actual_digest = base64.b64decode( x_twitter_webhooks_signature.split("=", 1)[-1].encode("utf8")) # compare the digests to check they match digests_match = hmac.compare_digest(expected_digest, actual_digest) if not digests_match: raise fastapi.HTTPException( status_code=fastapi.status.HTTP_400_BAD_REQUEST, detail="Twitter signature did not match", )
def update_todo( *, user_id: int, todo_id: int, todo_service: todo.TodoService, updates: typing.Dict[str, typing.Any], ) -> response.TodoResponse: original_todo = todo_service.get_by_id(user_id=user_id, todo_id=todo_id) if original_todo: data = original_todo.dict() data.update(updates) _, _, errors = pydantic.validate_model(todo.Todo, data) if errors: raise fastapi.HTTPException( status_code=fastapi.status.HTTP_400_BAD_REQUEST, detail=errors.json(), ) updated_todo = original_todo.copy(update=updates) updated_todo_from_db = todo_service.update_todo(user_id=user_id, todo=updated_todo) return response.TodoResponse.from_domain(updated_todo_from_db) else: raise fastapi.HTTPException( status_code=fastapi.status.HTTP_404_NOT_FOUND, detail="Todo does not exist.")
async def get_application( request: fastapi.Request, credentials: fastapi.security.HTTPAuthorizationCredentials = fastapi. Security( # noqa: B008 security), redis_links: redis_utils.RedisLinks = fastapi.Depends( # noqa: B008 redis.get_redis_links), ) -> application_mod.Application: scope: typing.Optional[github_types.GitHubLogin] = request.path_params.get( "owner") api_access_key = credentials.credentials[:config.API_ACCESS_KEY_LEN] api_secret_key = credentials.credentials[config.API_ACCESS_KEY_LEN:] try: app = await application_mod.Application.get(redis_links.cache, api_access_key, api_secret_key, scope) except application_mod.ApplicationUserNotFound: raise fastapi.HTTPException(status_code=403) # Seatbelt current_scope = None if app.account_scope is None else app.account_scope[ "login"] if scope is not None and (current_scope is None or current_scope.lower() != scope.lower()): LOG.error( "got application with wrong scope", expected_scope=scope, current_scope=current_scope, ) raise fastapi.HTTPException(status_code=403) return app
async def signature(request: requests.Request) -> None: # Only SHA1 is supported header_signature = request.headers.get("X-Hub-Signature") if header_signature is None: LOG.warning("Webhook without signature") raise fastapi.HTTPException(status_code=403) try: sha_name, signature = header_signature.split("=") except ValueError: sha_name = None if sha_name != "sha1": LOG.warning("Webhook signature malformed") raise fastapi.HTTPException(status_code=403) body = await request.body() current_hmac = utils.compute_hmac(body, config.WEBHOOK_SECRET) if hmac.compare_digest(current_hmac, str(signature)): return if config.WEBHOOK_SECRET_PRE_ROTATION is not None: future_hmac = utils.compute_hmac(body, config.WEBHOOK_SECRET_PRE_ROTATION) if hmac.compare_digest(future_hmac, str(signature)): return LOG.warning("Webhook signature invalid") raise fastapi.HTTPException(status_code=403)
async def datadict_method(item: Item): logger.debug(f"item {item}") if item.data1 != "data1": raise fastapi.HTTPException(status_code=400) if item.data2 != 12345: raise fastapi.HTTPException(status_code=400) if item.data3: raise fastapi.HTTPException(status_code=400) return {'success': True}
async def files_method(files: List[UploadFile] = File(...), data1: str = Form(...), data2: Optional[int] = Form(None)): logger.debug(f"") if len(files) != 3: raise fastapi.HTTPException(status_code=400) if data1 != "data1": raise fastapi.HTTPException(status_code=400) if data2 != 12345: raise fastapi.HTTPException(status_code=400) return {'success': True}
async def create_queue_freeze( queue_freeze_payload: QueueFreezePayload, application: application_mod.Application = fastapi.Depends( # noqa: B008 security.get_application ), queue_name: rules.QueueName = fastapi.Path( # noqa: B008 ..., description="The name of the queue" ), repository_ctxt: context.Repository = fastapi.Depends( # noqa: B008 security.get_repository_context ), ) -> QueueFreezeResponse: if queue_freeze_payload.reason == "": queue_freeze_payload.reason = "No freeze reason was specified." config_file = await repository_ctxt.get_mergify_config_file() if config_file is None: raise fastapi.HTTPException( status_code=404, detail="Mergify configuration file is missing." ) config = get_mergify_config(config_file) queue_rules = config["queue_rules"] if all(queue_name != rule.name for rule in queue_rules): raise fastapi.HTTPException( status_code=404, detail=f'The queue "{queue_name}" does not exist.' ) qf = await freeze.QueueFreeze.get(repository_ctxt, queue_name) if qf is None: qf = freeze.QueueFreeze( repository=repository_ctxt, name=queue_name, reason=queue_freeze_payload.reason, application_name=application.name, application_id=application.id, freeze_date=date.utcnow(), ) await qf.save() elif qf.reason != queue_freeze_payload.reason: qf.reason = queue_freeze_payload.reason await qf.save() return QueueFreezeResponse( queue_freezes=[ QueueFreeze( name=qf.name, reason=qf.reason, application_name=qf.application_name, application_id=qf.application_id, freeze_date=qf.freeze_date, ) ], )
def _get_dataset(dataset: str) -> HDF5Dataset: try: filepath = _datasets[dataset] except KeyError: raise fastapi.HTTPException(status_code=404, detail="unknown dataset") try: return HDF5Dataset(filepath) except FileNotFoundError: raise fastapi.HTTPException(status_code=500, detail="dataset file not found")
def user_update(user_id: str, user: schemas.UserData, db: orm.Session = fastapi.Depends(get_db)): try: crud.user_modify(db, user_id, user) except crud.UserNotExist: raise fastapi.HTTPException(status_code=404, detail='User not found') except crud.UpdateUserError: log.exception('User update error') raise fastapi.HTTPException(status_code=400, detail='User update error') return 'OK'
async def get_installation( application: application_mod.Application = fastapi.Depends( # noqa: B008 get_application), ) -> github_types.GitHubInstallation: if application.account_scope is None: raise fastapi.HTTPException(status_code=403) try: return await github.get_installation_from_account_id( application.account_scope["id"]) except exceptions.MergifyNotInstalled: raise fastapi.HTTPException(status_code=403)
def lfg_answer_put( *, ls: LoginSession = f.Depends(dep_loginsession), fr: f.Response, aid: int = f.Path(..., description="The id of the LFG that should be answered."), user: t.Optional[str] = f.Query(None, description="The id of the user you are answering on behalf of."), data: models.ResponseEditable = f.Body(..., description="The data of the response."), ): """ Respond to a LFG, or edit the response if it had already been sent, sending notifications via the webhooks. Requires the `answer:lfg` scope. Additionally requires the `answer:lfg_sudo` scope if you are answering on behalf of another user. """ if "answer:lfg" not in ls.cu.permissions: raise f.HTTPException(403, "Missing `answer:lfg` scope.") if user is None: user = ls.cu.sub if "answer:lfg_sudo" not in ls.cu.permissions and user != ls.cu.sub: raise f.HTTPException(403, "Missing `answer:lfg_sudo` scope.") response = ls.session.execute( ss.select(database.Response).where( ss.and_( database.Response.aid == aid, database.Response.partecipant_id == ls.cu.sub, ) ) ).scalar() if response is None: # noinspection PyArgumentList response = database.Response(**data.dict(), aid=aid, partecipant_id=user) ls.session.add(response) fr.status_code = 201 else: response.update(**data.dict()) fr.status_code = 200 ls.session.commit() send_message(ls.session, models.EventResponse( type="answer", code=fr.status_code, response=models.ResponseFull.from_orm(response), ).json()) return response
async def uppdatera_vattendrag( id: int, vattendrag: Vattendrag, db: Db = Depends(on_database)) -> Vattendrag: x: Optional[Vattendrag] = db.hamta_vattendrag(id) if x is None: raise fastapi.HTTPException( status_code=404, detail=f"Det finns inget vattendrag med id {id}.") if vattendrag.id != id: raise fastapi.HTTPException( status_code=409, detail=f"Vattendragets id kan inte ändras.") return db.spara_vattendrag(vattendrag)
def manage_endpoint_exceptions(): """ContextManager to catch internal exceptions and return a related HTTP Status Code and Error to the client. """ try: yield except (StopIteration, FileNotFoundError): raise fastapi.HTTPException(status_code=statuscode.HTTP_404_NOT_FOUND, detail="Stop not found for this user") except AssertionError: raise fastapi.HTTPException( status_code=statuscode.HTTP_409_CONFLICT, detail="Internal conflict detected. The admin must review the logs." )
async def uppdatera_lan(id: int, lan: Lan, db: Db = Depends(on_database)) -> Lan: x: Optional[Lan] = db.hamta_lan(id) if x is None: raise fastapi.HTTPException( status_code=404, detail=f"Det finns inget län med id {id}." ) if lan.id != id: raise fastapi.HTTPException( status_code=409, detail=f"Länets id kan inte ändras." ) return db.spara_lan(lan)
def lfg_cancel( *, ls: LoginSession = f.Depends(dep_loginsession), aid: int = f.Path(..., description="The id of the LFG that you want to cancel."), user: t.Optional[str] = f.Query(None, description="The id of the user you are acting on behalf of."), ): """ Cancel a LFG, sending notifications via the webhooks. Requires the `cancel:lfg` scope. Additionally requires the `cancel:lfg_sudo` if you're acting on behalf of another user. Additionally requires the `cancel:lfg_admin` if you're trying to start another user's LFG. """ if "cancel:lfg" not in ls.cu.permissions: raise f.HTTPException(403, "Missing `cancel:lfg` scope.") if user is None: user = ls.cu.sub if "cancel:lfg_sudo" not in ls.cu.permissions and user != ls.cu.sub: raise f.HTTPException(403, "Missing `cancel:lfg_sudo` scope.") lfg = ls.session.execute( ss.select(database.Announcement).where(database.Announcement.aid == aid) ).scalar() if lfg is None: raise f.HTTPException(404, "No such LFG.") if "cancel:lfg_admin" not in ls.cu.permissions and user != lfg.creator_id: raise f.HTTPException(403, "Missing `cancel:lfg_admin` scope.") if lfg.state > database.AnnouncementState.OPEN: raise f.HTTPException(409, "LFG has already closed.") lfg.state = database.AnnouncementState.CANCELLED lfg.closure_time = datetime.datetime.now(tz=datetime.timezone.utc) lfg.closure_id = user ls.session.commit() send_message(ls.session, models.EventAnnouncement( type="cancel", announcement=models.AnnouncementFull.from_orm(lfg), ).json()) return lfg
async def api_raw_plot_dataset(dataset: str, file: str, req: fastapi.Request): """ Returns a raw, image plot for a dataset. """ if not verify_permissions(portal, req, dataset=dataset): raise fastapi.HTTPException(status_code=401) # load the object from s3 content = s3.read_object(CONFIG.s3_bucket, f'plot/dataset/{dataset}/{file}') if content is None: raise fastapi.HTTPException(status_code=404) return fastapi.Response(content=content.read(), media_type='image/png')
async def uppdatera_forsstracka( id: int, forsstracka: Forsstracka, db: Db = Depends(on_database)) -> Forsstracka: x: Optional[Forsstracka] = db.hamta_forsstracka(id) if x is None: raise fastapi.HTTPException( status_code=404, detail=f"Det finns inget forsstracka med id {id}.") if forsstracka.id != id: raise fastapi.HTTPException( status_code=409, detail=f"Forssträckans id kan inte ändras.") return db.spara_forsstracka(forsstracka)
def from_authotization_header( authorization: str = fastapi.Header(None), ) -> Token: """ Validates an ``Authorization: Bearer`` header and returns a :class:`sni.uac.token.Token`. If the token string is invalid, raises a ``401``. Should be used as a FastAPI dependency. """ bearer = "Bearer " if not authorization or not authorization.startswith(bearer): raise fastapi.HTTPException( fastapi.status.HTTP_401_UNAUTHORIZED, headers={"WWW-Authenticate": "Bearer"}, ) try: token = get_token_from_jwt(authorization[len(bearer):]) logging.debug("Successfully validated token %s", token.uuid) return token except jwt_exceptions.InvalidSignatureError: error_msg = "Failed to validate token: invalid signature" except jwt_exceptions.ExpiredSignatureError: error_msg = "Failed to validate token: expired signature" except jwt_exceptions.InvalidAudienceError: error_msg = "Failed to validate token: invalid audience" except jwt_exceptions.InvalidIssuerError: error_msg = "Failed to validate token: invalid issuer" except jwt_exceptions.InvalidIssuedAtError: error_msg = "Failed to validate token: invalid issuance date" except jwt_exceptions.ImmatureSignatureError: error_msg = "Failed to validate token: immature signature" except jwt_exceptions.InvalidKeyError: error_msg = "Failed to validate token: invalid key" except jwt_exceptions.InvalidAlgorithmError: error_msg = "Failed to validate token: invalid algorithm" except jwt_exceptions.MissingRequiredClaimError: error_msg = "Failed to validate token: missing claim" except jwt_exceptions.DecodeError: error_msg = "Failed to validate token: token could not be decoded" except jwt_exceptions.InvalidTokenError: error_msg = "Failed to validate token: token is invalid" except KeyError: error_msg = "Failed to validate token: payload is invalid" logging.error(error_msg) raise fastapi.HTTPException( fastapi.status.HTTP_401_UNAUTHORIZED, detail=error_msg, headers={"WWW-Authenticate": "Bearer"}, )
async def token(request: requests.Request): authorization = request.headers.get("Authorization") if authorization: if authorization.startswith("token "): try: options = http.DEFAULT_CLIENT_OPTIONS.copy() options["headers"]["Authorization"] = authorization async with http.AsyncClient(base_url=config.GITHUB_API_URL, **options) as client: await client.get("/user") return except http.HTTPStatusError as e: raise fastapi.HTTPException(status_code=e.response.status_code) raise fastapi.HTTPException(status_code=403)
async def api_raw_plot_phenotype(phenotype: str, file: str, req: fastapi.Request): """ Returns a raw, image plot for the bottom-line analysis of a phenotype. """ if not verify_permissions(portal, req, phenotype=phenotype): raise fastapi.HTTPException(status_code=401) # load the object from s3 content = s3.read_object(CONFIG.s3_bucket, f'plot/phenotype/{phenotype}/{file}') if content is None: raise fastapi.HTTPException(status_code=404) return fastapi.Response(content=content.read(), media_type='image/png')
async def api_query_gql(req: fastapi.Request): """ Treat the body of the POST as a GraphQL query to be resolved. """ #restricted, auth_s = profile(restricted_keywords, portal, req) body = await req.body() # ensure the graphql schema is loaded if gql_schema is None: raise fastapi.HTTPException(status_code=503, detail='GraphQL Schema not built') try: query = body.decode(encoding='utf-8') # execute the query asynchronously using the schema co = asyncio.wait_for( graphql.graphql(gql_schema, query), timeout=CONFIG.script_timeout, ) # wait for it to complete result, query_s = await profile_async(co) if result.errors: raise fastapi.HTTPException( status_code=400, detail=[str(e) for e in result.errors], ) # send the response return { 'profile': { 'query': query_s, }, 'q': body, 'count': {k: len(v) for k, v in result.data.items()}, 'data': result.data, 'nonce': nonce(), } except asyncio.TimeoutError: raise fastapi.HTTPException( status_code=408, detail= f'Query execution timed out after {CONFIG.script_timeout} seconds') except ValueError as e: raise fastapi.HTTPException(status_code=400, detail=str(e))
def acquire(self): self.__mutex.acquire() if self.__flag: self.__flag = False self.__mutex.release() raise fastapi.HTTPException(409, detail=self.__message) self.__flag = True
async def get_queue_freeze( queue_name: rules.QueueName = fastapi.Path( # noqa: B008 ..., description="The name of the queue" ), repository_ctxt: context.Repository = fastapi.Depends( # noqa: B008 security.get_repository_context ), ) -> QueueFreezeResponse: qf = await freeze.QueueFreeze.get(repository_ctxt, queue_name) if qf is None: raise fastapi.HTTPException( status_code=404, detail=f'The queue "{queue_name}" does not exist or is not currently frozen.', ) return QueueFreezeResponse( queue_freezes=[ QueueFreeze( name=qf.name, reason=qf.reason, application_name=qf.application_name, application_id=qf.application_id, freeze_date=qf.freeze_date, ) ], )
def merge( ls: dependencies.LoginSession = f.Depends(dependencies.dependency_login_session), ss: sqlalchemy.orm.Session = f.Depends(dependencies.dependency_db_session_serializable), song_ids: List[int] = f.Query(..., description="The ids of the genres to merge."), ): """ Move the layers of all the specified songs into a single one, which will have the metadata of the first song specified. """ if len(song_ids) < 2: raise f.HTTPException(400, "Not enough songs specified") # Get the first genre main_song = ss.query(tables.Song).get(song_ids[0]) ls.log("song.merge.to", obj=main_song.id) # Get the other genres other_songs = ss.query(tables.Song).filter(tables.Song.id.in_(song_ids[1:])).all() # Replace and delete the other genres for merged_song in other_songs: for layer in merged_song.layers: layer.song = main_song ls.log("song.merge.from", obj=merged_song.id) ss.delete(merged_song) ss.commit() ss.close() ls.session.commit() return f.Response(status_code=204)
async def delete_queue_freeze( application: application_mod.Application = fastapi.Depends( # noqa: B008 security.get_application ), queue_name: rules.QueueName = fastapi.Path( # noqa: B008 ..., description="The name of the queue" ), repository_ctxt: context.Repository = fastapi.Depends( # noqa: B008 security.get_repository_context ), ) -> fastapi.Response: qf = freeze.QueueFreeze( repository=repository_ctxt, name=queue_name, application_name=application.name, application_id=application.id, ) if not await qf.delete(): raise fastapi.HTTPException( status_code=404, detail=f'The queue "{queue_name}" does not exist or is not currently frozen.', ) return fastapi.Response(status_code=HTTP_204_NO_CONTENT)
def download_charts(name, periodicity, region_name, begin_date, end_date): try: region_name = region_name.replace('_', ' ') chart_downloader = sc.ChartDownloader(name=name, periodicity=periodicity, region_names=[region_name], begin_date=begin_date, end_date=end_date, directory_path='charts') charts = chart_downloader[region_name].drop(columns=['region_name']) response = {'name': name, 'periodicity': periodicity, 'region_name': region_name, 'charts': []} for date, chart in charts.groupby('date', as_index=True): date = pendulum.instance(pd.Timestamp(date).to_pydatetime()).format('YYYY-MM-DD') chart = chart.drop(columns=['date']).to_dict(orient='records') response['charts'].append({'date': date, 'chart': chart}) return response except exceptions.ArgumentTypeError as exception: raise fastapi.HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exception))