#!/usr/bin/env python3 """ Session interface middlewares to integrate the fastapi app with Buckler. """ import json from datetime import datetime import httpx from fastapi import FastAPI, Request from fastapi.responses import RedirectResponse from starlette.middleware.base import BaseHTTPMiddleware import config class BucklerSessionMiddleware(BaseHTTPMiddleware): """ Verifies the user with the configured Buckler app and retrieves any session data they may have. Redirects them to the login page otherwise. """ def __init__(self, app): super().__init__(app) async def dispatch(self, request: Request, call_next): user_id = request.cookies.get('userid', '') user_sid = request.cookies.get('session', '') url = config.buckler['url'] + '/get_session' params = { 'app_id': config.buckler['app_id'], 'app_key': config.buckler['app_key'], 'userid': user_id, 'session': user_sid } async with httpx.AsyncClient() as client: resp = await client.get(url, params=params) data = resp.json() if data.get('error'): # user not logged in response = RedirectResponse(config.buckler['login_url']) response.set_cookie('redirect', config.server_homepage) return response request.state.session = data.get('session_data') request.state.meta = data.get('meta') response = await call_next(request) if request.state.session != data['session_data']: # session data modified url = config.buckler['url'] + '/set_session' data = json.dumps(request.state.session) async with httpx.AsyncClient() as client: await client.post(url, params=params, data=data) last_used = datetime.fromisoformat(request.state.meta['last_used']) now = datetime.now(last_used.tzinfo) delta = now - last_used if delta.seconds > 600: response.set_cookie( 'userid', user_id, domain=config.server_domain, max_age=30*24*60*60, secure=True, httponly=True, samesite='strict') response.set_cookie( 'session', user_sid, domain=config.server_domain, max_age=30*24*60*60, secure=True, httponly=True, samesite='strict') return response