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

Draft
wants to merge 15 commits into
base: master
Choose a base branch
from

Conversation

stevenbal
Copy link
Contributor

@stevenbal stevenbal commented Sep 23, 2024

Closes #4398

TODO:

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

Attention: Patch coverage is 97.72727% with 2 lines in your changes missing coverage. Please review.

Project coverage is 96.58%. Comparing base (4b73a1d) to head (15203ce).

Files with missing lines Patch % Lines
...nforms/registrations/contrib/objects_api/plugin.py 88.23% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4696      +/-   ##
==========================================
+ Coverage   96.57%   96.58%   +0.01%     
==========================================
  Files         747      748       +1     
  Lines       25400    25487      +87     
  Branches     3355     3366      +11     
==========================================
+ Hits        24529    24616      +87     
  Misses        608      608              
  Partials      263      263              

☔ 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
)
return

# TODO should this path be configurable?
Copy link
Member

Choose a reason for hiding this comment

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

yes

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What is the right place to configure this, should it be on both registration backend and prefill options? I don't know if there is a way around that, since I think it could be possible to use one without the other

Copy link
Member

Choose a reason for hiding this comment

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

Yeah indeed, and the UI copy button is then the convenience so that users can setup prefill from their registration configuration

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've added a TODO in the PR to add the field to the prefill config form as well (since this is probably reliant on #4799)

src/openforms/prefill/base.py Outdated Show resolved Hide resolved
src/openforms/prefill/contrib/objects_api/plugin.py Outdated Show resolved Hide resolved
@stevenbal stevenbal marked this pull request as draft November 4, 2024 08:46
@stevenbal stevenbal force-pushed the feature/4398-product-prefill-check-obj-permission branch 5 times, most recently from e40cc35 to 86abe7b Compare November 5, 2024 15:42
@stevenbal stevenbal force-pushed the feature/4398-product-prefill-check-obj-permission branch from 86abe7b to fc53ad1 Compare November 12, 2024 13:03
@@ -349,6 +357,16 @@ def validate(self, attrs: RegistrationOptions) -> RegistrationOptions:
{"version": _("Unknown version: {version}").format(version=version)}
)

if attrs.get("update_existing_object") and not attrs.get("auth_attribute_path"):
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure if this is a general issue with the objects API config modal, but it seems that the error returned by the backend is not shown on the field:

image

image

Copy link
Member

Choose a reason for hiding this comment

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

you should be able to set up the necesary frontend state & reproduce this problem in storybook! might be that we don't have validation error display hooked up into this arrayfield (?) yet

@stevenbal stevenbal force-pushed the feature/4398-product-prefill-check-obj-permission branch from fc53ad1 to 4fbbd10 Compare November 12, 2024 13:28
the initial data reference is passed from the SDK to the backend as part of the login URL. In order to pass it as part of the submission create body, it must be propagated from the authentication start/return views back to the SDK
if initial_data_reference is specified, Prefill plugins will verify ownership before attempting to prefill and registration plugins will do the same during pre registration
if the object could not be fetched for whatever reason, no longer raise a PermissionDenied, because we could not explicitly verify that the user is not the owner of the object. This is needed because in case of multiple prefill/registration backends, only one of them will actually contain the object, so we do not want to break the form due to 404 errors in the others
* move initial_data_reference ownership check to separate file
* change `validate_object_ownership` signature to accept an ObjectsClient and create the client in the calling code
it is no longer needed because the query parameter is now added to the nextUrl via the SDK
and raise errors if the plugin options is missing `auth_attribute_path`
…hip check

previously this looped over possible backends, but at the time of executing the `verify_initial_data_ownership` check, the registration_backend that is to be used is already known
… True

this field is necessary to perform the initial data reference ownership check, which is performed when updating existing objects
@sergei-maertens sergei-maertens force-pushed the feature/4398-product-prefill-check-obj-permission branch from 4fbbd10 to 15203ce Compare November 18, 2024 13:23
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