diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f6260ebb..9dc951aa 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -11,7 +11,8 @@ before_script: base-devel git gpgme protobuf pyalpm python-mysql-connector python-pygit2 python-srcinfo python-bleach python-markdown python-sqlalchemy python-alembic python-pytest python-werkzeug - python-pytest-tap python-fastapi uvicorn nginx + python-pytest-tap python-fastapi uvicorn nginx python-authlib + python-itsdangerous python-httpx test: script: diff --git a/TESTING b/TESTING index 7261df92..d7df3672 100644 --- a/TESTING +++ b/TESTING @@ -13,7 +13,8 @@ INSTALL. # pacman -S --needed php php-sqlite sqlite words fortune-mod \ python python-sqlalchemy python-alembic \ - python-fastapi uvicorn nginx + python-fastapi uvicorn nginx \ + python-authlib python-itsdangerous python-httpx Ensure to enable the pdo_sqlite extension in php.ini. diff --git a/aurweb/asgi.py b/aurweb/asgi.py index 9bb71ecc..60c7ade7 100644 --- a/aurweb/asgi.py +++ b/aurweb/asgi.py @@ -1,3 +1,16 @@ from fastapi import FastAPI +from starlette.middleware.sessions import SessionMiddleware + +import aurweb.config + +from aurweb.routers import sso app = FastAPI() + +session_secret = aurweb.config.get("fastapi", "session_secret") +if not session_secret: + raise Exception("[fastapi] session_secret must not be empty") + +app.add_middleware(SessionMiddleware, secret_key=session_secret) + +app.include_router(sso.router) diff --git a/aurweb/routers/__init__.py b/aurweb/routers/__init__.py new file mode 100644 index 00000000..35d43c03 --- /dev/null +++ b/aurweb/routers/__init__.py @@ -0,0 +1,5 @@ +""" +API routers for FastAPI. + +See https://fastapi.tiangolo.com/tutorial/bigger-applications/ +""" diff --git a/aurweb/routers/sso.py b/aurweb/routers/sso.py new file mode 100644 index 00000000..b16edffb --- /dev/null +++ b/aurweb/routers/sso.py @@ -0,0 +1,30 @@ +import fastapi + +from authlib.integrations.starlette_client import OAuth +from starlette.requests import Request + +import aurweb.config + +router = fastapi.APIRouter() + +oauth = OAuth() +oauth.register( + name="sso", + server_metadata_url=aurweb.config.get("sso", "openid_configuration"), + client_kwargs={"scope": "openid"}, + client_id=aurweb.config.get("sso", "client_id"), + client_secret=aurweb.config.get("sso", "client_secret"), +) + + +@router.get("/sso/login") +async def login(request: Request): + redirect_uri = aurweb.config.get("options", "aur_location") + "/sso/authenticate" + return await oauth.sso.authorize_redirect(request, redirect_uri, prompt="login") + + +@router.get("/sso/authenticate") +async def authenticate(request: Request): + token = await oauth.sso.authorize_access_token(request) + user = await oauth.sso.parse_id_token(request, token) + return dict(user) diff --git a/aurweb/spawn.py b/aurweb/spawn.py index e86f29fe..5da8587e 100644 --- a/aurweb/spawn.py +++ b/aurweb/spawn.py @@ -60,6 +60,9 @@ def generate_nginx_config(): location / {{ proxy_pass http://{aurweb.config.get("php", "bind_address")}; }} + location /sso {{ + proxy_pass http://{aurweb.config.get("fastapi", "bind_address")}; + }} }} }} """) diff --git a/conf/config.defaults b/conf/config.defaults index 447dacac..49259754 100644 --- a/conf/config.defaults +++ b/conf/config.defaults @@ -68,6 +68,14 @@ username-regex = [a-zA-Z0-9]+[.\-_]?[a-zA-Z0-9]+$ git-serve-cmd = /usr/local/bin/aurweb-git-serve ssh-options = restrict +[sso] +openid_configuration = +client_id = +client_secret = + +[fastapi] +session_secret = + [serve] repo-path = /srv/http/aurweb/aur.git/ repo-regex = [a-z0-9][a-z0-9.+_-]*$ diff --git a/conf/config.dev b/conf/config.dev index d752f61f..27e981f8 100644 --- a/conf/config.dev +++ b/conf/config.dev @@ -20,6 +20,12 @@ aur_location = http://127.0.0.1:8080 disable_http_login = 0 enable-maintenance = 0 +; Single sign-on +[sso] +openid_configuration = http://127.0.0.1:8083/auth/realms/aurweb/.well-known/openid-configuration +client_id = aurweb +client_secret = + [php] ; Address PHP should bind when spawned in development mode by aurweb.spawn. bind_address = 127.0.0.1:8081 @@ -30,3 +36,6 @@ htmldir = YOUR_AUR_ROOT/web/html [fastapi] ; Address uvicorn should bind when spawned in development mode by aurweb.spawn. bind_address = 127.0.0.1:8082 + +; Passphrase FastAPI uses to sign client-side sessions. +session_secret = 極秘、訳すな!あ、遅過ぎた。