Skip to content

Feature/user endpoints#26

Merged
Martin-E-Karlsson merged 7 commits into
mainfrom
feature/user-endpoints
Apr 14, 2026
Merged

Feature/user endpoints#26
Martin-E-Karlsson merged 7 commits into
mainfrom
feature/user-endpoints

Conversation

@Martin-E-Karlsson

@Martin-E-Karlsson Martin-E-Karlsson commented Apr 13, 2026

Copy link
Copy Markdown
Contributor
  • Endpoints for user added to to UserViewController and associated templates.
  • Navigation bar introduced
  • Basic SecurityUser wrapper and a UserDetailsServiceImpl created as a Spring Security workaround to make the user pages functional without scope creep
  • Dashboards for different users added to controllers and templates
  • Additional validation methods added to UserService

Summary by CodeRabbit

  • New Features
    • Role-based dashboards for applicants, admins, and system administrators
    • Signup and login flows with session-based authentication
    • Profile view and edit pages with access control
    • Admin views for assigned/unassigned cases; sysadmin views for users and audit logs
    • New header/navigation fragment used across pages
    • Password now requires a minimum of 8 characters
  • Enhancements
    • New audit and visa listing endpoints for dashboard displays

- Created SecurityUser, which is a wrapper for user meant to safely interact with Spring Security.
- Created UserViewerController mean tto contain User specific views.
- Added Dashboard logic to ApplicationViewController.
- Added helper methods for the dashboard to AuditService and VisaService which are yet to be correctly implemented.
- Added basic HTML templates for the applicant, admin and sysadmin dashboards.
- Fixed issues with recently VisaService methods
- Moved dashboards from ApplicationViewController to UserViewController
- Added basic controller endpoint structure
- Added basic user signup logic
- Added controller logic to viewProfile, showProfileEditForm, updateProfile, userListView
- Added additional validation methods for checking access requirements to UserService
- Changed from /applicant/dashboard to /dashboard/applicant

ApplicationViewController:
- Added dashboard redirection logic based on user role

Templates:
- Added templates following existing style in visa application pages for the following views: signup, view, login, list, header, edit
- Updated dashboards to follow the new theme

Security:
- Added UserDetailsServiceImpl which lets Spring Security access the UserRepository to fetch usernames
@Martin-E-Karlsson Martin-E-Karlsson linked an issue Apr 13, 2026 that may be closed by this pull request
@coderabbitai

coderabbitai Bot commented Apr 13, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds role-based authentication and authorization: a SecurityUser and UserDetailsService, authorization helpers in UserService, role-aware dashboard routing, new user/profile controllers, repository/service queries for visa/audit, and multiple Thymeleaf templates and fragments for role-specific UIs.

Changes

Cohort / File(s) Summary
Security & UserDetails
src/main/java/org/example/visacasemanagementsystem/user/security/SecurityUser.java, src/main/java/org/example/visacasemanagementsystem/user/security/UserDetailsServiceImpl.java
New SecurityUser implementing UserDetails (stores userId/fullName, maps role to ROLE_*); new UserDetailsServiceImpl loads users by email and returns SecurityUser or throws UsernameNotFoundException.
Top-level Routing
src/main/java/org/example/visacasemanagementsystem/ApplicationViewController.java
Added GET /dashboard handler that redirects authenticated principals to /dashboard/sysadmin, /dashboard/admin, or /dashboard/applicant based on granted authorities; redirects anonymous requests to /user/login.
User View Controller & UI Flows
src/main/java/org/example/visacasemanagementsystem/user/controller/UserViewController.java
New controller exposing signup/login, profile view/edit, sysadmin user listing, and three role dashboards (applicant/admin/sysadmin); handles form submission, validation errors, model population for templates.
Authorization Helpers & Validation
src/main/java/org/example/visacasemanagementsystem/user/service/UserService.java, src/main/java/org/example/visacasemanagementsystem/user/dto/CreateUserDTO.java
createUser now validates @Valid CreateUserDTO; password length enforced in DTO and additionally checked in service; added validateProfileAccess, validateSysAdmin, validateAdmin that inspect SecurityUser authorities and throw UnauthorizedException when appropriate.
Audit & Visa Service Enhancements
src/main/java/org/example/visacasemanagementsystem/audit/service/AuditService.java, src/main/java/org/example/visacasemanagementsystem/visa/service/VisaService.java
Added AuditService.findAll() returning List<AuditDTO>; added findVisasByApplicantId and findVisasByHandlerId to VisaService (sorted by updatedAt); minor catch-variable rename in findVisaByStatus.
Repository Additions
src/main/java/org/example/visacasemanagementsystem/visa/repository/VisaRepository.java
Added Spring Data query methods: findVisasByApplicantId(Long, Sort) and findVisasByHandlerId(Long, Sort).
Thymeleaf Templates & Fragments
src/main/resources/templates/fragments/header.html, src/main/resources/templates/dashboard/*.html, src/main/resources/templates/user/*.html, src/main/resources/templates/profile/*.html, src/main/resources/templates/user/list.html
Added shared header fragment with role-conditional links; new role-specific dashboards (applicant.html, admin.html, sysadmin.html), signup/login templates, profile view/edit templates, and user list template. Focus on role-aware displays, status badges, and conditional empty states.

Sequence Diagram

sequenceDiagram
    participant Browser as User (Browser)
    participant Auth as Spring Security
    participant UDS as UserDetailsServiceImpl
    participant Repo as UserRepository
    participant SU as SecurityUser
    participant MVC as UserViewController / ApplicationViewController
    participant VisaSvc as VisaService
    participant AuditSvc as AuditService

    Browser->>Auth: POST /user/login (email,password)
    Auth->>UDS: loadUserByUsername(email)
    UDS->>Repo: findByEmail(email)
    Repo-->>UDS: User entity
    UDS->>SU: new SecurityUser(user)
    SU-->>UDS: SecurityUser (ROLE_*)
    UDS-->>Auth: UserDetails
    Auth->>Auth: validate password
    Auth-->>Browser: authenticated (principal=SecurityUser)
    Browser->>MVC: GET /dashboard
    MVC->>MVC: inspect principal.authorities
    alt ROLE_SYSADMIN
        MVC-->>Browser: redirect /dashboard/sysadmin
        MVC->>AuditSvc: findAll()
        MVC->>Repo: (user listing via UserService)
    else ROLE_ADMIN
        MVC-->>Browser: redirect /dashboard/admin
        MVC->>VisaSvc: findVisasByHandlerId(...) / findVisasByApplicantId(...)
    else Default
        MVC-->>Browser: redirect /dashboard/applicant
        MVC->>VisaSvc: findVisasByApplicantId(principal.userId)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • dinbah18

Poem

🐰 I hopped through code with tiny paws,

Added roles, checks, and login laws.
Dashboards bloom for each brave role—
Applicants, Admins, Sysadmins whole.
✨ Hop, review, and merge with glee!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.24% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Feature/user endpoints' is vague and generic, using a naming convention rather than describing the specific changes made. Use a more descriptive title that highlights the main feature, such as 'Add user authentication and role-based dashboards' or 'Implement user signup, login, and role-based views'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/user-endpoints

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: 7

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/visacasemanagementsystem/user/service/UserService.java (1)

50-61: ⚠️ Potential issue | 🟠 Major

Password encryption should now be implemented.

Spring Security is being integrated in this PR, which was the condition for adding password encryption. The plaintext password storage at line 53 should be replaced with BCryptPasswordEncoder:

🔐 Proposed fix

Inject PasswordEncoder into the service:

private final PasswordEncoder passwordEncoder;

public UserService(UserRepository userRepository, UserMapper userMapper, PasswordEncoder passwordEncoder) {
    this.userRepository = userRepository;
    this.userMapper = userMapper;
    this.passwordEncoder = passwordEncoder;
}

Then encode the password:

-user.setPassword(dto.password());
+user.setPassword(passwordEncoder.encode(dto.password()));

Based on learnings: "password encryption in UserService.createUser() is intentionally deferred until Spring Security is integrated."

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

In
`@src/main/java/org/example/visacasemanagementsystem/user/service/UserService.java`
around lines 50 - 61, Replace storing plaintext password in createUser by
injecting and using a PasswordEncoder: add a private final PasswordEncoder field
to UserService, update the UserService constructor to accept PasswordEncoder
alongside UserRepository and UserMapper, and then replace
user.setPassword(dto.password()) in createUser(...) with
user.setPassword(passwordEncoder.encode(dto.password())); keep existing
exception handling and userAuthorization logic untouched.
🧹 Nitpick comments (7)
src/main/java/org/example/visacasemanagementsystem/visa/repository/VisaRepository.java (1)

27-29: Consider adding Sort parameter for consistency.

Other query methods in this repository accept a Sort parameter for ordering results. The new methods return unsorted lists, which may lead to inconsistent display ordering in dashboards. Consider adding Sort for parity:

List<Visa> findVisasByApplicantId(Long applicantId, Sort sort);
List<Visa> findVisasByHandlerId(Long handlerId, Sort sort);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/org/example/visacasemanagementsystem/visa/repository/VisaRepository.java`
around lines 27 - 29, In VisaRepository update the two finder signatures
(findVisasByApplicantId and findVisasByHandlerId) to accept a Spring Data Sort
parameter (e.g., change to findVisasByApplicantId(Long applicantId, Sort sort)
and findVisasByHandlerId(Long handlerId, Sort sort)), add the necessary import
for org.springframework.data.domain.Sort, and then update any callers/tests to
pass a Sort instance (or Sort.unsorted()) so results are returned with explicit
ordering consistent with other repository methods.
src/main/resources/templates/user/signup.html (2)

153-158: Consider adding client-side password requirements.

Adding minlength attribute provides immediate feedback to users about password requirements:

<input type="password" name="password"
       placeholder="Choose a password"
       minlength="8"
       required>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/templates/user/signup.html` around lines 153 - 158, The
password input currently lacks client-side length validation; update the
password <input> element used in the signup form (the password input with
name="password") to include a minlength attribute (e.g. minlength="8") so
browsers give immediate feedback, and optionally add a brief title or pattern if
you want stricter rules (keep required=true). Ensure you only modify the input
tag for the password field to include minlength="8".

112-121: Unused CSS classes.

The .is-invalid and .error-text CSS classes are defined but not used in the template. Either remove them or implement field-level validation styling.

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

In `@src/main/resources/templates/user/signup.html` around lines 112 - 121, The
CSS defines .is-invalid and .error-text but they are unused; either remove these
rules or apply them to the form inputs and error messages: update the signup
form inputs (e.g., the email, password, confirmPassword inputs) to conditionally
add the "is-invalid" class when validation errors exist and render a sibling
element with class "error-text" to show the field-specific error message (use
your template's existing validation/error object or server-side error
variables); if you prefer removal, delete the .is-invalid and .error-text rules
from the template stylesheet.
src/main/java/org/example/visacasemanagementsystem/audit/service/AuditService.java (1)

28-33: Consider pagination for audit log retrieval.

Fetching all audit logs without pagination may cause performance issues as the audit table grows. For an initial implementation this is acceptable, but consider adding pagination support (e.g., Pageable parameter) before production use.

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

In
`@src/main/java/org/example/visacasemanagementsystem/audit/service/AuditService.java`
around lines 28 - 33, The current AuditService.findAll method loads all audit
rows which will not scale; change its signature to accept a Pageable (e.g.,
findAll(Pageable pageable)) and use auditRepository.findAll(pageable) then map
results with auditMapper::toDTO, returning a Page<AuditDTO> (or Slice<AuditDTO>)
so pagination metadata is preserved; update callers and tests to supply Pageable
and handle the Page<AuditDTO> response accordingly.
src/main/java/org/example/visacasemanagementsystem/ApplicationViewController.java (1)

12-22: Consider null check for unauthenticated access.

If an unauthenticated user somehow reaches this endpoint (e.g., misconfigured security), principal will be null, causing an NPE. While Spring Security should protect this route, a defensive check provides clearer error handling:

🛡️ Proposed defensive check
 `@GetMapping`("/dashboard")
 public String dashboard(`@AuthenticationPrincipal` SecurityUser principal) {
+    if (principal == null) {
+        return "redirect:/user/login";
+    }
     boolean isSysAdmin = principal.getAuthorities().stream()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/org/example/visacasemanagementsystem/ApplicationViewController.java`
around lines 12 - 22, In ApplicationViewController.dashboard, add a defensive
null check for the AuthenticationPrincipal parameter (SecurityUser principal)
before calling principal.getAuthorities(); if principal is null, return a safe
response such as "redirect:/login" (or throw an AccessDeniedException) so an
unauthenticated request won’t cause an NPE; keep the rest of the authority
checks (isSysAdmin/isAdmin) unchanged and perform them only after the null
check.
src/main/java/org/example/visacasemanagementsystem/visa/service/VisaService.java (1)

248-260: Add null guards for new finder methods.

Line 248-Line 260 should validate applicantId and handlerId for null to match the defensive style used in the rest of this service.

🔧 Proposed refactor
 public List<VisaDTO> findVisasByApplicantId(Long applicantId) {
+    if (applicantId == null) {
+        throw new IllegalArgumentException("Applicant id cannot be null");
+    }
     return visaRepository.findVisasByApplicantId(applicantId)
             .stream()
             .map(visaMapper::toDTO)
             .toList();
 }

 public List<VisaDTO> findVisasByHandlerId(Long handlerId) {
+    if (handlerId == null) {
+        throw new IllegalArgumentException("Handler id cannot be null");
+    }
     return visaRepository.findVisasByHandlerId(handlerId)
             .stream()
             .map(visaMapper::toDTO)
             .toList();
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/org/example/visacasemanagementsystem/visa/service/VisaService.java`
around lines 248 - 260, The new finder methods findVisasByApplicantId and
findVisasByHandlerId must validate their input IDs are not null like other
methods in this service: add a null check for applicantId/handlerId at the start
of each method and throw an IllegalArgumentException (or use
Objects.requireNonNull with a clear message) when null, so the methods fail fast
with a descriptive message rather than passing null into visaRepository.
src/main/java/org/example/visacasemanagementsystem/user/controller/UserViewController.java (1)

157-158: Consider pagination/limits for sysadmin dashboard datasets.

Fetching all users and all audit logs for one dashboard request can become expensive and degrade page load as data grows.

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

In
`@src/main/java/org/example/visacasemanagementsystem/user/controller/UserViewController.java`
around lines 157 - 158, The dashboard currently calls userService.findAll() and
auditService.findAll() in UserViewController (variables allUsers and recentLogs)
which will load entire tables; change these calls to use paginated or limited
queries (e.g., userService.findPage(Pageable) or userService.findLatest(limit)
and auditService.findPage(Pageable) or auditService.findLatest(limit)) and
update UserViewController to accept/compute page/limit parameters (or use
sensible defaults like latest 50 entries) so the controller only fetches a
bounded result set rather than all rows.
🤖 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/visacasemanagementsystem/user/controller/UserViewController.java`:
- Around line 73-87: In viewProfile (UserViewController.viewProfile) add the
same ownership/role guard used by the edit routes by invoking the existing
validateProfileAccess(...) before loading the UserDTO; if validateProfileAccess
is a private helper on the controller reuse it (e.g.
validateProfileAccess(principal, userId)) or call the same service method used
by edit endpoints, and short-circuit (throw AccessDeniedException or return
appropriate error/redirect) when access is denied so only owners or sysadmins
can view arbitrary profiles.
- Around line 143-163: Add explicit role checks at the start of adminDashboard
and sysAdminDashboard to enforce defense-in-depth: verify the authenticated
SecurityUser (principal) has the required role (e.g., ROLE_ADMIN for
adminDashboard, ROLE_SYSADMIN for sysAdminDashboard) and if not, either throw an
AccessDeniedException or return a 403 view (e.g., "error/403"); mirror the
in-handler check pattern used in userListView to prevent direct URL access to
sensitive data. Ensure the check uses principal.getRoles() or equivalent and
short-circuit before calling visaService, userService, or auditService.

In
`@src/main/java/org/example/visacasemanagementsystem/user/security/SecurityUser.java`:
- Around line 33-47: SecurityUser currently returns plaintext passwords via
getPassword and UserService.createUser assigns dto.password() directly; inject a
PasswordEncoder (use BCryptPasswordEncoder) into UserService and replace
user.setPassword(dto.password()) in createUser with
user.setPassword(passwordEncoder.encode(dto.password())); also register a
BCryptPasswordEncoder bean (e.g., as PasswordEncoder) in your configuration so
Spring can autowire it and ensure SecurityUser.getPassword continues to return
the encoded value stored on the User entity.

In
`@src/main/java/org/example/visacasemanagementsystem/visa/service/VisaService.java`:
- Around line 72-74: The catch block in VisaService.java uses `_` as the
exception variable which is a reserved keyword in Java 25; change the catch
parameter to a valid identifier (e.g., catch (IllegalArgumentException e)) in
the method where the block appears and, if needed, reference the exception
variable when rethrowing or logging (for example include e.getMessage() if you
want details) to ensure the code compiles.

In `@src/main/resources/templates/dashboard/admin.html`:
- Around line 167-170: The dashboard action links currently use placeholders;
add controller endpoints that expose the existing service methods (e.g., create
POST/PUT routes wired to approveVisa(), rejectVisa(), assignHandler() in the
appropriate controller) and update the admin.html action anchors to point to
those routes (use proper HTTP verbs via forms or fetch calls for
Approve/Reject/Assign and a GET route for Review to reuse the Review handler).
Ensure route names and HTTP methods align with the service method semantics and
CSRF/authorization checks are applied when wiring the links.

In `@src/main/resources/templates/dashboard/applicant.html`:
- Line 139: Replace the placeholder anchors that use href="#" with real
application routes: update the anchor with class "btn-apply" to point to the
new-application route (e.g. the create/apply page) and update the other
placeholder anchor(s) on this template (the application-detail link at the later
anchor) to use the application details route including the application id
placeholder (e.g. /applications/{id} or your framework’s route helper). Ensure
you use your templating/URL helper (not a hardcoded "#") so links render
per-user (e.g., use the framework’s path helper or a variable like
application.id) and preserve the existing classes/attributes such as btn-apply
when making the change.

In `@src/main/resources/templates/profile/edit.html`:
- Around line 138-164: The POST profile edit form
(th:action="@{/profile/edit/{id}(id=${user.id()})}") lacks a CSRF token and will
be rejected by Spring Security; add a hidden input inside the form that uses
Thymeleaf's _csrf attributes (th:name="${_csrf.parameterName}" and
th:value="${_csrf.token}") so the token is submitted with the request.

---

Outside diff comments:
In
`@src/main/java/org/example/visacasemanagementsystem/user/service/UserService.java`:
- Around line 50-61: Replace storing plaintext password in createUser by
injecting and using a PasswordEncoder: add a private final PasswordEncoder field
to UserService, update the UserService constructor to accept PasswordEncoder
alongside UserRepository and UserMapper, and then replace
user.setPassword(dto.password()) in createUser(...) with
user.setPassword(passwordEncoder.encode(dto.password())); keep existing
exception handling and userAuthorization logic untouched.

---

Nitpick comments:
In
`@src/main/java/org/example/visacasemanagementsystem/ApplicationViewController.java`:
- Around line 12-22: In ApplicationViewController.dashboard, add a defensive
null check for the AuthenticationPrincipal parameter (SecurityUser principal)
before calling principal.getAuthorities(); if principal is null, return a safe
response such as "redirect:/login" (or throw an AccessDeniedException) so an
unauthenticated request won’t cause an NPE; keep the rest of the authority
checks (isSysAdmin/isAdmin) unchanged and perform them only after the null
check.

In
`@src/main/java/org/example/visacasemanagementsystem/audit/service/AuditService.java`:
- Around line 28-33: The current AuditService.findAll method loads all audit
rows which will not scale; change its signature to accept a Pageable (e.g.,
findAll(Pageable pageable)) and use auditRepository.findAll(pageable) then map
results with auditMapper::toDTO, returning a Page<AuditDTO> (or Slice<AuditDTO>)
so pagination metadata is preserved; update callers and tests to supply Pageable
and handle the Page<AuditDTO> response accordingly.

In
`@src/main/java/org/example/visacasemanagementsystem/user/controller/UserViewController.java`:
- Around line 157-158: The dashboard currently calls userService.findAll() and
auditService.findAll() in UserViewController (variables allUsers and recentLogs)
which will load entire tables; change these calls to use paginated or limited
queries (e.g., userService.findPage(Pageable) or userService.findLatest(limit)
and auditService.findPage(Pageable) or auditService.findLatest(limit)) and
update UserViewController to accept/compute page/limit parameters (or use
sensible defaults like latest 50 entries) so the controller only fetches a
bounded result set rather than all rows.

In
`@src/main/java/org/example/visacasemanagementsystem/visa/repository/VisaRepository.java`:
- Around line 27-29: In VisaRepository update the two finder signatures
(findVisasByApplicantId and findVisasByHandlerId) to accept a Spring Data Sort
parameter (e.g., change to findVisasByApplicantId(Long applicantId, Sort sort)
and findVisasByHandlerId(Long handlerId, Sort sort)), add the necessary import
for org.springframework.data.domain.Sort, and then update any callers/tests to
pass a Sort instance (or Sort.unsorted()) so results are returned with explicit
ordering consistent with other repository methods.

In
`@src/main/java/org/example/visacasemanagementsystem/visa/service/VisaService.java`:
- Around line 248-260: The new finder methods findVisasByApplicantId and
findVisasByHandlerId must validate their input IDs are not null like other
methods in this service: add a null check for applicantId/handlerId at the start
of each method and throw an IllegalArgumentException (or use
Objects.requireNonNull with a clear message) when null, so the methods fail fast
with a descriptive message rather than passing null into visaRepository.

In `@src/main/resources/templates/user/signup.html`:
- Around line 153-158: The password input currently lacks client-side length
validation; update the password <input> element used in the signup form (the
password input with name="password") to include a minlength attribute (e.g.
minlength="8") so browsers give immediate feedback, and optionally add a brief
title or pattern if you want stricter rules (keep required=true). Ensure you
only modify the input tag for the password field to include minlength="8".
- Around line 112-121: The CSS defines .is-invalid and .error-text but they are
unused; either remove these rules or apply them to the form inputs and error
messages: update the signup form inputs (e.g., the email, password,
confirmPassword inputs) to conditionally add the "is-invalid" class when
validation errors exist and render a sibling element with class "error-text" to
show the field-specific error message (use your template's existing
validation/error object or server-side error variables); if you prefer removal,
delete the .is-invalid and .error-text rules from the template stylesheet.
🪄 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: 1155dcd8-1268-4e6d-852f-cb7d659caeb2

📥 Commits

Reviewing files that changed from the base of the PR and between 44500d2 and 1dc6f92.

📒 Files selected for processing (17)
  • src/main/java/org/example/visacasemanagementsystem/ApplicationViewController.java
  • src/main/java/org/example/visacasemanagementsystem/audit/service/AuditService.java
  • src/main/java/org/example/visacasemanagementsystem/user/controller/UserViewController.java
  • src/main/java/org/example/visacasemanagementsystem/user/security/SecurityUser.java
  • src/main/java/org/example/visacasemanagementsystem/user/security/UserDetailsServiceImpl.java
  • src/main/java/org/example/visacasemanagementsystem/user/service/UserService.java
  • src/main/java/org/example/visacasemanagementsystem/visa/repository/VisaRepository.java
  • src/main/java/org/example/visacasemanagementsystem/visa/service/VisaService.java
  • src/main/resources/templates/dashboard/admin.html
  • src/main/resources/templates/dashboard/applicant.html
  • src/main/resources/templates/dashboard/sysadmin.html
  • src/main/resources/templates/fragments/header.html
  • src/main/resources/templates/profile/edit.html
  • src/main/resources/templates/profile/view.html
  • src/main/resources/templates/user/list.html
  • src/main/resources/templates/user/login.html
  • src/main/resources/templates/user/signup.html

Comment thread src/main/java/org/example/visacasemanagementsystem/visa/service/VisaService.java Outdated
Comment on lines +167 to +170
<a href="#" class="action-link">Approve</a>
<a href="#" class="action-link">Reject</a>
<a href="#" class="view-link">Review &rarr;</a>
</div>

@coderabbitai coderabbitai Bot Apr 13, 2026

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:

#!/bin/bash
# Check if backend endpoints exist for admin case actions
rg -n --type=java -C3 'Mapping\\(".*(approve|reject|assign|review)'
# Confirm placeholders still present in this template
rg -n -C2 'href="#"' src/main/resources/templates/dashboard/admin.html

Repository: ithsjava25/visa-case-management-system

Length of output: 803


🏁 Script executed:

#!/bin/bash
# Search for controller mappings related to case actions
echo "=== Searching for controller endpoints for approve, reject, assign ==="
rg -n --type=java -i '(approve|reject|assign|review)' --glob='*Controller*.java' -B2 -A2

echo -e "\n=== Check for RequestMapping or PostMapping annotations ==="
rg -n --type=java '(RequestMapping|PostMapping|GetMapping).*/(approve|reject|assign|review|admin)' -B1 -A3

