diff options
author | 2022-02-05 16:50:11 +0400 | |
---|---|---|
committer | 2022-02-05 17:20:40 +0400 | |
commit | 134b2f70e4cf947744f1b061766bb37fe616ad65 (patch) | |
tree | ef6b95bc5a78528d91ae969f3cfd00bc8e5be8ed /backend/routes | |
parent | Add Helper Functions For Managing Roles (diff) |
Overhaul Scope System
Adds discord role support to the pre-existing scopes system to power
more complex access permissions.
Signed-off-by: Hassan Abouelela <[email protected]>
Diffstat (limited to 'backend/routes')
-rw-r--r-- | backend/routes/auth/authorize.py | 12 | ||||
-rw-r--r-- | backend/routes/discord.py | 83 | ||||
-rw-r--r-- | backend/routes/forms/submit.py | 2 | ||||
-rw-r--r-- | backend/routes/roles.py | 36 |
4 files changed, 92 insertions, 41 deletions
diff --git a/backend/routes/auth/authorize.py b/backend/routes/auth/authorize.py index d4587f0..42fb3ec 100644 --- a/backend/routes/auth/authorize.py +++ b/backend/routes/auth/authorize.py @@ -17,7 +17,7 @@ from starlette.requests import Request from backend import constants from backend.authentication.user import User from backend.constants import SECRET_KEY -from backend.discord import fetch_bearer_token, fetch_user_details +from backend.discord import fetch_bearer_token, fetch_user_details, get_member from backend.route import Route from backend.validation import ErrorMessage, api @@ -34,8 +34,8 @@ class AuthorizeResponse(BaseModel): async def process_token( - bearer_token: dict, - request: Request + bearer_token: dict, + request: Request ) -> Union[AuthorizeResponse, AUTH_FAILURE]: """Post a bearer token to Discord, and return a JWT and username.""" interaction_start = datetime.datetime.now() @@ -46,6 +46,9 @@ async def process_token( AUTH_FAILURE.delete_cookie("token") return AUTH_FAILURE + user_id = user_details["id"] + member = await get_member(request.state.db, user_id, force_refresh=True) + max_age = datetime.timedelta(seconds=int(bearer_token["expires_in"])) token_expiry = interaction_start + max_age @@ -53,11 +56,12 @@ async def process_token( "token": bearer_token["access_token"], "refresh": bearer_token["refresh_token"], "user_details": user_details, + "in_guild": bool(member), "expiry": token_expiry.isoformat() } token = jwt.encode(data, SECRET_KEY, algorithm="HS256") - user = User(token, user_details) + user = User(token, user_details, member) response = responses.JSONResponse({ "username": user.display_name, diff --git a/backend/routes/discord.py b/backend/routes/discord.py new file mode 100644 index 0000000..a980d94 --- /dev/null +++ b/backend/routes/discord.py @@ -0,0 +1,83 @@ +"""Routes which directly interact with discord related data.""" + +import pydantic +from spectree import Response +from starlette.authentication import requires +from starlette.responses import JSONResponse +from starlette.routing import Request + +from backend import discord, models, route +from backend.validation import ErrorMessage, OkayResponse, api + +NOT_FOUND_EXCEPTION = JSONResponse( + {"error": "Could not find the requested resource in the guild or cache."}, status_code=404 +) + + +class RolesRoute(route.Route): + """Refreshes the roles database.""" + + name = "roles" + path = "/roles" + + class RolesResponse(pydantic.BaseModel): + """A list of all roles on the configured server.""" + + roles: list[models.DiscordRole] + + @requires(["authenticated", "admin"]) + @api.validate( + resp=Response(HTTP_200=OkayResponse), + tags=["roles"] + ) + async def patch(self, request: Request) -> JSONResponse: + """Refresh the roles database.""" + roles = await discord.get_roles(request.state.db, force_refresh=True) + + return JSONResponse( + {"status": "ok"}, + ) + + +class MemberRoute(route.Route): + """Retrieve information about a server member.""" + + name = "member" + path = "/member" + + class MemberRequest(pydantic.BaseModel): + """An ID of the member to update.""" + + user_id: str + + @requires(["authenticated", "admin"]) + @api.validate( + resp=Response(HTTP_200=models.DiscordMember, HTTP_400=ErrorMessage), + json=MemberRequest, + tags=["auth"] + ) + async def delete(self, request: Request): + """Force a resync of the cache for the given user.""" + body = await request.json() + member = await discord.get_member(request.state.db, body["user_id"], force_refresh=True) + + if member: + return JSONResponse(member.dict()) + else: + return NOT_FOUND_EXCEPTION + + @requires(["authenticated", "admin"]) + @api.validate( + resp=Response(HTTP_200=models.DiscordMember, HTTP_400=ErrorMessage), + json=MemberRequest, + tags=["auth"] + ) + async def get(self, request: Request): + """Get a user's roles on the configured server.""" + body = await request.json() + member = await discord.get_member(request.state.db, body["user_id"]) + + if member: + return JSONResponse(member.dict()) + else: + return NOT_FOUND_EXCEPTION diff --git a/backend/routes/forms/submit.py b/backend/routes/forms/submit.py index 95e30b0..baf403d 100644 --- a/backend/routes/forms/submit.py +++ b/backend/routes/forms/submit.py @@ -83,7 +83,7 @@ class SubmitForm(Route): try: if hasattr(request.user, User.refresh_data.__name__): old = request.user.token - await request.user.refresh_data() + await request.user.refresh_data(request.state.db) if old != request.user.token: try: diff --git a/backend/routes/roles.py b/backend/routes/roles.py deleted file mode 100644 index b18a04b..0000000 --- a/backend/routes/roles.py +++ /dev/null @@ -1,36 +0,0 @@ -import starlette.background -from pymongo.database import Database -from spectree import Response -from starlette.authentication import requires -from starlette.responses import JSONResponse -from starlette.routing import Request - -from backend import discord, route -from backend.validation import OkayResponse, api - - -async def refresh_roles(database: Database) -> None: - """Connect to the discord API and refresh the roles database.""" - roles = await discord.get_role_info() - roles_collection = database.get_collection("roles") - roles_collection.drop() - roles_collection.insert_many([role.dict() for role in roles]) - - -class RolesRoute(route.Route): - """Refreshes the roles database.""" - - name = "roles" - path = "/roles" - - @requires(["authenticated", "admin"]) - @api.validate( - resp=Response(HTTP_200=OkayResponse), - tags=["roles"] - ) - async def patch(self, request: Request) -> JSONResponse: - """Refresh the roles database.""" - return JSONResponse( - {"status": "ok"}, - background=starlette.background.BackgroundTask(refresh_roles, request.state.db) - ) |