summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrédéric Mangano-Tarumi <fmang@mg0.fr>2020-06-08 20:16:49 +0200
committerLukas Fleischer <lfleischer@archlinux.org>2021-02-20 17:24:30 +0100
commit42f8f160b6c556a8c2006788a25b6fafe7be1c08 (patch)
tree2358467765ef190b34a3cd396fa7f056c2846b27
parentc77e9d1de0d14253ab3c3b958f459b04b233aeb8 (diff)
downloadaur-42f8f160b6c556a8c2006788a25b6fafe7be1c08.tar.gz
aur-42f8f160b6c556a8c2006788a25b6fafe7be1c08.tar.xz
Open AUR sessions from SSO
Only the core functionality is implemented here. See the TODOs. Signed-off-by: Lukas Fleischer <lfleischer@archlinux.org>
-rw-r--r--aurweb/routers/sso.py51
1 files changed, 49 insertions, 2 deletions
diff --git a/aurweb/routers/sso.py b/aurweb/routers/sso.py
index b16edffb..d0802c34 100644
--- a/aurweb/routers/sso.py
+++ b/aurweb/routers/sso.py
@@ -1,9 +1,18 @@
+import time
+import uuid
+
import fastapi
from authlib.integrations.starlette_client import OAuth
+from fastapi import Depends, HTTPException
+from fastapi.responses import RedirectResponse
+from sqlalchemy.sql import select
from starlette.requests import Request
import aurweb.config
+import aurweb.db
+
+from aurweb.schema import Sessions, Users
router = fastapi.APIRouter()
@@ -23,8 +32,46 @@ async def login(request: Request):
return await oauth.sso.authorize_redirect(request, redirect_uri, prompt="login")
+def open_session(conn, user_id):
+ """
+ Create a new user session into the database. Return its SID.
+ """
+ # TODO check for account suspension
+ # TODO apply [options] max_sessions_per_user
+ sid = uuid.uuid4().hex
+ conn.execute(Sessions.insert().values(
+ UsersID=user_id,
+ SessionID=sid,
+ LastUpdateTS=time.time(),
+ ))
+ # TODO update Users.LastLogin and Users.LastLoginIPAddress
+ return sid
+
+
@router.get("/sso/authenticate")
-async def authenticate(request: Request):
+async def authenticate(request: Request, conn=Depends(aurweb.db.connect)):
+ """
+ Receive an OpenID Connect ID token, validate it, then process it to create
+ an new AUR session.
+ """
+ # TODO check for banned IPs
token = await oauth.sso.authorize_access_token(request)
user = await oauth.sso.parse_id_token(request, token)
- return dict(user)
+ sub = user.get("sub") # this is the SSO account ID in JWT terminology
+ if not sub:
+ raise HTTPException(status_code=400, detail="JWT is missing its `sub` field.")
+
+ aur_accounts = conn.execute(select([Users.c.ID]).where(Users.c.SSOAccountID == sub)) \
+ .fetchall()
+ if not aur_accounts:
+ return "Sorry, we don’t seem to know you Sir " + sub
+ elif len(aur_accounts) == 1:
+ sid = open_session(conn, aur_accounts[0][Users.c.ID])
+ response = RedirectResponse("/")
+ # TODO redirect to the referrer
+ response.set_cookie(key="AURSID", value=sid, httponly=True,
+ secure=request.url.scheme == "https")
+ return response
+ else:
+ # We’ve got a severe integrity violation.
+ raise Exception("Multiple accounts found for SSO account " + sub)