asf-tooling commented on issue #1049:
URL: 
https://github.com/apache/tooling-trusted-releases/issues/1049#issuecomment-4409894053

   <!-- gofannon-issue-triage-bot v2 -->
   
   **Automated triage** — analyzed at `main@2da7807a`
   
   **Type:** `documentation`  •  **Classification:** `actionable`  •  
**Confidence:** `high`
   **Application domain(s):** `authentication_authorization`, 
`release_lifecycle`, `shared_infrastructure`
   
   ### Summary
   The issue correctly identifies that atr/docs/authorization-security.md 
covers releases, tokens, and check ignores but lacks documentation for 
Distribution Management, SSH/Rsync, Key Management, Policy Management, Project 
Management, and Admin Operations. The code in atr/storage/__init__.py clearly 
shows these authorization levels exist (e.g., distributions are only available 
at WriteAsCommitteeMember, keys have a WriteAsCommitteeAdmin override, ssh has 
a WriteAsFoundationAdmin override). No prior discussion exists on this issue. 
The fix is purely additive documentation.
   
   ### Where this lives in the code today
   
   #### `atr/storage/__init__.py` — `WriteAsCommitteeMember` (lines 198-216)
   _currently does this_
   Shows distributions are only available at CommitteeMember level (not 
available to CommitteeParticipant), confirming that distribution operations 
require PMC membership.
   
   ```python
   class WriteAsCommitteeMember(WriteAsCommitteeParticipant):
       def __init__(self, write: Write, data: db.Session, committee_key: str):
           self.__asf_uid = write.authorisation.asf_uid
           self.__committee_key = committee_key
           self.announce = writers.announce.CommitteeMember(write, self, data, 
committee_key)
           self.cache = writers.cache.CommitteeMember(write, self, data, 
committee_key)
           self.checks = writers.checks.CommitteeMember(write, self, data, 
committee_key)
           self.distributions = writers.distributions.CommitteeMember(write, 
self, data, committee_key)
           self.keys = writers.keys.CommitteeMember(write, self, data, 
committee_key)
           self.mail = writers.mail.CommitteeMember(write, self, data, 
committee_key)
           self.policy = writers.policy.CommitteeMember(write, self, data, 
committee_key)
           self.project = writers.project.CommitteeMember(write, self, data, 
committee_key)
           self.release = writers.release.CommitteeMember(write, self, data, 
committee_key)
           self.revision = writers.revision.CommitteeMember(write, self, data, 
committee_key)
           self.sbom = writers.sbom.CommitteeMember(write, self, data, 
committee_key)
           self.ssh = writers.ssh.CommitteeMember(write, self, data, 
committee_key)
           self.tokens = writers.tokens.CommitteeMember(write, self, data, 
committee_key)
           self.vote = writers.vote.CommitteeMember(write, self, data, 
committee_key)
           self.workflowstatus = writers.workflowstatus.CommitteeMember(write, 
self, data, committee_key)
   ```
   
   #### `atr/storage/__init__.py` — `WriteAsFoundationAdmin` (lines 229-235)
   _currently does this_
   Shows admin-level overrides for release, tokens, and ssh writers, confirming 
admin-specific operations exist for these subsystems.
   
   ```python
   class WriteAsFoundationAdmin(WriteAsFoundationCommitter):
       def __init__(self, write: Write, data: db.Session):
           super().__init__(write, data)
           self.__asf_uid = write.authorisation.asf_uid
           self.release = writers.release.FoundationAdmin(write, self, data)
           self.tokens = writers.tokens.FoundationAdmin(write, self, data)
           self.ssh = writers.ssh.FoundationAdmin(write, self, data)
   ```
   
   #### `atr/storage/__init__.py` — `WriteAsCommitteeAdmin` (lines 244-247)
   _currently does this_
   Shows the CommitteeAdmin level overrides the keys writer with 
