Skip to content

[ActiveStorage for All] Unify staged upload params behind an ownership-enforcing resolver#7557

Draft
orangewolf wants to merge 1 commit into
mainfrom
active-storage-for-all/attach-contract
Draft

[ActiveStorage for All] Unify staged upload params behind an ownership-enforcing resolver#7557
orangewolf wants to merge 1 commit into
mainfrom
active-storage-for-all/attach-contract

Conversation

@orangewolf

Copy link
Copy Markdown
Member

Summary

Unifies how staged uploads (Hyrax::UploadedFile ids) travel from forms into attach code, behind a single ownership-enforcing resolver. Part of the [ActiveStorage for All] chain but independent — based directly on main.

What it does

  • Hyrax::UploadedFileResolver — the one seam that turns staged-upload id params into records, raising OwnershipError when a file belongs to another user. Adopted by:
    • work updates (WorksControllerBehavior#uploaded_files) and work creates (Hyrax::Action::CreateValkyrieWork#uploaded_files)
    • file-set version uploads (both AF and Valkyrie branches of FileSetsController#attempt_update*)
    • collection branding (banner_files[] / new logo_files[] ids, validated at the parameter edge of Dashboard::CollectionsController, covering both backends' branding paths)
  • Closes a security gap: the Valkyrie transaction path attached any user's staged file ids; the AF actor stack has validated ownership in CreateWithFilesActor since forever. Now both do.
  • Hyrax::EnforcesStagedUploadOwnership concern rescues violations (HTML: redirect back + translated alert, added to all 7 locales; JSON: 403).
  • Canonical param name: the file-set Versions form now accepts uploaded_files[] (same field the work form uses); the oddball files_files still works as a deprecated alias (Deprecation.warn). This is the first step in collapsing the four field names (uploaded_files[]/files_files/banner_files[]/logo_files[]) the upload audit flagged — the uploader-UI PR can then emit one name.

Reviewer attention

  • The ownership check now also covers the ActiveFedora version-upload and branding parameter paths, which previously had no such check. That tightens behavior for requests that were already anomalous (attaching someone else's staged bytes); AF storage/ingest itself is untouched. Flagging for explicit sign-off given the chain's "preserve AF behavior" ground rule.
  • Two pre-existing branding spec fixtures created uploads owned by a different user than the signed-in one; they now create them as the acting user (the previous shape is exactly what this PR rejects).

Dependencies

None (merges independently of the rest of the chain).

Supersedes #7551 (same change, moved from a fork branch to an in-repo branch and retitled).

Tests

Run locally against the koppie (Valkyrie) test app — dockerized Postgres/Solr/Redis, all green:

  • spec/services/hyrax/uploaded_file_resolver_spec.rb (new): resolution, order preservation, blanks, foreign-file rejection incl. batch, logging, RecordNotFound
  • spec/controllers/hyrax/monographs_controller_spec.rb: work update with a foreign staged file is refused with the alert
  • spec/controllers/hyrax/file_sets_controller_spec.rb: canonical uploaded_files[] ingests like files_files; files_files emits a deprecation warning; foreign staged file is refused
  • spec/controllers/hyrax/dashboard/collections_controller_spec.rb: branding save paths green with owned fixtures; foreign banner refused (104 examples, 0 failures)

AF-side (dassie) suite portions not run locally; CI covers them. RuboCop clean on touched files; git diff --check clean.

🤖 Generated with Claude Code

Introduces Hyrax::UploadedFileResolver as the single seam turning
staged-upload id params into Hyrax::UploadedFile records, enforcing
server side that the acting user owns every file being attached, and
routes the non-ActiveFedora attach paths through it:

- work updates (WorksControllerBehavior#uploaded_files) and work
  creates (Hyrax::Action::CreateValkyrieWork#uploaded_files) — closing
  a real gap: the Valkyrie transaction path attached any user's staged
  file ids, while the ActiveFedora actor stack has always validated
  ownership in CreateWithFilesActor
- file set version uploads (both branches of
  FileSetsController#attempt_update*)
- collection branding (banner_files/logo_files ids validated at the
  parameter edge in Dashboard::CollectionsController, covering the
  ActiveFedora and Valkyrie branding paths alike)

Violations raise Hyrax::UploadedFileResolver::OwnershipError, logged
and rescued by the new Hyrax::EnforcesStagedUploadOwnership concern
(HTML: redirect back with a translated alert; JSON: 403).

The version upload field is also unified: FileSetsController#update now
accepts the canonical uploaded_files parameter used by the work form,
keeping files_files as a deprecated alias (Deprecation.warn), one step
in consolidating the four staged-file field names the upload UIs
submit.

The ownership check now also covers the ActiveFedora version-upload and
branding parameter paths, which previously had no such check; flagged
for reviewer attention since it tightens (already-anomalous) requests.

Tests (koppie/Valkyrie app, dockerized Postgres/Solr/Redis, all green):
spec/services/hyrax/uploaded_file_resolver_spec.rb (new),
spec/controllers/hyrax/monographs_controller_spec.rb (foreign-file work
update refused), spec/controllers/hyrax/file_sets_controller_spec.rb
(canonical param ingests, files_files deprecation warning, foreign file
refused), spec/controllers/hyrax/dashboard/collections_controller_spec.rb
(branding fixtures now owned by the acting user; foreign banner
refused). AF-side suite portions deferred to CI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Test Results

    17 files  ± 0      17 suites  ±0   3h 19m 5s ⏱️ - 10m 34s
 7 992 tests +15   7 684 ✅ +14  307 💤 ±0  1 ❌ +1 
27 331 runs  +56  26 731 ✅ +55  599 💤 ±0  1 ❌ +1 

For more details on these failures, see this check.

Results for commit f934170. ± Comparison against base commit 5cc41d6.

This pull request removes 450 and adds 465 tests. Note that renamed tests count towards both.
spec.abilities.ability_spec ‑ Hyrax::Ability AdminSets and PermissionTemplates a user without edit access is expected not to be able to create #<Hyrax::PermissionTemplate:0x00007f37d68cb2d8>
spec.abilities.ability_spec ‑ Hyrax::Ability AdminSets and PermissionTemplates a user without edit access is expected not to be able to create #<Hyrax::PermissionTemplate:0x00007f4a43d1c448>
spec.abilities.ability_spec ‑ Hyrax::Ability AdminSets and PermissionTemplates a user without edit access is expected not to be able to create #<Hyrax::PermissionTemplate:0x00007fdf34ed9d90>
spec.abilities.ability_spec ‑ Hyrax::Ability AdminSets and PermissionTemplates a user without edit access is expected not to be able to create #<Hyrax::PermissionTemplate:0x00007fee6c78aa20>
spec.abilities.ability_spec ‑ Hyrax::Ability AdminSets and PermissionTemplates a user without edit access is expected not to be able to create #<Hyrax::PermissionTemplateAccess:0x00007f37d68c0b58>
spec.abilities.ability_spec ‑ Hyrax::Ability AdminSets and PermissionTemplates a user without edit access is expected not to be able to create #<Hyrax::PermissionTemplateAccess:0x00007f4a4ae00010>
spec.abilities.ability_spec ‑ Hyrax::Ability AdminSets and PermissionTemplates a user without edit access is expected not to be able to create #<Hyrax::PermissionTemplateAccess:0x00007fdf45ad4c48>
spec.abilities.ability_spec ‑ Hyrax::Ability AdminSets and PermissionTemplates a user without edit access is expected not to be able to create #<Hyrax::PermissionTemplateAccess:0x00007fee7038e120>
spec.abilities.ability_spec ‑ Hyrax::Ability AdminSets and PermissionTemplates a user without edit access is expected not to be able to destroy AdminSet: 2132c8ed-856c-4771-994f-282c496d9150
spec.abilities.ability_spec ‑ Hyrax::Ability AdminSets and PermissionTemplates a user without edit access is expected not to be able to destroy Hyrax::AdministrativeSet: 497eb04a-2b61-4268-a741-91b1fe06e111
…
spec.abilities.ability_spec ‑ Hyrax::Ability AdminSets and PermissionTemplates a user without edit access is expected not to be able to create #<Hyrax::PermissionTemplate:0x00007eff85449748>
spec.abilities.ability_spec ‑ Hyrax::Ability AdminSets and PermissionTemplates a user without edit access is expected not to be able to create #<Hyrax::PermissionTemplate:0x00007effd80fd348>
spec.abilities.ability_spec ‑ Hyrax::Ability AdminSets and PermissionTemplates a user without edit access is expected not to be able to create #<Hyrax::PermissionTemplate:0x00007f0035274c50>
spec.abilities.ability_spec ‑ Hyrax::Ability AdminSets and PermissionTemplates a user without edit access is expected not to be able to create #<Hyrax::PermissionTemplate:0x00007f13088ca790>
spec.abilities.ability_spec ‑ Hyrax::Ability AdminSets and PermissionTemplates a user without edit access is expected not to be able to create #<Hyrax::PermissionTemplateAccess:0x00007eff8519eed0>
spec.abilities.ability_spec ‑ Hyrax::Ability AdminSets and PermissionTemplates a user without edit access is expected not to be able to create #<Hyrax::PermissionTemplateAccess:0x00007effd817ac80>
spec.abilities.ability_spec ‑ Hyrax::Ability AdminSets and PermissionTemplates a user without edit access is expected not to be able to create #<Hyrax::PermissionTemplateAccess:0x00007f0045d35a08>
spec.abilities.ability_spec ‑ Hyrax::Ability AdminSets and PermissionTemplates a user without edit access is expected not to be able to create #<Hyrax::PermissionTemplateAccess:0x00007f1308bb30d8>
spec.abilities.ability_spec ‑ Hyrax::Ability AdminSets and PermissionTemplates a user without edit access is expected not to be able to destroy AdminSet: 9e7a9ad4-ce71-4dea-9c1a-8edd3bebcb8a
spec.abilities.ability_spec ‑ Hyrax::Ability AdminSets and PermissionTemplates a user without edit access is expected not to be able to destroy Hyrax::AdministrativeSet: 132d4d88-acb2-430a-8ca0-63dcf54bc2ef
…

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.

1 participant