{title}
+{subtitle}
+ {body.replace("__REMEMBER_ME_CHECKED__", remember_me_checked)} +diff --git a/app/admin.py b/app/admin.py index 6b6dd15..102ead0 100644 --- a/app/admin.py +++ b/app/admin.py @@ -14,14 +14,13 @@ import aioredis from fastapi import Depends, Form, HTTPException, Request from fastapi_admin import constants from fastapi_admin.app import app as admin_app -from fastapi_admin.depends import get_current_admin, get_resources +from fastapi_admin.depends import get_current_admin from fastapi_admin.providers import Provider from fastapi_admin.resources import ( Field, Link, Model, ) -from fastapi_admin.template import templates from fastapi_admin.widgets import displays, inputs from sqlalchemy import select from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint @@ -70,7 +69,6 @@ class EnvCredentialProvider(Provider): login_title: str = "Admin Login", login_logo_url: str | None = None, expire_seconds: int = 3600, - template: str = "providers/login/login.html", ) -> None: self._username = username self._password = password @@ -79,7 +77,6 @@ class EnvCredentialProvider(Provider): self.login_title = login_title self.login_logo_url = login_logo_url self.expire_seconds = expire_seconds - self.template = template async def register(self, app: "FastAPIAdmin") -> None: await super().register(app) @@ -93,30 +90,45 @@ class EnvCredentialProvider(Provider): app.post("/password")(self.password) app.add_middleware(BaseHTTPMiddleware, dispatch=self.authenticate) - def _template_response( + def _render_auth_page( self, request: Request, - name: str, - context: Dict[str, Any], + title: str, + subtitle: str, + body: str, status_code: int = 200, - ): - """Build a template response compatible with old/new Starlette signatures.""" - payload = {"request": request, **context} - try: - # Starlette >= 1.0 - return templates.TemplateResponse( - request=request, - name=name, - context=payload, - status_code=status_code, - ) - except TypeError: - # Starlette < 1.0 - return templates.TemplateResponse( - name, - context=payload, - status_code=status_code, - ) + ) -> HTMLResponse: + remember_me_checked = "checked" if request.cookies.get("remember_me") == "on" else "" + html = f""" + +
+ + +{subtitle}
+ {body.replace("__REMEMBER_ME_CHECKED__", remember_me_checked)} +Direct environment-backed admin access.
+ """ + return self._render_auth_page( request=request, - name=self.template, - context={ - "login_logo_url": self.login_logo_url, - "login_title": self.login_title, - }, + title=self.login_title, + subtitle="Use the configured admin credentials to access the dashboard.", + body=body, ) async def login( @@ -153,15 +174,23 @@ class EnvCredentialProvider(Provider): secrets.compare_digest(username, self._username) and secrets.compare_digest(password, self._password) ): - return self._template_response( + body = f""" +Signed in as {admin.username}.
+Password changes are disabled in the UI for this deployment.
+Update ADMIN_PASSWORD in the server environment, then restart the app.
Session expiry is currently set to {self.expire_seconds} seconds.
+ + """ + return self._render_auth_page( request=request, - name="providers/login/password.html", - context={"resources": resources}, + title="Password Management", + subtitle="Runtime password rotation is intentionally disabled.", + body=body, ) async def password( @@ -226,32 +267,19 @@ class EnvCredentialProvider(Provider): new_password: str = Form(...), re_new_password: str = Form(...), admin: AdminPrincipal = Depends(get_current_admin), - resources=Depends(get_resources), ): - _ = admin - if not secrets.compare_digest(old_password, self._password): - return self._template_response( - request=request, - name="providers/login/password.html", - context={ - "resources": resources, - "error": "Old password is incorrect", - }, - ) - if new_password != re_new_password: - return self._template_response( - request=request, - name="providers/login/password.html", - context={ - "resources": resources, - "error": "New passwords do not match", - }, - ) - - # Password is env-configured and immutable at runtime. - raise HTTPException( + _ = (old_password, new_password, re_new_password, admin) + body = f""" +Update ADMIN_PASSWORD in the server environment, then restart the app.