aioredis integration and forced authentication
This commit is contained in:
parent
1acc7705df
commit
96e718db3a
@ -1,26 +1,35 @@
|
|||||||
"""Community Keycloak SSO user management."""
|
"""Community Keycloak SSO user management."""
|
||||||
|
|
||||||
|
import json
|
||||||
from os import environ
|
from os import environ
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
from aioredis import create_redis_pool
|
||||||
from authlib.integrations.starlette_client import OAuth, OAuthError
|
from authlib.integrations.starlette_client import OAuth, OAuthError
|
||||||
from fastapi import FastAPI, Request
|
from fastapi import Depends, FastAPI, HTTPException, Request
|
||||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
|
from starlette.exceptions import HTTPException
|
||||||
from starlette.middleware.sessions import SessionMiddleware
|
from starlette.middleware.sessions import SessionMiddleware
|
||||||
|
|
||||||
APP_SECRET_KEY = environ.get("APP_SECRET_KEY")
|
APP_SECRET_KEY = environ.get("APP_SECRET_KEY")
|
||||||
|
|
||||||
KEYCLOAK_CLIENT_ID = environ.get("KEYCLOAK_CLIENT_ID")
|
KEYCLOAK_CLIENT_ID = environ.get("KEYCLOAK_CLIENT_ID")
|
||||||
KEYCLOAK_CLIENT_SECRET = environ.get("KEYCLOAK_CLIENT_SECRET")
|
KEYCLOAK_CLIENT_SECRET = environ.get("KEYCLOAK_CLIENT_SECRET")
|
||||||
|
|
||||||
KEYCLOAK_DOMAIN = environ.get("KEYCLOAK_DOMAIN")
|
KEYCLOAK_DOMAIN = environ.get("KEYCLOAK_DOMAIN")
|
||||||
KEYCLOAK_REALM = environ.get("KEYCLOAK_REALM")
|
KEYCLOAK_REALM = environ.get("KEYCLOAK_REALM")
|
||||||
|
BASE_URL = f"https://{KEYCLOAK_DOMAIN}/auth/realms/{KEYCLOAK_REALM}/protocol/openid-connect" # noqa
|
||||||
|
|
||||||
app = FastAPI()
|
REDIS_DB = environ.get("REDIS_DB")
|
||||||
|
REDIS_HOST = environ.get("REDIS_HOST")
|
||||||
|
REDIS_PORT = environ.get("REDIS_PORT")
|
||||||
|
|
||||||
|
app = FastAPI(docs_url=None, redoc_url=None)
|
||||||
app.add_middleware(SessionMiddleware, secret_key=APP_SECRET_KEY)
|
app.add_middleware(SessionMiddleware, secret_key=APP_SECRET_KEY)
|
||||||
templates = Jinja2Templates(directory="templates")
|
templates = Jinja2Templates(directory="templates")
|
||||||
|
|
||||||
BASE_URL = f"https://{KEYCLOAK_DOMAIN}/auth/realms/{KEYCLOAK_REALM}/protocol/openid-connect"
|
|
||||||
|
|
||||||
oauth = OAuth()
|
oauth = OAuth()
|
||||||
oauth.register(
|
oauth.register(
|
||||||
name="keycloak",
|
name="keycloak",
|
||||||
@ -33,17 +42,52 @@ oauth.register(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/", response_class=HTMLResponse)
|
class RequiresLoginException(Exception):
|
||||||
async def home(request: Request):
|
pass
|
||||||
user = request.session.get("user")
|
|
||||||
if user:
|
|
||||||
return templates.TemplateResponse(
|
@app.exception_handler(RequiresLoginException)
|
||||||
"admin.html", context={"request": request, "user": user}
|
async def requires_login(request, exception):
|
||||||
)
|
|
||||||
return RedirectResponse(request.url_for("login"))
|
return RedirectResponse(request.url_for("login"))
|
||||||
|
|
||||||
|
|
||||||
@app.get("/login", response_class=HTMLResponse)
|
@app.exception_handler(HTTPException)
|
||||||
|
async def http_exception_handler(request, exc):
|
||||||
|
home = request.url_for("login")
|
||||||
|
return HTMLResponse(f"<p>{exc.detail} (<a href='{home}'>home</a>)</p>")
|
||||||
|
|
||||||
|
|
||||||
|
async def logged_in(request: Request):
|
||||||
|
user = request.session.get("user")
|
||||||
|
if not user:
|
||||||
|
raise RequiresLoginException
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
async def get_user(request: Request):
|
||||||
|
return request.session.get("user")
|
||||||
|
|
||||||
|
|
||||||
|
@app.on_event("startup")
|
||||||
|
async def starup_event():
|
||||||
|
app.state.redis = create_redis_pool(
|
||||||
|
f"redis://{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}?encoding=utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.on_event("shutdown")
|
||||||
|
async def shutdown_event():
|
||||||
|
app.state.redis.close()
|
||||||
|
await app.state.redis.wait_closed()
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/", dependencies=[Depends(logged_in)])
|
||||||
|
async def home(request: Request, user=Depends(get_user)):
|
||||||
|
context = {"request": request, "user": user}
|
||||||
|
return templates.TemplateResponse("admin.html", context=context)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/login")
|
||||||
async def login(request: Request):
|
async def login(request: Request):
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"login.html", context={"request": request}
|
"login.html", context={"request": request}
|
||||||
@ -60,15 +104,27 @@ async def login_keycloak(request: Request):
|
|||||||
async def auth_keycloak(request: Request):
|
async def auth_keycloak(request: Request):
|
||||||
try:
|
try:
|
||||||
token = await oauth.keycloak.authorize_access_token(request)
|
token = await oauth.keycloak.authorize_access_token(request)
|
||||||
|
except Exception as exc:
|
||||||
|
return HTMLResponse(f"<p>{exc} (<a href='{home}'>home</a>)</p>")
|
||||||
|
|
||||||
user = await oauth.keycloak.parse_id_token(request, token)
|
user = await oauth.keycloak.parse_id_token(request, token)
|
||||||
request.session["user"] = dict(user)
|
request.session["user"] = dict(user)
|
||||||
|
|
||||||
return RedirectResponse(request.url_for("home"))
|
return RedirectResponse(request.url_for("home"))
|
||||||
except Exception as exception:
|
|
||||||
return HTMLResponse(f"<h1>{str(exception)}</h1>")
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/logout")
|
@app.get("/logout", dependencies=[Depends(logged_in)])
|
||||||
async def logout(request: Request):
|
async def logout(request: Request):
|
||||||
|
try:
|
||||||
httpx.get(f"{BASE_URL}/logout")
|
httpx.get(f"{BASE_URL}/logout")
|
||||||
|
except Exception as exc:
|
||||||
|
return HTMLResponse(f"<p>{exc} (<a href='{home}'>home</a>)</p>")
|
||||||
|
|
||||||
request.session.pop("user", None)
|
request.session.pop("user", None)
|
||||||
|
|
||||||
return RedirectResponse(request.url_for("login"))
|
return RedirectResponse(request.url_for("login"))
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/invite/keycloak", dependencies=[Depends(logged_in)])
|
||||||
|
async def invite_keycloak(request: Request):
|
||||||
|
pass
|
||||||
|
5
makefile
5
makefile
@ -1,5 +1,5 @@
|
|||||||
.DEFAULT: run
|
.DEFAULT: run
|
||||||
.PHONY: run
|
.PHONY: run redis
|
||||||
|
|
||||||
run:
|
run:
|
||||||
@if [ ! -d ".venv" ]; then \
|
@if [ ! -d ".venv" ]; then \
|
||||||
@ -8,3 +8,6 @@ run:
|
|||||||
.venv/bin/poetry install --dev; \
|
.venv/bin/poetry install --dev; \
|
||||||
fi
|
fi
|
||||||
.venv/bin/poetry run uvicorn keycloak_collective_portal:app --reload
|
.venv/bin/poetry run uvicorn keycloak_collective_portal:app --reload
|
||||||
|
|
||||||
|
redis:
|
||||||
|
@docker run -p 6379:6379 --name redis -d redis:6-alpine
|
||||||
|
81
poetry.lock
generated
81
poetry.lock
generated
@ -1,3 +1,15 @@
|
|||||||
|
[[package]]
|
||||||
|
name = "aioredis"
|
||||||
|
version = "1.3.1"
|
||||||
|
description = "asyncio (PEP 3156) Redis support"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
async-timeout = "*"
|
||||||
|
hiredis = "*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyio"
|
name = "anyio"
|
||||||
version = "3.1.0"
|
version = "3.1.0"
|
||||||
@ -34,6 +46,14 @@ python-versions = ">=3.6"
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"]
|
tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-timeout"
|
||||||
|
version = "3.0.1"
|
||||||
|
description = "Timeout context manager for asyncio programs"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.5.3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "authlib"
|
name = "authlib"
|
||||||
version = "0.15.4"
|
version = "0.15.4"
|
||||||
@ -166,6 +186,14 @@ category = "main"
|
|||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hiredis"
|
||||||
|
version = "2.0.0"
|
||||||
|
description = "Python wrapper for hiredis"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httpcore"
|
name = "httpcore"
|
||||||
version = "0.13.4"
|
version = "0.13.4"
|
||||||
@ -473,9 +501,13 @@ python-versions = ">=3.6.1"
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.9"
|
python-versions = "^3.9"
|
||||||
content-hash = "5c484b3f866449256a1928794c2787de8672804952c7887f059cb944beecdaf1"
|
content-hash = "3b87d7d886ec83f9a136e2535f8e9b2802f1c680464022c2938abce0033e55bd"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
|
aioredis = [
|
||||||
|
{file = "aioredis-1.3.1-py3-none-any.whl", hash = "sha256:b61808d7e97b7cd5a92ed574937a079c9387fdadd22bfbfa7ad2fd319ecc26e3"},
|
||||||
|
{file = "aioredis-1.3.1.tar.gz", hash = "sha256:15f8af30b044c771aee6787e5ec24694c048184c7b9e54c3b60c750a4b93273a"},
|
||||||
|
]
|
||||||
anyio = [
|
anyio = [
|
||||||
{file = "anyio-3.1.0-py3-none-any.whl", hash = "sha256:5e335cef65fbd1a422bbfbb4722e8e9a9fadbd8c06d5afe9cd614d12023f6e5a"},
|
{file = "anyio-3.1.0-py3-none-any.whl", hash = "sha256:5e335cef65fbd1a422bbfbb4722e8e9a9fadbd8c06d5afe9cd614d12023f6e5a"},
|
||||||
{file = "anyio-3.1.0.tar.gz", hash = "sha256:43e20711a9d003d858d694c12356dc44ab82c03ccc5290313c3392fa349dad0e"},
|
{file = "anyio-3.1.0.tar.gz", hash = "sha256:43e20711a9d003d858d694c12356dc44ab82c03ccc5290313c3392fa349dad0e"},
|
||||||
@ -488,6 +520,10 @@ asgiref = [
|
|||||||
{file = "asgiref-3.3.4-py3-none-any.whl", hash = "sha256:92906c611ce6c967347bbfea733f13d6313901d54dcca88195eaeb52b2a8e8ee"},
|
{file = "asgiref-3.3.4-py3-none-any.whl", hash = "sha256:92906c611ce6c967347bbfea733f13d6313901d54dcca88195eaeb52b2a8e8ee"},
|
||||||
{file = "asgiref-3.3.4.tar.gz", hash = "sha256:d1216dfbdfb63826470995d31caed36225dcaf34f182e0fa257a4dd9e86f1b78"},
|
{file = "asgiref-3.3.4.tar.gz", hash = "sha256:d1216dfbdfb63826470995d31caed36225dcaf34f182e0fa257a4dd9e86f1b78"},
|
||||||
]
|
]
|
||||||
|
async-timeout = [
|
||||||
|
{file = "async-timeout-3.0.1.tar.gz", hash = "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f"},
|
||||||
|
{file = "async_timeout-3.0.1-py3-none-any.whl", hash = "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"},
|
||||||
|
]
|
||||||
authlib = [
|
authlib = [
|
||||||
{file = "Authlib-0.15.4-py2.py3-none-any.whl", hash = "sha256:d9fe5edb59801b16583faa86f88d798d99d952979b9616d5c735b9170b41ae2c"},
|
{file = "Authlib-0.15.4-py2.py3-none-any.whl", hash = "sha256:d9fe5edb59801b16583faa86f88d798d99d952979b9616d5c735b9170b41ae2c"},
|
||||||
{file = "Authlib-0.15.4.tar.gz", hash = "sha256:37df3a2554bc6fe0da3cc6848c44fac2ae40634a7f8fc72543947f4330b26464"},
|
{file = "Authlib-0.15.4.tar.gz", hash = "sha256:37df3a2554bc6fe0da3cc6848c44fac2ae40634a7f8fc72543947f4330b26464"},
|
||||||
@ -585,6 +621,49 @@ h11 = [
|
|||||||
{file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
|
{file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
|
||||||
{file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"},
|
{file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"},
|
||||||
]
|
]
|
||||||
|
hiredis = [
|
||||||
|
{file = "hiredis-2.0.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b4c8b0bc5841e578d5fb32a16e0c305359b987b850a06964bd5a62739d688048"},
|
||||||
|
{file = "hiredis-2.0.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0adea425b764a08270820531ec2218d0508f8ae15a448568109ffcae050fee26"},
|
||||||
|
{file = "hiredis-2.0.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3d55e36715ff06cdc0ab62f9591607c4324297b6b6ce5b58cb9928b3defe30ea"},
|
||||||
|
{file = "hiredis-2.0.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:5d2a48c80cf5a338d58aae3c16872f4d452345e18350143b3bf7216d33ba7b99"},
|
||||||
|
{file = "hiredis-2.0.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:240ce6dc19835971f38caf94b5738092cb1e641f8150a9ef9251b7825506cb05"},
|
||||||
|
{file = "hiredis-2.0.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:5dc7a94bb11096bc4bffd41a3c4f2b958257085c01522aa81140c68b8bf1630a"},
|
||||||
|
{file = "hiredis-2.0.0-cp36-cp36m-win32.whl", hash = "sha256:139705ce59d94eef2ceae9fd2ad58710b02aee91e7fa0ccb485665ca0ecbec63"},
|
||||||
|
{file = "hiredis-2.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:c39c46d9e44447181cd502a35aad2bb178dbf1b1f86cf4db639d7b9614f837c6"},
|
||||||
|
{file = "hiredis-2.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:adf4dd19d8875ac147bf926c727215a0faf21490b22c053db464e0bf0deb0485"},
|
||||||
|
{file = "hiredis-2.0.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0f41827028901814c709e744060843c77e78a3aca1e0d6875d2562372fcb405a"},
|
||||||
|
{file = "hiredis-2.0.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:508999bec4422e646b05c95c598b64bdbef1edf0d2b715450a078ba21b385bcc"},
|
||||||
|
{file = "hiredis-2.0.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:0d5109337e1db373a892fdcf78eb145ffb6bbd66bb51989ec36117b9f7f9b579"},
|
||||||
|
{file = "hiredis-2.0.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:04026461eae67fdefa1949b7332e488224eac9e8f2b5c58c98b54d29af22093e"},
|
||||||
|
{file = "hiredis-2.0.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:a00514362df15af041cc06e97aebabf2895e0a7c42c83c21894be12b84402d79"},
|
||||||
|
{file = "hiredis-2.0.0-cp37-cp37m-win32.whl", hash = "sha256:09004096e953d7ebd508cded79f6b21e05dff5d7361771f59269425108e703bc"},
|
||||||
|
{file = "hiredis-2.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:f8196f739092a78e4f6b1b2172679ed3343c39c61a3e9d722ce6fcf1dac2824a"},
|
||||||
|
{file = "hiredis-2.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:294a6697dfa41a8cba4c365dd3715abc54d29a86a40ec6405d677ca853307cfb"},
|
||||||
|
{file = "hiredis-2.0.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:3dddf681284fe16d047d3ad37415b2e9ccdc6c8986c8062dbe51ab9a358b50a5"},
|
||||||
|
{file = "hiredis-2.0.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:dcef843f8de4e2ff5e35e96ec2a4abbdf403bd0f732ead127bd27e51f38ac298"},
|
||||||
|
{file = "hiredis-2.0.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:87c7c10d186f1743a8fd6a971ab6525d60abd5d5d200f31e073cd5e94d7e7a9d"},
|
||||||
|
{file = "hiredis-2.0.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:7f0055f1809b911ab347a25d786deff5e10e9cf083c3c3fd2dd04e8612e8d9db"},
|
||||||
|
{file = "hiredis-2.0.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:11d119507bb54e81f375e638225a2c057dda748f2b1deef05c2b1a5d42686048"},
|
||||||
|
{file = "hiredis-2.0.0-cp38-cp38-win32.whl", hash = "sha256:7492af15f71f75ee93d2a618ca53fea8be85e7b625e323315169977fae752426"},
|
||||||
|
{file = "hiredis-2.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:65d653df249a2f95673976e4e9dd7ce10de61cfc6e64fa7eeaa6891a9559c581"},
|
||||||
|
{file = "hiredis-2.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae8427a5e9062ba66fc2c62fb19a72276cf12c780e8db2b0956ea909c48acff5"},
|
||||||
|
{file = "hiredis-2.0.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:3f5f7e3a4ab824e3de1e1700f05ad76ee465f5f11f5db61c4b297ec29e692b2e"},
|
||||||
|
{file = "hiredis-2.0.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:e3447d9e074abf0e3cd85aef8131e01ab93f9f0e86654db7ac8a3f73c63706ce"},
|
||||||
|
{file = "hiredis-2.0.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:8b42c0dc927b8d7c0eb59f97e6e34408e53bc489f9f90e66e568f329bff3e443"},
|
||||||
|
{file = "hiredis-2.0.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:b84f29971f0ad4adaee391c6364e6f780d5aae7e9226d41964b26b49376071d0"},
|
||||||
|
{file = "hiredis-2.0.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:0b39ec237459922c6544d071cdcf92cbb5bc6685a30e7c6d985d8a3e3a75326e"},
|
||||||
|
{file = "hiredis-2.0.0-cp39-cp39-win32.whl", hash = "sha256:a7928283143a401e72a4fad43ecc85b35c27ae699cf5d54d39e1e72d97460e1d"},
|
||||||
|
{file = "hiredis-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:a4ee8000454ad4486fb9f28b0cab7fa1cd796fc36d639882d0b34109b5b3aec9"},
|
||||||
|
{file = "hiredis-2.0.0-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1f03d4dadd595f7a69a75709bc81902673fa31964c75f93af74feac2f134cc54"},
|
||||||
|
{file = "hiredis-2.0.0-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:04927a4c651a0e9ec11c68e4427d917e44ff101f761cd3b5bc76f86aaa431d27"},
|
||||||
|
{file = "hiredis-2.0.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:a39efc3ade8c1fb27c097fd112baf09d7fd70b8cb10ef1de4da6efbe066d381d"},
|
||||||
|
{file = "hiredis-2.0.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:07bbf9bdcb82239f319b1f09e8ef4bdfaec50ed7d7ea51a56438f39193271163"},
|
||||||
|
{file = "hiredis-2.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:807b3096205c7cec861c8803a6738e33ed86c9aae76cac0e19454245a6bbbc0a"},
|
||||||
|
{file = "hiredis-2.0.0-pp37-pypy37_pp73-manylinux1_x86_64.whl", hash = "sha256:1233e303645f468e399ec906b6b48ab7cd8391aae2d08daadbb5cad6ace4bd87"},
|
||||||
|
{file = "hiredis-2.0.0-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:cb2126603091902767d96bcb74093bd8b14982f41809f85c9b96e519c7e1dc41"},
|
||||||
|
{file = "hiredis-2.0.0-pp37-pypy37_pp73-win32.whl", hash = "sha256:f52010e0a44e3d8530437e7da38d11fb822acfb0d5b12e9cd5ba655509937ca0"},
|
||||||
|
{file = "hiredis-2.0.0.tar.gz", hash = "sha256:81d6d8e39695f2c37954d1011c0480ef7cf444d4e3ae24bc5e89ee5de360139a"},
|
||||||
|
]
|
||||||
httpcore = [
|
httpcore = [
|
||||||
{file = "httpcore-0.13.4-py3-none-any.whl", hash = "sha256:38e09649bb3906c913a2917c4eb3e3b3e11c83d4edebad8b53b7d757abc49267"},
|
{file = "httpcore-0.13.4-py3-none-any.whl", hash = "sha256:38e09649bb3906c913a2917c4eb3e3b3e11c83d4edebad8b53b7d757abc49267"},
|
||||||
{file = "httpcore-0.13.4.tar.gz", hash = "sha256:9fa4c623bb9d2280c009c34658cc6315e4fd425a395145645bee205d827263e4"},
|
{file = "httpcore-0.13.4.tar.gz", hash = "sha256:9fa4c623bb9d2280c009c34658cc6315e4fd425a395145645bee205d827263e4"},
|
||||||
|
@ -13,6 +13,7 @@ Jinja2 = "^3.0.1"
|
|||||||
itsdangerous = "^2.0.1"
|
itsdangerous = "^2.0.1"
|
||||||
Authlib = "^0.15.4"
|
Authlib = "^0.15.4"
|
||||||
httpx = "^0.18.1"
|
httpx = "^0.18.1"
|
||||||
|
aioredis = "^1.3.1"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
black = "^21.6b0"
|
black = "^21.6b0"
|
||||||
|
@ -7,5 +7,8 @@
|
|||||||
Hello, {{ user.preferred_username }}
|
Hello, {{ user.preferred_username }}
|
||||||
<small>(<a href="{{ url_for('logout') }}">logout</a>)</small>
|
<small>(<a href="{{ url_for('logout') }}">logout</a>)</small>
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="{{ url_for('invite_keycloak') }}">Generate an invite link</a>
|
||||||
|
</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
Reference in New Issue
Block a user