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

Support new params expect method to permit parameters #841

Open
Linuus opened this issue Nov 22, 2024 · 7 comments
Open

Support new params expect method to permit parameters #841

Linuus opened this issue Nov 22, 2024 · 7 comments

Comments

@Linuus
Copy link
Collaborator

Linuus commented Nov 22, 2024

In Rails 8 the method expect has been introduced to mitigate some issues with the current params.require(:foo).permit(:bar) approach. One issue with the current approach is that if someone sends unexpected data, say POST { foo: "bam" } the Rails app will crash with a NoMethodError because the .permit method doesn't exist on String. The expect method handles this issue and will instead return a proper 400 error.

So, we should support this in upcoming versions of Pundit.

The expect method has a bit of a different syntax unfortunately so we can't just change it. I guess we either we have to make it required > some version, or configurable in Pundit. I haven't looked closely on how to best handle this yet.

Docs: https://api.rubyonrails.org/classes/ActionController/Parameters.html#method-i-expect

@Burgestrand
Copy link
Member

This is probably my fastest reply ever.

Luckily we're using it in a very limited capacity, a single call-site:

# Retrieves the params for the given record.
#
# @param record [Object] the object we're retrieving params for
# @return [ActionController::Parameters] the params
def pundit_params_for(record)
params.require(PolicyFinder.new(record).param_key)
end

Pundit should definitely support Rails 8.

Will ponder some more!

@Burgestrand
Copy link
Member

Burgestrand commented Nov 22, 2024

So if I understand correctly, behaviour doesn't change in an upgrade to Rails 8. It's just that expect is a new way of doing things.

However, expect has an entirely different set of parameters, so a change would affect permitted_attributes too:

def permitted_attributes(record, action = action_name)
policy = policy(record)
method_name = if policy.respond_to?("permitted_attributes_for_#{action}")
"permitted_attributes_for_#{action}"
else
"permitted_attributes"
end
pundit_params_for(record).permit(*policy.public_send(method_name))
end

It effectively would reduce to something more like:

params.expect(PolicyFinder.new(record).param_key => policy.public_send(method_name))

... which I don't know if it works, but is indeed rather different and even makes pundit_params_for superfluous.

@Burgestrand
Copy link
Member

Burgestrand commented Nov 22, 2024

Two things important to me:

  • Backwards-compatibility. We should not break it.
  • A new way needs to be battle-tested.

We can achieve backwards-compatibility by adding to the API surface, e.g. def expected_attributes(record, action:) and then updating documentation to recommend the new and shiny.

However, what's important with that approach is that it's battle-tested before we recommend it. At the moment I'm not maintaining client projects with significant Pundit-usage (they don't need authorization), so I'm limited in my ability to battle-test it.

@Burgestrand
Copy link
Member

Kinda related to #742

@Linuus
Copy link
Collaborator Author

Linuus commented Nov 22, 2024

So if I understand correctly, behaviour doesn't change in an upgrade to Rails 8. It's just that expect is a new way of doing things.

Yes. Currently basically all Rails apps crash when you send some unexpected params (if they follow the recommended approach) which has been raised many times in the Rails repo. Now the expect syntax is the recommended style going forward. I don't know if the require(...).permit(...) will be deprecated and removed later or not.

Two things important to me:

  • Backwards-compatibility. We should not break it.

For sure!

  • A new way needs to be battle-tested.

We can achieve backwards-compatibility by adding to the API surface, e.g. def expected_attributes(record, action:) and then updating documentation to recommend the new and shiny.

Good idea 👍

However, what's important with that approach is that it's battle-tested before we recommend it. At the moment I'm not maintaining client projects with significant Pundit-usage (they don't need authorization), so I'm limited in my ability to battle-test it.

Sure, but Rails is already recommending it so I think we can too? But yeah we can give the option now and then change any recommendations later :)

@Burgestrand
Copy link
Member

I think my main worry is how Pundit integrates with the API, i.e. how we hook up all the helper methods and how they relate to each other.

pundit_params_for(record) exists for some reason, allowing an override. Should we still have this kind of separation for the top-level key in expect too? I'm thinking we could skip it from the start, and add it if it turns out to be needed. We would find this out pretty quickly as we try to use it for real.

... maybe some more thoughts, but it's still a bit too vague in my mind.

@Linuus
Copy link
Collaborator Author

Linuus commented Nov 22, 2024

Yeah, we don't need to rush anything :) I mainly wanted to open the issue to get the ball rolling a bit.

We will probably experiment with this a bit anyway, and I will get back later when we have some more experience with it. We already do some custom Pundit stuff so I don't think there will be any issues getting this to work in our app without changing pundit :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants