From fa92e3260df3c857864bd7e081d187e10b59645e Mon Sep 17 00:00:00 2001 From: Andrey Date: Sat, 12 Aug 2023 11:13:51 +0300 Subject: [PATCH 1/3] Split endpoints by public and authorized. --- engineapi/engineapi/routes/leaderboard.py | 66 +++++++++++++++++------ 1 file changed, 51 insertions(+), 15 deletions(-) diff --git a/engineapi/engineapi/routes/leaderboard.py b/engineapi/engineapi/routes/leaderboard.py index d5d95a73..d6fb2983 100644 --- a/engineapi/engineapi/routes/leaderboard.py +++ b/engineapi/engineapi/routes/leaderboard.py @@ -68,8 +68,8 @@ app.add_middleware( ) -@app.get("", response_model=List[data.LeaderboardPosition]) -@app.get("/", response_model=List[data.LeaderboardPosition]) +@app.get("", response_model=List[data.LeaderboardPosition], tags=["Public Endpoints"]) +@app.get("/", response_model=List[data.LeaderboardPosition], tags=["Public Endpoints"]) async def leaderboard( leaderboard_id: UUID = Query(..., description="Leaderboard ID"), limit: int = Query(10), @@ -108,8 +108,12 @@ async def leaderboard( return result -@app.post("", response_model=data.LeaderboardCreatedResponse) -@app.post("/", response_model=data.LeaderboardCreatedResponse) +@app.post( + "", response_model=data.LeaderboardCreatedResponse, tags=["Authorized Endpoints"] +) +@app.post( + "/", response_model=data.LeaderboardCreatedResponse, tags=["Authorized Endpoints"] +) async def create_leaderboard( request: Request, leaderboard: data.LeaderboardCreateRequest = Body(...), @@ -152,7 +156,11 @@ async def create_leaderboard( ) -@app.put("/{leaderboard_id}", response_model=data.LeaderboardUpdatedResponse) +@app.put( + "/{leaderboard_id}", + response_model=data.LeaderboardUpdatedResponse, + tags=["Authorized Endpoints"], +) async def update_leaderboard( request: Request, leaderboard_id: UUID = Path(..., description="Leaderboard ID"), @@ -209,7 +217,11 @@ async def update_leaderboard( ) -@app.delete("/{leaderboard_id}", response_model=data.LeaderboardDeletedResponse) +@app.delete( + "/{leaderboard_id}", + response_model=data.LeaderboardDeletedResponse, + tags=["Authorized Endpoints"], +) async def delete_leaderboard( request: Request, leaderboard_id: UUID = Path(..., description="Leaderboard ID"), @@ -263,7 +275,11 @@ async def delete_leaderboard( ) -@app.get("/leaderboards", response_model=List[data.Leaderboard]) +@app.get( + "/leaderboards", + response_model=List[data.Leaderboard], + tags=["Authorized Endpoints"], +) async def get_leaderboards( request: Request, db_session: Session = Depends(db.yield_db_session) ) -> List[data.Leaderboard]: @@ -299,7 +315,11 @@ async def get_leaderboards( return results -@app.get("/count/addresses", response_model=data.CountAddressesResponse) +@app.get( + "/count/addresses", + response_model=data.CountAddressesResponse, + tags=["Public Endpoints"], +) async def count_addresses( leaderboard_id: UUID = Query(..., description="Leaderboard ID"), db_session: Session = Depends(db.yield_db_session), @@ -325,7 +345,9 @@ async def count_addresses( return data.CountAddressesResponse(count=count) -@app.get("/info", response_model=data.LeaderboardInfoResponse) +@app.get( + "/info", response_model=data.LeaderboardInfoResponse, tags=["Public Endpoints"] +) async def leadeboard_info( leaderboard_id: UUID = Query(..., description="Leaderboard ID"), db_session: Session = Depends(db.yield_db_session), @@ -353,7 +375,11 @@ async def leadeboard_info( ) -@app.get("/scores/changes") +@app.get( + "/scores/changes", + response_model=List[data.LeaderboardScoresChangesResponse], + tags=["Public Endpoints"], +) async def get_scores_changes( leaderboard_id: UUID = Query(..., description="Leaderboard ID"), db_session: Session = Depends(db.yield_db_session), @@ -380,7 +406,7 @@ async def get_scores_changes( ] -@app.get("/quartiles", response_model=data.QuartilesResponse) +@app.get("/quartiles", response_model=data.QuartilesResponse, tags=["Public Endpoints"]) async def quartiles( leaderboard_id: UUID = Query(..., description="Leaderboard ID"), db_session: Session = Depends(db.yield_db_session), @@ -416,7 +442,11 @@ async def quartiles( ) -@app.get("/position", response_model=List[data.LeaderboardPosition]) +@app.get( + "/position", + response_model=List[data.LeaderboardPosition], + tags=["Public Endpoints"], +) async def position( leaderboard_id: UUID = Query(..., description="Leaderboard ID"), address: str = Query(..., description="Address to get position for."), @@ -465,7 +495,9 @@ async def position( return results -@app.get("/rank", response_model=List[data.LeaderboardPosition]) +@app.get( + "/rank", response_model=List[data.LeaderboardPosition], tags=["Public Endpoints"] +) async def rank( leaderboard_id: UUID = Query(..., description="Leaderboard ID"), rank: int = Query(1, description="Rank to get."), @@ -504,7 +536,7 @@ async def rank( return results -@app.get("/ranks", response_model=List[data.RanksResponse]) +@app.get("/ranks", response_model=List[data.RanksResponse], tags=["Public Endpoints"]) async def ranks( leaderboard_id: UUID = Query(..., description="Leaderboard ID"), db_session: Session = Depends(db.yield_db_session), @@ -537,7 +569,11 @@ async def ranks( return results -@app.put("/{leaderboard_id}/scores", response_model=List[data.LeaderboardScore]) +@app.put( + "/{leaderboard_id}/scores", + response_model=List[data.LeaderboardScore], + tags=["Authorized Endpoints"], +) async def leaderboard_push_scores( request: Request, leaderboard_id: UUID = Path(..., description="Leaderboard ID"), From b85cbb5f008d564e9f00736afe90c069a340b829 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 15 Aug 2023 13:55:13 +0300 Subject: [PATCH 2/3] Add Header parameter. --- engineapi/engineapi/routes/leaderboard.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/engineapi/engineapi/routes/leaderboard.py b/engineapi/engineapi/routes/leaderboard.py index d6fb2983..4a4c8932 100644 --- a/engineapi/engineapi/routes/leaderboard.py +++ b/engineapi/engineapi/routes/leaderboard.py @@ -6,7 +6,7 @@ import logging from uuid import UUID from web3 import Web3 -from fastapi import FastAPI, Request, Depends, Response, Query, Path, Body +from fastapi import FastAPI, Request, Depends, Response, Query, Path, Body, Header from sqlalchemy.orm import Session from sqlalchemy.orm.exc import NoResultFound from typing import Any, Dict, List, Optional @@ -75,6 +75,9 @@ async def leaderboard( limit: int = Query(10), offset: int = Query(0), db_session: Session = Depends(db.yield_db_session), + Authorization: str = Header( + ..., description="The expected format is 'Bearer YOUR_MOONSTREAM_ACCESS_TOKEN'." + ), ) -> List[data.LeaderboardPosition]: """ Returns the leaderboard positions. @@ -118,6 +121,9 @@ async def create_leaderboard( request: Request, leaderboard: data.LeaderboardCreateRequest = Body(...), db_session: Session = Depends(db.yield_db_session), + Authorization: str = Header( + ..., description="The expected format is 'Bearer YOUR_MOONSTREAM_ACCESS_TOKEN'." + ), ) -> data.LeaderboardCreatedResponse: """ @@ -166,6 +172,9 @@ async def update_leaderboard( leaderboard_id: UUID = Path(..., description="Leaderboard ID"), leaderboard: data.LeaderboardUpdateRequest = Body(...), db_session: Session = Depends(db.yield_db_session), + Authorization: str = Header( + ..., description="The expected format is 'Bearer YOUR_MOONSTREAM_ACCESS_TOKEN'." + ), ) -> data.LeaderboardUpdatedResponse: """ Update leaderboard. @@ -226,6 +235,9 @@ async def delete_leaderboard( request: Request, leaderboard_id: UUID = Path(..., description="Leaderboard ID"), db_session: Session = Depends(db.yield_db_session), + Authorization: str = Header( + ..., description="The expected format is 'Bearer YOUR_MOONSTREAM_ACCESS_TOKEN'." + ), ) -> data.LeaderboardDeletedResponse: """ Delete leaderboard. @@ -281,7 +293,11 @@ async def delete_leaderboard( tags=["Authorized Endpoints"], ) async def get_leaderboards( - request: Request, db_session: Session = Depends(db.yield_db_session) + request: Request, + db_session: Session = Depends(db.yield_db_session), + Authorization: str = Header( + ..., description="The expected format is 'Bearer YOUR_MOONSTREAM_ACCESS_TOKEN'." + ), ) -> List[data.Leaderboard]: """ Returns leaderboard list to which user has access. @@ -588,6 +604,9 @@ async def leaderboard_push_scores( True, description="Normalize addresses to checksum." ), db_session: Session = Depends(db.yield_db_session), + Authorization: str = Header( + ..., description="The expected format is 'Bearer YOUR_MOONSTREAM_ACCESS_TOKEN'." + ), ) -> List[data.LeaderboardScore]: """ Put the leaderboard to the database. From 23d067071471543502f4a2f6eb21f741e514a46f Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 15 Aug 2023 19:04:08 +0300 Subject: [PATCH 3/3] Add changes. --- engineapi/engineapi/routes/leaderboard.py | 43 +++++++++++++---------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/engineapi/engineapi/routes/leaderboard.py b/engineapi/engineapi/routes/leaderboard.py index 4a4c8932..d2a5fbfa 100644 --- a/engineapi/engineapi/routes/leaderboard.py +++ b/engineapi/engineapi/routes/leaderboard.py @@ -26,9 +26,27 @@ logger = logging.getLogger(__name__) tags_metadata = [ - {"name": "leaderboard", "description": "Moonstream Engine leaderboard API"} + { + "name": "Public Endpoints", + "description": "Endpoints under this tag can be accessed without any authentication. They are open to all and do not require any specific headers or tokens to be passed. Suitable for general access and non-sensitive operations.", + }, + { + "name": "Authorized Endpoints", + "description": """ +Endpoints under this tag require authentication. To access these endpoints, a valid `moonstream token` must be included in the request header as: + +``` +Authorization: Bearer +``` + +Failure to provide a valid token will result in unauthorized access errors. These endpoints are suitable for operations that involve sensitive data or actions that only authenticated users are allowed to perform.""", + }, ] +AuthHeader = Header( + ..., description="The expected format is 'Bearer YOUR_MOONSTREAM_ACCESS_TOKEN'." +) + leaderboad_whitelist = { f"/leaderboard/{DOCS_TARGET_PATH}": "GET", @@ -75,9 +93,6 @@ async def leaderboard( limit: int = Query(10), offset: int = Query(0), db_session: Session = Depends(db.yield_db_session), - Authorization: str = Header( - ..., description="The expected format is 'Bearer YOUR_MOONSTREAM_ACCESS_TOKEN'." - ), ) -> List[data.LeaderboardPosition]: """ Returns the leaderboard positions. @@ -121,9 +136,7 @@ async def create_leaderboard( request: Request, leaderboard: data.LeaderboardCreateRequest = Body(...), db_session: Session = Depends(db.yield_db_session), - Authorization: str = Header( - ..., description="The expected format is 'Bearer YOUR_MOONSTREAM_ACCESS_TOKEN'." - ), + Authorization: str = AuthHeader, ) -> data.LeaderboardCreatedResponse: """ @@ -172,9 +185,7 @@ async def update_leaderboard( leaderboard_id: UUID = Path(..., description="Leaderboard ID"), leaderboard: data.LeaderboardUpdateRequest = Body(...), db_session: Session = Depends(db.yield_db_session), - Authorization: str = Header( - ..., description="The expected format is 'Bearer YOUR_MOONSTREAM_ACCESS_TOKEN'." - ), + Authorization: str = AuthHeader, ) -> data.LeaderboardUpdatedResponse: """ Update leaderboard. @@ -235,9 +246,7 @@ async def delete_leaderboard( request: Request, leaderboard_id: UUID = Path(..., description="Leaderboard ID"), db_session: Session = Depends(db.yield_db_session), - Authorization: str = Header( - ..., description="The expected format is 'Bearer YOUR_MOONSTREAM_ACCESS_TOKEN'." - ), + Authorization: str = AuthHeader, ) -> data.LeaderboardDeletedResponse: """ Delete leaderboard. @@ -295,9 +304,7 @@ async def delete_leaderboard( async def get_leaderboards( request: Request, db_session: Session = Depends(db.yield_db_session), - Authorization: str = Header( - ..., description="The expected format is 'Bearer YOUR_MOONSTREAM_ACCESS_TOKEN'." - ), + Authorization: str = AuthHeader, ) -> List[data.Leaderboard]: """ Returns leaderboard list to which user has access. @@ -604,9 +611,7 @@ async def leaderboard_push_scores( True, description="Normalize addresses to checksum." ), db_session: Session = Depends(db.yield_db_session), - Authorization: str = Header( - ..., description="The expected format is 'Bearer YOUR_MOONSTREAM_ACCESS_TOKEN'." - ), + Authorization: str = AuthHeader, ) -> List[data.LeaderboardScore]: """ Put the leaderboard to the database.