nathanl / authority

*CURRENTLY UNMAINTAINED*. Authority helps you authorize actions in your Rails app. It's ORM-neutral and has very little fancy syntax; just group your models under one or more Authorizer classes and write plain Ruby methods on them.
MIT License
1.21k stars 67 forks source link

Overriding class method? #48

Closed johnrees closed 11 years ago

johnrees commented 11 years ago

Apologies if I'm missing something really obvious here.

I want any user to be able to visit the new page for an Event, but only be able to create the Event if they own the chosen Event's Venue. I thought this might work

in view

= link_to "Add Event", new_event_path if current_user.can_create? Event

in authorizer

def self.creatable_by?(user)
  true
end

def creatable_by?(user)
  resource.venue.owner == user
end

but I think the class method is always taking precedence and the controller is ignoring the def creatable_by condition. So right now the user can create an event for any venue.

nathanl commented 11 years ago

Don't worry, I'm happy to help. :)

I think what you're missing is the concept of the "controller action map". This has default global settings and can be overridden per-controller as well.

Your controller has before_filters for its actions. By default, it's going to check current_user.can_create?(Event) before both new and create. If you want it to check a different method before new, you might do authority_actions new: :initialize in that controller.

Then it will ask current_user.can_initialize?(Event), which will be a NoMethodError. To fix that, you'll have to update config.abilities to include initialize => :initializable and define def self.initializable on your controller.

So that's how you do it.

In my opinion, it's confusing for a user to be able to see the new form but not be able to submit it; I've had a similar problem in an app where show/edit were not distinct, so some users could see the edit form but weren't allowed to update. That's why Authority groups new/create and edit/update together by default. But of course you have to decide what makes sense in your app.

johnrees commented 11 years ago

Ah I think I get it now thanks. I'm just in a cancan mindset still, it'd have been something along the lines of

can :create, Event do |event|
  event.venue.owner == user
end

As in every 'honest' user will be able to view and submit the form, I just wanted an layer of security so that if anyone did something mischievous with the venue_id value (ie try to add the event to a venue that they don't own) Authority would step in and prevent it. I suppose scoping everything to current_user is the answer in this case?

nathanl commented 11 years ago

Yeah, if your workflow is 1. user looks through their own venues, 2. selects a venue, 3. creates an event for that venue, then starting with current_user.venues should keep normal users from getting into trouble.

Then you can leave your authorizer as it was, because if they're trying to hit the new page for someone else's venue, clearly they're being mischievous.

nathanl commented 11 years ago

Closing this issue for now - feel free to reopen if you want to discuss further.