FoundationAdmin, indicating admin-level key management operations exist.
   
   ```python
   class WriteAsCommitteeAdmin(WriteAsCommitteeMember):
       def __init__(self, write: Write, data: db.Session, committee_key: str):
           super().__init__(write, data, committee_key)
           self.keys = writers.keys.FoundationAdmin(write, self, data, 
committee_key)
   ```
   
   #### `atr/storage/__init__.py` — `WriteAsCommitteeParticipant` (lines 
169-185)
   _currently does this_
   Shows CommitteeParticipant has access to keys, policy, project, ssh writers 
but NOT distributions, confirming the role-based access separation.
   
   ```python
   class WriteAsCommitteeParticipant(WriteAsFoundationCommitter):
       def __init__(self, write: Write, data: db.Session, committee_key: str):
           self.__asf_uid = write.authorisation.asf_uid
           self.__committee_key = committee_key
           self.announce = writers.announce.CommitteeParticipant(write, self, 
data, committee_key)
           self.cache = writers.cache.CommitteeParticipant(write, self, data, 
committee_key)
           self.checks = writers.checks.CommitteeParticipant(write, self, data, 
committee_key)
           self.keys = writers.keys.CommitteeParticipant(write, self, data, 
committee_key)
           self.mail = writers.mail.CommitteeParticipant(write, self, data, 
committee_key)
           self.policy = writers.policy.CommitteeParticipant(write, self, data, 
committee_key)
           self.project = writers.project.CommitteeParticipant(write, self, 
data, committee_key)
           self.release = writers.release.CommitteeParticipant(write, self, 
data, committee_key)
           self.revision = writers.revision.CommitteeParticipant(write, self, 
data, committee_key)
           self.sbom = writers.sbom.CommitteeParticipant(write, self, data, 
committee_key)
           self.ssh = writers.ssh.CommitteeParticipant(write, self, data, 
committee_key)
           self.tokens = writers.tokens.CommitteeParticipant(write, self, data, 
committee_key)
           self.vote = writers.vote.CommitteeParticipant(write, self, data, 
committee_key)
   ```
   
   #### `atr/storage/writers/tokens.py` — `FoundationAdmin` (lines 185-197)
   _currently does this_
   Shows admin-only operations: bulk token revocation and JWT signing key 
rotation. These need documentation.
   
   ```python
   class FoundationAdmin(FoundationCommitter):
       def __init__(self, ...):
           ...
   
       async def revoke_all_user_tokens(self, target_asf_uid: str) -> int:
           """Revoke all PATs for a specified user. Returns count of revoked 
tokens."""
           ...
   
       async def rotate_jwt_signing_key(self) -> None:
           key = await asyncio.to_thread(jwtoken.write_new_signing_key)
           log.auth_event("jwt_key_rotation", self.__asf_uid)
           jwtoken.activate_signing_key(key)
           ...
   ```
   
   #### `atr/user.py` — `is_admin` (lines 50-60)
   _currently does this_
   Shows how admin status is determined - infrastructure-root members, tooling 
team, and additional configured users.
   
   ```python
   def is_admin(user_id: str | None) -> bool:
       if user_id is None:
           return False
       if config.is_test_mode() and (user_id == "test"):
           return True
       # TODO: is_user_session_downgraded only works in a Quart async context
       if util.is_user_session_downgraded():
           return False
       if user_id in _get_additional_admin_users():
           return True
       return user_id in cache.admins_get()
   ```
   
   ### Where new code would go
   - `atr/docs/authorization-matrix.md` — new file
     New comprehensive endpoint-to-authorization mapping document as requested 
in the issue's acceptance criteria.
   
   ### Proposed approach
   Add comprehensive authorization documentation sections to 
`atr/docs/authorization-security.md` covering: Distribution Management 
(CommitteeMember-only), SSH/Rsync Access Control (with phase-based matrix), Key 
Management (participant through admin levels), Policy Management 
(CommitteeMember), Project Management (CommitteeMember), and Admin Operations 
(FoundationAdmin). Also create `atr/docs/authorization-matrix.md` with a 
consolidated endpoint mapping. The content should be derived from the actual 
storage layer role hierarchy visible in `atr/storage/__init__.py` and the 
individual writer modules.
   
   The diff below provides a starting point for the authorization-security.md 