echo -e "\n=== List Java files in controllers directory ==="
fd --type f '\.java$' | grep -i controller | head -20

Repository: ithsjava25/visa-case-management-system

Length of output: 2655


🏁 Script executed:

#!/bin/bash
# Search for service methods related to case actions
echo "=== Search in service files for approve/reject/assign methods ==="
rg -n --type=java -i 'def\s+(approve|reject|assign)' -B1 -A5

echo -e "\n=== Search for Visa/Case service implementations ==="
fd --type f '\.java$' | grep -i 'service' | head -20

echo -e "\n=== Check VisaService for action methods ==="
fd --type f -i 'visaservice.java' -x cat {} +

Repository: ithsjava25/visa-case-management-system

Length of output: 9622


🏁 Script executed:

#!/bin/bash
# Search for controllers that call approveVisa, rejectVisa, or assignHandler
echo "=== Search for usage of service action methods ==="
rg -n --type=java '(approveVisa|rejectVisa|assignHandler)' -B2 -A5

echo -e "\n=== Check for any REST or API controllers ==="
fd --type f '\.java$' | xargs grep -l 'PostMapping\|PutMapping' | head -10

echo -e "\n=== Search for any href patterns in admin.html ==="
rg -n 'href=' src/main/resources/templates/dashboard/admin.html | head -20

Repository: ithsjava25/visa-case-management-system

Length of output: 4497


🏁 Script executed:

#!/bin/bash
# Check CommentController to see if there's a pattern for POST mappings
echo "=== Check CommentController structure ==="
rg -n --type=java '@PostMapping|@PutMapping' src/main/java/org/example/visacasemanagementsystem/comment/controller/CommentController.java -B1 -A5

echo -e "\n=== Check if there are any forms in admin.html for form submission ==="
rg -n '<form' src/main/resources/templates/dashboard/admin.html -A10

echo -e "\n=== Search for any script or AJAX in admin.html ==="
rg -n '<script|fetch|XMLHttpRequest' src/main/resources/templates/dashboard/admin.html

Repository: ithsjava25/visa-case-management-system

Length of output: 557


Create endpoints for visa action flows and wire dashboard links to real routes.

The Approve, Reject, Review, and Assign actions in the admin dashboard still use href="#" placeholders (lines 167-170 and 210), making these workflows non-functional. While the backend service methods exist (approveVisa(), rejectVisa(), assignHandler()), no controller endpoints expose them. These links must be wired to actual routes once the corresponding POST/PUT endpoints are implemented.

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

In `@src/main/resources/templates/dashboard/admin.html` around lines 167 - 170,
The dashboard action links currently use placeholders; add controller endpoints
that expose the existing service methods (e.g., create POST/PUT routes wired to
approveVisa(), rejectVisa(), assignHandler() in the appropriate controller) and
update the admin.html action anchors to point to those routes (use proper HTTP
verbs via forms or fetch calls for Approve/Reject/Assign and a GET route for
Review to reuse the Review handler). Ensure route names and HTTP methods align
with the service method semantics and CSRF/authorization checks are applied when
wiring the links.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

They will be linked in a future issue

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@Martin-E-Karlsson, understood! Would you like me to open a GitHub issue to track wiring the Approve, Reject, Review, and Assign action links in the admin dashboard to their respective controller endpoints (backed by approveVisa(), rejectVisa(), and assignHandler() in VisaService)? That way it won't get lost.


🧠 Learnings used
Learnt from: eeebbaandersson
Repo: ithsjava25/visa-case-management-system PR: 16
File: src/main/java/org/example/visacasemanagementsystem/comment/service/CommentService.java:42-43
Timestamp: 2026-04-09T11:13:00.080Z
Learning: In the `visa-case-management-system` repository, the `authorId` field in `CreateCommentDTO` is currently trusted directly in `CommentService.createComment()` (file: `src/main/java/org/example/visacasemanagementsystem/comment/service/CommentService.java`, around line 42). This is a known security gap intentionally deferred until Spring Security is integrated. Once Spring Security is added, the author should be resolved from the authenticated principal (e.g., via `SecurityContextHolder`) rather than from `dto.authorId()`, and the `authorId` field should be removed from `CreateCommentDTO` or restricted to admin use only.

<!-- Empty state -->
<div th:if="${visas.isEmpty()}" class="empty-state">
<p>You haven't submitted any visa applications yet.</p>
<a href="#" class="btn-apply">Apply for a Visa</a>

@coderabbitai coderabbitai Bot Apr 13, 2026

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

Wire applicant CTA/actions to actual routes.

Line 139 and Line 170 are placeholder links (href="#"), so users can’t apply for a visa or open application details from this page.

Also applies to: 170-170

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

In `@src/main/resources/templates/dashboard/applicant.html` at line 139, Replace
the placeholder anchors that use href="#" with real application routes: update
the anchor with class "btn-apply" to point to the new-application route (e.g.
the create/apply page) and update the other placeholder anchor(s) on this
template (the application-detail link at the later anchor) to use the
application details route including the application id placeholder (e.g.
/applications/{id} or your framework’s route helper). Ensure you use your
templating/URL helper (not a hardcoded "#") so links render per-user (e.g., use
the framework’s path helper or a variable like application.id) and preserve the
existing classes/attributes such as btn-apply when making the change.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

They will be linked in a future issue

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@Martin-E-Karlsson, understood! Would you like me to open a GitHub issue to track the wiring of the placeholder links (href="#") on the applicant dashboard (the "Apply for a Visa" CTA and the application-detail link) to their real routes? That way it won't be forgotten.

Comment thread src/main/resources/templates/profile/edit.html
- Implemented Code Rabbit suggestions

@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

🧹 Nitpick comments (1)
src/main/java/org/example/visacasemanagementsystem/ApplicationViewController.java (1)

17-20: Extract role check helper to reduce duplication.

The two stream scans are repetitive and harder to maintain as roles grow. A small helper (hasRole(principal, role)) will keep this logic cleaner.

