Skip to content

Feature/preview flow - #23

Merged
addee1 merged 49 commits into
mainfrom
feature/preview-flow
Apr 16, 2026
Merged

Feature/preview flow#23
addee1 merged 49 commits into
mainfrom
feature/preview-flow

Conversation

@FionaSprinkles

@FionaSprinkles FionaSprinkles commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

This PR introduces an improved ticket submission flow with support for anonymous reporting using tokens and a preview step before submission.

Anonymous users can submit tickets without logging in
Tokens are currently stored in plain text

Summary by CodeRabbit

  • New Features

    • Public start page with CTAs; preview and “Ticket Created” pages that show redeemable tokens
    • Anonymous ticket submission (optional reporter token) and token-based ticket lookup
    • “My tickets” page and global model attributes exposing login state/username
  • UI/UX Improvements

    • Restyled create/login/signup/layout with new fonts, header links, toast notifications and flash success banners
    • Logout moved to /auth/logout and clears JWT cookie via Set-Cookie
  • Bug Fixes

    • Invalid token now redirects to login with an error indicator

@coderabbitai

coderabbitai Bot commented Apr 15, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@addee1 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 39 minutes and 55 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 39 minutes and 55 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 985263e2-04b8-48f9-a968-758d6b580b5a

📥 Commits

Reviewing files that changed from the base of the PR and between d5b8315 and 089b0e8.

📒 Files selected for processing (2)
  • src/main/java/org/example/alfs/services/TicketService.java
  • src/main/resources/static/css/style.css
📝 Walkthrough

Walkthrough

Adds a public start page and anonymous-ticket flow: new StartPageController and global model attributes; ticket creation now permits anonymous users via reporter tokens and preview/ticket-created flows; security rules and JWT filter updated to allow new public endpoints; templates, layout, and CSS updated for UI and success toasts.

Changes

Cohort / File(s) Summary
Security & Auth
src/main/java/org/example/alfs/config/SecurityConfig.java, src/main/java/org/example/alfs/security/JwtAuthenticationFilter.java, src/main/java/org/example/alfs/controllers/AuthViewController.java
Expanded permitAll() to include /startPage, /, /tickets/create, /tickets/previewTicket, and /auth/logout; Jwt filter now skips /startPage; switched cookie handling to ResponseCookie, changed logout route to /auth/logout, and set flash success messages.
Controllers
src/main/java/org/example/alfs/controllers/StartPageController.java, src/main/java/org/example/alfs/controllers/TicketController.java, src/main/java/org/example/alfs/controllers/AuthViewController.java
Added StartPageController for /; relaxed create endpoint authorization in TicketController (removed @PreAuthorize on create), added preview and ticket-created endpoints and token-based redirect/lookup; AuthViewController signatures updated to accept error/token params and use RedirectAttributes.
Global Model & Security Utils
src/main/java/org/example/alfs/config/GlobalModelAttributes.java, src/main/java/org/example/alfs/security/SecurityUtils.java
New ControllerAdvice injects isLoggedIn and username into all models; added getCurrentUserOrNull() to SecurityUtils that returns null for unauthenticated/absent users.
Service, DTO & Entity
src/main/java/org/example/alfs/services/TicketService.java, src/main/java/org/example/alfs/dto/ticket/TicketViewDTO.java, src/main/java/org/example/alfs/entities/Ticket.java
TicketService.createNewTicket now allows anonymous creation: uses getCurrentUserOrNull(), sets reporter when present or generates/stores reporterToken and returns it in TicketViewDTO; Ticket.reporterToken made nullable and PrePersist no longer auto-generates it; DTO adds token and getFormattedCreatedAt().
Templates / Static UI
src/main/jte/startPage.jte, src/main/jte/previewTicket.jte, src/main/jte/create.jte, src/main/jte/layout.jte, src/main/jte/login.jte, src/main/jte/my-tickets.jte, src/main/jte/signup.jte, src/main/jte/ticket-created.jte, src/main/jte/view.jte, src/main/resources/static/css/style.css
Added start, preview, ticket-created, my-tickets templates; updated layout for auth-aware nav and toast success; create/login/signup templates accept success/token params and styling updates; CSS adds fonts and toast animation.
Tests
src/test/java/org/example/alfs/services/TicketServiceTest.java
Refactored and expanded tests: use getCurrentUserOrNull() stubbing, added anonymous-creation test asserting null reporter and reporterToken presence; adjusted unauthorized tests to assert no reporter repo call.

Sequence Diagram(s)

sequenceDiagram
    participant Browser
    participant Controller as TicketController
    participant Service as TicketService
    participant DB as Database

    Browser->>Controller: POST /tickets/previewTicket (dto)
    Controller-->>Browser: Render previewTicket

    Browser->>Controller: POST /tickets/create (dto)
    Controller->>Service: createNewTicket(dto)
    activate Service
    Service->>Service: securityUtils.getCurrentUserOrNull() -> null or User
    alt user == null
        Service->>Service: generate UUID token
        Service->>DB: save ticket with reporterToken
    else user present
        Service->>DB: save ticket with reporter
    end
    DB-->>Service: saved ticket
    Service-->>Controller: TicketViewDTO (includes token if anonymous)
    deactivate Service
    Controller-->>Browser: Redirect to /tickets/ticket-created?token={token} or /tickets/{id}
    Browser->>Controller: GET /tickets/ticket-created?token=...
    Controller-->>Browser: Render ticket-created view
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

Poem

🐇 I hopped from form to preview bright,
A secret token held just right,
Anonymous paws left a trace,
A tiny toast, a smiling face,
Reports now travel safe at night.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feature/preview flow' is directly related to the PR's main objective of introducing a preview step before ticket submission and an improved submission flow with anonymous reporting support. It clearly summarizes the primary feature being added.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/preview-flow

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/org/example/alfs/services/TicketService.java (1)

41-54: ⚠️ Potential issue | 🔴 Critical

createNewTicket doesn't set reporterToken, violating NOT NULL constraint.

Per the entity definition (Ticket.java line 39-40), reporterToken is @Column(nullable = false). The createAnonymousTicket method correctly sets this field with a UUID, but createNewTicket does not. This will cause a database constraint violation when authenticated users submit tickets.

