Skip to content

Feature/login plugin#162

Open
kappsegla wants to merge 23 commits into
mainfrom
feature/loginPlugin
Open

Feature/login plugin#162
kappsegla wants to merge 23 commits into
mainfrom
feature/loginPlugin

Conversation

@kappsegla

@kappsegla kappsegla commented Jul 5, 2026

Copy link
Copy Markdown

Summary by CodeRabbit

  • New Features
    • Added session-based authentication using SID cookies, including background cleanup of idle sessions.
    • Introduced /login, /logout, and /session/status endpoints.
    • Home page “Log out” link now appears only when the session status reports the user as authenticated.
  • Bug Fixes
    • Unauthenticated requests are consistently redirected to /login (GET) or return 401 Unauthorized (non-GET).
    • Logout now reliably invalidates the session and clears the SID cookie.
  • Documentation
    • Updated pipeline usage notes to clarify global BasicAuth behavior.
  • Tests
    • Added coverage for login, logout, session authentication, and session store cleanup behavior.

…ugin and GatewayRouter. Updated Bootstrap to integrate BasicAuth and adjusted RouterConfig for login endpoint.
… handling. Update BasicAuthPlugin to support dynamic realm rotation.
…ionAuthFilter`, `SessionStore`, and related plugins (`LogoutPlugin`, `SessionStatusPlugin`). Update tests for new session handling implementation.
…ionAuthFilter`, `SessionStore`, and related plugins (`LogoutPlugin`, `SessionStatusPlugin`). Update tests for new session handling implementation.
@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

Adds server-side sessions, a global authentication filter, login/logout/status endpoints, route registration, frontend logout visibility, credential handling, and tests.

Changes

Session Authentication Feature

Layer / File(s) Summary
Session model and store
src/main/java/org/juv25d/auth/Session.java, src/main/java/org/juv25d/auth/SessionStore.java
Adds session identity, timestamps, secure IDs, idle expiration, lookup, access tracking, and invalidation.
Cookie utility and global auth filter
src/main/java/org/juv25d/util/CookieUtils.java, src/main/java/org/juv25d/filter/SessionAuthFilter.java, src/test/java/org/juv25d/filter/SessionAuthFilterTest.java
Parses SID cookies and enforces public-path bypass, redirects, 401 responses, and authenticated filter-chain pass-through.
Login and credential handling
src/main/java/org/juv25d/plugin/LoginPlugin.java, config/Users, src/test/java/org/juv25d/plugin/LoginPluginTest.java
Adds CSRF-token rendering, session creation, SID cookies, configurable user loading, plain-text/PBKDF2 verification, and login tests. CSRF mismatch and origin rejection are not enforced.
Logout and session status
src/main/java/org/juv25d/plugin/LogoutPlugin.java, src/main/java/org/juv25d/plugin/SessionStatusPlugin.java, src/test/java/org/juv25d/plugin/LogoutPluginTest.java
Adds GET/POST logout with session invalidation and cookie clearing, plus JSON authentication status reporting.
Routing, bootstrap, and frontend integration
src/main/java/org/juv25d/router/RouterConfig.java, src/main/java/org/juv25d/Bootstrap.java, src/main/resources/static/index.html, docs/notes/pipeline-usage.md
Registers authentication routes, invokes router configuration during bootstrap, adds conditional logout visibility, and documents global BasicAuth handling.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant SessionAuthFilter
  participant LoginPlugin
  participant SessionStore
  participant LogoutPlugin

  Browser->>SessionAuthFilter: request protected path
  SessionAuthFilter->>SessionStore: get(sid)
  SessionStore-->>SessionAuthFilter: no session
  SessionAuthFilter-->>Browser: 302 to /login
  Browser->>LoginPlugin: POST credentials
  LoginPlugin->>SessionStore: create(username)
  SessionStore-->>LoginPlugin: Session
  LoginPlugin-->>Browser: 302 with SID cookie
  Browser->>SessionAuthFilter: request with SID cookie
  SessionAuthFilter->>SessionStore: get(sid)
  SessionStore-->>SessionAuthFilter: Session
  SessionAuthFilter-->>Browser: continue request
  Browser->>LogoutPlugin: GET or POST /logout
  LogoutPlugin->>SessionStore: invalidate(sid)
  LogoutPlugin-->>Browser: 302 with cleared cookie
Loading

Possibly related PRs

Suggested labels: enhancement

Suggested reviewers: bamsemats

Poem

A rabbit guards the login gate,
With SID tucked in, I hop in late.
Sessions bloom and cookies glow,
Status tells which paths to go.
Logout clears the burrow bright! 🐰

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.00% 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 is related to the auth work, but it is too generic and does not clearly describe the broader login/session/logout changes. Rename it to a concise summary of the main change, such as adding login/session authentication plugins and routing.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/loginPlugin

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.

@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)
config/Users (1)

1-2: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Replace the committed default login credentials. LoginPlugin loads config/Users by default, so lunk:lunk becomes a real credential unless every deployment overrides the path. If this is only a sample fixture, keep it out of the runtime fallback or replace it with clearly non-production data.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@config/Users` around lines 1 - 2, Replace the committed default credentials
in config/Users so LoginPlugin does not ship with a real fallback login. Update
the default data used by LoginPlugin’s config loading path to remove lunk:lunk,
and if this file is only a sample fixture, keep it out of the runtime fallback
or replace it with clearly non-production placeholder credentials.
🧹 Nitpick comments (4)
src/main/java/org/juv25d/auth/SessionStore.java (1)

31-38: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Cleanup runs synchronously on every session creation.

cleanupExpired() performs a full scan of sessions on every create() call, so login latency grows with the number of stored sessions. Consider decoupling expiry cleanup from the hot path (e.g., a scheduled background sweep or a counter-triggered periodic sweep) instead of running it on every login.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/org/juv25d/auth/SessionStore.java` around lines 31 - 38,
SessionStore.create currently calls cleanupExpired() on every new session,
putting a full sessions scan on the login hot path. Move the expiry sweep out of
create by making cleanupExpired run on a background schedule or only after a
periodic trigger/counter threshold, and keep create focused on creating and
storing the Session. Use the SessionStore and cleanupExpired symbols to locate
the change.
src/test/java/org/juv25d/filter/SessionAuthFilterTest.java (1)

22-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test leaves session in the shared singleton store.

SessionStore is a process-wide singleton; the session created in authenticatedRequest_passesThrough_andSetsHeaders is never invalidated, so it persists across tests/test classes sharing the JVM. Consider invalidating created sessions in @AfterEach.

♻️ Proposed cleanup
+    `@AfterEach`
+    void tearDown() {
+        // invalidate any sessions created during the test
+    }

Also applies to: 61-79

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/java/org/juv25d/filter/SessionAuthFilterTest.java` around lines 22 -
27, The SessionAuthFilterTest setup leaves state behind in the process-wide
SessionStore singleton, so sessions created by
authenticatedRequest_passesThrough_andSetsHeaders can leak into later tests.
Update SessionAuthFilterTest to track any created session IDs and invalidate
them in an `@AfterEach` cleanup method, alongside any other test-created sessions,
while keeping the existing setup() reset of SessionStore intact.
src/test/java/org/juv25d/plugin/LoginPluginTest.java (1)

43-101: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding edge-case test coverage.

Current tests cover GET render, valid login, and invalid credentials. Missing: empty username/password (line 41-44 branch in LoginPlugin), unsupported HTTP method (405 branch), and missing/incorrect Content-Type on POST.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/java/org/juv25d/plugin/LoginPluginTest.java` around lines 43 - 101,
Add edge-case coverage for LoginPlugin.handle by testing the branches not
currently exercised: empty username/password input, unsupported HTTP methods
that should return 405, and POST requests with missing or incorrect
Content-Type. Extend LoginPluginTest with focused cases that call plugin.handle
with these request variants and assert the expected status/body behavior so the
login form and validation paths are fully covered.
src/test/java/org/juv25d/plugin/LogoutPluginTest.java (1)

24-53: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding a no-cookie/invalid-session logout test.

Only the happy path (valid session) is covered. Add a case where no SID cookie is sent (or it references an unknown session) to confirm handle() still clears the cookie and redirects gracefully.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/java/org/juv25d/plugin/LogoutPluginTest.java` around lines 24 - 53,
Add a logout test that covers the no-cookie or invalid-session path in
LogoutPluginTest, since only the valid-session flow is currently asserted.
Create a request without an SID cookie, or with one that does not match any
SessionStore entry, then call LogoutPlugin.handle and verify it still returns a
302 redirect to "/" and sets a clearing SID cookie with the expected attributes.
Use the existing SessionStore, HttpRequest, HttpResponse, and LogoutPlugin
symbols to keep the test aligned with the current happy-path setup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/org/juv25d/auth/SessionStore.java`:
- Around line 13-22: SessionStore is intended to be a singleton, but it is still
publicly instantiable because no constructor is declared. Add a private
constructor inside SessionStore so only getInstance() can create/access the
single shared instance, and keep the existing INSTANCE field as the sole source
of truth used by SessionAuthFilter, LoginPlugin, and LogoutPlugin.

In `@src/main/java/org/juv25d/Bootstrap.java`:
- Around line 33-39: Bootstrap currently relies on an unused RouterConfig DI
lookup to trigger route registration, which is easy to remove accidentally. In
the bootstrap flow that builds the Pipeline, replace the implicit
side-effect-only container.get(RouterConfig.class) with an explicit
initialization step on RouterConfig so the /login, /logout, /session/status, and
proxy routes are registered intentionally. Keep the base Router retrieval and
Pipeline construction tied to that explicit setup so the behavior remains
obvious and harder to break during refactors.

In `@src/main/java/org/juv25d/filter/SessionAuthFilter.java`:
- Around line 60-64: The authenticated response headers in SessionAuthFilter are
leaking user identity to clients, so remove the X-Authenticated and X-User
response header writes from the authenticated path and keep authentication state
exposed only through the existing /session/status flow. Update the doFilter
logic in SessionAuthFilter so it still allows the request through with
chain.doFilter(req, res) but no longer sets client-visible identity headers on
every authenticated response.

In `@src/main/java/org/juv25d/plugin/LoginPlugin.java`:
- Around line 46-51: The login check in LoginPlugin is comparing cleartext
passwords from loadUsers() directly against the submitted password, so update
the credential flow to store hashed passwords (for example via bcrypt or argon2)
and verify against the hash instead of using Objects.equals() on raw strings.
Keep the fix localized around loadUsers(), resolveUsersFilePath(), and the
username/password validation block so the login path performs a hash
verification with constant-time comparison for the final check.
- Around line 37-63: The LoginPlugin login handler accepts credentials without
any CSRF validation, so add a same-origin safeguard before creating the session.
Update the login flow in LoginPlugin to reject requests unless a valid CSRF
token is present or the request passes an Origin/Referer same-origin check, and
make sure the login form generated by renderLoginForm includes the token when
applicable. Apply the check in the credential-processing path that currently
uses parseForm, loadUsers, and SessionStore.create so the session cookie is only
issued for legitimate same-site submissions.

In `@src/main/java/org/juv25d/plugin/LogoutPlugin.java`:
- Around line 36-60: The readCookie logic in LogoutPlugin is duplicated across
session-related components, so move it into a shared utility such as CookieUtils
and have LogoutPlugin, SessionStatusPlugin, LoginPlugin, and SessionAuthFilter
call that common helper. Keep the same parsing and decoding behavior in the
extracted method, and update the UTF_8 reference to use the imported
StandardCharsets instead of the fully qualified name.
- Around line 18-34: The logout handler in LogoutPlugin.handle() currently
accepts any HTTP method, which allows CSRF via GET. Add an explicit method check
at the start of handle() so only POST is accepted, and reject other methods with
an appropriate non-success response before touching SessionStore or clearing the
SID cookie. Use the existing handle(HttpRequest, HttpResponse) entry point and
its req.method() access to enforce the restriction consistently with
LoginPlugin’s method-specific behavior.

---

Outside diff comments:
In `@config/Users`:
- Around line 1-2: Replace the committed default credentials in config/Users so
LoginPlugin does not ship with a real fallback login. Update the default data
used by LoginPlugin’s config loading path to remove lunk:lunk, and if this file
is only a sample fixture, keep it out of the runtime fallback or replace it with
clearly non-production placeholder credentials.

---

Nitpick comments:
In `@src/main/java/org/juv25d/auth/SessionStore.java`:
- Around line 31-38: SessionStore.create currently calls cleanupExpired() on
every new session, putting a full sessions scan on the login hot path. Move the
expiry sweep out of create by making cleanupExpired run on a background schedule
or only after a periodic trigger/counter threshold, and keep create focused on
creating and storing the Session. Use the SessionStore and cleanupExpired
symbols to locate the change.

In `@src/test/java/org/juv25d/filter/SessionAuthFilterTest.java`:
- Around line 22-27: The SessionAuthFilterTest setup leaves state behind in the
process-wide SessionStore singleton, so sessions created by
authenticatedRequest_passesThrough_andSetsHeaders can leak into later tests.
Update SessionAuthFilterTest to track any created session IDs and invalidate
them in an `@AfterEach` cleanup method, alongside any other test-created sessions,
while keeping the existing setup() reset of SessionStore intact.

In `@src/test/java/org/juv25d/plugin/LoginPluginTest.java`:
- Around line 43-101: Add edge-case coverage for LoginPlugin.handle by testing
the branches not currently exercised: empty username/password input, unsupported
HTTP methods that should return 405, and POST requests with missing or incorrect
Content-Type. Extend LoginPluginTest with focused cases that call plugin.handle
with these request variants and assert the expected status/body behavior so the
login form and validation paths are fully covered.

In `@src/test/java/org/juv25d/plugin/LogoutPluginTest.java`:
- Around line 24-53: Add a logout test that covers the no-cookie or
invalid-session path in LogoutPluginTest, since only the valid-session flow is
currently asserted. Create a request without an SID cookie, or with one that
does not match any SessionStore entry, then call LogoutPlugin.handle and verify
it still returns a 302 redirect to "/" and sets a clearing SID cookie with the
expected attributes. Use the existing SessionStore, HttpRequest, HttpResponse,
and LogoutPlugin symbols to keep the test aligned with the current happy-path
setup.
🪄 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: 38a84309-4ed6-4b87-9d34-9c182997f2ac

📥 Commits

Reviewing files that changed from the base of the PR and between d3b747c and a02963e.

📒 Files selected for processing (14)
  • config/Users
  • docs/notes/pipeline-usage.md
  • src/main/java/org/juv25d/Bootstrap.java
  • src/main/java/org/juv25d/auth/Session.java
  • src/main/java/org/juv25d/auth/SessionStore.java
  • src/main/java/org/juv25d/filter/SessionAuthFilter.java
  • src/main/java/org/juv25d/plugin/LoginPlugin.java
  • src/main/java/org/juv25d/plugin/LogoutPlugin.java
  • src/main/java/org/juv25d/plugin/SessionStatusPlugin.java
  • src/main/java/org/juv25d/router/RouterConfig.java
  • src/main/resources/static/index.html
  • src/test/java/org/juv25d/filter/SessionAuthFilterTest.java
  • src/test/java/org/juv25d/plugin/LoginPluginTest.java
  • src/test/java/org/juv25d/plugin/LogoutPluginTest.java

Comment thread src/main/java/org/juv25d/auth/SessionStore.java
Comment thread src/main/java/org/juv25d/Bootstrap.java Outdated
Comment thread src/main/java/org/juv25d/filter/SessionAuthFilter.java Outdated
Comment thread src/main/java/org/juv25d/plugin/LoginPlugin.java
Comment thread src/main/java/org/juv25d/plugin/LoginPlugin.java
Comment thread src/main/java/org/juv25d/plugin/LogoutPlugin.java
Comment thread src/main/java/org/juv25d/plugin/LogoutPlugin.java Outdated
…iguration. Update `Bootstrap` to initialize routes using `RouterConfig`.
…date form rendering and tests for CSRF token validation.
…invalid methods. Update test to reflect change.
…Password` method for authentication and `hashPassword` utility for administrators. Update tests to reflect hashed password handling.
…. Refactor `SessionAuthFilter`, `LoginPlugin`, `SessionStatusPlugin`, and `LogoutPlugin` to use the new utility.
…. Refactor `SessionAuthFilter`, `LoginPlugin`, `SessionStatusPlugin`, and `LogoutPlugin` to use the new utility.

@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/test/java/org/juv25d/plugin/LoginPluginTest.java (1)

21-45: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Restore the previous system-property values.

setup() clears any existing users.* properties, and cleanup() clears them again, permanently changing JVM-global state for other tests. Save the original values and restore them in @AfterEach to avoid order-dependent failures.

Proposed fix
+    private String previousUsersFile;
+    private String previousUsersDir;
+    private String previousUsersFilename;
+
     `@BeforeEach`
     void setup() throws IOException {
+        previousUsersFile = System.getProperty("users.file");
+        previousUsersDir = System.getProperty("users.dir");
+        previousUsersFilename = System.getProperty("users.filename");
         System.clearProperty("users.file");
         System.clearProperty("users.dir");
         System.clearProperty("users.filename");
         // ...
     }

     `@AfterEach`
     void cleanup() throws IOException {
-        System.clearProperty("users.file");
-        System.clearProperty("users.dir");
-        System.clearProperty("users.filename");
+        restoreProperty("users.file", previousUsersFile);
+        restoreProperty("users.dir", previousUsersDir);
+        restoreProperty("users.filename", previousUsersFilename);
         // ...
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/java/org/juv25d/plugin/LoginPluginTest.java` around lines 21 - 45,
Update LoginPluginTest setup() and cleanup() to capture the original values of
users.file, users.dir, and users.filename before clearing them, then restore
those saved values in cleanup() instead of always clearing the properties.
Preserve null values by removing properties that were originally unset, and
continue deleting tempUsersFile as before.
🧹 Nitpick comments (1)
src/test/java/org/juv25d/plugin/LoginPluginTest.java (1)

