Repository navigation
COG-1154: W1 · UserResource enable/disable → POST (server + Angular caller) - #338
Conversation
Co-Authored-By: patrick.bradley <patrick.bradley@cognition.ai>
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
| const headers = new Headers(); | ||
| const match = document.cookie.match(/(?:^|;\s*)XSRF-TOKEN=([^;]*)/); | ||
| if (match) { | ||
| headers.append('X-XSRF-TOKEN', decodeURIComponent(match[1])); |
There was a problem hiding this comment.
🔴 CSRF cookie blocks admin account actions
When xsrfHeaders finds an XSRF cookie, both account actions fail from the Angular portal. The custom header triggers a preflight, but RequestFilter excludes x-xsrf-token from allowed headers.
Learn more
The admin portal runs on localhost:4200 and sends credentialed requests to localhost:8080. An XSRF-TOKEN cookie makes xsrfHeaders add a custom header to each POST. Browsers then send an OPTIONS preflight before the POST. RequestFilter responds with an allow-list that omits x-xsrf-token, so the browser never sends the action. The new standalone MVC tests do not exercise browser CORS behavior.
Example: An admin loads the portal after localhost:8080 issues an XSRF-TOKEN cookie. Clicking Disable sends an OPTIONS request asking to use x-xsrf-token; the response rejects the header, so the user remains enabled.
Recommended fix: Add x-xsrf-token to RequestFilter's preflight Access-Control-Allow-Headers list in the same change as the new header, and add a test for an OPTIONS request with Access-Control-Request-Headers: x-xsrf-token.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Intentionally not changed here. XSRF-TOKEN is only issued once W2 turns CSRF on (CookieCsrfTokenRepository). CSRF is disabled on this branch, so xsrfHeaders() returns empty headers and the POST stays a CORS simple request with no preflight. I checked this live: POST from Origin http://localhost:4200 returns 200 with the ACAO/ACAC headers (evidence in the PR body). The allow-list fix is owned by W2 sibling COG-1165, which replaces RequestFilter with a CorsConfigurationSource whose allowedHeaders already includes x-xsrf-token. Editing RequestFilter here would break the file-disjoint slicing and conflict with that ticket.
|
|
||
| @Before | ||
| public void setUp() { | ||
| mockMvc = MockMvcBuilders.standaloneSetup(userResource).build(); |
There was a problem hiding this comment.
This is on purpose. UserResourceTest only checks the HTTP-method mapping, which is the change in this PR. Authorization (@PreAuthorize("hasRole('ADMIN')") on the class) is untouched. Real-app coverage with security on comes from the W0 Testcontainers admin smoke suite, and this PR merges after it. I also exercised the endpoints manually against the running app as a logged-in ROLE_ADMIN user (see the PR body).
Linear: COG-1154 — W1 forward-compatible refactor on Boot 1.5.4 (no Boot/Java bump).
Summary
/api/user/{username}/enableand/disablewere mapped with a bare@RequestMapping(nomethod), so they accepted GET. Once W2 turns CSRF on,CsrfFilterskips safe methods, so those GETs would stay forgeable (<img src=".../api/user/bob/disable">). This converts them to POST now, while CSRF is still off, so the change is behaviour-neutral.AdminPortal
UserService.enableUser/disableUser:The header is only added when the
XSRF-TOKENcookie is present. Today (CSRF disabled) there is no such cookie, so the request stays a CORS "simple" request (POST, no body/content-type, no custom header) and needs no preflight changes.Review notes
CookieCsrfTokenRepositoryare on and the header is actually sent,RequestFilter's preflightAccess-Control-Allow-Headersmust includex-xsrf-token, or the browser will block these POSTs.masteryet, so nothing to update here. AddedUserResourceTest(standalone MockMvc) to cover POST -> 200 + service called, GET -> 405 + service not called. This PR should merge after the smoke suite, and whoever lands second should switch the admin smoke enable/disable calls to POST.UserAccountComponentstill callslocation.reload()right aftersubscribe()(existing race, unchanged).Verification
mvn -B -f UserFront/pom.xml verify(Temurin 1.8.0_504, MySQL 5.7 container,SPRING_DATASOURCE_URL=...?useSSL=falsebecause Temurin 8 disables TLSv1 used by connector 5.1.42):AdminPortal (Node 12):
ng buildsucceeds.ng lintreports no new issues on the changed lines (the remaining errors inuser.service.tsare on unchanged lines and were already there).Manual run: app jar started against MySQL; signed up
admin1+alice, gaveadmin1ROLE_ADMIN, logged in viaPOST /index:Link to Devin session: https://app.devin.ai/sessions/604694d8ca1744fa8f2aef2722fcf9d0
Open in Devin Desktop: https://app.devin.ai/desktop/session/604694d8ca1744fa8f2aef2722fcf9d0?variant=devin
Requested by: @patrickbradley-cog
Devin Review