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

Scopes passed to #find not honored for #update_attributes or #destroy #74

Closed
elDub opened this issue Jan 24, 2019 · 5 comments
Closed

Comments

@elDub
Copy link

elDub commented Jan 24, 2019

The passed in scope as the second argument to Graphiti::Resource#find works for the actual render of a resource, however is not used/honored for the #update_attributes or #destroy methods.

@richmolj
Copy link
Contributor

The important thing here is persistence operations should account for sideposting, ie not having an override in the endpoint. If you need this override logic, you probably need it in the Resource itself to account for this, which makes me think #base_scope.

Or am I wrong? What's the scenario where you'd want an override for an endpoint, but not sideposts?

Alternatively, maybe this should be #base_scope_for_writes as opposed to #base_scope?

@elDub
Copy link
Author

elDub commented Mar 30, 2019

Show sequence

Controller

  def show
    contact = MMApi::ContactResource.find(params, current_mm_tenant.contacts)
    respond_to do |format|
      format.jsonapi { render jsonapi: contact }
    end
  end

Database Hits

  MMContact Load (0.3ms)  SELECT  `mm_contacts`.* FROM `mm_contacts` WHERE `mm_contacts`.`tenant_id` = 1 AND `mm_contacts`.`id` = 150 LIMIT 20 OFFSET 0

Destroy sequence

Controller

  def destroy
    contact = MMApi::ContactResource.find(params, current_mm_tenant.contacts)
    if contact.destroy
      render jsonapi: { meta: {} }, status: :ok
    else
      render jsonapi_errors: contact
    end
  end

Database Hits

  MMContact Load (0.2ms)  SELECT  `mm_contacts`.* FROM `mm_contacts` WHERE `mm_contacts`.`id` = 254 LIMIT 20 OFFSET 0
  MMContact Destroy (0.3ms)  DELETE FROM `mm_contacts` WHERE `mm_contacts`.`id` = 254

Note that for the destroy it never verified that the record belonged to the tenant in the query.

I'm trying to avoid writing controller-specific code in the resources like this:

  def base_scope
    resource_scope || model
  end

  def resource_scope
    case context
    when MMApi::ContactsController
      current_mm_tenant.contacts
    end
  end

Perhaps I'm doing something wrong in the context of an API?

@richmolj
Copy link
Contributor

Note that for the destroy it never verified that the record belonged to the tenant in the query.

Wouldn't you want this logic to apply for every action, and in that case use #base_scope?

And if not, would code like this work?

before_save do |model|
  unless context.current_user.can_update?(model) # or whatever
    raise 'not allowed'
  end
end

@elDub
Copy link
Author

elDub commented Mar 30, 2019

You are absolutely correct. It has just taken me too long to understand what you were suggesting and how to code it, but I have come up with the following changes to my resource objects that actually appears to do what I was hoping.

class AssetResource < MMApiResource
  before_attributes do |attrs|
    attrs[:tenant_id] = current_tenant.id
  end

  before_attributes only: :create do |attrs|
    attrs[:added_by_id] = current_user.id
  end

  def base_scope
    current_tenant.assets
  end

  # ...
end

Up until now I was concerned about the base_scope getting applied when this resource was not the root resource being worked with, but it seems to work no matter what.

I believe this code will handle automatically applying the tenant data for new records as well as ensuring that reads and deletes are properly filtered as well. Does this code need the before_save code you proposed, or is that only because I resisted your other methods of handling this for so long?

@richmolj
Copy link
Contributor

Cool, glad we figured it out 🎉 ! You're right - they key here is #base_scope, which applies to all finders including the ones for update/destroy. If the asset belongs to someone else, you'd get an error and 404 response.

The before_save code isn't needed for your specific case I don't think, but more for a general case where maybe only admins are allowed to add new assets. Of course, make sure to test your scenarios regardless.

There might eventually be some work to do in this area, but looks like the original issue is solved. Thanks for your patience @elDub !

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