Skip to content

Conversation

@plantfansam
Copy link
Contributor

Hi! In an effort to make jsonapi-authorization's test suite pass against jsonapi-resources 0.9, this PR does a few things:

  1. Conditionally sets callbacks for :create_to_many_relationship :replace_to_many_relationship :remove_to_many_relationship based on whether or not JSONAPI::Processor responds to the singular or plural version of the method (See Not compatible with v0.9.0 of jsonapi-resources #36).
  2. Adds a callback and stub authorization method for replace_polymorphic_to_one_relationship, which is present in jsonapi-resources 0.9 and 0.8.0.beta1. I don't think this is strictly necessary for 0.9 compatibility but is good to have in my opinion.
  3. Tries to fix up authorize_remove_to_many_relationship by updating the logic to find all related records and call authorizer.remove_to_many_relationship on each of them.

When this branch is run against the suite with jsonapi-resources at 0.8.0.beta1, there are a couple of failures. I am not sure what the backwards compatibility requirements are between point releases of the gem, but I definitely tried to stay somewhat backwards compatible in the conditional callback setting strategy.

I am super new to this project and definitely want to get some experienced 👀 on this! Happy to chop it up into separate PRs and fix stuff up, as this one has a few distinct threads to it.

@valscion
Copy link
Member

Hi, and welcome! Thank you for starting a PR for this. I'm very excited to soon jump to review it!

Hopefully it's ok that this is going to take me a few days at least until I can look into this more. I've got a tight schedule at work for a while, and as my wrist is aching again I won't be able to look at this on my freetime anytime soon.

I won't forget about this PR, though :). I'll check this out when I've got a good timeslot for this 💞

@valscion valscion closed this Mar 19, 2017
@valscion valscion reopened this Mar 19, 2017
@valscion
Copy link
Member

Closed and reopened to try to trigger a CI build... wonder what's going on 😕

@valscion
Copy link
Member

This seems to be going to a good direction, thank you!

I'm not sure whether we've got a good answer to the backwards compatibility here. The relationship processor hooks have changed a bit, and when we started to authorize for relationship actions we hit these changes, too: #40 (comment)

I'd be OK with going by just supporting 0.9 in a future release, as it would make the relationship authorizations cleaner as well. 0.9.0 is out of beta so it's fine if we only target that version, too.

Would you be OK with setting this PR up to only support 0.9 and removing the then-unnecessary checks from your changes?

@plantfansam
Copy link
Contributor Author

plantfansam commented Mar 19, 2017 via email

@plantfansam
Copy link
Contributor Author

plantfansam commented Mar 20, 2017

OK, I have updated the gemspec to require jsonapi-resources 0.9 and updated the code accordingly. I also had to update the remove_to_many_relationship calls to match the new method signature.

context 'unauthorized for remove_to_many_relationship' do
before do
disallow_operation('remove_to_many_relationship', article, kind_of(Comment), :comments)
disallow_operation('remove_to_many_relationship', article, kind_of(Comment), "comments")
Copy link
Member

Choose a reason for hiding this comment

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

Hmm I wonder whether this change was necessary?

end

def authorize_replace_polymorphic_to_one_relationship
raise NotImplementedError
Copy link
Member

Choose a reason for hiding this comment

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

Good call with just raising NotImplementedError here.

We'll need to figure out how to authorize for this polymorphic case before shipping v1.0.0 but that might be better to discuss outside of this PR.

Copy link
Member

Choose a reason for hiding this comment

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

Actually, I think we'd like to skip raising this here and figure out what to do with the polymorphic relationship scenario outside of this PR. This way we wouldn't add too much noise to this upgrade path.

Would you like to remove this callback from the processor?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure! Removed it here 6377da9


def self.creatable_fields(context)
super + [:id]
end
Copy link
Member

Choose a reason for hiding this comment

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

Hmm what was the reason for this change? Is there a reason why it is needed?

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 didn't discover the reason in jsonapi-resources, but there are 22 failures if this is left out. In included_resources_spec, the failures look approximately like this: "Param not allowed", "detail" : "id is not allowed." I can search for more details if you like!

Copy link
Member

Choose a reason for hiding this comment

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

Hmm I wonder if it has got to with @thibaudgg's PR #11 and the changes done to the dummy app ArticleResource in https://github.com/venuu/jsonapi-authorization/pull/11/files#diff-cc66704bb355f2769ceb1ed76d5f02b4

I'm not really sure how the self.verify_key and id= methods work, and why exactly they were necessary to have. @thibaudgg do you have any insight to this?

Copy link
Contributor

Choose a reason for hiding this comment

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

Hi, I'll have a look next week and give a try to this branch with our app.
Thanks @handlers for all the work on that one! 👍

Copy link
Member

@valscion valscion Mar 24, 2017

Choose a reason for hiding this comment

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

Thanks @thibaudgg! Let us know if you hit roadblocks.

These might be relevant to your testing, if you haven't updated in a while: #53 #40

Copy link
Member

Choose a reason for hiding this comment

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

I'm waiting on @thibaudgg to be able to test this and confirm it works nicely :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🆒 thanks!

Copy link
Contributor

Choose a reason for hiding this comment

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

I launched our test suite on our CI with jsonapi-resource 0.9 and this PR and we get a bunch of errors (unexpected 406 - Not Acceptable responses). It's hard to tell right now if it's because of upgrading to JR 0.9 or this PR specifically.
Sadly I didn't have much time to investigate more this week. I'll try to block an hour next week.

Copy link
Member

Choose a reason for hiding this comment

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

Huh, that 406 error does indeed seem weird. It might very well be because of JR 0.9, too

Copy link
Member

Choose a reason for hiding this comment

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

Ok turns out that these lines are indeed needed since v0.8.3 of JR as IDs are generated on the client side.

See https://github.com/cerebris/jsonapi-resources/releases/tag/v0.8.3

source_record,
related_record,
params[:relationship_type]
)
Copy link
Member

Choose a reason for hiding this comment

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

Instead of looping through all the related_records, we might want to instead change the API of remove_to_many_relationship to take an array of related_records (instead of one at a time).

That way we wouldn't even have to do the if related_resource.empty? check you're doing before calling the authorizer right now, too.

There was a comment #40 (comment) that maybe related to this not being possible before 0.9 version of JR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This makes sense to me. I will try!

@valscion valscion mentioned this pull request Mar 20, 2017
12 tasks
@valscion
Copy link
Member

You'll want to change the .travis.yml to configure Travis to use 0.9.0 version of JR when running tests

set_callback :create_to_many_relationships, :before, :authorize_create_to_many_relationships
set_callback :replace_to_many_relationships, :before, :authorize_replace_to_many_relationships
set_callback :remove_to_many_relationships, :before, :authorize_remove_to_many_relationships
set_callback :replace_polymorphic_to_one_relationship, :before, :authorize_replace_polymorphic_to_one_relationship
Copy link
Member

Choose a reason for hiding this comment

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

This line is too long and will cause the linter to fail the build. Sorry about that 😥

Maybe write it like like this?

set_callback(
  :replace_polymorphic_to_one_relationship,
  :before,
  :authorize_replace_polymorphic_to_one_relationship
)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

@plantfansam
Copy link
Contributor Author

I've updated the PR to send records to the authorizer as a batch (7280547) instead of iterating over them individually. Travis is also now running successfully :). There is still an open thread related to an update required to make the dummy app's ArticleResource work with existing threads that we should resolve.


# find_by_keys doesn't raise if no records, so raise explicitly here
if related_resources.empty?
fail JSONAPI::Exceptions::RecordNotFound, params[:associated_keys]
Copy link
Member

Choose a reason for hiding this comment

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

Do we need this check here? What specs fail if we'd remove this part of the code?

Maybe those tests that would fail have incorrect behaviour which we'd like to change. I don't know, but this seems a bit out of place here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question! If this is removed, one spec failure occurs: when "authorized for remove_to_many_relationship" and "limited by policy scope on comments" it { is_expected.to be_not_found }. The spec expects a 404 if the comments scope (CommentPolicy::Scope) is Comment.none.

With the new code, it instead tries to run remove_from_comments?, which raises NotImplementedError.

  1) Relationship operations DELETE /articles/:id/relationships/comments authorized for remove_to_many_relationship limited by policy scope on comments
     Failure/Error: raise NotImplementedError

     NotImplementedError:
       NotImplementedError
     # ./spec/dummy/app/policies/article_policy.rb:44:in `remove_from_comments?'
     # ./lib/jsonapi/authorization/default_pundit_authorizer.rb:238:in `public_send'
     # ./lib/jsonapi/authorization/default_pundit_authorizer.rb:238:in `authorize_relationship_operation'
     # ./lib/jsonapi/authorization/default_pundit_authorizer.rb:183:in `remove_to_many_relationship'
     # ./lib/jsonapi/authorization/authorizing_processor.rb:201:in `authorize_remove_to_many_relationships'
     # ./spec/requests/relationship_operations_spec.rb:275:in `block (3 levels) in <top (required)>'
     # ./spec/requests/relationship_operations_spec.rb:302:in `block (5 levels) in <top (required)>'

I included the fail to preserve the old 404 behavior. Perhaps we could move that fail to authorize_relationship_operation?

Copy link
Member

Choose a reason for hiding this comment

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

Hmm I guess we'd really want to test that the DELETE operation authorizes properly, and doesn't use 404 when the comments can't be found.

Can you modify the tests to expect that remove_to_many_relationship authorizer method will be called with the proper arguments?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure! I have updated the tests! b4b02f5 d2e3a57

