Skip to content

Authorize included resources#10

Merged
valscion merged 34 commits intomasterfrom
authorize-included-resources
Mar 8, 2016
Merged

Authorize included resources#10
valscion merged 34 commits intomasterfrom
authorize-included-resources

Conversation

@valscion
Copy link
Copy Markdown
Member

@valscion valscion commented Feb 6, 2016

Closes #7

Logics to implement:

  • One-level deep has_many relationship (?include=other-resources) logic
  • One-level deep has_one relationship (?include=another-resource) logic
  • Multiple one-level relationships (?include=other-resources,another-resource) logic
  • Parse include directive graphs and authorize all relationships in the way (?include=author.comments)
  • Requests that have failed before authorization (e.g. due to validation errors) must not try to authorize include directives, too.
  • Add the source relation alongside the authorization checks

Attach those logics to all the end points which return records:

  • GET /resources
  • GET /resources/1
  • PATCH /resources/1
  • POST /resources
  • GET /resources/other-resources
  • GET /resources/another-resource

Review notes:

  • Improve variable names in the operations processor recursive authorize method
  • Swap the authorizer method's argument order for consistency with other methods
  • Don't use public_send on the model level as it bypasses restrictions imposed on the resource level
  • Simplify specs to just expect receiving an_instance_of(Article) style logic everywhere. It's not worth it to test for exact records, as we aren't able to do that on POST requests anyway.

@valscion valscion added the wip label Feb 6, 2016
@thibaudgg
Copy link
Copy Markdown
Contributor

POST /resources This seems to be impossible. It can be done later if we figure it out.

Why would it be impossible? We actually have that case where we want to create an object and get related resources in the response. Something like: POST /articles?include=author.articles (create an article and get all existing articles from the same author). Why would it be an issue? We have the author from relationship data of the POST request and we can check index? on the Article class easily.

Thanks for working on this!

@valscion
Copy link
Copy Markdown
Member Author

valscion commented Feb 6, 2016

Why would it be impossible?

The record itself isn't created by by the time the operation processor runs for create_action, so it gets quite difficult to try to get the related models underneath before the root object is built.

The policy scoping will be applied still for the has_many resources, but this PR doesn't yet support stopping the entire POST request if the user wouldn't be authorized to include the author.articles records.

We can always tackle that issue on a follow-up PR. This risks getting too large and difficult to review.

@valscion
Copy link
Copy Markdown
Member Author

valscion commented Feb 6, 2016

and we can check index? on the Article class easily.

Yes, but we'd first have to go through the author and use show? on it. Currently this functionality relies on the record being present by the time we go and fetch all the relationships etc. There might be a possibility to still get an index? authorization without having the record available yet, but show? for has_one associations is a no-go.

The way the include directives are parsed is by going up the hash tree available to operations processor via @request.include_directives. It relies on going through all the steps. And it might actually even crash for nils currently if a has_one association is null in a graph scenario 😅 author.comments will crash if author is nil.

@thibaudgg
Copy link
Copy Markdown
Contributor

I think what we discussed in #7 was to authorize all included resource from inside the records_for method and not at the operation level like your current implementation here. Handling POST request would not be an issue if all the logic is handled at the JSONAPI::Resource#records_for`. Don't you think?

@valscion
Copy link
Copy Markdown
Member Author

valscion commented Feb 7, 2016

I think what we discussed in #7 was to authorize all included resource from inside the records_for method

Yeah you're right. I wanted to see what kinds of requests trigger that records_for method. It seems that basic relationship operations also trigger it, and we can't really get enough context on the resource level #records_for on what kind of request is incoming to call only the correct authorizer function.

I think that my approach here, which relies on before hooks is a bit flawed. As inclusion of related resources happens after the operation is complete, maybe I should also just authorize the inclusion in after callbacks. This way I might even have the resource available somehow in the operation level instance variables, and it would work for POST requests, too.

I'd like to keep the authorizing logic inside the operations processor if at all possible, and if we do get proper insight to requests on resource level callbacks and methods, we might want to look into moving every authorization inside it.

@thibaudgg
Copy link
Copy Markdown
Contributor

Ok it makes sense to keep all the authorization logic inside the operations processor, in that case the after callback could be the way to go. 👍

We have to use complex before/after tests for checking authorizing calls, as
the resource in question might not be available until the request has been made
thibaudgg added a commit to electric-feel/jsonapi-authorization that referenced this pull request Feb 8, 2016
thibaudgg added a commit to electric-feel/jsonapi-authorization that referenced this pull request Feb 9, 2016
This can be used to test that authorization works even when there are
validation errors
This allows the Authorizer to be fine-tuned to check for different policies
based on the source of the relation.
@valscion
Copy link
Copy Markdown
Member Author

valscion commented Feb 9, 2016

Ok the functionality seems to be complete now, alongside with passing the relationship source record to the authorizer. Phew. The specs are a bit messy right now, but at least they should be complete.

Array.wrap(
source_record.public_send(
relationship.relation_name(@operation.options[:context])
)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This public_send straight to the record will short-circuit policy scoping on the resource level. And that's bad.

valscion and others added 3 commits February 10, 2016 10:06
The specs are so involved that it's difficult enough to follow along
with what happens. The benefit from very exact before-after stub
checks becomes questionable.
thibaudgg added a commit to electric-feel/jsonapi-authorization that referenced this pull request Feb 10, 2016
@thibaudgg
Copy link
Copy Markdown
Contributor

I have tested this branch on merged with #11 and it works fine with that fix, all our 336 API specs are green 💚.

@lime
Copy link
Copy Markdown
Contributor

lime commented Feb 10, 2016

Great stuff, both of you! 👍

There is most definitely still work that could be done here, Currently authorize_include_item is such a beast that it's bound to come back and bite us. 😬 Still, it might be best to get this merged and keep coding on top of it, so as to lessen the risk of conflicts for other PRs.

@valscion
Copy link
Copy Markdown
Member Author

Fixed the merge conflicts here after #11 was merged :)

@thibaudgg
Copy link
Copy Markdown
Contributor

Just tested that branch and everything still green for us. I'll use that one until it's merged.
Thanks a lot for your great work here. 🎉

@valscion
Copy link
Copy Markdown
Member Author

valscion commented Mar 8, 2016

We discussed this with @lime today. Sorry about the delay, I'll merge this now and release a new version soon after — I'll put up an issue about the public_send with relationships. I could need a little help with that, and it'll be much easier to do when the amount of code that needs to be changed is much smaller than this PR.

valscion added a commit that referenced this pull request Mar 8, 2016
@valscion valscion merged commit a81a2be into master Mar 8, 2016
@valscion valscion deleted the authorize-included-resources branch March 8, 2016 09:59
@valscion
Copy link
Copy Markdown
Member Author

valscion commented Mar 8, 2016

v0.6.0 pushed out :) feel free to use that one @thibaudgg, it should contain all the necessary bits :)

@valscion
Copy link
Copy Markdown
Member Author

valscion commented Mar 8, 2016

I'll put up an issue about the public_send with relationships. I could need a little help with that, and it'll be much easier to do when the amount of code that needs to be changed is much smaller than this PR.

Actually, I think that currently we are doing the right thing when fetching the relationships directly from the record instead of going through resource classes. If we hit a case for trying to sideload a record we're not allowed to see with include directive, we should authorize for them even if we'd get a 404 when we'd try to show that resource via a normal show operation.

@thibaudgg
Copy link
Copy Markdown
Contributor

👍 using 0.6 now! Thanks!

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