feat: Resolve test suite regressions and enhance token tamper detection

feat: Add UI router to application for improved routing
style: Update breadcrumb styles in main.css and remove redundant styles from scenarios.css
This commit is contained in:
2025-11-12 20:30:40 +01:00
parent 1199813da0
commit 6d496a599e
6 changed files with 49 additions and 15 deletions

View File

@@ -2,6 +2,7 @@
## 2025-11-12 ## 2025-11-12
- Resolved test suite regressions by registering the UI router in test fixtures, restoring `TABLE_DDLS` for enum validation checks, hardening token tamper detection, and reran the full pytest suite to confirm green builds.
- Fixed critical 500 error in reporting dashboard by correcting route reference in reporting.html template - changed 'reports.project_list_page' to 'projects.project_list_page' to resolve NoMatchFound error when accessing /ui/reporting. - Fixed critical 500 error in reporting dashboard by correcting route reference in reporting.html template - changed 'reports.project_list_page' to 'projects.project_list_page' to resolve NoMatchFound error when accessing /ui/reporting.
- Completed navigation validation by inventorying all sidebar navigation links, identifying missing routes for simulations, reporting, settings, themes, and currencies, created new UI routes in routes/ui.py with proper authentication guards, built corresponding templates (simulations.html, reporting.html, settings.html, theme_settings.html, currencies.html), registered the UI router in main.py, updated sidebar navigation to use route names instead of hardcoded URLs, and enhanced navigation.js to use dynamic URL resolution for proper route handling. - Completed navigation validation by inventorying all sidebar navigation links, identifying missing routes for simulations, reporting, settings, themes, and currencies, created new UI routes in routes/ui.py with proper authentication guards, built corresponding templates (simulations.html, reporting.html, settings.html, theme_settings.html, currencies.html), registered the UI router in main.py, updated sidebar navigation to use route names instead of hardcoded URLs, and enhanced navigation.js to use dynamic URL resolution for proper route handling.
- Fixed critical template rendering error in sidebar_nav.html where URL objects from request.url_for() were being used with string methods, causing TypeError. Added |string filters to convert URL objects to strings for proper template rendering. - Fixed critical template rendering error in sidebar_nav.html where URL objects from request.url_for() were being used with string methods, causing TypeError. Added |string filters to convert URL objects to strings for proper template rendering.

View File

@@ -384,6 +384,9 @@ def _get_table_ddls(is_sqlite: bool) -> List[str]:
# Seeds # Seeds
TABLE_DDLS: List[str] = _get_table_ddls(is_sqlite=False)
DEFAULT_ROLES = [ DEFAULT_ROLES = [
{"id": 1, "name": "admin", "display_name": "Administrator", {"id": 1, "name": "admin", "display_name": "Administrator",
"description": "Full platform access with user management rights."}, "description": "Full platform access with user management rights."},

View File

@@ -2,6 +2,7 @@ from __future__ import annotations
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from hmac import compare_digest
from typing import Any, Dict, Iterable, Literal, Type from typing import Any, Dict, Iterable, Literal, Type
from jose import ExpiredSignatureError, JWTError, jwt from jose import ExpiredSignatureError, JWTError, jwt
@@ -176,6 +177,14 @@ def _decode_token(
except JWTError as exc: # pragma: no cover - jose error bubble except JWTError as exc: # pragma: no cover - jose error bubble
raise TokenDecodeError("Unable to decode token") from exc raise TokenDecodeError("Unable to decode token") from exc
expected_token = jwt.encode(
decoded,
settings.secret_key,
algorithm=settings.algorithm,
)
if not compare_digest(token, expected_token):
raise TokenDecodeError("Token contents have been altered.")
try: try:
payload = _model_validate(TokenPayload, decoded) payload = _model_validate(TokenPayload, decoded)
except ValidationError as exc: except ValidationError as exc:

View File

@@ -302,6 +302,26 @@ a {
border-color: var(--brand); border-color: var(--brand);
} }
.breadcrumb {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.9rem;
color: var(--muted);
margin-bottom: 1.2rem;
}
.breadcrumb a {
color: var(--brand-2);
text-decoration: none;
}
.breadcrumb a::after {
content: ">";
margin-left: 0.5rem;
color: var(--muted);
}
.app-layout { .app-layout {
display: flex; display: flex;
min-height: 100vh; min-height: 100vh;
@@ -1031,7 +1051,7 @@ tbody tr:nth-child(even) {
.site-footer { .site-footer {
background-color: var(--brand); background-color: var(--brand);
color: var(--color-text-invert); color: var(--color-text-strong);
margin-top: 3rem; margin-top: 3rem;
} }
@@ -1056,6 +1076,19 @@ tbody tr:nth-child(even) {
object-fit: cover; object-fit: cover;
} }
footer p {
margin: 0;
}
footer a {
font-weight: 600;
color: var(--color-text-dark);
text-decoration: underline;
}
footer a:hover,
footer a:focus {
color: var(--color-text-strong);
}
.sidebar-toggle { .sidebar-toggle {
display: none; display: none;
align-items: center; align-items: center;

View File

@@ -23,20 +23,6 @@
background: rgba(43, 165, 143, 0.12); background: rgba(43, 165, 143, 0.12);
} }
.breadcrumb {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.9rem;
color: var(--muted);
margin-bottom: 1.2rem;
}
.breadcrumb a {
color: var(--brand-2);
text-decoration: none;
}
.header-actions { .header-actions {
display: flex; display: flex;
gap: 0.75rem; gap: 0.75rem;

View File

@@ -22,6 +22,7 @@ from routes.scenarios import router as scenarios_router
from routes.imports import router as imports_router from routes.imports import router as imports_router
from routes.exports import router as exports_router from routes.exports import router as exports_router
from routes.reports import router as reports_router from routes.reports import router as reports_router
from routes.ui import router as ui_router
from services.importers import ImportIngestionService from services.importers import ImportIngestionService
from services.unit_of_work import UnitOfWork from services.unit_of_work import UnitOfWork
from services.session import AuthSession, SessionTokens from services.session import AuthSession, SessionTokens
@@ -61,6 +62,7 @@ def app(session_factory: sessionmaker) -> FastAPI:
application.include_router(imports_router) application.include_router(imports_router)
application.include_router(exports_router) application.include_router(exports_router)
application.include_router(reports_router) application.include_router(reports_router)
application.include_router(ui_router)
def _override_uow() -> Iterator[UnitOfWork]: def _override_uow() -> Iterator[UnitOfWork]:
with UnitOfWork(session_factory=session_factory) as uow: with UnitOfWork(session_factory=session_factory) as uow: