feat: update specs and add auth service test

This commit is contained in:
Zzzz
2026-04-27 18:50:24 +08:00
parent 2120774b05
commit a297d7a8cf
6 changed files with 140 additions and 32 deletions
+39 -12
View File
@@ -7,28 +7,34 @@
## Overview
MySQL is the project data layer. DAO classes perform CRUD and query operations
against MySQL. Application source and schema files are not present yet, so table
and class names here are illustrative conventions for future implementation.
against MySQL. The initial scaffold schema exists at
`src/main/resources/db/schema.sql`; future module tables should follow the same
DDL style and DAO boundaries.
---
## Core Tables
Use primary keys for every table and foreign keys for cross-entity integrity.
Illustrative table names:
Implemented scaffold tables:
- `roles`: administrator, librarian, reader, and future role definitions.
- `permissions`: permission definitions for protected actions.
- `role_permissions`: role-to-permission mapping.
- `users`: login accounts for administrator, librarian, and reader roles.
- `system_logs`: key operation logs, backup events, and exception traces.
Planned module tables:
- `books`: book information, inventory count/status, category reference.
- `book_categories`: category names and descriptions.
- `readers`: reader profiles, borrowing eligibility, contact information.
- `borrow_records`: book-reader borrowing, return, renew, and overdue data.
- `administrators`: administrator/librarian login and profile data.
- `roles`: administrator, librarian, reader, and future role definitions.
- `permissions`: permission definitions for protected actions.
- `role_permissions`: role-to-permission mapping.
- `system_logs`: key operation logs, backup events, and exception traces.
When schema files are introduced, record the actual path, DDL style, and exact
table names here.
Record new schema changes in `src/main/resources/db/schema.sql` and update this
spec with exact table names, key columns, and DAO/service contracts.
---
@@ -42,8 +48,9 @@ table names here.
inventory status.
- Return entities or small query result objects to services, not HTML or
servlet response objects.
- Keep MySQL connection details in a shared configuration/helper once one
exists, for example `JdbcUtil` plus `db.properties`.
- Keep MySQL connection details in `src/main/resources/db.properties` loaded by
`JdbcUtil`. The required keys are `db.driver`, `db.url`, `db.username`, and
`db.password`.
---
@@ -66,6 +73,9 @@ table names here.
- `borrow_records.reader_id` should reference `readers`.
- Administrator-role and role-permission mapping tables should use foreign keys
to preserve authorization integrity.
- `users.role_code` must reference `roles.code`.
- `role_permissions.role_code` must reference `roles.code`.
- `role_permissions.permission_code` must reference `permissions.code`.
- Prefer explicit status columns/enums for inventory and borrowing states, then
document the chosen values once code exists.
@@ -83,6 +93,12 @@ table names here.
- DAO signature: `UserDao.findActiveByUsername(String username)`.
- Service signature: `AuthService.authenticate(String username, String password)`.
- Permission signature: `AuthService.hasPermission(AuthenticatedUser user, Permission permission)`.
- Servlet route: `POST /login` with `username`, `password`, and optional
same-application `redirect`.
- Protected routes: `/dashboard`, `/admin/home`, `/librarian/home`, and
`/reader/home`.
- Session keys: `authenticatedUser`, `userRole`, and `userPermissions`.
- DB config keys: `db.driver`, `db.url`, `db.username`, and `db.password`.
- Login tables: `roles`, `permissions`, `role_permissions`, `users`, and
`system_logs`.
@@ -93,9 +109,16 @@ table names here.
`pbkdf2_sha256$iterations$saltBase64$hashBase64` format.
- `users.role_code`: foreign key to `roles.code`; supported scaffold values
are `administrator`, `librarian`, and `reader`.
- `users.active`: only rows with `active = 1` can authenticate.
- `roles.code`, `permissions.code`, and `role_permissions` must match the Java
`Role` and `Permission` enum codes exactly.
- `db.properties` must be local configuration. Commit
`db.properties.example`, but do not commit real credentials.
- Session state stores an `AuthenticatedUser` snapshot, role code, and
permission-code set. It must not store raw passwords or DAO result objects
with password hashes.
- Login redirects must stay inside the current application context. Reject
values that do not start with a single `/` or that contain CR/LF characters.
### 4. Validation & Error Matrix
@@ -103,6 +126,7 @@ table names here.
`Username and password are required.`
- Unknown user, inactive user, or hash mismatch -> request returns to login JSP
with `Invalid username or password.`
- Unsafe or blank redirect -> ignore and route to `/dashboard` after success.
- Missing `db.properties`, JDBC failure, or unsupported role code -> request
returns a generic service-unavailable message and logs server-side details.
- Authenticated user missing a required permission -> HTTP 403 and
@@ -123,6 +147,8 @@ table names here.
unavailable.
- Run `PermissionPolicyCheck` or equivalent assertions for administrator,
librarian, and reader permissions.
- Run `AuthServiceCheck` or equivalent assertions for required-field failures,
invalid credentials, success, permission checks, and DAO failure fallback.
- When Maven/Tomcat dependencies are installed, run `mvn test` or
`mvn clean package` to compile Servlet and JSP integration.
@@ -131,11 +157,12 @@ table names here.
#### Wrong
```java
// JSP or Servlet opens JDBC and checks passwords directly.
// JSP, Servlet, or session code opens JDBC and stores password_hash.
```
#### Correct
```text
login.jsp -> LoginServlet -> AuthService -> UserDao -> users/roles tables
session -> AuthenticatedUser snapshot + role/permission codes only
```
+7 -8
View File
@@ -7,10 +7,9 @@
## Overview
The developer has established the backend architecture as a B/S Java web
application using JSP + Servlet, MySQL, Tomcat, and IDEA. Application source
code does not exist in this workspace yet, so package names, class names, and
table names below are illustrative project conventions for future code rather
than references to existing files.
application using JSP + Servlet, MySQL, Tomcat, and IDEA. The initial scaffold
now exists under `src/main/`; remaining module names below describe conventions
for future feature work.
Use a layered design:
@@ -46,7 +45,7 @@ Before backend implementation, read:
- `.trellis/spec/backend/error-handling.md`
- `.trellis/spec/backend/logging-guidelines.md`
- `.trellis/spec/backend/quality-guidelines.md`
- `.trellis/tasks/00-bootstrap-guidelines/research/project-requirements.md`
- `.trellis/tasks/archive/2026-04/00-bootstrap-guidelines/research/project-requirements.md`
---
@@ -67,9 +66,9 @@ Before backend implementation, read:
## Evidence
- `.trellis/tasks/00-bootstrap-guidelines/research/repo-scan.md` records that
no application source code exists yet.
- `.trellis/tasks/00-bootstrap-guidelines/research/project-requirements.md`
- `.trellis/tasks/archive/2026-04/00-bootstrap-guidelines/research/repo-scan.md`
records the initial pre-scaffold repo scan.
- `.trellis/tasks/archive/2026-04/00-bootstrap-guidelines/research/project-requirements.md`
records the developer-provided stack, architecture, modules, and data model.
---
+4 -4
View File
@@ -40,15 +40,15 @@ Before frontend implementation, read:
- `.trellis/spec/frontend/state-management.md`
- `.trellis/spec/frontend/type-safety.md`
- `.trellis/spec/frontend/quality-guidelines.md`
- `.trellis/tasks/00-bootstrap-guidelines/research/project-requirements.md`
- `.trellis/tasks/archive/2026-04/00-bootstrap-guidelines/research/project-requirements.md`
---
## Evidence
- `.trellis/tasks/00-bootstrap-guidelines/research/repo-scan.md` records that
no application source code exists yet.
- `.trellis/tasks/00-bootstrap-guidelines/research/project-requirements.md`
- `.trellis/tasks/archive/2026-04/00-bootstrap-guidelines/research/repo-scan.md`
records the initial pre-scaffold repo scan.
- `.trellis/tasks/archive/2026-04/00-bootstrap-guidelines/research/project-requirements.md`
records the developer-provided JSP presentation approach and image-to-JSP
workflow.
+75
View File
@@ -23,6 +23,81 @@ rendering.
---
## Scenario: Login JSP And Servlet Contract
### 1. Scope / Trigger
- Trigger: the initial login scaffold introduced a cross-layer JSP/Servlet
contract for authentication screens and protected role pages.
### 2. Signatures
- Login form: `POST /login`.
- Request fields: `username`, `password`, and optional `redirect`.
- Login JSP request attributes: `errorMessage`, `username`, and `redirect`.
- Dashboard/role JSP session attributes: `authenticatedUser`, `userRole`, and
`userPermissions`.
- Role page request attributes: `areaName` and `areaSummary`.
### 3. Contracts
- `username` is trimmed in the Servlet before service authentication and may be
echoed back only through JSP escaping.
- `password` is submitted to the Servlet and must never be written to a request
attribute or session attribute.
- `redirect` must be a same-application path beginning with one `/`; invalid
values are ignored.
- JSPs render data with JSP EL/JSTL, not scriptlet Java.
- JSPs may read safe session snapshots, but they must not call DAOs or inspect
password hashes.
### 4. Validation & Error Matrix
- Missing `username` or `password` -> `errorMessage` displays
`Username and password are required.`
- Invalid credentials -> `errorMessage` displays
`Invalid username or password.`
- Login service unavailable -> `errorMessage` displays a generic unavailable
message; details stay in server logs.
- Missing authenticated session on protected pages -> redirect to `/login` with
a safe `redirect` value.
- Insufficient role permission -> render `/unauthorized`.
### 5. Good/Base/Bad Cases
- Good: failed login keeps the escaped username and never redisplays the
password.
- Base: dashboard reads `sessionScope.authenticatedUser.displayName` and
`sessionScope.userRole` only for display/navigation.
- Bad: JSP uses scriptlets, JDBC, or raw request parameters to decide
authentication.
### 6. Tests Required
- Scan JSPs to confirm there are no scriptlets beyond directives.
- Scan JSP/static assets to confirm no SQL/JDBC access exists in presentation
files.
- Run service-level auth checks for required fields, invalid credentials,
success, DAO fallback, and permission checks.
- When Maven/Tomcat is available, run a Servlet/JSP compile or package check.
### 7. Wrong vs Correct
#### Wrong
```jsp
<%-- JSP checks request.getParameter("password") or runs SQL directly. --%>
```
#### Correct
```text
login.jsp displays attributes set by LoginServlet; AuthService and UserDao own
authentication and database access.
```
---
## Validation
- Parse and validate IDs, dates, enum/status values, and required strings in
+11 -8
View File
@@ -39,14 +39,14 @@ Build the initial Java Web project scaffold and a login/permission skeleton for
## Acceptance Criteria (evolving)
* [x] The selected first module or feature is explicitly confirmed.
* [ ] The implementation follows the documented JSP + Servlet + MySQL layered architecture.
* [ ] A fresh checkout has recognizable Java Web/Tomcat project structure and build configuration.
* [ ] Login page submits credentials to a Servlet controller and displays validation/authentication failures safely.
* [ ] Authentication logic is represented through service and DAO boundaries rather than embedded in JSP.
* [ ] Session state stores the authenticated user and role in a controlled way.
* [ ] Basic role/permission constants or helpers exist for administrator, librarian, and reader.
* [ ] SQL/schema guidance exists for the minimal account/role tables needed by login.
* [ ] Lint, type-check, compile, or equivalent project validation is run where available.
* [x] The implementation follows the documented JSP + Servlet + MySQL layered architecture.
* [x] A fresh checkout has recognizable Java Web/Tomcat project structure and build configuration.
* [x] Login page submits credentials to a Servlet controller and displays validation/authentication failures safely.
* [x] Authentication logic is represented through service and DAO boundaries rather than embedded in JSP.
* [x] Session state stores the authenticated user and role in a controlled way.
* [x] Basic role/permission constants or helpers exist for administrator, librarian, and reader.
* [x] SQL/schema guidance exists for the minimal account/role tables needed by login.
* [x] Lint, type-check, compile, or equivalent project validation is run where available.
## Definition of Done (team quality bar)
@@ -68,3 +68,6 @@ Build the initial Java Web project scaffold and a login/permission skeleton for
* Frontend pre-development checklist includes directory structure, JSP component guidelines, state management, type safety, and quality guidelines.
* Codebase retrieval on 2026-04-27 found no application source code and surfaced the project specs as the main implementation context.
* Spec indexes reference `.trellis/tasks/00-bootstrap-guidelines/research/project-requirements.md`, but that file is absent in the current workspace, so context curation uses only present spec files.
* Implementation completed the scaffold under `src/main/`, added two standalone service checks under `src/test/`, and updated code-specs for the login database/JSP contracts.
* Local verification passed for non-Servlet Java compilation, `PermissionPolicyCheck`, `AuthServiceCheck`, and JSP/static boundary scans.
* Full Maven/Tomcat compile remains blocked in this environment because `mvn` is not installed and no local Servlet API jar is available outside Maven.
@@ -10,6 +10,8 @@ import com.mzh.library.service.impl.AuthServiceImpl;
import com.mzh.library.util.PasswordHasher;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
public final class AuthServiceCheck {
private static final String REQUIRED_MESSAGE = "Username and password are required.";
@@ -21,6 +23,8 @@ public final class AuthServiceCheck {
}
public static void main(String[] args) {
Logger.getLogger(AuthServiceImpl.class.getName()).setLevel(Level.OFF);
User admin = user(1L, "admin", "System Administrator", Role.ADMINISTRATOR, "correct-password");
AuthService authService = new AuthServiceImpl(username -> "admin".equals(username)
? Optional.of(admin)