-
Notifications
You must be signed in to change notification settings - Fork 60
Authorize relationship actions with custom policy methods #40
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
Conversation
|
Given the following scenario: class User < ActiveRecord::Base
has_and_belongs_to_many :projects
end
class Project < ActiveRecord::Base
has_and_belongs_to_many :users
endand class UserResource < BaseResource
has_many :projects
end
class ProjectResource < BaseResource
has_many :users
endA user can be added to a project through Now you can allow this action independently from allowing to update the project using class ProjectPolicy < ApplicationPolicy
def allow_relationship_users? users
true
end
# but
def update?
false
end
end |
|
Yeah, that could work to allow for the relationship operations first to define more specific authorizations 🤔. That way we could first implement the relationship operations and only afterwards think about implementing the same thing in the generic update. I think I was too bogged down in thinking how to allow We'd need tests to avoid breaking this functionality in the future and to make sure it works properly. The current tests are a bit... unwieldy right now, sorry about that 😳. They are meant to be request-level tests testing the actual JSON API payloads and ensuring authorization is ran properly and that all cases are covered. Is there anything I could help you with? |
| allowed = policy.send(relationship_method, new_related_records) | ||
| else | ||
| allowed = policy.update? | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The build is currently failing because Rubocop would like this piece of code to look something like this:
allowed =
if policy.respond_to?(relationship_method)
policy.send(relationship_method, new_related_records)
else
policy.update?
end| # * +relationship_type+ - The relationship type | ||
| def create_to_many_relationship(source_record, new_related_records, relationship_type) | ||
| policy = Pundit.policy(user, source_record) | ||
| relationship_method = 'allow_relationship_' + relationship_type.to_s + '?' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you know that you could use this, too?
relationship_method = "allow_relationship_#{relationship_type}?"It might make it more easier to add dynamic stuff together to form a string.
|
Thanks for the feedback! I've just updated the code according to your reviews and added some tests. |
|
Thanks! Do you want to keep on going with this approach for the other relationship operation types, too? I think we could take this as the first proper approach for handling the complex cases and then later look into what it would mean if we'd get it done for the common If you do want to keep on going for the other relationship types, too, I guess you could modify the pull request title to really say how amazing things are happening here 😄 |
|
That is, what do you want the scope of this pull request to be? Do you think this PR should be merged at some point, and if so, when do you feel like this PR is done? What things would you like to see implemented here? |
|
Yeah, I think the other relationship operation types should be implemented as well before the PR is mergeable. I'd say that the result of this PR should be that as a user of JA you can define policies for relationship operations on a per relationship level (instead of relying just on I'm still in the process of trying out though and I don't have a clear picture of how the result should look like. As a next step I've implemented the What I noticed and wanted to document here is that a user of JA needs to ensure that |
👏 Yes, great that you think so, too! I'll modify the PR title accordingly. I think that we can leave out the entire
Yeah I figured that only after testing out actually implementing these, would we discover things that cannot easily be seen upfront. The final solution can be something different than the original proposal idea, as long as the principles I outlined back in #30 stand: Prefer being overly restrictive rather than too permissive by accident. I really appreciate you communicating your ideas and I'm very open in rethinking a solution together 😌
👍 Sounds reasonable, and this gem should allow for such a scenario to be handled, too.
I like that name 👍
👍 This makes sense, feel free to do so. That has a nice symmetry with the remove method.
Yes, this was in fact a big thing discussed over at #30 and was one of the reasons why the discussion got a bit heated. There were talk about inspecting the relationship itself and then, with some logic, decide which of the two policies should be called. I liked the idea @aaronbhansen posted in #30 (comment), but I can't yet really form a proper opinion on the validity of that approach before I see some code implementing that feature. Anyway, I'm a bit wary of going down that rabbit hole yet. Do you think it could be reasonable to solve that "both policies need to look the same" issue after we've got the primitive API working in this PR? I fear that tackling that issue might open a can of worms where we must start to add swathes of tests to cover different types of relationships. And oh boy, there are many. It might be too soon to start figuring it out in this PR already. I did hack around a bit to see if that would even remotely be possible, and I found out that yes,
I'm really grateful for your efforts here! Would you like to get added as a contributor to this project once we get this out? |
|
Hiya @matthias-g! What's the status with this PR? Anything I can do to help you move forwards? |
|
Hey, thanks for asking. I'm sorry for being quiet the last weeks - I've just been busy with other work. |
|
Yay, great to hear! If you need help bouncing around ideas or anything, we can book a https://room.co/ video chat (just open your browser and it works, no software install required) to talk things through if that would help not getting stuck with this :) |
Calls replace_<relation>? of policy if this method is present. Otherwise update? is being called.
This changes with JR 0.9, see cerebris/jsonapi-resources@78e3560
Definitely. I'm not even sure if solving this on the side of JA is the best way. Maybe a design pattern for the policies could work as well. Like creating a concern per relationship that includes methods which are being called by the respective policy methods. But it's a good idea to discuss that later. 😉
Thanks, that would be nice - once this has been merged. For the recent commits I want to document two thoughts:
|
👍 Glad we're on the same page on this one 😅
We'll be glad to have you with us I really like the way you went with the Also, it makes sense to provide a separate |
|
So, I've just pushed the implementations for the missing |
|
👍 Good call |
|
Huh, sorry, I hadn't figured out that this is already functionally complete! Sorry 😅 Would you like to try out documenting this in the README somehow? |
No problem. I didn't stress that because I was still thinking about how the new policy methods could be useful for the generic update. But I didn't find a good way and I actually think that it is not that important as you can use the specific relationship operations if
Sure. I've just added a paragraph to the README. What do you think about it? |
|
This is looking super great, thank you! We'll look at this PR with @lime soon and reply back to you and maybe even merge this as it is 😊 |
|
All in all this looks very good and we really appreciate your effort! We have some ideas on what we'd like to see here. I'll create a PR against your PR based on our review to get this shipped |
|
Here's some suggestions we came up with @lime as we reviewed this PR. What do you think?
EDIT: The things below were originally cluttered in the tasks above, but it might make more sense to focus on these after we've got this PR done We were also thinking of doing these, but it might be a better idea to do them at a later point in time. I'm just writing these here for posterity:
Merging this could be the first step to getting to 1.0 release. It might be a good idea to create a "Roadmap to 1.0" issue that documents what needs to be done until we're happy with a major version release. Before that, though, we could release a
|
|
It looks like including relationships in the payload on a POST or PATCH to a resource still just defaults to checking permissions via I have done a bit of work on these based on the work @valscion did in his Or are those routes outside the scope of this PR? EDIT: Just another nitpick. The |
|
I am also curious what is thought to be a better way of handling has_many related records: sending them as an array to the relationship method (like in this PR) or iterating through the array of records in the DefaultPunditAuthorizer class and passing one record at a time to the relationship method? Basically, |
|
Thanks for your constructive feedback, @valscion, @lime!
Comments added. ✔️
Thanks, I had overlooked that. ✔️
It does now. ✔️ |
|
@gnfisher Thanks for reviewing and providing feedback as well!
Yes, that is true. And I prefer an implementation that uses the relationship policy actions, too.
This is because the other relationship callbacks don't pass the current related records as well.
That is a good question. And the current implementation is even inconsistent: It's |
Of course. 🤦♂️ Sorry!
Ok. yes, I spoke to Larry in the JR chat about some other topic and I do remember him saying that in the next release it will pass all records versuses going through the processor for every record in the array. Awesome! |
|
Looks great to me, thank you! I will follow-up later with more PRs and the 1.0 roadmap issue until releasing a new version of this gem. I expect to have time for this next week. We are tracking progress on getting this done on our internal Kanban board at work so this shouldn't be forgotten 😄 |
This PR is to discuss a proposal for how the authorization of relationships could be handled (see #30).