ARCH-02 — Project-scoped repository associations and authorized resource lookup #831

Open
opened 2026-07-22 19:47:26 -05:00 by jcwalker3 · 0 comments
Owner

Candidate architecture requiring executable validation; not yet approved, production-ready, or certified.

Parents: #820, #828. Depends on: #829 (bindings), #830 (resources).

1. Summary / objective

Implement project-scoped repository associations and authorized resource lookup: authenticate the caller, check the current project→repository grant, check the current association, and return data only through the sanctioned interface.

2. Security/correctness problem

Associations must never become independent authorization: a revoked grant must immediately make an old association unreadable. On SQLite there are no table-level read privileges or SELECT triggers, so read authorization is [TRUSTED-SERVICE] and must be stated as such, not overclaimed as [SCHEMA].

3. Threat model / failure modes

Direct SELECT bypassing the service; cross-project disclosure; reading via a stale/revoked grant; reading an archived resource; spoofed actor context.

4. In-scope behavior

  • lookup_resource_for_project [TRUSTED-SERVICE]: authenticate caller; require an active project_repository_grants row for the resource's binding and an existing association; return only through the sanctioned interface; prohibit direct DB access by the application role.
  • ensure/associate authorization derives from binding-level grants (ensure = internal_service; associate_project = project.admin), never from a pre-existing association. [SCHEMA] on the write triggers.
  • PostgreSQL may use a properly-privileged SECURITY DEFINER function or security-barrier view; SQLite uses a service routine + [PERM] (application role has no direct DB read of these tables).
  • Association creation, suspension, retirement, revocation; immediate behavior after a grant/association becomes inactive (subsequent lookups fail closed).

5. Exclusions

No resource identity/refs (→ #830), no bindings (→ #829). project_repository_grants is owned by ARCH-03 (out of this program's leaves) — this issue depends on its properties (active/revoked status, (project,binding) active uniqueness, non-NULL grantor) and must declare that dependency explicitly rather than redefining it.

6. Schema/operation contracts

resource_project_associations (status lifecycle). Operations: lookup_resource_for_project, associate_project, suspend_association, retire_association, revoke_association.

7. SQLite behavior

Read authorization is a service routine; the application role has no direct read of canonical_resources/associations (filesystem/role separation). [TRUSTED-SERVICE].

8. PostgreSQL behavior

SECURITY DEFINER lookup or security-barrier view with cp_readonly restricted; privileges specified; direct SELECT by the application role denied.

9. Concurrency / transaction requirements

A lookup concurrent with grant revocation must not read through the revoked grant (recheck under snapshot); define behavior precisely.

10. Structured results

RESOURCE_RETURNED, NOT_FOUND, PROJECT_NOT_AUTHORIZED, ASSOCIATION_INACTIVE, GRANT_INACTIVE, INVALID_ACTOR_CONTEXT.

11. Enforcement classification

Write-side association/ensure authorization [SCHEMA] (triggers); read-side lookup authorization [TRUSTED-SERVICE] (SQLite) / DB-mediated via SECURITY DEFINER (PostgreSQL).

12. Acceptance criteria

  1. Lookup requires an active grant AND an association.
  2. A revoked grant makes an old association unreadable.
  3. Associations never confer independent authorization.
  4. associate_project requires an active grant; project.admin cannot write canonical_resources.
  5. Cross-project disclosure impossible.
  6. Direct SELECT by the application role denied (PG) / unavailable (SQLite).
  7. Spoofed/absent context fails closed.

13. Named tests

t_lookup_requires_grant_and_assoc(+/−), t_revoked_grant_unreadable(−), t_assoc_not_authorization(−), t_associate_requires_grant(−), t_admin_cannot_write_resource(−), t_cross_project_disclosure(−), t_direct_select(−, PG), t_spoofed_context(−).

14. Audit events / evidence

resource_lookup, association_created/suspended/retired/revoked. Test output evidence.

15. Dependencies / parent

Parents #820, #828; depends #829, #830, and ARCH-03 project_repository_grants (external to this program's leaves — declared, not redefined).

16. Definition of done

Executable SQLite + PostgreSQL; ACs pass incl. direct-SELECT and spoof; bounded PR; no self-review/merge.

17. Known limitations / deferred

project_repository_grants semantics are assumed from ARCH-03; if ARCH-03 is unavailable, a minimal grant table stub with the required properties must be created as a linked follow-up rather than folding grant semantics into this issue.

**Candidate architecture requiring executable validation; not yet approved, production-ready, or certified.** Parents: #820, #828. Depends on: #829 (bindings), #830 (resources). ## 1. Summary / objective Implement project-scoped repository associations and authorized resource lookup: authenticate the caller, check the current project→repository grant, check the current association, and return data only through the sanctioned interface. ## 2. Security/correctness problem Associations must never become independent authorization: a revoked grant must immediately make an old association unreadable. On SQLite there are no table-level read privileges or SELECT triggers, so read authorization is `[TRUSTED-SERVICE]` and must be stated as such, not overclaimed as `[SCHEMA]`. ## 3. Threat model / failure modes Direct `SELECT` bypassing the service; cross-project disclosure; reading via a stale/revoked grant; reading an archived resource; spoofed actor context. ## 4. In-scope behavior - `lookup_resource_for_project` `[TRUSTED-SERVICE]`: authenticate caller; require an **active** `project_repository_grants` row for the resource's binding **and** an existing association; return only through the sanctioned interface; prohibit direct DB access by the application role. - `ensure`/`associate` authorization derives from binding-level grants (`ensure` = internal_service; `associate_project` = project.admin), never from a pre-existing association. `[SCHEMA]` on the write triggers. - PostgreSQL may use a properly-privileged SECURITY DEFINER function or security-barrier view; SQLite uses a service routine + `[PERM]` (application role has no direct DB read of these tables). - Association creation, suspension, retirement, revocation; immediate behavior after a grant/association becomes inactive (subsequent lookups fail closed). ## 5. Exclusions No resource identity/refs (→ #830), no bindings (→ #829). `project_repository_grants` is owned by ARCH-03 (out of this program's leaves) — this issue depends on its properties (active/revoked status, `(project,binding)` active uniqueness, non-NULL grantor) and must declare that dependency explicitly rather than redefining it. ## 6. Schema/operation contracts `resource_project_associations` (status lifecycle). Operations: `lookup_resource_for_project`, `associate_project`, `suspend_association`, `retire_association`, `revoke_association`. ## 7. SQLite behavior Read authorization is a service routine; the application role has no direct read of `canonical_resources`/associations (filesystem/role separation). `[TRUSTED-SERVICE]`. ## 8. PostgreSQL behavior SECURITY DEFINER lookup or security-barrier view with `cp_readonly` restricted; privileges specified; direct SELECT by the application role denied. ## 9. Concurrency / transaction requirements A lookup concurrent with grant revocation must not read through the revoked grant (recheck under snapshot); define behavior precisely. ## 10. Structured results `RESOURCE_RETURNED`, `NOT_FOUND`, `PROJECT_NOT_AUTHORIZED`, `ASSOCIATION_INACTIVE`, `GRANT_INACTIVE`, `INVALID_ACTOR_CONTEXT`. ## 11. Enforcement classification Write-side association/ensure authorization `[SCHEMA]` (triggers); read-side lookup authorization `[TRUSTED-SERVICE]` (SQLite) / DB-mediated via SECURITY DEFINER (PostgreSQL). ## 12. Acceptance criteria 1. Lookup requires an active grant AND an association. 2. A revoked grant makes an old association unreadable. 3. Associations never confer independent authorization. 4. `associate_project` requires an active grant; project.admin cannot write `canonical_resources`. 5. Cross-project disclosure impossible. 6. Direct `SELECT` by the application role denied (PG) / unavailable (SQLite). 7. Spoofed/absent context fails closed. ## 13. Named tests `t_lookup_requires_grant_and_assoc`(+/−), `t_revoked_grant_unreadable`(−), `t_assoc_not_authorization`(−), `t_associate_requires_grant`(−), `t_admin_cannot_write_resource`(−), `t_cross_project_disclosure`(−), `t_direct_select`(−, PG), `t_spoofed_context`(−). ## 14. Audit events / evidence `resource_lookup`, `association_created/suspended/retired/revoked`. Test output evidence. ## 15. Dependencies / parent Parents #820, #828; depends #829, #830, and ARCH-03 `project_repository_grants` (external to this program's leaves — declared, not redefined). ## 16. Definition of done Executable SQLite + PostgreSQL; ACs pass incl. direct-SELECT and spoof; bounded PR; no self-review/merge. ## 17. Known limitations / deferred `project_repository_grants` semantics are assumed from ARCH-03; if ARCH-03 is unavailable, a minimal grant table stub with the required properties must be created as a linked follow-up rather than folding grant semantics into this issue.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Scaled-Tech-Consulting/Gitea-Tools#831