68-115: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add negative CSRF and same-origin tests.

These tests only exercise valid CSRF tokens. Add cases for missing/mismatched CSRF tokens and cross-origin requests, asserting rejection and that no SID cookie is issued.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/java/org/juv25d/plugin/LoginPluginTest.java` around lines 68 - 115,
Add negative tests in LoginPluginTest alongside the existing postLogin tests for
a missing CSRF token, a mismatched CSRF token, and a cross-origin request.
Invoke LoginPlugin.handle for each case and assert the request is rejected, the
response is not a successful login redirect, and the Set-Cookie header does not
contain a SID cookie.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/org/juv25d/plugin/LoginPlugin.java`:
- Around line 76-77: Update the password assignment in the login flow to
preserve the exact form value, removing the trim operation while retaining the
existing empty default. Keep username normalization unchanged and ensure the
unmodified password is passed to verification and hashPassword consistently.
- Around line 278-286: Update LoginPlugin.hashPassword to use a substantially
higher PBKDF2-HMAC-SHA256 iteration count and add a version identifier to the
returned hash format. Preserve the salt and derived-hash encoding, and ensure
the format clearly distinguishes new hashes so existing Users entries can be
migrated.
- Around line 218-229: Update isSameOrigin to parse the Origin and Referer
values as URIs, then compare their normalized host and port exactly against the
request Host authority. Remove the contains-based matching and ensure a missing
Host or invalid URI fails closed rather than matching every value.

---

Outside diff comments:
In `@src/test/java/org/juv25d/plugin/LoginPluginTest.java`:
- Around line 21-45: Update LoginPluginTest setup() and cleanup() to capture the
original values of users.file, users.dir, and users.filename before clearing
them, then restore those saved values in cleanup() instead of always clearing
the properties. Preserve null values by removing properties that were originally
unset, and continue deleting tempUsersFile as before.

---

Nitpick comments:
In `@src/test/java/org/juv25d/plugin/LoginPluginTest.java`:
- Around line 68-115: Add negative tests in LoginPluginTest alongside the
existing postLogin tests for a missing CSRF token, a mismatched CSRF token, and
a cross-origin request. Invoke LoginPlugin.handle for each case and assert the
request is rejected, the response is not a successful login redirect, and the
Set-Cookie header does not contain a SID cookie.
🪄 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: 5be5072d-e362-45ad-8c47-029554c04f1a

📥 Commits

Reviewing files that changed from the base of the PR and between a02963e and df44deb.

📒 Files selected for processing (11)
  • src/main/java/org/juv25d/Bootstrap.java
  • src/main/java/org/juv25d/auth/SessionStore.java
  • src/main/java/org/juv25d/filter/SessionAuthFilter.java
  • src/main/java/org/juv25d/plugin/LoginPlugin.java
  • src/main/java/org/juv25d/plugin/LogoutPlugin.java
  • src/main/java/org/juv25d/plugin/SessionStatusPlugin.java
  • src/main/java/org/juv25d/router/RouterConfig.java
  • src/main/java/org/juv25d/util/CookieUtils.java
  • src/test/java/org/juv25d/filter/SessionAuthFilterTest.java
  • src/test/java/org/juv25d/plugin/LoginPluginTest.java
  • src/test/java/org/juv25d/plugin/LogoutPluginTest.java
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main/java/org/juv25d/auth/SessionStore.java
  • src/main/java/org/juv25d/router/RouterConfig.java
  • src/test/java/org/juv25d/plugin/LogoutPluginTest.java

Comment thread src/main/java/org/juv25d/plugin/LoginPlugin.java Outdated
Comment thread src/main/java/org/juv25d/plugin/LoginPlugin.java Outdated
Comment thread src/main/java/org/juv25d/plugin/LoginPlugin.java Outdated
…t`, `Origin`, and `Referer` headers. Update tests to include `Host` and `Origin`.
…ks in `LoginPlugin`. Support case-insensitive username matching and add backwards compatibility to password verification. Upgrade PBKDF2 iteration count and introduce versioned password hashing format.
…LoginPlugin`, `LogoutPlugin`, and `SessionStatusPlugin`.
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@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

♻️ Duplicate comments (2)
src/main/java/org/juv25d/plugin/LogoutPlugin.java (1)

19-33: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Logout accepts GET → CSRF-via-GET.

