Repository navigation
fix: serve error page without login and show a clear message for stale edits - #987
Merged
Merged
Conversation
…e edits Fixes #951: Spring Security authorized the container's ERROR dispatch to /error, so any error for an unauthenticated request (for example a 400 from the OAuth2 authorization endpoint) was turned into a redirect to /login and ended as a 200. The web chain now permits the ERROR dispatcher type and GET /error. GammaErrorController always sets the HTTP status (a direct GET /error no longer returns 200), handles all request methods, renders HTML for clients that accept it and JSON otherwise, and shows the real status code and reason on the error page. The header templates no longer crash when the principal is an API key or JWT, which broke the error page for errors raised in the API and OAuth2 chains. Fixes #907: a version mismatch in MutableEntity.increaseVersion now throws the domain exception it.chalmers.gamma.app.common.StaleDomainObjectException. ThymeleafAdvice renders a 409 alert (htmx) or a 409 page (plain request) telling the user the item was modified by someone else and should be reloaded; ApiExceptionAdvice returns 409 JSON. E2E tests cover the anonymous error path, API-key and OAuth2 error responses, and a stale group edit from two tabs. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Every test boots fresh Postgres, Redis and Gamma containers, which takes 13-20 s per test depending on the runner. With 65 tests the step can exceed the previous 20 minute limit on a slow runner (main already timed out once with 61 tests). The job-level timeout stays at 60 minutes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #951
Closes #907
Root causes
#951 –
/errorrequired authenticationanyRequest().authenticated()rejected the forward to/errorfor unauthenticated requests, so any error (e.g. a 400 from/oauth2/authorize?client_id=bogus…) became a 302 to/loginand a 200 after redirects.GammaErrorControllernever set the response status, so a directGET /error(used by the existingredirect:/error) returned 200 with the 500 page.pages/error.htmlhard-coded "500 - Internal Server Error" for every status.common/header.html/header-without-nav.htmlevaluated#authentication.getDetails().gammaUser()for any authenticated principal. For errors raised in the API-key chain or the OAuth2 resource-server chain the principal is not aUserAuthentication, so rendering the error page itself threw.#907 – stale edits produced a generic 500
MutableEntity.increaseVersionthrew a JPA-adapter-levelStaleDomainObjectExceptionon a version mismatch and nothing handled it.Fixes
SecurityFiltersConfig(web chain): permitDispatcherType.ERRORandGET /error. The other chains are unchanged; normal routes still require login.GammaErrorController:@RequestMappingfor all methods; always sets the status (unknown codes → 500); HTML handler for clients that accepttext/html(browsers, htmx), JSON (ApiErrorBody) for everything else, mirroring Spring Boot'sBasicErrorController; passesstatusCode/statusReasonto the template. Exception details are still only shown to admins.UserAuthentication.StaleDomainObjectExceptionmoved to the domain (it.chalmers.gamma.app.common) and thrown directly byMutableEntity.increaseVersion, so no per-facade translation is needed (adapters already throw domain exceptions).ThymeleafAdvicereturns 409 with a non-auto-dismissing alert in#alerts(htmx,HX-Retarget/HX-Reswap) or a full409 - Conflictpage;ApiExceptionAdvicereturns 409 JSON.Tests added
e2e/tests/ui/error-page-does-not-require-login.it.ts: anonymous bogus authorize request → 400 HTML/JSON without a login redirect;GET /error→ 500;/usersstill redirects to/login; browser shows "400 - Bad Request".e2e/tests/api/api-error-responses.it.ts: unknown/api/info/v1/...path with a valid API key → 404 JSON/HTML without redirect or template crash; OAuth2 bearer token + bogus authorize → 400 JSON/HTML, userinfo still 200.e2e/tests/ui/groups-admin-edit-stale-group.it.ts: group edited in a second tab, stale save → 409 and the "modified by someone else" alert; direct non-htmx form submit → 409 page; reload shows the other tab's value.CI note
The first CI run hit the
Run E2E testsstep's 20 minute timeout (62 of 65 tests had run, no test failed). Every test boots fresh containers at 13-20 s per test depending on the runner, andmainalready timed out once the same way with 61 tests (run 39836548292). This PR raises that step's timeout to 30 minutes; the job-level limit stays at 60.Verification
./gradlew check(spotless + compile) and e2eformat:check,lint,typecheckpass.groups-admin-edit-group,session-logout-protects-routes,api-info-endpoint-requires-valid-api-keyandoauth2-authentication-contractpass against an image built from this branch (some runs hit container start-up timeouts because the dev machine's disk filled up; each test passed in at least one run and no assertion failed).🤖 Generated with Claude Code