Refactor sketch
-        boolean isSysAdmin = principal.getAuthorities().stream()
-                .anyMatch(a -> Objects.equals(a.getAuthority(), "ROLE_SYSADMIN"));
-        boolean isAdmin = principal.getAuthorities().stream()
-                .anyMatch(a -> Objects.equals(a.getAuthority(), "ROLE_ADMIN"));
+        boolean isSysAdmin = hasRole(principal, "ROLE_SYSADMIN");
+        boolean isAdmin = hasRole(principal, "ROLE_ADMIN");
private boolean hasRole(SecurityUser principal, String role) {
    return principal.getAuthorities().stream()
            .anyMatch(a -> Objects.equals(a.getAuthority(), role));
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/org/example/visacasemanagementsystem/ApplicationViewController.java`
around lines 17 - 20, Duplicate role-check streams in ApplicationViewController
(calculating isSysAdmin and isAdmin) should be replaced by a small helper to
centralize logic: add a private boolean hasRole(SecurityUser principal, String
role) that performs principal.getAuthorities().stream().anyMatch(a ->
Objects.equals(a.getAuthority(), role)), then replace the two duplicated
expressions with hasRole(principal, "ROLE_SYSADMIN") and hasRole(principal,
"ROLE_ADMIN") respectively to improve readability and maintainability.
🤖 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/visacasemanagementsystem/ApplicationViewController.java`:
- Line 24: The controller currently defaults any authenticated user to
"redirect:/dashboard/applicant" without verifying ROLE_APPLICANT; update the
routing logic in ApplicationViewController so you explicitly check the user's
authorities (e.g. via
SecurityContextHolder.getContext().getAuthentication().getAuthorities()) for the
ROLE_APPLICANT authority before returning "redirect:/dashboard/applicant",
mirror the pattern used by validateAdmin() and validateSysAdmin() (or add a new
validateApplicant() method) and if the applicant role is missing route to an
access-denied page or return an appropriate error/redirect instead of silently
sending them to the applicant dashboard.

In
`@src/main/java/org/example/visacasemanagementsystem/user/dto/CreateUserDTO.java`:
- Line 11: The service currently persists plaintext passwords in
UserService.createUser() via user.setPassword(dto.password()); fix this by
declaring a BCryptPasswordEncoder bean (e.g., a `@Bean` public PasswordEncoder
passwordEncoder() returning new BCryptPasswordEncoder()), inject PasswordEncoder
into UserService (constructor injection), and replace the plaintext assignment
with user.setPassword(passwordEncoder.encode(dto.password())); ensure imports
reference org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder and
PasswordEncoder and update any tests to expect encoded passwords.

---

Nitpick comments:
In
`@src/main/java/org/example/visacasemanagementsystem/ApplicationViewController.java`:
- Around line 17-20: Duplicate role-check streams in ApplicationViewController
(calculating isSysAdmin and isAdmin) should be replaced by a small helper to
centralize logic: add a private boolean hasRole(SecurityUser principal, String
role) that performs principal.getAuthorities().stream().anyMatch(a ->
Objects.equals(a.getAuthority(), role)), then replace the two duplicated
expressions with hasRole(principal, "ROLE_SYSADMIN") and hasRole(principal,
"ROLE_ADMIN") respectively to improve readability and maintainability.
🪄 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: c4a81d6e-eae6-42a4-9f15-9cc16afb1a6c

📥 Commits

Reviewing files that changed from the base of the PR and between 1dc6f92 and 40da957.

📒 Files selected for processing (7)
  • src/main/java/org/example/visacasemanagementsystem/ApplicationViewController.java
  • src/main/java/org/example/visacasemanagementsystem/user/controller/UserViewController.java
  • src/main/java/org/example/visacasemanagementsystem/user/dto/CreateUserDTO.java
  • src/main/java/org/example/visacasemanagementsystem/user/service/UserService.java
  • src/main/java/org/example/visacasemanagementsystem/visa/repository/VisaRepository.java
  • src/main/java/org/example/visacasemanagementsystem/visa/service/VisaService.java
  • src/main/resources/templates/profile/edit.html
✅ Files skipped from review due to trivial changes (1)
  • src/main/resources/templates/profile/edit.html
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/main/java/org/example/visacasemanagementsystem/visa/repository/VisaRepository.java
  • src/main/java/org/example/visacasemanagementsystem/user/service/UserService.java
  • src/main/java/org/example/visacasemanagementsystem/visa/service/VisaService.java
  • src/main/java/org/example/visacasemanagementsystem/user/controller/UserViewController.java

@NotBlank(message = "Full name must be specified") String fullName,
@NotBlank(message = "Email must be specified") String email,
@NotBlank String password,
@Size(min = 8, message = "Password must be at least 8 characters") @NotBlank String password,

@coderabbitai coderabbitai Bot Apr 13, 2026

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) Locate user service classes"
fd -i "UserService*.java"

echo
echo "2) Check password handling in service layer"
rg -n --type=java -C3 '\b(setPassword\s*\(|passwordEncoder|BCryptPasswordEncoder|\.encode\s*\()' src/main/java

echo
echo "3) Check create-user flow for direct dto.password usage"
rg -n --type=java -C3 '\bdto\.password\s*\(' src/main/java

Repository: ithsjava25/visa-case-management-system

Length of output: 2259


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Checking for Spring Security integration in this PR ==="
echo

echo "1) Look for Spring Security config files:"
fd -i ".*security.*config.*\.java"

echo
echo "2) Check for Spring Security dependencies in pom.xml:"
if [ -f "pom.xml" ]; then
  rg -A2 "spring-security" pom.xml
else
  echo "No pom.xml found"
fi

echo
echo "3) Look for SecurityContextHolder or Spring Security annotations:"
rg -n --type=java '\b(SecurityContextHolder|@EnableWebSecurity|SecurityConfig|@PreAuthorize|@Secured)' src/main/java | head -20

echo
echo "4) Check if Spring Security was added in this specific service:"
head -40 src/main/java/org/example/visacasemanagementsystem/user/service/UserService.java | grep -E "import|@|class"

Repository: ithsjava25/visa-case-management-system

Length of output: 239


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Checking pom.xml for Spring Security status ==="
if [ -f "pom.xml" ]; then
  echo "pom.xml found. Checking Spring dependencies:"
  cat pom.xml | grep -A5 -B5 "spring-security\|spring-boot" | head -50
else
  echo "pom.xml not found. Listing root files:"
  ls -la | head -20
fi

echo
echo "=== Complete UserService.createUser method ==="
sed -n '51,70p' src/main/java/org/example/visacasemanagementsystem/user/service/UserService.java

Repository: ithsjava25/visa-case-management-system

Length of output: 3259


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Searching for PasswordEncoder configuration ==="
echo

echo "1) Search for PasswordEncoder bean definitions:"
rg -n --type=java '\b(PasswordEncoder|BCryptPasswordEncoder|@Bean.*[Pp]assword)' src/main/java

