Skip to content

feat(api-token): scope-annotate read endpoints + OpenAPI security (ADR-021 Phase 4)#559

Merged
CybotTM merged 2 commits into
mainfrom
feat/api-token-auth-phase4
Jul 5, 2026
Merged

feat(api-token): scope-annotate read endpoints + OpenAPI security (ADR-021 Phase 4)#559
CybotTM merged 2 commits into
mainfrom
feat/api-token-auth-phase4

Conversation

@CybotTM

@CybotTM CybotTM commented Jul 5, 2026

Copy link
Copy Markdown
Member

What

Opens the read data API to personal access tokens, fail-closed by default.

21 read endpoints now declare #[RequireScope('resource:read')]:

Scope Endpoints
customers:read /getAllCustomers
projects:read /getProjects
users:read /getUsers, /getAllUsers
teams:read /getAllTeams
presets:read /getAllPresets
contracts:read /getContracts, /getContractHours
ticketsystems:read /getTicketSystems
entries:read /tracking/entry/{id}
reporting:read /getTimeSummary, /getTicketTimeSummary/{ticket}, the /interpretation/* group, /controlling/export, /export/{days}

Admin (ROLE_ADMIN) endpoints keep their role gate — #[RequireScope] composes with it (a token needs both an admin user and the scope). Holidays, /admin/status, and the jira-sync trigger are left unannotated on purpose (not token-facing → unreachable by tokens).

OpenAPI (public/api.yml)

Added the bearerAuth (+ cookieAuth) securityScheme, a top-level security, and the token/scope model in the description. Per-operation scope tags are intentionally not duplicated into the static YAML — the code's #[RequireScope] is the single source of truth, so spec and enforcement can't drift.

Coverage

  • RequireScopeCoverageTest — every declared #[RequireScope] uses a valid ApiScope (catches typos that would silently break an endpoint) + a floor count so annotations can't vanish.
  • ApiTokenAuthTest — a Phase-4 endpoint is reachable with its scope and denied with a wrong one; the fail-closed example moved to a still-unannotated endpoint.

Gates: PHPStan L10, php-cs-fixer, Rector, phpat clean; full suite 2185 tests green (session access unaffected — RequireScopeSubscriber only gates token requests).

…R-021 Phase 4)

Open the read data API to tokens, fail-closed by default. 21 read endpoints now
declare #[RequireScope('resource:read')] — customers/projects/users/teams/presets/
contracts/ticketsystems, the interpretation/reporting group, entry-by-id, and the
exports — each at its matching resource scope. Admin (ROLE_ADMIN) endpoints keep
their role gate; the scope composes with it (both must pass). Holidays, admin
status and the jira-sync trigger are left unannotated (not token-facing).

OpenAPI (public/api.yml): add the `bearerAuth` (+ `cookieAuth`) securityScheme,
a top-level `security`, and the token/scope model in the description. Per-operation
scope tags are deliberately not duplicated — the code's #[RequireScope] is the
single source of truth, so the two can't drift.

Coverage: RequireScopeCoverageTest asserts every declared scope is a valid
ApiScope and guards the count; ApiTokenAuthTest gains end-to-end checks that a
Phase-4 endpoint is reachable with its scope and denied with a wrong one, and the
fail-closed example moves to a still-unannotated endpoint.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
Copilot AI review requested due to automatic review settings July 5, 2026 13:10

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request implements Phase 4 of ADR-021 (API Token Authentication) by annotating read endpoints with the #[RequireScope] attribute, updating the OpenAPI specification with bearer security schemes and scope documentation, and adding integration and coverage tests. Feedback on the changes suggests making the regular expression in the new RequireScopeCoverageTest more robust to handle different formatting styles of PHP attributes.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread tests/Security/RequireScopeCoverageTest.php Outdated
@codecov

codecov Bot commented Jul 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.75%. Comparing base (c77c6bf) to head (3560866).

Additional details and impacted files
@@            Coverage Diff            @@
##               main     #559   +/-   ##
=========================================
  Coverage     84.75%   84.75%           
  Complexity     3286     3286           
=========================================
  Files           229      229           
  Lines          8934     8934           
=========================================
  Hits           7572     7572           
  Misses         1362     1362           
Flag Coverage Δ
integration 55.84% <ø> (ø)
unit 48.04% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI 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.

Pull request overview

This PR continues ADR-021 Phase 4 by formally opening selected read-only API endpoints to personal access tokens via server-side #[RequireScope('…:read')] annotations, while keeping the token firewall fail-closed by default and documenting the auth model in the OpenAPI spec.

Changes:

  • Annotates additional read endpoints with #[RequireScope('resource:read')] (including reporting + interpretation endpoints, and /tracking/entry/{id}).
  • Extends test coverage with a controller-scan guard (RequireScopeCoverageTest) and Phase-4 token/scope authorization checks (ApiTokenAuthTest).
  • Updates public/api.yml and ADR-021 documentation to describe Bearer tokens + scope model.

Reviewed changes

Copilot reviewed 25 out of 25 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/Security/RequireScopeCoverageTest.php Adds a scan-based guard to validate #[RequireScope] strings against ApiScope and enforce a minimum annotation count.
tests/Controller/ApiTokenAuthTest.php Adds Phase-4 e2e checks for scope allow/deny behavior and updates the fail-closed example endpoint.
src/Controller/Tracking/GetEntryAction.php Requires entries:read scope for token access to single-entry reads.
src/Controller/Interpretation/GroupByWorktimeAction.php Requires reporting:read for interpretation aggregation endpoint (token-gated).
src/Controller/Interpretation/GroupByUserAction.php Requires reporting:read for interpretation aggregation endpoint (token-gated).
src/Controller/Interpretation/GroupByTicketAction.php Requires reporting:read for interpretation aggregation endpoint (token-gated).
src/Controller/Interpretation/GroupByProjectAction.php Requires reporting:read for interpretation aggregation endpoint (token-gated).
src/Controller/Interpretation/GroupByCustomerAction.php Requires reporting:read for interpretation aggregation endpoint (token-gated).
src/Controller/Interpretation/GroupByActivityAction.php Requires reporting:read for interpretation aggregation endpoint (token-gated).
src/Controller/Interpretation/GetLastEntriesAction.php Requires reporting:read for interpretation last-entries endpoint (token-gated).
src/Controller/Default/GetUsersAction.php Requires users:read for token access to users listing.
src/Controller/Default/GetTimeSummaryAction.php Requires reporting:read for time summary reads via tokens.
src/Controller/Default/GetTicketTimeSummaryAction.php Requires reporting:read for ticket time summary reads via tokens.
src/Controller/Default/GetProjectsAction.php Requires projects:read for token access to project reads.
src/Controller/Default/GetContractHoursAction.php Requires contracts:read for token access to contract-hours reads.
src/Controller/Default/ExportCsvAction.php Requires reporting:read for CSV export reads via tokens.
src/Controller/Controlling/ExportAction.php Requires reporting:read in addition to admin role for controlling export reads via tokens.
src/Controller/Admin/GetUsersAction.php Adds users:read scope requirement atop admin role for admin users endpoint.
src/Controller/Admin/GetTicketSystemsAction.php Adds ticketsystems:read scope requirement atop admin role for ticket systems endpoint.
src/Controller/Admin/GetTeamsAction.php Adds teams:read scope requirement atop admin role for teams endpoint.
src/Controller/Admin/GetPresetsAction.php Adds presets:read scope requirement to presets list endpoint.
src/Controller/Admin/GetCustomersAction.php Adds customers:read scope requirement atop admin role for customers endpoint.
src/Controller/Admin/GetContractsAction.php Adds contracts:read scope requirement atop admin role for contracts endpoint.
public/api.yml Documents Bearer/cookie schemes and adds global security requirements for auth.
docs/adr/ADR-021-api-token-authentication.md Marks Phase 4 as done and documents scope annotation + OpenAPI changes.

Comment thread public/api.yml
Comment thread tests/Security/RequireScopeCoverageTest.php Outdated
Comment thread tests/Security/RequireScopeCoverageTest.php Outdated
- RequireScopeCoverageTest reads #[RequireScope] via reflection instead of a
  regex (robust to quoting/grouping/formatting), reports the FQCN on failure,
  and raises the floor to 27 (the actual count).
- OpenAPI: the public operations (/login, /status/check, /status/page) override
  the global security with `security: []`, so the spec no longer marks them as
  requiring authentication.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
@sonarqubecloud

sonarqubecloud Bot commented Jul 5, 2026

Copy link
Copy Markdown

@CybotTM CybotTM merged commit b35bf47 into main Jul 5, 2026
26 checks passed
@CybotTM CybotTM deleted the feat/api-token-auth-phase4 branch July 5, 2026 13:22
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