Skip to content

feat: unify staged upload params behind an ownership-enforcing resolver#7551

Closed
orangewolf wants to merge 1 commit into
samvera:mainfrom
orangewolf:feat/unified-upload-attach-contract
Closed

feat: unify staged upload params behind an ownership-enforcing resolver#7551
orangewolf wants to merge 1 commit into
samvera:mainfrom
orangewolf:feat/unified-upload-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 upload-unification chain (#7547#7550) 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 audit flagged — the upcoming 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 #7547#7550).

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

    1 files   -     16      1 suites   - 16   0s ⏱️ - 3h 19m 30s
1 924 tests  -  6 033  1 924 ✅  -  5 719  0 💤  - 307  0 ❌  - 7 
1 924 runs   - 25 266  1 924 ✅  - 24 660  0 💤  - 599  0 ❌  - 7 

Results for commit f934170. ± Comparison against base commit 16ddae4.

@orangewolf

Copy link
Copy Markdown
Member Author

Superseded by #7557 — same change moved to an in-repo branch (active-storage-for-all/attach-contract) and retitled.

@orangewolf orangewolf closed this Jul 18, 2026
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