aboutsummaryrefslogtreecommitdiffstats
path: root/backend/routes
diff options
context:
space:
mode:
Diffstat (limited to 'backend/routes')
-rw-r--r--backend/routes/auth/authorize.py12
-rw-r--r--backend/routes/discord.py83
-rw-r--r--backend/routes/forms/submit.py2
-rw-r--r--backend/routes/roles.py36
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)
- )