Allowing GET lets a malicious page force-invalidate a victim's session with a simple <img src="/logout">. Restrict logout to POST (or require the CSRF token) instead of accepting GET for convenience.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/org/juv25d/plugin/LogoutPlugin.java` around lines 19 - 33,
Update the method validation in LogoutPlugin to accept only POST requests,
removing the GET allowance while preserving the existing 405 response for other
methods. Keep the SID cookie lookup and SessionStore invalidation behavior
unchanged for valid POST requests.
src/main/java/org/juv25d/plugin/LoginPlugin.java (1)

50-58: 🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy lift

CSRF protection is generated but never enforced.

A CSRF token is issued as a cookie and embedded as _csrf, but the POST path never compares them, and the same-origin check was removed (Line 50). A cross-site page can auto-submit credentials and the 302 will set the victim's SID, silently logging them into the attacker's account. Validate the submitted _csrf against the CSRF-TOKEN cookie (constant-time) before creating the session, or reinstate an Origin/Referer same-origin check that fails closed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/org/juv25d/plugin/LoginPlugin.java` around lines 50 - 58,
Update the POST handling around parseForm in LoginPlugin to enforce CSRF
protection before creating a session: retrieve the submitted _csrf value and
CSRF-TOKEN cookie, compare them using a constant-time method, and reject the
request when either is missing or mismatched. Alternatively, reinstate a
same-origin Origin/Referer check that fails closed; ensure session creation and
the login redirect occur only after validation succeeds.
🧹 Nitpick comments (1)
src/main/resources/static/index.html (1)

28-30: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Prefer a POST form for logout.

This link invokes the state-changing logout handler through GET, allowing cross-site navigations or prefetches to invalidate a user’s session. Make logout POST-only with CSRF protection, or explicitly document this trade-off.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/static/index.html` around lines 28 - 30, Update the
nav-logout markup to submit logout through a POST form instead of the current
GET link, and include the application’s established CSRF protection mechanism.
Preserve the existing hidden styling and logout presentation while targeting the
existing /logout handler.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@config/Users`:
- Line 1: Replace the cleartext sample credential in config/Users at lines 1-1
with a PBKDF2 hash generated by LoginPlugin.hashPassword(...). In
src/main/java/org/juv25d/plugin/LoginPlugin.java at lines 229-236, remove the
plaintext fallback so verification accepts only pbkdf2:-formatted entries and
rejects cleartext passwords.

In `@src/main/java/org/juv25d/filter/SessionAuthFilter.java`:
- Around line 10-14: Clean up imports in SessionAuthFilter by removing the
unused URLDecoder, HashMap, and Map imports, retain StandardCharsets, and apply
the project’s Spotless formatting so import ordering and formatting pass CI.

---

Duplicate comments:
In `@src/main/java/org/juv25d/plugin/LoginPlugin.java`:
- Around line 50-58: Update the POST handling around parseForm in LoginPlugin to
enforce CSRF protection before creating a session: retrieve the submitted _csrf
value and CSRF-TOKEN cookie, compare them using a constant-time method, and
reject the request when either is missing or mismatched. Alternatively,
reinstate a same-origin Origin/Referer check that fails closed; ensure session
creation and the login redirect occur only after validation succeeds.

In `@src/main/java/org/juv25d/plugin/LogoutPlugin.java`:
- Around line 19-33: Update the method validation in LogoutPlugin to accept only
POST requests, removing the GET allowance while preserving the existing 405
response for other methods. Keep the SID cookie lookup and SessionStore
invalidation behavior unchanged for valid POST requests.

---

Nitpick comments:
In `@src/main/resources/static/index.html`:
- Around line 28-30: Update the nav-logout markup to submit logout through a
POST form instead of the current GET link, and include the application’s
established CSRF protection mechanism. Preserve the existing hidden styling and
logout presentation while targeting the existing /logout handler.
🪄 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 Plus

Run ID: aec6d8cc-b187-400e-b045-3bd5a25e80b5

📥 Commits

Reviewing files that changed from the base of the PR and between d3b747c and e8da11a.

📒 Files selected for processing (15)
  • config/Users
  • docs/notes/pipeline-usage.md
  • src/main/java/org/juv25d/Bootstrap.java
  • src/main/java/org/juv25d/auth/Session.java
  • src/main/java/org/juv25d/auth/SessionStore.java
  • src/main/java/org/juv25d/filter/SessionAuthFilter.java
  • src/main/java/org/juv25d/plugin/LoginPlugin.java
  • src/main/java/org/juv25d/plugin/LogoutPlugin.java
  • src/main/java/org/juv25d/plugin/SessionStatusPlugin.java
  • src/main/java/org/juv25d/router/RouterConfig.java
  • src/main/java/org/juv25d/util/CookieUtils.java
  • src/main/resources/static/index.html
  • src/test/java/org/juv25d/filter/SessionAuthFilterTest.java
  • src/test/java/org/juv25d/plugin/LoginPluginTest.java
  • src/test/java/org/juv25d/plugin/LogoutPluginTest.java

Comment thread config/Users
Comment thread src/main/java/org/juv25d/filter/SessionAuthFilter.java Outdated
@Linsss123

Copy link
Copy Markdown

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@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

♻️ Duplicate comments (3)
src/main/java/org/juv25d/plugin/LogoutPlugin.java (1)

18-28: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Logout still reachable via GET (CSRF-via-GET).

This intentionally keeps GET enabled for logout, but that still means any cross-site page (<img src="/logout">) can force-invalidate a victim's session without consent. This was previously flagged and remains open. If “logout via link” convenience is required, consider requiring POST only and using a JS-driven fetch(..., {method:'POST'}) from the nav link (as done for other authenticated actions) instead of a plain anchor tag.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/org/juv25d/plugin/LogoutPlugin.java` around lines 18 - 28,
Update LogoutPlugin.handle to accept POST only and reject GET requests with the
existing 405 response path. Preserve the current response headers and body for
unsupported methods, and move logout-link convenience to a client-side POST
request rather than retaining GET access.
src/main/java/org/juv25d/plugin/LoginPlugin.java (1)

33-58: 🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

CSRF protection is fully disabled despite the token scaffolding.

The CSRF token is generated, set as a cookie (line 36), and rendered into a hidden form field (line 129), but on POST the _csrf value from parseForm is never read or compared — the comments at lines 50 and 54 explicitly say the same-origin check and CSRF validation are disabled/removed. The previously-flagged isSameOrigin helper no longer exists at all. This is a regression from an already-flagged critical issue: previously there was an imperfect same-origin check, now there is none, so any cross-site page can submit credentials to /login and the resulting 302 will set the victim's session cookie (login CSRF / session fixation via forged submission).

🔒 Suggested fix
         Map<String, String> form = parseForm(req);
-
-        // CSRF-validering är inaktiverad – värden används inte här.
-
+        String csrfCookie = CookieUtils.readCookie(req, "CSRF-TOKEN");
+        String csrfForm = form.getOrDefault("_csrf", "");
+        if (csrfCookie == null || csrfForm.isEmpty() || !MessageDigest.isEqual(
+                csrfCookie.getBytes(StandardCharsets.UTF_8), csrfForm.getBytes(StandardCharsets.UTF_8))) {
+            res.setStatusCode(403);
+            res.setStatusText("Forbidden");
+            renderLoginForm(res, "Invalid or missing CSRF token", generateCsrfToken());
+            return;
+        }
         String username = form.getOrDefault("username", "").trim();

Also applies to: 106-150, 204-208

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/org/juv25d/plugin/LoginPlugin.java` around lines 33 - 58,
Restore CSRF validation in LoginPlugin.handle: on POST, read the submitted _csrf
value from parseForm and compare it with the CSRF token from the request cookie
before processing credentials, rejecting missing or mismatched tokens with the
existing bad-request/error response path. Ensure the GET-generated token remains
available in the hidden form field and cookie, and remove the comments or flow
that explicitly disable validation.
config/Users (1)

1-1: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Plaintext credential handling remains unresolved across config/Users and LoginPlugin.java. The sample file still stores a cleartext password, and the verifier still accepts non-hashed entries; together they let plaintext credentials authenticate, bypassing the PBKDF2 hardening added elsewhere in this file.

  • config/Users#L1-L1: replace lunk:lunk with a PBKDF2 hash produced by LoginPlugin.hashPassword(...).
  • src/main/java/org/juv25d/plugin/LoginPlugin.java#L229-L236: remove the plaintext fallback branch so only pbkdf2:-formatted entries verify, and reject cleartext.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@config/Users` at line 1, Replace the cleartext credential in config/Users
line 1 with a PBKDF2 hash generated by LoginPlugin.hashPassword(...). In
src/main/java/org/juv25d/plugin/LoginPlugin.java lines 229-236, remove the
plaintext verification fallback so only pbkdf2:-formatted entries are accepted
and cleartext credentials are rejected.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/org/juv25d/auth/SessionStore.java`:
- Around line 33-38: Move expired-session cleanup out of the create method’s
only trigger by scheduling cleanupExpired() through the application lifecycle,
using the existing SessionStore ownership boundaries. Ensure the scheduler is
started with the store/application and shut down cleanly, while preserving
immediate cleanup during create if desired. Add coverage proving sessions expire
and are removed without a subsequent login.

In `@src/main/java/org/juv25d/filter/SessionAuthFilter.java`:
- Around line 13-20: Correct the class Javadoc for the authentication filter:
remove the claim that valid sessions expose X-Authenticated and X-User headers,
and add /session/status to the documented public unauthenticated paths. Update
only the documentation near the filter declaration, preserving the
implementation behavior.

---

Duplicate comments:
In `@config/Users`:
- Line 1: Replace the cleartext credential in config/Users line 1 with a PBKDF2
hash generated by LoginPlugin.hashPassword(...). In
src/main/java/org/juv25d/plugin/LoginPlugin.java lines 229-236, remove the
plaintext verification fallback so only pbkdf2:-formatted entries are accepted
and cleartext credentials are rejected.

In `@src/main/java/org/juv25d/plugin/LoginPlugin.java`:
- Around line 33-58: Restore CSRF validation in LoginPlugin.handle: on POST,
read the submitted _csrf value from parseForm and compare it with the CSRF token
from the request cookie before processing credentials, rejecting missing or
mismatched tokens with the existing bad-request/error response path. Ensure the
GET-generated token remains available in the hidden form field and cookie, and
remove the comments or flow that explicitly disable validation.

In `@src/main/java/org/juv25d/plugin/LogoutPlugin.java`:
- Around line 18-28: Update LogoutPlugin.handle to accept POST only and reject
GET requests with the existing 405 response path. Preserve the current response
headers and body for unsupported methods, and move logout-link convenience to a
client-side POST request rather than retaining GET access.
🪄 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 Plus

Run ID: c448ecf3-3b79-469e-ad98-067faa4be818

📥 Commits

Reviewing files that changed from the base of the PR and between d3b747c and aa1cf6f.

📒 Files selected for processing (15)
  • config/Users
  • docs/notes/pipeline-usage.md
  • src/main/java/org/juv25d/Bootstrap.java
  • src/main/java/org/juv25d/auth/Session.java
  • src/main/java/org/juv25d/auth/SessionStore.java
  • src/main/java/org/juv25d/filter/SessionAuthFilter.java
  • src/main/java/org/juv25d/plugin/LoginPlugin.java
  • src/main/java/org/juv25d/plugin/LogoutPlugin.java
  • src/main/java/org/juv25d/plugin/SessionStatusPlugin.java
  • src/main/java/org/juv25d/router/RouterConfig.java
  • src/main/java/org/juv25d/util/CookieUtils.java
  • src/main/resources/static/index.html
  • src/test/java/org/juv25d/filter/SessionAuthFilterTest.java
  • src/test/java/org/juv25d/plugin/LoginPluginTest.java
  • src/test/java/org/juv25d/plugin/LogoutPluginTest.java

Comment thread src/main/java/org/juv25d/auth/SessionStore.java
Comment thread src/main/java/org/juv25d/filter/SessionAuthFilter.java
… tests. Update application lifecycle to manage scheduler startup/shutdown.

@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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/org/juv25d/auth/SessionStore.java`:
- Line 99: Update the session access and cleanup paths to serialize
expiry-check-plus-touch with expiry removal per session ID. In the method
handling session refresh (the get/touch flow), use the session map’s per-key
atomic operation so expiration validation and touch occur together; update
safeCleanup to use the same atomic per-key operation when removing expired
sessions, preserving existing behavior for missing or non-expired sessions.

In `@src/test/java/org/juv25d/auth/SessionStoreTest.java`:
- Around line 33-37: Update the cleanup test around store.get(id) to verify
scheduled eviction without triggering lazy eviction: poll the package-private
sessions membership directly until a deadline, asserting the entry is removed,
then optionally retain the get(id) null check. Use short polling intervals and a
bounded timeout to avoid a fixed sleep masking scheduler failures.
🪄 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 Plus

Run ID: bb411582-7023-436e-ade6-b417c8f33217

📥 Commits

Reviewing files that changed from the base of the PR and between e8da11a and 8054e11.

📒 Files selected for processing (5)
  • src/main/java/org/juv25d/App.java
  • src/main/java/org/juv25d/auth/SessionStore.java
  • src/main/java/org/juv25d/filter/SessionAuthFilter.java
  • src/main/java/org/juv25d/plugin/LoginPlugin.java
  • src/test/java/org/juv25d/auth/SessionStoreTest.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/org/juv25d/filter/SessionAuthFilter.java
  • src/main/java/org/juv25d/plugin/LoginPlugin.java

Comment thread src/main/java/org/juv25d/auth/SessionStore.java
Comment thread src/test/java/org/juv25d/auth/SessionStoreTest.java Outdated
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.

2 participants