Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ [#4398] Check object ownership when creating submission #4696

Merged

Conversation

stevenbal
Copy link
Contributor

@stevenbal stevenbal commented Sep 23, 2024

Closes #4398

Changes

  • Check object ownership when creating submission

Checklist

Check off the items that are completed or not relevant.

  • Impact on features

    • Checked copying a form
    • Checked import/export of a form
    • Config checks in the configuration overview admin page
    • Problem detection in the admin email digest is handled
  • Release management

    • I have labelled the PR as "needs-backport" accordingly
  • I have updated the translations assets (you do NOT need to provide translations)

    • Ran ./bin/makemessages_js.sh
    • Ran ./bin/compilemessages_js.sh
  • Commit hygiene

    • Commit messages refer to the relevant Github issue
    • Commit messages explain the "why" of change, not the how

@stevenbal stevenbal marked this pull request as draft September 23, 2024 14:09
@stevenbal stevenbal force-pushed the feature/4398-product-prefill-check-obj-permission branch 2 times, most recently from 8e700b5 to 7891ead Compare October 1, 2024 13:24
Copy link

codecov bot commented Oct 1, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 96.61%. Comparing base (0bda3f3) to head (44c89d8).
Report is 42 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4696      +/-   ##
==========================================
- Coverage   96.61%   96.61%   -0.01%     
==========================================
  Files         751      752       +1     
  Lines       25762    25848      +86     
  Branches     3413     3422       +9     
==========================================
+ Hits        24891    24974      +83     
- Misses        609      612       +3     
  Partials      262      262              

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

@stevenbal stevenbal changed the title 🚧 [#4398] Check object ownership when creating submission ✨ [#4398] Check object ownership when creating submission Oct 1, 2024
@stevenbal stevenbal force-pushed the feature/4398-product-prefill-check-obj-permission branch from 7891ead to 18dc4e6 Compare October 1, 2024 14:16
@stevenbal stevenbal marked this pull request as ready for review October 1, 2024 15:07
@stevenbal stevenbal force-pushed the feature/4398-product-prefill-check-obj-permission branch from 18dc4e6 to ac6305a Compare October 8, 2024 14:30
Copy link
Member

@sergei-maertens sergei-maertens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is happening at the wrong place with the wrong assumptions 😬

For starters, session[FORM_AUTH_SESSION_KEY] should not be relied on - ideally once the submission is created/started, this should be cleaned from the session data entirely since we have a hook/signal that records it on the submission itself.

Second - we should always pass a submission instance when performing this kind of validation, exactly because different credentials could apply to a submission vs. what's in the session (the session data is a data race, since you can have different authentication details there compared to a submission that is also still in your session, for example when you are filling out two forms at the same time with different login options).

I think the prefill plugin itself should perform this access control, since:

  1. in submissions.api.viewsets.SubmissionViewSet.perform_create we dispatch the submission start signal, that ensures we have the auth details stored
  2. after that, we call prefill_variables, which receives the submission instance and passes it to the plugin
  3. inside the plugin, you should be able to raise PermissionDenied if anything is fishy
  4. if that exception is raised, it should bubble up to the API endpoint, will result in an HTTP 403 and the DB transaction should be rolled back so that the submission record is never persisted in the database

I'm afraid that makes this PR/ticket depend on #4620 then 🤔

@stevenbal stevenbal marked this pull request as draft October 10, 2024 08:38
@stevenbal stevenbal force-pushed the feature/4398-product-prefill-check-obj-permission branch 4 times, most recently from 55dc86e to 84e00ce Compare October 14, 2024 09:42
@stevenbal
Copy link
Contributor Author

@sergei-maertens it was mentioned in #4721 that the mechanism to pass initial_data_reference and update existing objects for Objects API registration (instead of creating new ones) should also function if prefill is not used. What would be the best place to do this permission check without having to implement it both in the registration backend and the prefill plugin?

@sergei-maertens
Copy link
Member

@sergei-maertens it was mentioned in #4721 that the mechanism to pass initial_data_reference and update existing objects for Objects API registration (instead of creating new ones) should also function if prefill is not used. What would be the best place to do this permission check without having to implement it both in the registration backend and the prefill plugin?

uh... right. I'll have to think about that 😅

@stevenbal
Copy link
Contributor Author

@sergei-maertens maybe there should be some kind of hook in SubmissionViewSet.perform_create that runs any validation for initial_data_reference for the configured registration backend and prefill plugins? That way it's generic, though I'm not sure what we could do to avoid performing the same validation twice, perhaps we could skip the validation for the prefill plugin if validation with the same PLUGIN_IDENTIFIER (in this case objects_api) has already been performed for a registration backend?

@sergei-maertens
Copy link
Member

@stevenbal I still don't see a simple/elegant way to do this, especially because data can be mutated externally and be outdated. E.g., if you verify it at perform_create time and track that it was okay at that time, some other system could make corrections to the data and by the time it's being registered, it could possibly no longer be verified (because a BSN was updated due to a mistake, for example).

I'm kind of leaning towards implementing the permission check itself once in openforms.contrib.objects_api and then re-using that functionality in both prefill and registration. This means that the check is done twice, but it does make it very explicit and I'd rather check too often than not often enough.

It's again a case of two different systems doing very similar things, but that doesn't make them the same. The prefill mapping and registration mapping are technically independent from each other too, we'll just offer convenience UI options to use one as a source for the other, but in the backend, they don't know of each other's existence and that's fine.

The meaning of initial_data_reference only becomes clear when interpreted in the context of the plugin using it anyway - so I don't see an agnostic "generic" mechanism. There's also a risk involved with that though - you may accidentally forget to implement access controls.

Perhaps for registration plugins we define a new hook on the base plugin which raises NotImplementError by default (so it causes a crash if forgotten). Let's call it verify_initial_data_ownership and it only gets called if submission.initial_data_reference is populated, so it won't break existing forms. You can then implement this calling logic in openforms.registrations.tasks.pre_registration so that it's plugin-agnostic. A similar pattern could then probably be applied to prefill?

@stevenbal
Copy link
Contributor Author

@sergei-maertens that sound like a good idea, I'll see if I can implement this

@stevenbal stevenbal force-pushed the feature/4398-product-prefill-check-obj-permission branch 5 times, most recently from 52daf64 to ca09bd7 Compare October 29, 2024 13:43
@stevenbal stevenbal marked this pull request as ready for review October 29, 2024 14:41
src/openforms/authentication/views.py Outdated Show resolved Hide resolved
src/openforms/contrib/objects_api/validators.py Outdated Show resolved Hide resolved
src/openforms/contrib/objects_api/validators.py Outdated Show resolved Hide resolved
src/openforms/contrib/objects_api/validators.py Outdated Show resolved Hide resolved
src/openforms/contrib/objects_api/validators.py Outdated Show resolved Hide resolved
src/openforms/prefill/base.py Outdated Show resolved Hide resolved
src/openforms/prefill/contrib/objects_api/plugin.py Outdated Show resolved Hide resolved
@sergei-maertens sergei-maertens force-pushed the feature/4398-product-prefill-check-obj-permission branch from 2aa507c to 1cd37e4 Compare December 2, 2024 12:06
The TargetPathSelect component is now decoupled from its possible
FieldArray parent, and the option label display is incorporated in
the component itself so that options no longer need to be pre-processed.

On top of that, it's now refactored to be based on react-select for
consistency in the UI, and separate stories have been added so we can
do (visual) regression testing/isolated development.

We use this component in a number of places now:

* the path to the auth attribute (no variablesMapping parent context),
  used in registration and prefill plugins for the Objects API
* the path for the generic Object Types V2 registration mapping, here
  a variablesMapping parent container/context is relevant and different
  form state management semantics apply
* the path to specialized AddressNL subfield mappings - a parent is
  relevant, but not within a bigger variable mapping so we can use the
  standard field assignment semantics of Formik.
@sergei-maertens sergei-maertens force-pushed the feature/4398-product-prefill-check-obj-permission branch from 3ceac3e to 215fa49 Compare December 2, 2024 18:12
This is now possible because we render modals in the proper portal/body
element to make these queries work.
@sergei-maertens sergei-maertens force-pushed the feature/4398-product-prefill-check-obj-permission branch from 45b3acb to 3f25d66 Compare December 3, 2024 11:55
Instead of prop-drilling, make sure to process validation errors at the
locations where the shape/structure is known and rely on the generic
mechanisms to pass/display the validation errors.

This also adds some JSDoc type hinting to the helpers because I keep
getting lost on what the expected shape of data/input is.
@sergei-maertens sergei-maertens force-pushed the feature/4398-product-prefill-check-obj-permission branch from 3f25d66 to 953d574 Compare December 3, 2024 13:07
@sergei-maertens sergei-maertens marked this pull request as draft December 3, 2024 14:19
@sergei-maertens sergei-maertens force-pushed the feature/4398-product-prefill-check-obj-permission branch 2 times, most recently from de93db9 to f5f620b Compare December 3, 2024 17:19
* Ensure we use the deserialized, strongly typed options in
  plugins
* Dropped unused error cases/flows that are obsoleted
* Made serializer options all required, since the prefill
  cannot possibly work without and must match the type
  definitions of the options.
@sergei-maertens sergei-maertens force-pushed the feature/4398-product-prefill-check-obj-permission branch from f5f620b to 6d45ca2 Compare December 3, 2024 17:29
…n details

The important pattern is that certain validation checks run, not the
implementation details of the objects api prefill plugin. So, we
can properly isolate this by setting up a different plugin
registry and doing the dependency injection through a helper,
applying the same pattern as with registration plugins.
Dropped the bits that are not relevant.
The task/generic machinery takes care of deserializing the plugin
options and passes them to the hook for ownership verification, greatly
simplifying the code that needs to be implemented.
Properly mock the registry of plugins instead of having to rely on
particular demo plugins having certain behaviours or needing to
mock implementation details in tests.
* Converted the tests to make use of VCR for the actual implementation
  details of the ownership verification
* Simplified the tests and ensured they're unit tests focused around
  a single method. We have a generic pattern test that ensures the
  method gets called.
Copy link
Member

@sergei-maertens sergei-maertens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy with it being in this state, let's hope CI passes too and then it's finally done! 🎉

@sergei-maertens sergei-maertens marked this pull request as ready for review December 4, 2024 17:21
@sergei-maertens sergei-maertens merged commit f678921 into master Dec 4, 2024
33 of 37 checks passed
@sergei-maertens sergei-maertens deleted the feature/4398-product-prefill-check-obj-permission branch December 4, 2024 17:52
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.

Add validation to the Objects API prefill plugin to check if the user is the owner of the object
3 participants