echo
echo "2) Search for config classes that might define security beans:"
fd -i "config.*\.java" src/main/java

echo
echo "3) Check all imports in UserService to see what's available:"
head -30 src/main/java/org/example/visacasemanagementsystem/user/service/UserService.java | grep "import"

Repository: ithsjava25/visa-case-management-system

Length of output: 182


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Quick check: any password encoding in entire project ==="
rg "encode\|Encoder\|hash" src/main/java --type=java

echo
echo "=== List all config files in project ==="
find src/main/java -name "*Config*.java" -o -name "*Security*.java"

Repository: ithsjava25/visa-case-management-system

Length of output: 142


Passwords are stored in plaintext despite Spring Security integration—implement password encoding immediately.

Spring Security is active in the project, but UserService.createUser() still persists raw passwords without encoding (line 54: user.setPassword(dto.password())). DTO-level validation on line 11 is good, but insufficient. Configure a BCryptPasswordEncoder bean and inject it into UserService, replacing the plaintext assignment with passwordEncoder.encode(dto.password()) before persistence.

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

In
`@src/main/java/org/example/visacasemanagementsystem/user/dto/CreateUserDTO.java`
at line 11, The service currently persists plaintext passwords in
UserService.createUser() via user.setPassword(dto.password()); fix this by
declaring a BCryptPasswordEncoder bean (e.g., a `@Bean` public PasswordEncoder
passwordEncoder() returning new BCryptPasswordEncoder()), inject PasswordEncoder
into UserService (constructor injection), and replace the plaintext assignment
with user.setPassword(passwordEncoder.encode(dto.password())); ensure imports
reference org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder and
PasswordEncoder and update any tests to expect encoded passwords.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Bcrypt will be implemented in a future pull request

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@Martin-E-Karlsson, understood! Since Spring Security is now integrated in this PR, I'll update the note in long-term memory to reflect that BCrypt password encoding is the remaining piece, explicitly deferred to a future PR.

Would you like me to open a GitHub issue to track the BCrypt implementation as a follow-up task?


✏️ Learnings added
Learnt from: Martin-E-Karlsson
Repo: ithsjava25/visa-case-management-system PR: 26
File: src/main/java/org/example/visacasemanagementsystem/user/dto/CreateUserDTO.java:11-11
Timestamp: 2026-04-13T17:12:11.362Z
Learning: In the `visa-case-management-system` repository, Spring Security has been integrated (via `SecurityUser` and `UserDetailsServiceImpl` in PR `#26`), but password hashing in `UserService.createUser()` (file: `src/main/java/org/example/visacasemanagementsystem/user/service/UserService.java`, line ~54) still uses plaintext (`user.setPassword(dto.password())`). BCrypt encoding is intentionally deferred to a future pull request. When implemented, a `BCryptPasswordEncoder` `Bean` should be configured and injected into `UserService` via constructor injection, replacing the plaintext assignment with `passwordEncoder.encode(dto.password())`.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


⛔ Learnings removed
Learnt from: Martin-E-Karlsson
Repo: ithsjava25/visa-case-management-system PR: 14
File: src/main/java/org/example/visacasemanagementsystem/user/service/UserService.java:53-53
Timestamp: 2026-04-08T23:24:51.563Z
Learning: In the `visa-case-management-system` repository, password encryption in `UserService.createUser()` (file: `src/main/java/org/example/visacasemanagementsystem/user/service/UserService.java`, line ~53) is intentionally deferred until Spring Security is integrated. The current `user.setPassword(dto.password())` is a temporary plaintext placeholder. When Spring Security is added, a `BCryptPasswordEncoder` bean should be configured and injected into `UserService`, replacing the direct assignment with `passwordEncoder.encode(dto.password())`.

🧠 Learnings used
Learnt from: Martin-E-Karlsson
Repo: ithsjava25/visa-case-management-system PR: 14
File: src/main/java/org/example/visacasemanagementsystem/user/service/UserService.java:53-53
Timestamp: 2026-04-08T23:24:51.563Z
Learning: In the `visa-case-management-system` repository, password encryption in `UserService.createUser()` (file: `src/main/java/org/example/visacasemanagementsystem/user/service/UserService.java`, line ~53) is intentionally deferred until Spring Security is integrated. The current `user.setPassword(dto.password())` is a temporary plaintext placeholder. When Spring Security is added, a `BCryptPasswordEncoder` bean should be configured and injected into `UserService`, replacing the direct assignment with `passwordEncoder.encode(dto.password())`.

Learnt from: eeebbaandersson
Repo: ithsjava25/visa-case-management-system PR: 16
File: src/main/java/org/example/visacasemanagementsystem/comment/service/CommentService.java:42-43
Timestamp: 2026-04-09T11:13:00.080Z
Learning: In the `visa-case-management-system` repository, the `authorId` field in `CreateCommentDTO` is currently trusted directly in `CommentService.createComment()` (file: `src/main/java/org/example/visacasemanagementsystem/comment/service/CommentService.java`, around line 42). This is a known security gap intentionally deferred until Spring Security is integrated. Once Spring Security is added, the author should be resolved from the authenticated principal (e.g., via `SecurityContextHolder`) rather than from `dto.authorId()`, and the `authorId` field should be removed from `CreateCommentDTO` or restricted to admin use only.

@Martin-E-Karlsson
Martin-E-Karlsson merged commit 41f3231 into main Apr 14, 2026
2 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Apr 27, 2026
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.

Implement start of endpoints for application users

1 participant