Sign-in page redesign and Portal roles Super Admin / Admin / Viewer

Sign-in: brand panel, show / hide password, Caps Lock warning, moving
background, tab icon. Roles: Super Admin manages Admins and Viewers,
Admin manages Viewers only (and can Start / Stop, see Sign-ins), Viewer
sees claims and Runs only. create_tables.sql moves an old table.
71 test files, 1220 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ManikandanandClaude Opus 5.5 committed 2026-10-27 13:45:44 +05:30
1 parent 120fd6808a
commit ce9bdbd974
11 files changed
+408 -99

No files matched your search

+14 -9
View File
@@ -54,8 +54,9 @@ from ecw_bot.core.run_events import run_id_of # noqa: E402
from .repo import SqlResultsRepo # noqa: E402
from . import run_status # noqa: E402
from . import runs as run_view # noqa: E402
from .users import (Accounts, CAN_RUN, ROLES, Sessions, # noqa: E402
SqlUserStore, UserError)
from .users import (Accounts, CAN_ADMIN, CAN_RUN, # noqa: E402
IDLE_MINUTES, LOCK_AFTER, LOCK_MINUTES, MANAGES,
Sessions, SqlUserStore, UserError)
from .bot_runner import (AlreadyRunning, BotRunner, # noqa: E402
NotRunning, StopFailed)
@@ -379,7 +380,7 @@ def create_app(repo, allowed_hosts=ALLOWED_HOSTS, runner=None,
"run_token": app.state.run_token, "crumbs": crumbs,
"categories": CATEGORIES, "user": user,
"can_run": bool(user and user["role"] in CAN_RUN),
"is_admin": bool(user and user["role"] == "Admin"),
"is_admin": bool(user and user["role"] in CAN_ADMIN),
**context})
@app.get("/", response_class=HTMLResponse)
@@ -548,7 +549,9 @@ def create_app(repo, allowed_hosts=ALLOWED_HOSTS, runner=None,
def _signin_page(request, error="", next_url="/", status_code=200):
page = _page(request, "signin.html", [["Sign in", None]],
error=error, next_url=_safe_next(next_url),
no_users=not _db(store.list))
no_users=not _db(store.list),
idle_minutes=IDLE_MINUTES, lock_after=LOCK_AFTER,
lock_minutes=LOCK_MINUTES)
page.status_code = status_code
return page
@@ -611,26 +614,28 @@ def create_app(repo, allowed_hosts=ALLOWED_HOSTS, runner=None,
sessions.refresh_user(updated)
return RedirectResponse("/", status_code=303)
# ── Portal Users (Admin) ─────────────────────────────────────────────
# ── Portal Users (Super Admin, Admin) ────────────────────────────────
def _users_page(request, error="", message="", status_code=200):
page = _page(request, "users.html",
[["Dashboard", "/"], ["Portal Users", None]],
users=_db(store.list), roles=ROLES, error=error,
users=_db(store.list),
roles=MANAGES.get(request.state.user["role"], ()),
error=error,
message=message)
page.status_code = status_code
return page
@app.get("/admin/users", response_class=HTMLResponse)
def users_page(request: Request, msg: str = ""):
_need(request, ("Admin",))
_need(request, CAN_ADMIN)
return _users_page(request, message=msg[:200])
def _admin_change(request, token, done, fn, *args):
"""One Admin change: checked, made, then back to the list with
`done` said — or the list again with the problem."""
_check_token(request, token)
me = _need(request, ("Admin",))["username"]
me = _need(request, CAN_ADMIN)["username"]
try:
_db(fn, *args, by=me)
except UserError as exc:
@@ -686,7 +691,7 @@ def create_app(repo, allowed_hosts=ALLOWED_HOSTS, runner=None,
@app.get("/admin/signins", response_class=HTMLResponse)
def signins_page(request: Request):
_need(request, ("Admin",))
_need(request, CAN_ADMIN)
return _page(request, "signins.html",
[["Dashboard", "/"], ["Sign-in history", None]],
events=_db(store.history))