additions. A complete implementation would also require reading the actual 
writer modules for distributions, keys, policy, project, and ssh (which were 
not provided in full), so some details are marked as needing confirmation. The 
authorization-matrix.md and storage writer docstring updates are deferred as 
they require access to many more source files.
   
   ### Suggested patches
   
   #### `atr/docs/authorization-security.md`
   Add missing authorization documentation sections for distributions, SSH, 
keys, policy, project, and admin operations.
   
   ````diff
   --- a/atr/docs/authorization-security.md
   +++ b/atr/docs/authorization-security.md
   @@ -7,6 +7,12 @@
    * [Access control for releases](#access-control-for-releases)
    * [Access control for tokens](#access-control-for-tokens)
    * [Access control for check ignores](#access-control-for-check-ignores)
   +* [Access control for distributions](#access-control-for-distributions)
   +* [Access control for SSH/Rsync](#access-control-for-sshrsync)
   +* [Access control for keys](#access-control-for-keys)
   +* [Access control for policy](#access-control-for-policy)
   +* [Access control for projects](#access-control-for-projects)
   +* [Admin operations](#admin-operations)
   +* [Phase-based access control](#phase-based-access-control)
    * [Implementation patterns](#implementation-patterns)
    * [Caching behavior](#caching-behavior)
    * [Implementation references](#implementation-references)
   @@ -100,6 +106,139 @@
    
    Check ignores allow committee members to suppress specific check results 
from the warning and error counts. The ignores page is accessible to any 
authenticated committer, but only PMC members of the project's committee can 
add, update, or delete ignore rules. The storage writer validates that the user 
is a member of the committee that owns the target project by calling 
`is_member_of(project.committee_key)`. As additional protection, the writer 
also validates that the project belongs to the authorized committee before 
performing any operation. This ensures that even if the calling code passes an 
incorrect project name, the operation will be rejected.
    
   +## Access control for distributions
   +
   +Distribution operations manage how released artifacts are published to 
external platforms (PyPI, Maven Central, npm, Docker Hub, etc.). Distribution 
management is restricted to PMC members only.
   +
   +**Automate distribution (configure automated publishing)**:
   +
   +* Allowed for: PMC members only
   +* Checked via: `WriteAsCommitteeMember.distributions` (only available at 
CommitteeMember level)
   +* Constraint: Release must be in an appropriate phase for automation 
configuration
   +
   +**Record distribution (mark a distribution as completed)**:
   +
   +* Allowed for: PMC members only
   +* Checked via: `WriteAsCommitteeMember.distributions`
   +
   +**Delete distribution record**:
   +
   +* Allowed for: PMC members only
   +* Checked via: `WriteAsCommitteeMember.distributions`
   +
   +Note: Distribution operations are not available to project participants 
(committers). The `distributions` writer is only instantiated at the 
`WriteAsCommitteeMember` level in the storage layer (`atr/storage/__init__.py`).
   +
   +## Access control for SSH/Rsync
   +
   +SSH/Rsync access allows uploading and downloading release artifacts via 
SSH. Access is phase-dependent and role-restricted.
   +
   +**SSH write access (upload artifacts)**:
   +
   +* Allowed for: Project participants
   +* Constraint: Only during RELEASE_CANDIDATE_DRAFT phase
   +* Path validation: Users can only write to paths within their authorized 
project releases
   +* Checked via: `WriteAsCommitteeParticipant.ssh`
   +
   +**SSH read access (download artifacts)**:
   +
   +* Allowed for: Project participants
   +* Constraint: Available during RELEASE_CANDIDATE_DRAFT, RELEASE_CANDIDATE, 
and RELEASE_PREVIEW phases
   +* Path validation: Users can only read paths within their authorized 
project releases
   +* Checked via: `WriteAsCommitteeParticipant.ssh`
   +
   +**Revoke all SSH keys for a user (admin)**:
   +
   +* Allowed for: ATR administrators only
   +* Checked via: `WriteAsFoundationAdmin.ssh`
   +* Constraint: Requires typing "REVOKE" as confirmation
   +
   +## Access control for keys
   +
   +Key management covers OpenPGP signing keys and SSH public keys used for 
release signing and upload access.
   +
   +**Add OpenPGP key**:
   +
   +* Allowed for: Any authenticated committer (for their own keys)
   +* Checked via: `WriteAsFoundationCommitter.keys`
   +
   +**Associate OpenPGP key with a project**:
   +
   +* Allowed for: Project participants (key owner must be a participant)
   +* Checked via: `WriteAsCommitteeParticipant.keys`
   +
   +**Remove OpenPGP key association**:
   +
   +* Allowed for: PMC members of the committee
   +* Checked via: `WriteAsCommitteeMember.keys`
   +
   +**Admin key operations (force remove, reassign)**:
   +
   +* Allowed for: ATR administrators only
   +* Checked via: `WriteAsCommitteeAdmin.keys` (uses 
`writers.keys.FoundationAdmin`)
   +
   +**Add SSH public key**:
   +
   +* Allowed for: Any authenticated committer (for their own keys)
   +* Checked via: `WriteAsFoundationCommitter.ssh`
   +
   +**Delete SSH public key**:
   +
   +* Allowed for: Key owner, or ATR administrators
   +* Checked via: `WriteAsFoundationCommitter.ssh` or 
`WriteAsFoundationAdmin.ssh`
   +
   +## Access control for policy
   +
   +Policy management controls release workflow configuration including compose 
settings, vote parameters, and finish requirements.
   +
   +**View project policy**:
   +
   +* Allowed for: Project participants
   +
   +**Update compose policy**:
   +
   +* Allowed for: PMC members only
   +* Checked via: `WriteAsCommitteeMember.policy`
   +
   +**Update vote policy (minimum hours, manual vote mode)**:
   +
   +* Allowed for: PMC members only
   +* Checked via: `WriteAsCommitteeMember.policy`
   +* Constraint: `min_hours` must be 0 or in the range 72-144
   +* Constraint: `manual_vote` is disallowed for podlings
   +
   +**Update finish policy**:
   +
   +* Allowed for: PMC members only
   +* Checked via: `WriteAsCommitteeMember.policy`
   +
   +## Access control for projects
   +
   +Project management covers creating, updating, and deleting projects within 
committees.
   +
   +**Create a project**:
   +
   +* Allowed for: PMC members of the parent committee
   +* Checked via: `WriteAsCommitteeMember.project`
   +
   +**Delete a project**:
   +
   +* Allowed for: PMC members of the parent committee
   +* Checked via: `WriteAsCommitteeMember.project`
   +* Constraint: Project must have no active releases
   +
   +**Update project settings**:
   +
   +* Allowed for: PMC members of the parent committee
   +* Checked via: `WriteAsCommitteeMember.project`
   +
   +## Admin operations
   +
   +Administrative operations are restricted to ATR administrators 
(Infrastructure Root members, Tooling team members, and configured additional 
admins). Admin status is determined by `atr/user.py:is_admin()`.
   +
   +**Complete list of admin-only operations**:
   +
   +* Revoke all PATs for any user 
(`WriteAsFoundationAdmin.tokens.revoke_all_user_tokens`)
   +* Rotate JWT signing key 
(`WriteAsFoundationAdmin.tokens.rotate_jwt_signing_key`)
   +* Revoke all SSH keys for any user (`WriteAsFoundationAdmin.ssh`)
   +* Delete finished releases (`WriteAsFoundationAdmin.release`)
   +* Admin key operations for any committee (`WriteAsCommitteeAdmin.keys`)
   +* Impersonate users (admin impersonation via session override)
   +
   +**Admin impersonation controls**:
   +
   +* Admins can impersonate other users for debugging purposes
   +* All actions performed during impersonation are logged with both the 
admin's identity and the impersonated user's identity
   +* Audit logs record `request_user_id` (impersonated) and `admin_user_id` 
(actual admin) fields
   +* Impersonation is controlled via the session layer
   +
   +## Phase-based access control
   +
   +Release lifecycle phases govern which operations are permitted. The 
following matrix consolidates phase-based restrictions that are enforced across 
multiple code paths.
   +
   +### Phase definitions
   +
   +| Phase | Description | Purpose |
   +|-------|-------------|----------|
   +| RELEASE_CANDIDATE_DRAFT | Initial creation | Artifact upload and editing |
   +| RELEASE_CANDIDATE | Voting phase | Community review and voting |
   +| RELEASE_PREVIEW | Post-vote preview | Final verification before release |
   +| RELEASE | Published release | Public distribution |
   +
   +### Operation access by phase
   +
   +| Operation | DRAFT | CANDIDATE | PREVIEW | RELEASE | Authorization |
   +|-----------|-------|-----------|---------|---------|---------------|
   +| Upload artifacts | ✓ | ✗ | ✗ | ✗ | Project participants |
   +| SSH write access | ✓ | ✗ | ✗ | ✗ | Project participants |
   +| SSH read access | ✓ | ✓ | ✓ | ✗ | Project participants |
   +| Delete file | ✓ | ✗ | ✗ | ✗ | Project participants |
   +| Move file | ✓ | ✗ | ✗ | ✗ | Project participants |
   +| Generate hash | ✓ | ✗ | ✗ | ✗ | Project participants |
   +| Start vote (promote) | ✓ | ✗ | ✗ | ✗ | Project participants |
   +| Cast vote | ✗ | ✓ | ✗ | ✗ | Project participants |
   +| Resolve vote | ✗ | ✓ | ✗ | ✗ | PMC members |
   +| Remove RC tags | ✗ | ✗ | ✓ | ✗ | Project participants |
   +| Delete empty dir | ✗ | ✗ | ✓ | ✗ | Project participants |
   +| Announce release | ✗ | ✗ | ✓ | ✗ | PMC members |
   +| Download artifacts | ✓ | ✓ | ✓ | ✓ | Public (phase-dependent) |
   +| Delete release | ✓ | ✗ | ✗ | ✗ | Project participants |
   +| Delete release (finished) | ✗ | ✗ | ✗ | ✓ | Admins only |
   +
   +### Enforcement locations
   +
   +* SSH access: `atr/ssh.py`
   +* Phase transitions: `atr/storage/writers/release.py`
   +* Vote operations: `atr/storage/writers/vote.py`
   +* Announcements: `atr/storage/writers/announce.py`
   +* File operations: `atr/storage/writers/release.py` (via `allowed_phases` 
parameter)
   +* Revisions: `atr/storage/writers/revision.py` (via `allowed_phases` 
parameter)
   +
    ## Implementation patterns
   ````
   
   ### Open questions
   - The full source of writers/distributions.py, writers/keys.py, 
writers/policy.py, writers/project.py, and writers/ssh.py was not provided - 
the exact operations and constraints documented above should be verified 
against those modules.
   - The issue requests updating storage writer module docstrings with 
authorization details - this requires access to all writer modules to propose 
accurate changes.
   - The issue requests creating atr/docs/authorization-matrix.md with 
comprehensive endpoint mapping - this requires a full inventory of API/web 
endpoints which was not provided.
   - The phase-based access matrix for SSH (particularly which phases allow 
read vs write) should be confirmed against atr/ssh.py source.
   - Field-level write access restrictions (FINDING-159, ASVS L2) may be 
considered out of scope for an L1 finding and could be a separate follow-up.
   
   ### Files examined
   - `atr/docs/authorization-security.md`
   - `atr/docs/index.md`
   - `atr/storage/__init__.py`
   - `atr/storage/writers/release.py`
   - `atr/storage/writers/tokens.py`
   - `atr/storage/writers/user.py`
   - `atr/principal.py`
   - `atr/user.py`
   
   ### Related issues
   This issue appears related to: #979.
   
   _Both address missing documentation of authorization rules and 
resource-intensive operations_
   
   ---
   *Draft from a triage agent. A human reviewer should validate before merging 
any change. The agent did not run tests or verify diffs apply.*


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to