Thanks so much for the 👀!

last_response
end

it { is_expected.not_to be_not_found }
Copy link
Member

Choose a reason for hiding this comment

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

Can we write this spec as either

it { is_expected.to be_successful }

or

it { is_expected.to be_forbidden }

like we have done with all the other specs?

If this feels a bit over your head, I could also tackle these at some point in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No problem! I updated here 3731696. I am glad to keep revising the PR, but if it's easier for you to finish it, that's fine too!

Copy link
Member

@valscion valscion left a comment

Choose a reason for hiding this comment

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

Hi! I left final review comments for the specs. If these can be addressed, we could merge this PR and fix issues that crop up with JR 0.9 in a future PR ☺️

before do
allow_operation('remove_to_many_relationship', article, kind_of(Comment), :comments)
allow_any_instance_of(ArticlePolicy).to receive(
:remove_from_comments?).and_return(true)
Copy link
Member

Choose a reason for hiding this comment

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

It would be great if we didn't have to worry about policies in here at all, and could create this spec in terms of only the operations stubbing instead.

Would it be possible to restructure this spec to look something like this?

context 'limited by policy scope on comments' do
  let(:comments_scope) { Comment.none }
  before do
    allow_operation('remove_to_many_relationship', article, [], :comments)
  end

  it { is_expected.to be_succesful }
end

This would also ensure that the remove_to_many_relationship is indeed called with an empty array instead of any comments.

[article, comments_to_remove.first, :comments],
[article, comments_to_remove.second, :comments]
])
allow_operation('remove_to_many_relationship', article, kind_of(Array), "comments")
Copy link
Member

Choose a reason for hiding this comment

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

Could we restructure this to test for the actual comments instead of just kind_of(Array)? The kind_of(Array) might be a bit too vague spec and could cause bugs to slip through as it doesn't care about the type of the objects inside the array itself.

Would something like this work?

allow_operation(
  'remove_to_many_relationship',
  article,
  [comments_to_remove.first, comments_to_remove.second],
  :comments
)

That would be the best. If the specs fail because of this due to some matchers not working correctly, we should fix the matchers instead of trying to work around them by using less specific assertions.

Also, as you can see, let's try to use the symbol version for the relationship name in the assertions (:comments instead of "comments") or otherwise it might result in an inconsistent API with the other authorizers)

context 'unauthorized for remove_to_many_relationship' do
before do
disallow_operation('remove_to_many_relationship', article, kind_of(Comment), :comments)
disallow_operation('remove_to_many_relationship', article, kind_of(Array), "comments")
Copy link
Member

Choose a reason for hiding this comment

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

Same thing as in https://github.com/venuu/jsonapi-authorization/pull/52/files#r109101102, just that the assertion should be disallow_operation instead of allow_operation

context 'limited by policy scope on articles' do
before do
allow_operation('remove_to_many_relationship', article, kind_of(Comment), :comments)
allow_operation('remove_to_many_relationship', article, kind_of(Array), :comments)
Copy link
Member

Choose a reason for hiding this comment

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


allow(AUTHORIZER_CLASS).to receive(:new).with(Hash).and_return(authorizer)
authorizer
end
Copy link
Member

Choose a reason for hiding this comment

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

We hopefully don't need to add this new method here, if we can satisfy the spec in https://github.com/venuu/jsonapi-authorization/pull/52/files#r109100787 with the existing matchers

@plantfansam
Copy link
Contributor Author

Thanks so much @valscion! I am going to try to update the PR today or tomorrow.

@thibaudgg
Copy link
Contributor

@valscion I was able to have all our specs passed with that PR, so it looks good so far for us. 👍

@handlers could you please rebase it on master? I would like to test it more now that #51 has been merged. Thanks!

@valscion
Copy link
Member

valscion commented Apr 11, 2017

Thank you so much @thibaudgg for testing this out, really appreciate it!

We actually just moments ago stumbled upon an issue in JR (it was this one if you're interested) and as it has been fixed in 0.9.0 we'll want to push this PR through if not today, then tomorrow.

could you please rebase it on master?

We'd actually prefer merge commits over rebasing. We like to keep the history of the conversation in place.

@valscion
Copy link
Member

I'll tackle this tomorrow (which in our timezone would be around 16 hours from now).

So if you feel like addressing the tests before that, @handlers, feel free to, but if you don't have the time I'll happily plow through them as well and get this PR merged ☺️

Thank you so much for your efforts here! 💞

@plantfansam
Copy link
Contributor Author

plantfansam commented Apr 11, 2017 via email

Copy link
Member

@valscion valscion left a comment

Choose a reason for hiding this comment

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

🎉 Thank you!

@valscion valscion merged commit 100d6eb into venuu:master Apr 12, 2017
@thibaudgg
Copy link
Contributor

👏

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants