Task: Implement the active sessions read
Table of Contents
This page documents a task in the Implement the credentials journeys in ores.web story. It captures the goal, current status, acceptance, and any notes or results.
1. Goal
Make iam.v1.sessions.active answer the sessions that have not ended.
session_operations_handler::active replies {.success = true} with no rows,
so the audit screen's active list is empty by construction and the browser route
in front of it can only report "not available".
2. Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Implement the credentials journeys in ores.web |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-10-01 |
3. Acceptance
activefills the response with the tenant's open sessions and reports failure when the store fails, rather than answering success with nothing.- The operation checks
iam::sessions:read, as the derived CRUD reads of the same resource do. - A repository case states what the read includes and leaves out.
- The component builds and
ores.iam.core.testspasses. - The live subject answers rows, checked through the browser's own route.
4. Plan
- Fill the handler's response and guard the operation, verifying the request context first, which the two operations in that handler did not do before.
- Read the sessions through the generated repository and keep the open ones. The repository and the service are generated, so a read added to either is erased by the next regeneration; the drift gate says so in as many words.
- Build, run the suite, restart the fleet, and read the subject through
GET /api/sessions/active.
5. Measurements
| What | Value | Command | Commit |
|---|---|---|---|
| Rows the stub answered | 0, always | the handler before this change | 1b473e560a |
Assertions in ores.iam.core.tests |
448 in 169 cases, all passing | cmake --build ... --target ores.iam.core.tests |
working tree |
| Sessions in the store, live | 17 | curl -b jar '.../api/sessions?limit=100' |
bff from source, 21803 |
| Rows the active read answers, live | 17, every one with an empty endTime |
curl -b jar .../api/sessions/active |
bff from source, 21803 |
| Rows with an end time after a sign-out, live | 0 | curl -X DELETE .../api/session then the list again |
bff from source, 21803 |
| Hand-written files changed | 1, the operations handler | git diff --stat |
working tree |
6. Notes
- A session is open while its
end_timeis empty. The store states that as an empty string rather than a null, because the column istext not null default '', so the read filters on the empty string and not onis null. - The generated layers cannot be extended by hand. The first attempt added a
read to
session_repositoryand a method tosession_service; both files carryAUTO-GENERATED FILE - DO NOT EDIT MANUALLY, and the drift gate reported that regeneration would delete them, which is the same lesson the review taught one layer up. The filter therefore lives in the hand-written operations handler, over the generated listing. It costs a scan of the tenant's sessions, and the proper fix is a read declared in the entity model, which is its own task. - The operations in that handler verified nothing before.
activeandsamplesreplied with success without proving the caller or checking a permission.activenow proves its request context and checksiam::sessions:read;samplesis left as it was and is not part of this task. - Nothing writes
end_time, so every session is open. Signing out answers 200 and the session list still shows no row with an end time: the count went from 16 to 17 and the ended rows stayed at zero. The read is right, but the active list is every session the deployment has ever created, and the audit screen's "ended" state does not exist yet. Filed as Nothing ends a session, so every session is an open one. - No unit test covers the filter. The repository case that proved the exclusion was reverted with the read it tested, and the operations handler has no harness: the component has no case that drives a handler's reply. The live read is the evidence, and it proves the read answers open rows, not that it leaves ended ones out, because the store holds none.
- The first administrator had to be created to read the subject live. The
database was recreated earlier in this story, so the deployment was in
bootstrap mode;
POST /api/bootstrap/administratorcreated it and the session cookie followed.
7. Test Scenarios
| Scenario | State | Notes |
|---|---|---|
8. PRs
| PR | Title |
|---|---|
| #2468 | [iam] Implement the active sessions read |
9. Review
| Comment summary | File | Decision | Notes |
|---|---|---|---|
10. Result
Done 2026-10-01, with the store's own limit recorded below.
iam.v1.sessions.active answers the open sessions. The filter lives in the
hand-written operations handler, over the generated repository's listing,
because the repository and the service are generated and a read added to either
is erased by the next regeneration. The operation also proves its request
context and checks iam::sessions:read, which neither operation in that handler
did before.
Verification: ores.iam.core.tests passes whole, 448 assertions in 169 test
cases; the drift sweep reports that no file this branch touched would change.
Live: the fleet was restarted onto the new library, and GET
/api/sessions/active answered 17 rows, every one with an empty endTime.
What the live read does not prove. The store holds no ended session: signing out answers 200 and still leaves no row with an end time, so the active list is every session the deployment has created. The exclusion is therefore asserted by inspection and not by a case, and the audit screen's "ended" state has no producer yet.