🐛 Proposed fix
 public TicketViewDTO createNewTicket(TicketCreateDTO ticketCreateDTO) {

     Ticket ticket = new Ticket();

     ticket.setTitle(ticketCreateDTO.getTitle());
     ticket.setDescription(ticketCreateDTO.getDescription());

     User user = requireCurrentUser();
     ticket.setReporter(user);
+    ticket.setReporterToken(java.util.UUID.randomUUID().toString());

     Ticket savedTicket = ticketRepository.save(ticket);

     return ticketMapper.entityToViewDTO(savedTicket);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/alfs/services/TicketService.java` around lines 41 -
54, The createNewTicket method in TicketService fails to set the required
reporterToken (nullable = false) before saving; update createNewTicket to
generate and set a reporterToken (e.g., UUID.randomUUID().toString()) on the
Ticket instance—similar to createAnonymousTicket—and ensure
ticket.setReporterToken(...) is called prior to ticketRepository.save(ticket),
using the provided TicketCreateDTO and requireCurrentUser() flow.
🧹 Nitpick comments (4)
src/main/java/org/example/alfs/security/JwtAuthenticationFilter.java (1)

29-33: Minor inconsistency: / not bypassed in filter but permitted in SecurityConfig.

The filter bypasses /startPage but not / (root path). Both paths are permitAll() in SecurityConfig. This works because the filter gracefully handles missing JWTs (lines 63-66), but for consistency and minor performance optimization, consider adding / to the bypass list:

-        return path.startsWith("/auth") || path.startsWith("/h2-console") || path.startsWith("/startPage");
+        return path.startsWith("/auth") || path.startsWith("/h2-console") || path.startsWith("/startPage") || path.equals("/");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/alfs/security/JwtAuthenticationFilter.java` around
lines 29 - 33, The shouldNotFilter method in JwtAuthenticationFilter currently
skips "/auth", "/h2-console", and "/startPage" but not the root path; update the
shouldNotFilter(HttpServletRequest request) implementation to also bypass "/" by
including path.startsWith("/") (or an explicit equality check for "/") in the
return condition so the filter consistently aligns with SecurityConfig
permitAll() and avoids unnecessary processing for the root path.
src/main/jte/create.jte (1)

6-22: Button text doesn't match the new preview action.

The form now submits to /tickets/previewTicket, but the button still says "Create". Consider updating the button text to reflect the preview step:

         <button
                 type="submit"
                 class="bg-[`#1a2b49`] text-white px-4 py-2 rounded">
-            Create
+            Preview
         </button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/jte/create.jte` around lines 6 - 22, The submit button text is
inconsistent with the form action (/tickets/previewTicket); update the button
label in the template (the <button> element inside the form that currently
displays "Create") to something that indicates a preview step (e.g., "Preview"
or "Preview Ticket") so the UI matches the new preview action.
src/main/jte/login.jte (1)

9-15: Inconsistent HTML structure for form fields.

The username field's <label> and <input> are outside any wrapper <div>, while the password field is wrapped. Consider consistent structure:

<div>
    <label for="username">Username</label>
    <input id="username" name="username" />
</div>
<div>
    <label for="password">Password</label>
    <input id="password" type="password" name="password" />
</div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/jte/login.jte` around lines 9 - 15, Wrap the username label and
input in the same wrapper structure used for the password field so markup is
consistent: move the <label for="username"> and <input id="username"
name="username" /> into a containing <div> matching the password block (keeping
<label for="password">, <input id="password" type="password" name="password" />
as-is), ensuring both fields use the same wrapper and preserve the id/name
attributes for accessibility.
src/main/java/org/example/alfs/config/SecurityConfig.java (1)

46-51: /submit-authenticated is accessible without authentication due to /tickets/** wildcard.

The /tickets/** permitAll (line 51) allows unauthenticated access to /tickets/submit-authenticated. While the service layer will return 401 when it can't find the authenticated user, this is semantically incorrect—the endpoint name implies authentication is required.

When tightening security for production, consider:

  1. Removing the broad /tickets/** permitAll
  2. Explicitly listing only the endpoints that should be public
.requestMatchers("/tickets/create").permitAll()
.requestMatchers("/tickets/previewTicket").permitAll()
.requestMatchers("/tickets/submit-anonymous").permitAll()
.requestMatchers("/tickets/token/**").permitAll()
// /tickets/submit-authenticated should NOT be permitAll
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/alfs/config/SecurityConfig.java` around lines 46 -
51, In SecurityConfig, remove the broad
.requestMatchers("/tickets/**").permitAll() that currently makes
/tickets/submit-authenticated publicly accessible; instead explicitly permit
only the intended public ticket endpoints (e.g., "/tickets/create",
"/tickets/previewTicket", "/tickets/submit-anonymous", "/tickets/token/**") via
individual .requestMatchers(...).permitAll() entries so that
/tickets/submit-authenticated remains protected and requires authentication.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/example/alfs/controllers/TicketController.java`:
- Line 64: The redirect in TicketController exposes reporter tokens by building
"redirect:/tickets/token/" + ticket.getToken(); remove the token from the URL
and instead convey it via a non-URL channel: store ticket.getToken() in the
user's HttpSession or a flash attribute (e.g.
session.setAttribute("reporterToken", token) or
RedirectAttributes.addFlashAttribute("reporterToken", token)) and change the
redirect target to a non-token path such as "redirect:/tickets/" +
ticket.getId() (or another safe view) and adjust the ticket view handlers to
read the token from session/flash for any reporter-specific logic, ensuring
tokens are never placed in logs/paths.
- Around line 68-76: The submitAuthenticated method in TicketController lacks
the `@PreAuthorize` check, allowing anonymous requests to reach
ticketService.createNewTicket and trigger a late 401; add
`@PreAuthorize`("isAuthenticated()") above the submitAuthenticated method (and
ensure the PreAuthorize import is present and method-level security is enabled
in the app) so authentication is enforced at the controller boundary consistent
with other methods.
- Around line 51-56: The previewTicket method (and the other submit endpoints
that accept TicketCreateDTO) currently take the DTO without validation; update
each such controller method to annotate the TicketCreateDTO parameter with
`@Valid` and add a BindingResult parameter immediately after it (e.g.,
previewTicket(`@Valid` `@ModelAttribute`("ticket") TicketCreateDTO dto,
BindingResult bindingResult, Model model)), then check bindingResult.hasErrors()
and, if true, return the original form view (the same view used by your /create
flow) with the model so validation errors are shown instead of proceeding to the
service layer.

In `@src/main/java/org/example/alfs/dto/ticket/TicketViewDTO.java`:
- Line 20: getTicketByToken in TicketService currently maps the Ticket to a
TicketViewDTO via ticketMapper.entityToViewDTO(), which doesn't set the new
token field (defined in TicketViewDTO and populated in createAnonymousTicket),
so update getTicketByToken to explicitly set the token on the returned DTO
(e.g., call view.setToken(ticket.getReporterToken()) after mapping) so anonymous
callers retrieving by token receive a DTO with token populated.

In `@src/main/jte/layout.jte`:
- Line 34: The "Home" anchor in layout.jte currently points to a non-existent
route "/home"; update the href on the anchor whose text is "Home" (the <a ...>
element with class "hover:text-[`#b39359`]") to point to "/" instead so it routes
to StartPageController's root endpoint; ensure only the href value changes and
keep the existing class and link text intact.

In `@src/main/jte/login.jte`:
- Around line 26-34: The form currently submits the sensitive token via GET
(form method="get" action="/tickets/token"), exposing it in URLs; change the
form to use method="post" and update the server-side handler mapping from
`@GetMapping`("/tickets/token") to `@PostMapping`("/tickets/token") (or add a
separate `@PostMapping` handler) so the token is sent in the request body; also
ensure the controller reads the token from the request body/parameter
appropriately and that any CSRF protection/middleware is handled for POST
endpoints.

In `@src/main/jte/previewTicket.jte`:
- Around line 25-32: The "Submit as logged-in user" form (action
"/tickets/submit-authenticated") is rendered for all visitors; change the
template in previewTicket.jte to only render that <form> block when the
request/session indicates an authenticated user (e.g., check the template's
currentUser or session.isAuthenticated() variable), and for anonymous users
replace it with a sign-in CTA (a link/button that directs to your sign-in route)
so anonymous sessions don't see the authenticated-submit path.

In `@src/main/jte/startPage.jte`:
- Around line 19-20: Replace the absolute claim "This system is super secure" in
the startPage.jte copy with a tempered, accurate statement; edit the <h3> or the
adjacent paragraph text that currently reads "This system is super secure" to
something like "Designed with multiple security measures" or "Built with strong
security practices" (or other approved legal-safe phrasing) so the UI no longer
makes categorical security guarantees.

---

Outside diff comments:
In `@src/main/java/org/example/alfs/services/TicketService.java`:
- Around line 41-54: The createNewTicket method in TicketService fails to set
the required reporterToken (nullable = false) before saving; update
createNewTicket to generate and set a reporterToken (e.g.,
UUID.randomUUID().toString()) on the Ticket instance—similar to
createAnonymousTicket—and ensure ticket.setReporterToken(...) is called prior to
ticketRepository.save(ticket), using the provided TicketCreateDTO and
requireCurrentUser() flow.

---

Nitpick comments:
In `@src/main/java/org/example/alfs/config/SecurityConfig.java`:
- Around line 46-51: In SecurityConfig, remove the broad
.requestMatchers("/tickets/**").permitAll() that currently makes
/tickets/submit-authenticated publicly accessible; instead explicitly permit
only the intended public ticket endpoints (e.g., "/tickets/create",
"/tickets/previewTicket", "/tickets/submit-anonymous", "/tickets/token/**") via
individual .requestMatchers(...).permitAll() entries so that
/tickets/submit-authenticated remains protected and requires authentication.

In `@src/main/java/org/example/alfs/security/JwtAuthenticationFilter.java`:
- Around line 29-33: The shouldNotFilter method in JwtAuthenticationFilter
currently skips "/auth", "/h2-console", and "/startPage" but not the root path;
update the shouldNotFilter(HttpServletRequest request) implementation to also
bypass "/" by including path.startsWith("/") (or an explicit equality check for
"/") in the return condition so the filter consistently aligns with
SecurityConfig permitAll() and avoids unnecessary processing for the root path.

In `@src/main/jte/create.jte`:
- Around line 6-22: The submit button text is inconsistent with the form action
(/tickets/previewTicket); update the button label in the template (the <button>
element inside the form that currently displays "Create") to something that
indicates a preview step (e.g., "Preview" or "Preview Ticket") so the UI matches
the new preview action.

In `@src/main/jte/login.jte`:
- Around line 9-15: Wrap the username label and input in the same wrapper
structure used for the password field so markup is consistent: move the <label
for="username"> and <input id="username" name="username" /> into a containing
<div> matching the password block (keeping <label for="password">, <input
id="password" type="password" name="password" /> as-is), ensuring both fields
use the same wrapper and preserve the id/name attributes for accessibility.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: aeb12666-6897-4cca-ac67-4d798c054584

📥 Commits

Reviewing files that changed from the base of the PR and between 2e8d686 and 419a420.

📒 Files selected for processing (12)
  • src/main/java/org/example/alfs/config/SecurityConfig.java
  • src/main/java/org/example/alfs/controllers/StartPageController.java
  • src/main/java/org/example/alfs/controllers/TicketController.java
  • src/main/java/org/example/alfs/dto/ticket/TicketViewDTO.java
  • src/main/java/org/example/alfs/entities/Ticket.java
  • src/main/java/org/example/alfs/security/JwtAuthenticationFilter.java
  • src/main/java/org/example/alfs/services/TicketService.java
  • src/main/jte/create.jte
  • src/main/jte/layout.jte
  • src/main/jte/login.jte
  • src/main/jte/previewTicket.jte
  • src/main/jte/startPage.jte
💤 Files with no reviewable changes (1)
  • src/main/java/org/example/alfs/entities/Ticket.java

Comment on lines +51 to +56
@PostMapping("/previewTicket")
public String previewTicket(@ModelAttribute("ticket") TicketCreateDTO dto, Model model) {

model.addAttribute("ticket", dto);
return "previewTicket";
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine the TicketController.java file
fd -t f "TicketController.java" --exec cat -n {} \;

Repository: ithsjava25/project-backend-alfs

Length of output: 5356


🏁 Script executed:

# Also look for TicketCreateDTO to understand validation annotations
fd -t f "TicketCreateDTO.java" --exec cat -n {} \;

Repository: ithsjava25/project-backend-alfs

Length of output: 704


Add server-side validation to the new submit endpoints.

Lines 52, 60, and 69 accept TicketCreateDTO without @Valid annotation or BindingResult handling, allowing invalid payloads (missing title/description) to bypass the validation checks enforced in the existing /create endpoint (line 39) and reach the service layer.

Suggested fix
 `@PostMapping`("/previewTicket")
-public String previewTicket(`@ModelAttribute`("ticket") TicketCreateDTO dto, Model model) {
+public String previewTicket(`@ModelAttribute`("ticket") `@Valid` TicketCreateDTO dto,
+                            BindingResult bindingResult,
+                            Model model) {
+    if (bindingResult.hasErrors()) {
+        return "create";
+    }
     model.addAttribute("ticket", dto);
     return "previewTicket";
 }

 `@PostMapping`("/submit-anonymous")
-public String submitAnonymous(`@ModelAttribute` TicketCreateDTO dto) {
+public String submitAnonymous(`@ModelAttribute` `@Valid` TicketCreateDTO dto,
+                              BindingResult bindingResult,
+                              Model model) {
+    if (bindingResult.hasErrors()) {
+        model.addAttribute("ticket", dto);
+        return "previewTicket";
+    }
     TicketViewDTO ticket = ticketService.createAnonymousTicket(dto);
     return "redirect:/tickets/token/" + ticket.getToken();
 }

 `@PostMapping`("/submit-authenticated")
-public String submitAuthenticated(`@ModelAttribute` TicketCreateDTO dto) {
+public String submitAuthenticated(`@ModelAttribute` `@Valid` TicketCreateDTO dto,
+                                  BindingResult bindingResult,
+                                  Model model) {
+    if (bindingResult.hasErrors()) {
+        model.addAttribute("ticket", dto);
+        return "previewTicket";
+    }
     TicketViewDTO ticket = ticketService.createNewTicket(dto);
     return "redirect:/tickets/" + ticket.getId();
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/alfs/controllers/TicketController.java` around
lines 51 - 56, The previewTicket method (and the other submit endpoints that
accept TicketCreateDTO) currently take the DTO without validation; update each
such controller method to annotate the TicketCreateDTO parameter with `@Valid` and
add a BindingResult parameter immediately after it (e.g., previewTicket(`@Valid`
`@ModelAttribute`("ticket") TicketCreateDTO dto, BindingResult bindingResult,
Model model)), then check bindingResult.hasErrors() and, if true, return the
original form view (the same view used by your /create flow) with the model so
validation errors are shown instead of proceeding to the service layer.


TicketViewDTO ticket = ticketService.createAnonymousTicket(dto);

return "redirect:/tickets/token/" + ticket.getToken();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Do not expose reporter tokens in URL paths.

Line 64 places the token in the redirect URL, which leaks sensitive access material through logs/history/referrers. This is especially risky while tokens are stored in plain text.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/alfs/controllers/TicketController.java` at line 64,
The redirect in TicketController exposes reporter tokens by building
"redirect:/tickets/token/" + ticket.getToken(); remove the token from the URL
and instead convey it via a non-URL channel: store ticket.getToken() in the
user's HttpSession or a flash attribute (e.g.
session.setAttribute("reporterToken", token) or
RedirectAttributes.addFlashAttribute("reporterToken", token)) and change the
redirect target to a non-token path such as "redirect:/tickets/" +
ticket.getId() (or another safe view) and adjust the ticket view handlers to
read the token from session/flash for any reporter-specific logic, ensuring
tokens are never placed in logs/paths.

Comment on lines +68 to +76
@PostMapping("/submit-authenticated")
public String submitAuthenticated(
@ModelAttribute TicketCreateDTO dto
) {

TicketViewDTO ticket = ticketService.createNewTicket(dto);

return "redirect:/tickets/" + ticket.getId();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -name "TicketController.java" -type f | head -5

Repository: ithsjava25/project-backend-alfs

Length of output: 141


🏁 Script executed:

cat -n src/main/java/org/example/alfs/controllers/TicketController.java

Repository: ithsjava25/project-backend-alfs

Length of output: 5356


🏁 Script executed:

find . -name "*TicketService*.java" -type f | head -5

Repository: ithsjava25/project-backend-alfs

Length of output: 135


🏁 Script executed:

cat -n src/main/java/org/example/alfs/services/TicketService.java | head -100

Repository: ithsjava25/project-backend-alfs

Length of output: 4049


🏁 Script executed:

rg -n "requireCurrentUser" --type java

Repository: ithsjava25/project-backend-alfs

Length of output: 1120


🏁 Script executed:

rg -n "submit-authenticated" --type java

Repository: ithsjava25/project-backend-alfs

Length of output: 184


🏁 Script executed:

find . -name "*.html" -o -name "*.jsp" | xargs grep -l "submit-authenticated" 2>/dev/null

Repository: ithsjava25/project-backend-alfs

Length of output: 59


🏁 Script executed:

find . -type f \( -name "*.html" -o -name "*.jsp" -o -name "*.ftl" \) | head -20

Repository: ithsjava25/project-backend-alfs

Length of output: 57


🏁 Script executed:

rg -n "preview" --type java -i | head -30

Repository: ithsjava25/project-backend-alfs

Length of output: 668


🏁 Script executed:

find . -name "*SecurityConfig*.java" -o -name "*Security*.java" | head -10

Repository: ithsjava25/project-backend-alfs

Length of output: 195


🏁 Script executed:

cat -n src/main/java/org/example/alfs/config/SecurityConfig.java

Repository: ithsjava25/project-backend-alfs

Length of output: 3517


🏁 Script executed:

rg -A 20 "public.*createNewTicket" src/main/java/org/example/alfs/services/TicketService.java

Repository: ithsjava25/project-backend-alfs

Length of output: 741


🏁 Script executed:

rg -B 5 -A 10 "requireCurrentUser" src/main/java/org/example/alfs/services/TicketService.java

Repository: ithsjava25/project-backend-alfs

Length of output: 4815


🏁 Script executed:

wc -l src/main/java/org/example/alfs/services/TicketService.java

Repository: ithsjava25/project-backend-alfs

Length of output: 137


🏁 Script executed:

cat -n src/main/java/org/example/alfs/services/TicketService.java

Repository: ithsjava25/project-backend-alfs

Length of output: 13799


🏁 Script executed:

find . -name "*AuthService*.java" -o -name "*SecurityUtil*.java" | head -10

Repository: ithsjava25/project-backend-alfs

Length of output: 194


🏁 Script executed:

rg -l "requireCurrentUser" --type java

Repository: ithsjava25/project-backend-alfs

Length of output: 133


🏁 Script executed:

rg -B 2 "@PostMapping\|@GetMapping\|@PreAuthorize" src/main/java/org/example/alfs/controllers/TicketController.java -A 3

Repository: ithsjava25/project-backend-alfs

Length of output: 57


Add explicit authentication guard at controller boundary.

Line 68 should declare @PreAuthorize("isAuthenticated()") to enforce authentication upfront and align with the pattern used elsewhere in the controller (e.g., lines 90, 102, 122). Currently, anonymous users reach the service layer and fail with a late-stage 401 error instead of being rejected early.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/alfs/controllers/TicketController.java` around
lines 68 - 76, The submitAuthenticated method in TicketController lacks the
`@PreAuthorize` check, allowing anonymous requests to reach
ticketService.createNewTicket and trigger a late 401; add
`@PreAuthorize`("isAuthenticated()") above the submitAuthenticated method (and
ensure the PreAuthorize import is present and method-level security is enabled
in the app) so authentication is enforced at the controller boundary consistent
with other methods.

Comment thread src/main/java/org/example/alfs/dto/ticket/TicketViewDTO.java
Comment thread src/main/jte/layout.jte Outdated
Comment thread src/main/jte/login.jte Outdated
Comment on lines +26 to +34
<form method="get" action="/tickets/token" class="space-y-4">
<h2 class="text-xl font-bold leading-tight">Login with token</h2>
<input type="text" name="token"
placeholder="Enter your token" />
<button type="submit"
class="w-full bg-white text-[#1a2b49] py-2 rounded font-semibold hover:bg-gray-100 transition">
View Report
</button>
</form>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Token exposed in URL via GET method.

Using method="get" puts the token in the URL query string (/tickets/token?token=xxx). This exposes the sensitive token in:

  • Browser history
  • Server access logs
  • Referrer headers when navigating away

Consider using POST instead:

-        <form method="get" action="/tickets/token" class="space-y-4">
+        <form method="post" action="/tickets/token" class="space-y-4">

This would require updating the corresponding controller endpoint from @GetMapping to @PostMapping.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/jte/login.jte` around lines 26 - 34, The form currently submits the
sensitive token via GET (form method="get" action="/tickets/token"), exposing it
in URLs; change the form to use method="post" and update the server-side handler
mapping from `@GetMapping`("/tickets/token") to `@PostMapping`("/tickets/token") (or
add a separate `@PostMapping` handler) so the token is sent in the request body;
also ensure the controller reads the token from the request body/parameter
appropriately and that any CSRF protection/middleware is handled for POST
endpoints.

Comment thread src/main/jte/previewTicket.jte Outdated
Comment on lines +25 to +32
<form method="post" action="/tickets/submit-authenticated">
<input type="hidden" name="title" value="${ticket.getTitle()}" />
<input type="hidden" name="description" value="${ticket.getDescription()}" />

<button class="bg-[#1a2b49] text-white px-4 py-2 rounded">
Submit as logged-in user
</button>
</form>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hide authenticated-submit action for anonymous sessions.

Lines 25–32 render “Submit as logged-in user” for everyone. For anonymous users this leads to a failed path; render this button only when authenticated and show a sign-in CTA otherwise.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/jte/previewTicket.jte` around lines 25 - 32, The "Submit as
logged-in user" form (action "/tickets/submit-authenticated") is rendered for
all visitors; change the template in previewTicket.jte to only render that
<form> block when the request/session indicates an authenticated user (e.g.,
check the template's currentUser or session.isAuthenticated() variable), and for
anonymous users replace it with a sign-in CTA (a link/button that directs to
your sign-in route) so anonymous sessions don't see the authenticated-submit
path.

Comment thread src/main/jte/startPage.jte Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/resources/static/css/style.css (1)

1-35: ⚠️ Potential issue | 🟡 Minor

Resolve the current Stylelint failures in this block.

Line 1, Line 8, and Line 28 already match the static-analysis errors: import notation, quoted Roboto, and non-kebab-case keyframe naming. This will keep the stylesheet red in lint until those are aligned.

Suggested fix
-@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&family=Roboto:ital,wght@0,100..900;1,100..900&display=swap');
+@import 'https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&family=Roboto:ital,wght@0,100..900;1,100..900&display=swap';
@@
-    font-family: "Roboto", sans-serif;
+    font-family: Roboto, sans-serif;
@@
-@keyframes toastProgress {
+@keyframes toast-progress {
@@
 .animate-toast {
-    animation: toastProgress 3s linear forwards;
+    animation: toast-progress 3s linear forwards;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/static/css/style.css` around lines 1 - 35, Replace the
`@import` url(...) notation with a plain `@import`
"https://fonts.googleapis.com/..." statement, remove the quotes around the
Roboto family name in the body font-family declaration (change font-family:
"Roboto", sans-serif; to font-family: Roboto, sans-serif;), and rename the
`@keyframes` toastProgress to a kebab-case name (e.g., `@keyframes` toast-progress)
and update the .animate-toast rule to use animation: toast-progress 3s linear
forwards; so the symbols to change are the `@import` line, the body font-family,
the `@keyframes` toastProgress declaration, and the .animate-toast animation
reference.
♻️ Duplicate comments (1)
src/main/java/org/example/alfs/controllers/TicketController.java (1)

55-57: ⚠️ Potential issue | 🔴 Critical

Keep reporter tokens out of query strings and path segments.

The anonymous flow still transports the reporter token via redirect:/tickets/ticket-created?token=..., GET /tickets/token/{token}, and @RequestParam String token on the confirmation page. That leaks a bearer-style secret into browser history, access logs, and referrers, which is especially risky here because the PR explicitly notes plain-text token storage.

Suggested direction
-        if (ticket.getToken() != null) {
-            return "redirect:/tickets/ticket-created?token=" + ticket.getToken();
-        }
+        if (ticket.getToken() != null) {
+            redirectAttributes.addFlashAttribute("reporterToken", ticket.getToken());
+            return "redirect:/tickets/ticket-created";
+        }
@@
-    `@GetMapping`("/token/{token}")
-    public String viewTicketByToken(`@PathVariable` String token, Model model) {
+    `@PostMapping`("/token")
+    public String viewTicketByToken(`@RequestParam` String token, Model model) {
@@
-    public String ticketCreated(`@RequestParam` String token, Model model) {
+    public String ticketCreated(`@ModelAttribute`("reporterToken") String token, Model model) {
         model.addAttribute("token", token);
         return "ticket-created";
     }

Also applies to: 66-67, 134-137

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/alfs/controllers/TicketController.java` around
lines 55 - 57, The controller currently exposes reporter tokens via query params
and path segments (see TicketController usage of ticket.getToken(), the redirect
"redirect:/tickets/ticket-created?token=...", the GET /tickets/token/{token}
endpoint and handlers with `@RequestParam` String token); instead, stop sending
tokens in URLs by storing the token server-side (e.g., put ticket.getToken()
into the HttpSession or set a short-lived secure, HttpOnly cookie) before
redirecting to "redirect:/tickets/ticket-created" (no token param), then have
the confirmation handler (formerly reading `@RequestParam` String token or path
variable) retrieve and consume the token from the session/cookie and immediately
remove it; update the token-consumption endpoints (including the GET
/tickets/token/{token} handler) to accept tokens only from session/cookie or via
a POST body to avoid URL exposure.
🧹 Nitpick comments (2)
src/main/java/org/example/alfs/controllers/AuthViewController.java (2)

78-85: Extract JWT cookie writing into one helper.

Login and logout duplicate the JWT cookie attributes today. If one side later changes Secure, Domain, or SameSite, logout can stop clearing the same cookie that login created.

♻️ Suggested refactor
+    private void writeJwtCookie(HttpServletResponse response, String value, Duration maxAge) {
+        ResponseCookie cookie = ResponseCookie.from("JWT", value)
+                .httpOnly(true)
+                .path("/")
+                .maxAge(maxAge)
+                .sameSite("Lax")
+                .build();
+
+        response.addHeader("Set-Cookie", cookie.toString());
+    }
+
     `@PostMapping`("/login-form")
     public String loginForm(
             `@RequestParam` String username,
             `@RequestParam` String password,
             HttpServletResponse response,
             RedirectAttributes redirectAttributes
     ) {
         try {
             User user = authService.login(username, password);
 
             String token = jwtService.generateToken(user);
-
-            ResponseCookie cookie = ResponseCookie.from("JWT", token)
-                    .httpOnly(true)
-                    .path("/")
-                    .maxAge(Duration.ofDays(1))
-                    .sameSite("Lax")
-                    .build();
-
-            response.addHeader("Set-Cookie", cookie.toString());
+            writeJwtCookie(response, token, Duration.ofDays(1));
 
             redirectAttributes.addFlashAttribute("success", "You are signed in!");
             return "redirect:/";
@@
     `@PostMapping`("/auth/logout")
     public String logout(HttpServletResponse response, RedirectAttributes redirectAttributes) {
-
-        ResponseCookie cookie = ResponseCookie.from("JWT", "")
-                .httpOnly(true)
-                .path("/")
-                .maxAge(Duration.ZERO)
-                .sameSite("Lax")
-                .build();
-
-        response.addHeader("Set-Cookie", cookie.toString());
+        writeJwtCookie(response, "", Duration.ZERO);
         redirectAttributes.addFlashAttribute("success", "Successfully signed out");
         return "redirect:/";
     }

Also applies to: 105-112

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/alfs/controllers/AuthViewController.java` around
lines 78 - 85, Extract the duplicated JWT cookie construction into a single
helper in AuthViewController (e.g., a private buildJwtCookie(String token) and a
private clearJwtCookie()) and use those from both the login and logout flows;
ensure the helper centralizes attributes httpOnly(true), path("/"),
sameSite("Lax"), maxAge(Duration.ofDays(1)) and any Secure/Domain settings, and
have clearJwtCookie produce a cookie with the same name/attributes but expired
(maxAge 0 or equivalent) so logout clears exactly the same cookie that login
sets.

78-83: Consider a profile-backed Secure flag for the auth cookie.

HttpOnly and SameSite are set, but the JWT cookie lacks the Secure attribute. For production deployments, enable Secure via a profile-based configuration and keep logout aligned with the same flag. Spring's ResponseCookie API fully supports .secure(boolean) since Spring 5.0+.

Given that this project is development-only, this hardening can be deferred for local environments.

Also applies to: 105-110

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/alfs/controllers/AuthViewController.java` around
lines 78 - 83, The JWT ResponseCookie in AuthViewController is missing the
Secure attribute; update the cookie creation (the ResponseCookie.from(...) call
in the login flow) to set .secure(...) based on a profile/config flag and apply
the same flag when clearing the cookie in the logout flow (the ResponseCookie
built around lines 105-110). Add a configurable boolean (e.g.,
`@Value`("${auth.cookie.secure:false}") or inject Environment and use
Environment.acceptsProfiles("prod")) into AuthViewController, defaulting to
false for local/dev, then pass that boolean to ResponseCookie.secure(secureFlag)
for both the login token cookie and the logout/clear-cookie ResponseCookie so
production profiles enable Secure while dev stays compatible.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/example/alfs/config/GlobalModelAttributes.java`:
- Around line 22-27: In GlobalModelAttributes, narrow the broad catch by
replacing the catch(Exception) around securityUtils.getCurrentUser() with
catch(RuntimeException) and add a log statement that records the exception
(e.g., using the class logger) and a brief contextual message; keep the existing
isLoggedIn/username assignments in the try block and ensure the catch only logs
the exception instead of silently swallowing it so issues from getCurrentUser()
are observable.

In `@src/main/jte/layout.jte`:
- Around line 101-124: The toast progress and dismissal are out of sync because
`#toast-bar`'s CSS uses a 3s animation while the script sets bar.style.transition
= "width 5s linear" and removes the toast after 3s; update the script so the JS
is the single source of truth by changing the transition to "width 3s linear"
(or remove the CSS animation and keep JS at 3s), ensure bar initial
width/starting state matches (e.g., start at 100% and set to "0%"), and keep the
setTimeout removal at 3000 so toast, bar, and setTimeout durations (toast, bar,
setTimeout) are all consistent.

In `@src/main/jte/login.jte`:
- Around line 49-57: The onsubmit handler currently concatenates
this.token.value directly into the route, which allows leading/trailing spaces
or reserved characters to corrupt the URL; update the form submit logic
(onsubmit) to trim the token and URL-encode it before building the path (use
something like encodeURIComponent on the trimmed value) so the route becomes
'/tickets/token/' + encodedToken and avoids invalid-token redirects.

---

Outside diff comments:
In `@src/main/resources/static/css/style.css`:
- Around line 1-35: Replace the `@import` url(...) notation with a plain `@import`
"https://fonts.googleapis.com/..." statement, remove the quotes around the
Roboto family name in the body font-family declaration (change font-family:
"Roboto", sans-serif; to font-family: Roboto, sans-serif;), and rename the
`@keyframes` toastProgress to a kebab-case name (e.g., `@keyframes` toast-progress)
and update the .animate-toast rule to use animation: toast-progress 3s linear
forwards; so the symbols to change are the `@import` line, the body font-family,
the `@keyframes` toastProgress declaration, and the .animate-toast animation
reference.

---

Duplicate comments:
In `@src/main/java/org/example/alfs/controllers/TicketController.java`:
- Around line 55-57: The controller currently exposes reporter tokens via query
params and path segments (see TicketController usage of ticket.getToken(), the
redirect "redirect:/tickets/ticket-created?token=...", the GET
/tickets/token/{token} endpoint and handlers with `@RequestParam` String token);
instead, stop sending tokens in URLs by storing the token server-side (e.g., put
ticket.getToken() into the HttpSession or set a short-lived secure, HttpOnly
cookie) before redirecting to "redirect:/tickets/ticket-created" (no token
param), then have the confirmation handler (formerly reading `@RequestParam`
String token or path variable) retrieve and consume the token from the
session/cookie and immediately remove it; update the token-consumption endpoints
(including the GET /tickets/token/{token} handler) to accept tokens only from
session/cookie or via a POST body to avoid URL exposure.

---

Nitpick comments:
In `@src/main/java/org/example/alfs/controllers/AuthViewController.java`:
- Around line 78-85: Extract the duplicated JWT cookie construction into a
single helper in AuthViewController (e.g., a private buildJwtCookie(String
token) and a private clearJwtCookie()) and use those from both the login and
logout flows; ensure the helper centralizes attributes httpOnly(true),
path("/"), sameSite("Lax"), maxAge(Duration.ofDays(1)) and any Secure/Domain
settings, and have clearJwtCookie produce a cookie with the same name/attributes
but expired (maxAge 0 or equivalent) so logout clears exactly the same cookie
that login sets.
- Around line 78-83: The JWT ResponseCookie in AuthViewController is missing the
Secure attribute; update the cookie creation (the ResponseCookie.from(...) call
in the login flow) to set .secure(...) based on a profile/config flag and apply
the same flag when clearing the cookie in the logout flow (the ResponseCookie
built around lines 105-110). Add a configurable boolean (e.g.,
`@Value`("${auth.cookie.secure:false}") or inject Environment and use
Environment.acceptsProfiles("prod")) into AuthViewController, defaulting to
false for local/dev, then pass that boolean to ResponseCookie.secure(secureFlag)
for both the login token cookie and the logout/clear-cookie ResponseCookie so
production profiles enable Secure while dev stays compatible.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2560a6da-831d-4594-ad79-9726c94f3edb

📥 Commits

Reviewing files that changed from the base of the PR and between 419a420 and c55e12a.

📒 Files selected for processing (18)
  • src/main/java/org/example/alfs/config/GlobalModelAttributes.java
  • src/main/java/org/example/alfs/config/SecurityConfig.java
  • src/main/java/org/example/alfs/controllers/AuthViewController.java
  • src/main/java/org/example/alfs/controllers/StartPageController.java
  • src/main/java/org/example/alfs/controllers/TicketController.java
  • src/main/java/org/example/alfs/dto/ticket/TicketViewDTO.java
  • src/main/java/org/example/alfs/entities/Ticket.java
  • src/main/java/org/example/alfs/services/TicketService.java
  • src/main/jte/create.jte
  • src/main/jte/layout.jte
  • src/main/jte/login.jte
  • src/main/jte/my-tickets.jte
  • src/main/jte/previewTicket.jte
  • src/main/jte/signup.jte
  • src/main/jte/startPage.jte
  • src/main/jte/ticket-created.jte
  • src/main/jte/view.jte
  • src/main/resources/static/css/style.css
🚧 Files skipped from review as they are similar to previous changes (8)
  • src/main/java/org/example/alfs/config/SecurityConfig.java
  • src/main/java/org/example/alfs/entities/Ticket.java
  • src/main/java/org/example/alfs/controllers/StartPageController.java
  • src/main/java/org/example/alfs/dto/ticket/TicketViewDTO.java
  • src/main/jte/previewTicket.jte
  • src/main/jte/startPage.jte
  • src/main/java/org/example/alfs/services/TicketService.java
  • src/main/jte/create.jte

Comment thread src/main/java/org/example/alfs/config/GlobalModelAttributes.java
Comment thread src/main/jte/layout.jte
Comment thread src/main/jte/login.jte
addee1 and others added 4 commits April 16, 2026 11:42
- replace exception-based user lookup with getCurrentUserOrNull
- prevent NullPointerException in createNewTicket
- fix failing tests for unauthenticated and missing users
…` tests and add test for anonymous reporter scenario

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/example/alfs/security/SecurityUtils.java`:
- Around line 31-36: The current getCurrentUserOrNull method swallows all
RuntimeException instances; change it to only catch authentication-related
exceptions (e.g., Spring Security's AuthenticationException and its common
subclasses like BadCredentialsException, InsufficientAuthenticationException,
AuthenticationCredentialsNotFoundException) when calling getCurrentUser(),
returning null for those cases, and rethrow any other unexpected exceptions so
infrastructure/DB errors are not masked; update the catch clause(s) in
getCurrentUserOrNull to reference those authentication exception types and let
other exceptions propagate.

In `@src/test/java/org/example/alfs/services/TicketServiceTest.java`:
- Around line 78-127: The tests for ticketService.createNewTicket need stronger
token assertions: in the authenticated test
(createNewTicket_shouldSetReporterAsUser) assert the returned
TicketViewDTO.getToken() is null and that the saved Ticket has a null
reporterToken (verify ticketRepository.save(...) asserts
ticket.getReporterToken() == null); in the anonymous test
(createNewTicket_shouldSetReporterToken_whenAnonymous) capture the saved Ticket
(use ArgumentCaptor<Ticket> or verify with a lambda) and assert its
getReporterToken() is non-null and equals the returned TicketViewDTO.getToken()
(i.e., savedTicket.getReporterToken().equals(result.getToken())). Ensure
ticketRepository.save and ticketMapper.entityToViewDTO stubs remain so the saved
instance is available for comparison.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3589af35-175a-4b41-ab97-4002c050cc20

📥 Commits

Reviewing files that changed from the base of the PR and between c55e12a and 0230126.

📒 Files selected for processing (3)
  • src/main/java/org/example/alfs/security/SecurityUtils.java
  • src/main/java/org/example/alfs/services/TicketService.java
  • src/test/java/org/example/alfs/services/TicketServiceTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/org/example/alfs/services/TicketService.java

Comment thread src/main/java/org/example/alfs/security/SecurityUtils.java
Comment thread src/test/java/org/example/alfs/services/TicketServiceTest.java

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
src/main/jte/startPage.jte (1)

13-15: ⚠️ Potential issue | 🟠 Major

Avoid absolute identity guarantees in user-facing copy.

Line 14 says “We guarantee that your identity is protected…”. That is still a categorical promise and creates legal/compliance risk, especially while the flow still has known security limitations.

Suggested copy change
-                We guarantee that your identity is protected in accordance with applicable whistleblowing legislation.
+                We work to protect your identity and handle reports in accordance with applicable whistleblowing legislation.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/jte/startPage.jte` around lines 13 - 15, In startPage.jte update the
user-facing sentence that currently reads “We guarantee that your identity is
protected in accordance with applicable whistleblowing legislation.” to avoid an
absolute legal promise; replace it with qualified wording such as “We take
measures to protect your identity in accordance with applicable whistleblowing
legislation” or “We aim to protect your identity, subject to applicable law and
technical limitations,” so the copy in the template (startPage.jte) expresses
intent and mitigations rather than a categorical guarantee.
src/main/jte/login.jte (1)

49-51: ⚠️ Potential issue | 🟠 Major

Reporter token is still exposed through URL navigation.

At Line 50, redirecting to /tickets/token/{token} places the token in browser history, logs, and potential referrers. Since this token grants ticket access, it should be exchanged via request body (POST) and then redirected to an opaque/non-secret route.

Suggested direction
-        <form method="get"
-              onsubmit="event.preventDefault(); window.location='/tickets/token/' + encodeURIComponent(this.token.value.trim())"
+        <form method="post" action="/tickets/token/resolve"
               class="space-y-4">
-            <input type="text" name="token"
+            <input type="text" name="token"
                    placeholder="Enter your token"
                    class="w-full px-3 py-2 rounded bg-white text-black" />

Then resolve the token server-side and redirect to a non-sensitive URL (for example by ticket id or a short-lived server-side session key), instead of carrying the token in the path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/jte/login.jte` around lines 49 - 51, The current form uses GET and
client-side redirect (onsubmit ->
window.location='/tickets/token/'+encodeURIComponent(this.token.value.trim()))
which exposes the reporter token in the URL; change the form to submit via POST
(remove the onsubmit JS) to an endpoint that accepts the token in the request
body (e.g., POST /tickets/token), then have server-side code resolve the token
and issue a redirect to a non-sensitive URL (ticket id or short-lived session
key). Update the form element and any handler tied to this token input (the
reference this.token.value) so the token is sent in the POST body and never
embedded in a path or query string, and implement server logic to map the token
to an opaque identifier before redirecting.
🧹 Nitpick comments (1)
src/main/jte/startPage.jte (1)

39-48: Consider clearer CTA labels for intent.

“Leave new report” can be made clearer (e.g., “Submit anonymous report”) to reduce ambiguity on the start page.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/jte/startPage.jte` around lines 39 - 48, Update the ambiguous CTA
text "Leave new report" for the link with href "/tickets/create" to a clearer
label such as "Submit anonymous report" (or another concise phrasing
communicating anonymity and action), and ensure the complementary sign-in link
text for href "/login" remains clear; modify the anchor text in the
startPage.jte template where the <a href="/tickets/create"> contains "Leave new
report" to the new wording and verify hover/aria/title text (if present)
reflects the change for accessibility.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/main/jte/login.jte`:
- Around line 49-51: The current form uses GET and client-side redirect
(onsubmit ->
window.location='/tickets/token/'+encodeURIComponent(this.token.value.trim()))
which exposes the reporter token in the URL; change the form to submit via POST
(remove the onsubmit JS) to an endpoint that accepts the token in the request
body (e.g., POST /tickets/token), then have server-side code resolve the token
and issue a redirect to a non-sensitive URL (ticket id or short-lived session
key). Update the form element and any handler tied to this token input (the
reference this.token.value) so the token is sent in the POST body and never
embedded in a path or query string, and implement server logic to map the token
to an opaque identifier before redirecting.

In `@src/main/jte/startPage.jte`:
- Around line 13-15: In startPage.jte update the user-facing sentence that
currently reads “We guarantee that your identity is protected in accordance with
applicable whistleblowing legislation.” to avoid an absolute legal promise;
replace it with qualified wording such as “We take measures to protect your
identity in accordance with applicable whistleblowing legislation” or “We aim to
protect your identity, subject to applicable law and technical limitations,” so
the copy in the template (startPage.jte) expresses intent and mitigations rather
than a categorical guarantee.

---

Nitpick comments:
In `@src/main/jte/startPage.jte`:
- Around line 39-48: Update the ambiguous CTA text "Leave new report" for the
link with href "/tickets/create" to a clearer label such as "Submit anonymous
report" (or another concise phrasing communicating anonymity and action), and
ensure the complementary sign-in link text for href "/login" remains clear;
modify the anchor text in the startPage.jte template where the <a
href="/tickets/create"> contains "Leave new report" to the new wording and
verify hover/aria/title text (if present) reflects the change for accessibility.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 66e220f3-13e2-4043-86dd-eecebec74237

📥 Commits

Reviewing files that changed from the base of the PR and between 0230126 and d5b8315.

📒 Files selected for processing (6)
  • src/main/java/org/example/alfs/config/GlobalModelAttributes.java
  • src/main/java/org/example/alfs/security/SecurityUtils.java
  • src/main/jte/layout.jte
  • src/main/jte/login.jte
  • src/main/jte/startPage.jte
  • src/test/java/org/example/alfs/services/TicketServiceTest.java
✅ Files skipped from review due to trivial changes (1)
  • src/main/jte/layout.jte
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main/java/org/example/alfs/security/SecurityUtils.java
  • src/main/java/org/example/alfs/config/GlobalModelAttributes.java
  • src/test/java/org/example/alfs/services/TicketServiceTest.java

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants