Git Product home page Git Product logo

authority's People

Contributors

adamhunter avatar awinograd avatar binarypaladin avatar div avatar dteoh avatar emsk avatar erikaxel avatar forced-request avatar humanshell avatar ivor avatar kevmoo avatar mguymon avatar mp211 avatar nathanl avatar paukul avatar sanemat avatar scottmartin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

authority's Issues

Deployment issue with Capistrano

This is a cross-post from me at stackoverflow.

http://stackoverflow.com/questions/19238540/error-when-deploying-the-authority-gem-with-capistrano

If a get a response from there I will post it back here.

I'm deploying a rails4 app from a dev machine to a server. This deployment has been working smoothly.

I have added authorisation to my application using the authority gem. When I run ./bin/cap deploy I now get the error in the unicorn log that is listed below.

All of the files created with rails g authority:install on the dev machine have been committed to git and are being deployed to the server using ./bin/cap deploy.

Any help appreciated.

I, [2013-10-08T14:06:42.579240 #2021]  INFO -- : worker=0 spawning...
I, [2013-10-08T14:06:42.583257 #3594]  INFO -- : worker=0 spawned pid=3594
I, [2013-10-08T14:06:42.583904 #3594]  INFO -- : Refreshing Gem list
E, [2013-10-08T14:06:42.743225 #3588] ERROR -- : uninitialized constant Authority (NameError)
/home/deployer/apps/app-name/releases/20131008015646/config/initializers/authority.rb:1:in `<top (required)>'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/railties-4.0.0/lib/rails/engine.rb:609:in `block (2 levels) in <class:Engine>'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/railties-4.0.0/lib/rails/engine.rb:608:in `each'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/railties-4.0.0/lib/rails/engine.rb:608:in `block in <class:Engine>'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/railties-4.0.0/lib/rails/initializable.rb:30:in `instance_exec'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/railties-4.0.0/lib/rails/initializable.rb:30:in `run'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/railties-4.0.0/lib/rails/initializable.rb:55:in `block in run_initializers'
/home/deployer/.rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/tsort.rb:150:in `block in tsort_each'
/home/deployer/.rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/tsort.rb:183:in `block (2 levels) in each_strongly_connected_component'
/home/deployer/.rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/tsort.rb:210:in `block (2 levels) in each_strongly_connected_component_from'
/home/deployer/.rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/tsort.rb:219:in `each_strongly_connected_component_from'
/home/deployer/.rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/tsort.rb:209:in `block in each_strongly_connected_component_from'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/railties-4.0.0/lib/rails/initializable.rb:44:in `each'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/railties-4.0.0/lib/rails/initializable.rb:44:in `tsort_each_child'
/home/deployer/.rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/tsort.rb:203:in `each_strongly_connected_component_from'
/home/deployer/.rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/tsort.rb:182:in `block in each_strongly_connected_component'
/home/deployer/.rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/tsort.rb:180:in `each'
/home/deployer/.rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/tsort.rb:180:in `each_strongly_connected_component'
/home/deployer/.rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/tsort.rb:148:in `tsort_each'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/railties-4.0.0/lib/rails/initializable.rb:54:in `run_initializers'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/railties-4.0.0/lib/rails/application.rb:215:in `initialize!'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/railties-4.0.0/lib/rails/railtie/configurable.rb:30:in `method_missing'
/home/deployer/apps/app-name/releases/20131008015646/config/environment.rb:5:in `<top (required)>'
config.ru:4:in `require'
config.ru:4:in `block in <main>'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/rack-1.5.2/lib/rack/builder.rb:55:in `instance_eval'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/rack-1.5.2/lib/rack/builder.rb:55:in `initialize'
config.ru:1:in `new'
config.ru:1:in `<main>'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/unicorn-4.6.3/lib/unicorn.rb:48:in `eval'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/unicorn-4.6.3/lib/unicorn.rb:48:in `block in builder'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/unicorn-4.6.3/lib/unicorn/http_server.rb:722:in `call'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/unicorn-4.6.3/lib/unicorn/http_server.rb:722:in `build_app!'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/unicorn-4.6.3/lib/unicorn/http_server.rb:595:in `init_worker_process'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/unicorn-4.6.3/lib/unicorn/http_server.rb:615:in `worker_loop'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/unicorn-4.6.3/lib/unicorn/http_server.rb:500:in `spawn_missing_workers'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/unicorn-4.6.3/lib/unicorn/http_server.rb:511:in `maintain_worker_count'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/unicorn-4.6.3/lib/unicorn/http_server.rb:277:in `join'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/gems/unicorn-4.6.3/bin/unicorn:126:in `<top (required)>'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/bin/unicorn:23:in `load'
/home/deployer/apps/app-name/shared/bundle/ruby/2.0.0/bin/unicorn:23:in `<main>'
E, [2013-10-08T14:06:42.758457 #2021] ERROR -- : reaped #<Process::Status: pid 3588 exit 1> worker=1

Authorizers don't automatically reload in development

When I edit the code in an authorizer it isn't picked up by the application (in development) until i've restarted the app. Is there a way to make the authorizers automatically reload with each request in development like models/controllers/views/assets do?

Using multiple current_user models

Hi there,

I'm using Devise for my authentication. It allows me to have multiple 'user' models. I have an Admin model and a User model. I have successfully wired up Authority with my backend Admin controllers and views. The Authority config has current_user as current_admin - as per Devise instructions.

However, I now need to authorize my Users on some front end controllers (viewing their orders, updating their contact details etc) but don't seem to have a way to instruct Authority to now use current_user instead of current_admin.

Is there a way to configure Authority to use current_admin when working in a certain namespace (i.e. /admin/admin_controller) but use current_user when working in the front end? (i.e. orders/index would only allow the current_user to view their own orders).

Thanks in advance for any suggestions.

Shaun

Can I map controller actions to multiple verbs?

I like the default controller_action_map configuration, however I was wondering if it was also possible to create an additional verb, say "manage" and map "manage" to all the controller actions.

In short, I would like to keep the granularity of your default controller_action_map, but also have a nice shortcut way to group actions together while still keeping granularity of the individual CRUD maps.

Error messages when the 'User' is nil in OmniAuth

An Application using Omniauth will either have the current_user method set to the user record, or nil (no one logged in)

While accessing am Authority protected resource, it will return the error

undefined_method 'can_read?' for nil:nilClass

Is there a way to say if the current_user (or the user method specified) returns nil, the methods as a default should just return false, rather than erroring ?

Models could assume an authorizer based on name, if it exists

A possible feature for consideration: models currently start out assuming that their authorizer is ApplicationAuthorizer. The Comment model doesn't automatically look for a CommentAuthorizer because it often makes sense to group multiple models under the same authorizer.

However, wouldn't it be sensible to at least check for the existence of an authorizer with a corresponding name? It's unlikely that you'd create a Comment model and a CommentAuthorizer and not want to hook them together.

If Comment hasn't been told what authorizer to use, should it look for CommentAuthorizer first, then failing that, assume ApplicationAuthorizer?

Views of controllers that don't have a resource and passing options

Views of controllers that don't have a resource and passing options

At the wiki, we can see "Using authority on controllers that don't have a resource". But how about the view?

Simple example: I whould like to have a link, on a view, only if the user has rights to it.

Models:
User,Course,Role (from rolify)
Controllers: Course and Teacher

I have a controller where only the course's owner can manage the course's teachers.
I don't have a Teacher Model. A teacher is a teacher on a Course if it has a role teacher on that course.

Since I don't have a Model, I'm using the example at "Using authority on controllers that don't have a resource".

It that example, how can I create a link, on the view, only if the user has rights to acess it?

class TeachersController < ApplicationController
before_filter :authenticate_user!
before_filter :load_course
authorize_actions_for TeacherAuthorizer, :for => @Course

So far so good, but how can I use it on the view?

= link_to 'Manage Teachers', course_teachers_path(@Course)
if current_user.can_create?(WHAT_HERE?)

I could use the User model, since the Teacher is a User, but it would require to set User.authorizer_name = TeacherAuthorizer.
But that doesn't fell right.

An other approach is to try to call the TeacherAuthorizer, in the view:

= link_to 'Manage Teachers', course_teachers_path(@Course)
if TeacherAuthorizer.creatable_by?(current_user, :for=>@Course)

But I would get a "wrong number of arguments (2 for 1)".

I think, if we could, somehow say at TeacherAuthorizer:
class TeacherAuthorizer < Authority::Authorizer
def self.auth_symbol = :teacher
Or gets from the Authorizer name of the class

And then, if we could:

= link_to 'Manage Teachers', course_teachers_path(@Course)
if current_user.can_create?(:teacher, :for=>@Course)

Thoughts on Authority and Rails 4 view caching?

Besides helping you declare permissions logic and enforce it, Authority facilities the familiar style of checking permissions in views: show a delete link if current_user.can_delete?(@widget).

In DHH's Railsconf keynote, he talked about how view caching runs into issues when different users need to see different things, as in our example here. He gives a quick example of a link with "data-visible-to" => 'admin creator' and says that Javascript can be used to "decorate" the page and show/hide those links as needed.

My question is: what, if anything, should Authority do to support this new approach in Rails 4?

Whatever we do, I also want to continue making Authority more usable outside of Rails, as @christhekeele has been helping with.

add can_*? methods to nil/not-logged-in-user class

If we added can_<action>? methods (returning 'false' by default) to the nil class, that would allow current_user.can_<action>? even when user is not logged in. We could then dispense with long-winded checks like user_signed_in? && current_user.can_read?(resource)

I'm new to ruby so I'm not sure whether it's good practice to add methods to the nil class.

We could also add a configuration parameter to set the class which represents a non-logged-in user, in case it's not nil.

If I'm somehow using authority incorrectly and there is already an elegant way to avoid this, please let me know!

If this issue is approved I'm happy to fork, implement and send a pull request.

authorize_action_for: wrong number of arguments

I'm having some issues implementing Authority for my controller actions. It seems that Ruby chokes somewhere in the method_missing call stack. I am using Ruby 2.0, could it be related to this?

Here's the stack trace:

ArgumentError - wrong number of arguments (2 for 1):
   ~/.rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/forwardable.rb:171:in `readable_by?'
   ~/.rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/forwardable.rb:171:in `readable_by?'
   (gem) authority-2.5.0/lib/authority/user_abilities.rb:15:in `can_read?'
   (gem) authority-2.5.0/lib/authority.rb:43:in `action_authorized?'
   (gem) authority-2.5.0/lib/authority.rb:35:in `enforce'
   (gem) authority-2.5.0/lib/authority/controller.rb:76:in `authorize_action_for'
   app/controllers/accounts_controller.rb:7:in `call'

It all started with resource not being available in AccountAuthorizer#readable_by?, so I tried setting some options, this broke things:

authorize_action_for(accounts.first, :testing => accounts)

Authorizing a given action

Hi,

I'm using Authority for a complex project and found that sometimes it's authorisation model is too restrictive. "authorize_action_for" doesn't work if I want to authorize manually rather than rely on action name.

For example, authorising a user to edit it's profile. I'd have to create authorizers (or a shared authorizer) for children resources. IMO it's easier and as DRY to check if user can edit user object and be done with it. I added a "authorize_for(action, object, *options)" method and it works pretty well for me.

Would you add such a method? I can make a pull request once I prepare tests.

defining authorisations with has_many / belongs_to association

Sorry for all the noob questions and thank ya'll for helping
I have nested models

class Orgunit < ActiveRecord::Base
  has_many :departments, :dependent => destroy
  ...
end

class Department < ActiveRecord::Base
  has_many :groups, :dependent => destroy
  belongs_to :orgunit
  ...
end

class Group < ActiveRecord::Base
  belongs_to :department
  ...
end

I am implementing Authority with instance authorisations.
I use Rolify and grant authority per resource.

class InstanceAuthorizer < ApplicationAuthorizer
  def self.default(able, user)
    false
  end

  def updatable_by?(user)
    (user.has_role_for_resource? :admin, resource) || (user.has_role? :superadmin)
  end

the method has_role_for_resource? is defined in the user model:

def has_role_for_resource?(role_name, resource = nil)
  self.roles.detect { |r| r.name == role_name.to_s && (r.resource == resource) }.present?
end

I would like to give a role for a top resource (Orgunit) and that the user will be have authorisation for all it's child resources.
Is there a quick and easy way to accomplish this?

authorize controller without resources ?

Is it possible to authorize a controlleur without a resource ?

For example, I have a PrivateController which has in index the summary table of all the administrative resources. I would like to allow administrators and managers to access this page.

the authorizer is easy :

class PrivateAuthorizer < ApplicationAuthorizer
  def self.readable_by?(user)
    user.has_any_role?(:admin, :manager)
  end
end

What should I put in the controller to use this authorizer. As I do not realy have model resources associated with my controller, I do not know what I have to put in authorize_action_for.

Memoizing calls to Authorizer methods

The logic in my Authorizer methods has gotten pretty complex. It checks for various roles, permission levels, etc. and also relies on other Authorizer methods. To give a simple example:

def readable_by?(user)
  #complex logic, or
  updateable_by?(user) || manageable_by?(user)
end

def updateable_by?(user)
  # More complex logic, or
  user.has_role?(:editor, resource) || manageable_by?(user)
end

def manageable_by?(user)
  user.has_role?(:admin, resource)
end

In practice, I've found this to be very slow. For example, when calling readable_by?, first some complex logic happens, then it calls updateable_by? and does more complex logic. If this fails, it calls manageable_by?, which checks for an :admin role. If this fails, it rolls back up the call stack to readable_by? and calls manageable_by? again!

My proposal is to memoize (i.e. cache) calls to Authorizer methods during the lifetime of a single request. My assumptions are:

  1. Permissions are usually only being checked for current_user, or at least for the same user over and over again.
  2. A user's permissions on a resource will usually not change during the lifetime of a single request. If they do change, and we care, the cache can be cleared.

The design in my head involves a Authority::Memoization module to implement the memoization (perhaps using a Thread local variable for caching?), an API to clear the cache, and a middleware or controller after filter to clear the cache after each request. The memoization would be opt-in via a method call somewhere.

I'd be willing to try writing this.

Thoughts?

Authority DSL

What do you think about packaging Authority with a simple little DSL for making long chains of conditionals in Authorizer methods more readable?

As my authorizers have grown more complex, I realized I could re-use the mini DSL from my Strong Parameters Sanitizer classes.

You can find the writeup here.

How to handle index actions

Hi

How do you handle index actions with Authority? In my app I have User which can be either 'user' or 'admin'. They belong to a Company.

So, a 'user' can only visit the user#show action if it's their own user record. And an 'admin' can view all users from her company:

  def readable_by?(user)
    if user.role == 'admin'
      user.company == resource.company
    elsif user.role == 'user'
      user == resource
    else
      false
    end
  end

But what about the index action? I want an 'admin' to be able to access the user#index action but not the 'user'. I tried to use this:

  def self.readable_by?(user)
    user.role == 'admin'
  end

And added: "authorize_actions_for User" in the controller
But that doesn't work since it restricts a 'user' to read any user.

Am I supposed to just use authorize_action_for and pass the class instead of an instance? It seems to work.

  def index
    authorize_action_for User
    @users = current_user.company.users.order("name ASC")
  end

But then I'm a bit confused about when to use the "authorize_actions_for User" filter?

Hide warnings in capybara feature specs?

If I have this feature spec spec/features/brands/adding_a_brand.rb

require 'spec_helper'

feature "Adding a brand" do

  scenario "as a visitor" do
    visit new_brand_path
    expect(page.status_code).to eq(403)
  end

end

It will pass, but I will get the following WARN message in my logs

Adding a brand
W, [2013-12-01T20:37:55.842741 #45068]  WARN -- :   is not authorized to create this resource: #<Brand:0x007fc2f7c0d980>
  as a visitor

Is there a way to suppress these warnings?

Can abilities be defined on a per model/authorizer basis?

Controllers get a lot of love for customizing. However, in looking through the docs and the code it appears that the abilities that get added to a model are all or nothing.

What's the best method to handle this? For instance, in an app I'm working on I have some models that fit the very simple CRUD permissions, however there are some other models that have some fairly granular permissions in terms of what specifically can be read and/or updated.

What ends up happening is that while most models need a few delegated methods, ALL models get the full list even though many don't even apply.

At the moment it's not a functional problem because methods that aren't needed can be ignored, but for the sake of clarity it's a bit of an issue. It would be nice if I could define abilities on the authorizer itself or if, during inclusion it all of the "is_*_by" methods could automatically forwarded.

namespaces

Hi,
is it possible to use authority in app with some namespaces? For example - i have 3 namespaces:
/posts
/admin/posts
/moderator/posts

and i'd like to separate authorisers for different namespaces? How to do it?

Getting Started

OK so I am being thick but I cant get this to work and hopefully you can point me in the right direction. I just keep getting the error below when going to path listings/new

undefined method `can_create?' for nil:NilClass
User Model
class User < ActiveRecord::Base
  include Authority::UserAbilities
  ...
Listings Controller
class ListingsController < ApplicationController
  authorize_actions_for Listing
  ....
Listing Model
class Listing < ActiveRecord::Base
  include Authority::Abilities
  ....
ListingAuthorizer
class ListingAuthorizer < ApplicationAuthorizer
  def creatable_by?(user)
    true
  end

  def self.creatable_by?(user)
    true
  end

end
Authority.rb
Authority.configure do |config|
  config.user_method = :current_user
  config.controller_action_map = {
    :index   => 'read',
    :show    => 'read',
    :new     => 'create',
    :create  => 'create',
    :edit    => 'update',
    :update  => 'update',
    :destroy => 'delete'
  }
  config.abilities =  {
    :create => 'creatable',
    :read   => 'readable',
    :update => 'updatable',
    :delete => 'deletable'
  }
end
ApplicationAuthorizer.rb
class ApplicationAuthorizer < Authority::Authorizer
  def self.default(adjective, user)
    false
  end
end

Hope you can get me on the right path !

Allow for application controller level authorize_actions_for when using authority on controllers that don't have a resource

I've followed the instructions here to use authority on controllers that don't have a resource but I want to do this for every controller in my app except the authentication pages ( I am using devise ).

Right now it seems I have to include an authorize file for every controller and add the authorize_actions_for filter to the top of every controller. To DRY things up it would be nice if I could do something like this in my application controller:

authorize_actions_for ApplicationAuthorizer :unless => :devise_controller?

Trying that at the moment just throws the error:

undefined method `ApplicationAuthorizer' for ApplicationController:Class

Passing options and authorize_action_for

I'm using Authority (like it!) on a project that has some nesting of resources, and have a question about authorizing controller actions. Is there a way to use authorize_action_for in a controller, and pass options with it?

I set up my authorizer classes to handle user.can_update?(Comment, for: @post), and it's working great.

If I try something like this:

authorize_action_for(Comment, for: @post)

I get argument error. Am I holding it wrong? Should I just use can_update? and friends, and raise an exception myself if the user cannot do the thing?

Add scopes for authorized resources

While this will correctly return posts a user is authorized to read:

Post.all.select{ |post| current_user.can_read? post }

...it would obviously be more efficient to let the database do this filtering. Several people have asked about Authority supporting scopes to return only items the user is authorized to access.

I hadn't tried to support that for two reasons: 1) I wanted to remain database and ORM agnostic, and 2) the phrase "authorized to access" is ambiguous; someone may be authorized to read but not edit, so should the item be shown in the index view or not?

However, I think I've changed my mind. The first objection is solved by doing scoping the way we do it in Searchlight, and the second objection can be answered with "the scope can do some minimal filtering, and it's up to the user to decide what."

I slapped my forehead when I realized that the approach from Searchlight was so obviously applicable here, and I realized it only after seeing it being done in Pundit, a library I recently stumbled across and frankly am wondering if it obsoletes Authority because it's so simple and clear. I haven't used it yet, though.

Anyway, I think I'd like to add authorization scopes in a similar fashion.

Authorize any_instance stubbing does not work on RSpec 2.14+

Hello!

in case it helps someone else, I just noticed that the following spec is not passing anymore when using Authority together with RSpec 2.14+:

    it 'verifies authorization' do
      MembershipAuthorizer.any_instance.stub(:deletable_by?).and_return(false)
      expect {
        post :destroy, valid_params
      }.to raise_error(Authority::SecurityViolation)
    end

Rolling back to RSpec 2.13.0 makes the spec pass again.

Note that this is not necessarily an issue with Authority itself, but I thought I would share it here in case, to track that down and share it with others, since it took me a bit of time to track it initially.

Correction to README

# Send 'em back where they came from with a slap on the wrist
def authority_forbidden(exception)
  Authority.logger.warn(error.message)
  redirect_to request.referrer.presence || root_path, :alert => 'You are not authorized to complete that action.'
end

should be...

# Send 'em back where they came from with a slap on the wrist
def authority_forbidden(exception)
  Authority.logger.warn(exception.message)
  redirect_to request.referrer.presence || root_path, :alert => 'You are not authorized to complete that action.'
end

error is a typo.

Custom violation messaging

While I'm in here, I made another tiny change for my own use that may be of worth to others as well. It allows for custom SecurityViolation messaging to be optionally returned by able_by? methods in the authorizers:

class PostAuthorizer < ApplicationAuthorizer
  def readable_by(user)
    if user.has_role?(:reader)
      true
    else
      return false, I18n.t('app.posts.auth.readfail')
    end
  end
end

That sort of thiing. Which would just use the translation string over the SecurityViolation default message when raised. The only thing folks would need to remember is that it requires an explicit return.

Happy to contribute this as well, just say the word.

Using authority with inherited_resources

Authority looks really simple and succinct but I was wondering if you had any ideas on how it could be used with inherited_resources so that it could authorise the resource after it has been loaded. From what I can tell one needs to insert these checks manually which would defeat inherited_resources code minimisation strategy.

I dig that you want this to be as agnostic as possible, but just wondered if you'd come across this use-case already?

Whitelisting controller actions

First of all, great gem. I've recently switched over from CanCan to Authority because of the lack of updates for CanCan, but even more so because of the awesome structure of Authority. So two ๐Ÿ‘ 's up.

Having implemented Authority in my first model, I really like the flow of the authorization, and especially like the unit tests that are possible with this gem (here's a short example):

require 'spec_helper'

describe AccountAuthorizer do
  include_examples 'AuthorizedModel'
  Given(:account) { authorizer_account }

  context 'for account with active membership' do
    context '#updatable_by?' do
      Then { account.authorizer.should be_updatable_by(owner) }
    end

    context '#creatable_by?' do
      Then { account.authorizer.should restrict_all_access_to(:creatable) }
    end
  end

  context 'blocks all access for guests' do
    Then { account.authorizer.should restrict_all_access_for(guest) }
  end

  context 'blocks all access for user of different account' do
    Given(:account) { Account.new }
    Then { account.authorizer.should restrict_all_access_for(owner) }
  end
end

I just started implementing Authority for my controllers, but I couldn't find any mention in the documentation regarding whitelisting (even though it is possible on a model level, with the ApplicationAuthorizer.

Is there a nice and clean way to enable whitelisting? From what I could read, I have to manually add authorize_actions_for for class-level authorization. I would like all access to be restricted by default and open the gates from there.

Overriding class method?

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.

Grouping actions

Hi @nathanl, hope you're having a great day :)

I was wondering if there is any capability to group actions but still allow for fine grained checking of abilities.

For example, CUD actions could be grouped as :manage and R actions could be grouped as :view or something like that. It's arbitrary, but is it possible to do as the current code stands?

In my case I just have :view and :edit as abilities, but in the example of admin resources it'd be nice to have :manage instead (encompassing both :view and :edit) so that I can say manageble_by?. Right now I can just say viewable_by? and know that that implies editable_by? and therefore manageable_by? but it seems less clear.

I'm not sure if I've thought this through well enough though so any ideas are welcome. I'm only in this habit through using Cancan :)

undefined local variable or method `resource' for SchoolAuthorizer:Class

Hi,
I try using authority (great gem and concept).
I have followed the readme.
When I try to check an instance, I get the following error:

undefined local variable or method `resource' for SchoolAuthorizer:Class

My Group model looks like:

class Group < ActiveRecord::Base

  resourcify
  include Authority::Abilities
  self.authorizer_name = 'SchoolAuthorizer'
  ...

My GroupController looks like:

class GroupsController < ApplicationController
  authorize_actions_for Group
  ...
  def show
    @group = Group.find(params[:id])
    authorize_action_for @group
    @users = @group.users.where("first_name ilike :name OR last_name ilike :name", {:name => "%%#{@q}%%"}).paginate(:page => params[:page], :per_page => params[:limit] || Settings.users_per_page)
    @title = @group.name
  end

And my SchoolAuthorizer:

class SchoolAuthorizer < ApplicationAuthorizer
  def self.creatable_by?(user)
    user.has_role?("admin")
  end

  def self.readable_by?(user)
    true
  end

  def self.updatable_by?(user)
    user.has_role?("admin", resource)
  end
end

And I get the error above (undefined local variable or method `resource' for SchoolAuthorizer:Class )
What am I doing wrong?
Thanks for helping

Undefined method 'authorize_actions_for'

I'm getting undefined method 'authorize_actions_for' in the controller when using rails-api. Any ideas what I need to include? Here's my code:

Gemfile:

...
gem 'authority', '~> 2.9.0'
gem 'rails-api', '~> 0.1.0'
...

app/authorizers/session_authorizer.rb:

class SessionAuthorizer < ApplicationAuthorizer
  def deletable_by?(user)
    user.sessions.include?(resource)
  end
end

app/controllers/v1/sessions_controller.rb:

class V1::SessionsController < ApplicationController
  authorize_actions_for Session
  ...
end

Allow multiple `authorize_actions_for`

I'm thinking of doing this in my controller:

class UsersController < ApplicationController
  authorize_actions_for User, only: %w(index create)
  authorize_actions_for :user, except: %w(index create)

  ...

  protected

  def user
    @user ||= User.find(params[:id])
  end
end

Unfortunately, it looks like invoking authorize_actions_for a second time completely overrides the authority_resource to :user.

Right now, to get away with this, this is how I get away with this issue:

class UsersController < ApplicationController
  authorize_actions_for :authority_resource

  ...

  protected

  def authority_resource
   params[:action].in?(%w(index create)) ? User : user
  end

  def user
    @user ||= User.find(params[:id])
  end
end

Authority not redirecting

Hi,

I have an admin_controller that other namespaced controllers inherit from. IN the admin_controller i want to have a base check (which works). current_user.can?(:login_to_admin). Then in the app authorizer i have

def self.authorizes_to_login_to_admin?(user,options = {})
user.has_role?(:admin)
end

Testing on a user without that role works.

1.9.3-p327 :002 > u.can?(:login_to_admin)
(0.1ms) SELECT COUNT(*) FROM roles INNER JOIN users_roles ON roles.id = users_roles.role_id WHERE users_roles.user_id = 2 AND (((roles.name = 'admin') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL)))
=> false

However, the user does not get redirect to a 403 in the app. Is this supposed to happen automatically ?

Allow resource to specify the authorizer by class

Add the ability to define the authorizer by class, such as

self.authorizer_class = GreatAuthorizer

in addition to

self.authorizer_name = "GreateAuithorizer"

This will allow runtime classes to be used as the Authorizer.

For my scenario, we have a authorizer generator that tweaks the authorizer at runtime for each class.

Multi tenant apps against the current_site?

I am having a little problem with getting it all together in a multi tenant app. Passing the site as options is little cumbersome and seems completely impossible.

Has anyone used authority for building the authorization for a multitenant app when its impossible to pass the current site or any params to the authorizer?

Authority and Capybara

I am not sure this is a problem in the gem, but just not sure where to post a question, so hoping you could help me.

I have a Company resource which has
self.authorizer_name = "OperationsAuthorizer"

In the companies controller I have authorize_actions_for(Company)

In my specs, I log in as an operations user, and even check that
expect(Company.creatable_by?(user)).to be_truthy
which passes

But when I submit the form in my feature spec (using capybara), I get an error:
User is not authorized to create this resource: Company
And if I add debugger in my create action, I don't even get to it.
And the path stays on the new view.

I do not get this error when I check the form in the browser.

What am I missing?

Rendering html output when raising exception

Hi,

I've just added Authority to a web services project that I'm working on. I've run into an issue I'm not sure how to resolve given my current experience with ruby and rails.

I've got an app that uses RocketPants as the framework for delivering JSON services. When authorisation fails through authorize_action_for in the controller it renders html with a http status of 500 rather than honouring the json output.

Take a look at https://gist.github.com/jasonelston/4721540 for some more details.

If I overwrite authorize_action_for I obviously get the correct JSON rendered output.

If I overwrite authority_forbidden I get the html rendered error (with the JSON error text).

Obviously there's something that's defaulting to rendering html output when handling the exception but I can't seem to find it. The only reference to render I can find is in the authority_forbidden.

At this point I'm at the ends of my current ability. Any help to solve this would be greatly appreciated.

Thanks,
Jason

nil is not a symbol

@nathanl

When I use the ruby ruby-1.9.3-p327 and authority 2.4.1, I have this code in my controller.

class MembersController < ApplicationController

  authorize_actions_for Admin
...
end

class AdminsController < MembersController
end

And I get the following error when I visit admins index page.

TypeError in AdminsController#index

nil is not a symbol
Rails.root: /home/xinlangzi/workspace/ads

Application Trace | Framework Trace | Full Trace
authority (2.4.1) lib/authority/controller.rb:99:in `authority_resource'
authority (2.4.1) lib/authority/controller.rb:94:in `run_authorization_check'
activesupport (3.2.11) lib/active_support/callbacks.rb:451:in `_run__3818043125594197570__process_action__2898453861387782050__callbacks'
activesupport (3.2.11) lib/active_support/callbacks.rb:405:in `__run_callback'
activesupport (3.2.11) lib/active_support/callbacks.rb:385:in `_run_process_action_callbacks'
activesupport (3.2.11) lib/active_support/callbacks.rb:81:in `run_callbacks'
actionpack (3.2.11) lib/abstract_controller/callbacks.rb:17:in `process_action'
actionpack (3.2.11) lib/action_controller/metal/rescue.rb:29:in `process_action'
actionpack (3.2.11) lib/action_controller/metal/instrumentation.rb:30:in `block in process_action'
activesupport (3.2.11) lib/active_support/notifications.rb:123:in `block in instrument'
activesupport (3.2.11) lib/active_support/notifications/instrumenter.rb:20:in `instrument'
activesupport (3.2.11) lib/active_support/notifications.rb:123:in `instrument'
actionpack (3.2.11) lib/action_controller/metal/instrumentation.rb:29:in `process_action'
actionpack (3.2.11) lib/action_controller/metal/params_wrapper.rb:207:in `process_action'
activerecord (3.2.11) lib/active_record/railties/controller_runtime.rb:18:in `process_action'
actionpack (3.2.11) lib/abstract_controller/base.rb:121:in `process'
actionpack (3.2.11) lib/abstract_controller/rendering.rb:45:in `process'
actionpack (3.2.11) lib/action_controller/metal.rb:203:in `dispatch'
actionpack (3.2.11) lib/action_controller/metal/rack_delegation.rb:14:in `dispatch'
actionpack (3.2.11) lib/action_controller/metal.rb:246:in `block in action'
actionpack (3.2.11) lib/action_dispatch/routing/route_set.rb:73:in `call'
actionpack (3.2.11) lib/action_dispatch/routing/route_set.rb:73:in `dispatch'
actionpack (3.2.11) lib/action_dispatch/routing/route_set.rb:36:in `call'
journey (1.0.4) lib/journey/router.rb:68:in `block in call'
journey (1.0.4) lib/journey/router.rb:56:in `each'
journey (1.0.4) lib/journey/router.rb:56:in `call'
actionpack (3.2.11) lib/action_dispatch/routing/route_set.rb:601:in `call'
warden (1.2.1) lib/warden/manager.rb:35:in `block in call'
warden (1.2.1) lib/warden/manager.rb:34:in `catch'
warden (1.2.1) lib/warden/manager.rb:34:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/best_standards_support.rb:17:in `call'
rack (1.4.5) lib/rack/etag.rb:23:in `call'
rack (1.4.5) lib/rack/conditionalget.rb:25:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/head.rb:14:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/params_parser.rb:21:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/flash.rb:242:in `call'
rack (1.4.5) lib/rack/session/abstract/id.rb:210:in `context'
rack (1.4.5) lib/rack/session/abstract/id.rb:205:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/cookies.rb:341:in `call'
activerecord (3.2.11) lib/active_record/query_cache.rb:64:in `call'
activerecord (3.2.11) lib/active_record/connection_adapters/abstract/connection_pool.rb:479:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/callbacks.rb:28:in `block in call'
activesupport (3.2.11) lib/active_support/callbacks.rb:405:in `_run__1533354125810535700__call__4474434814390027399__callbacks'
activesupport (3.2.11) lib/active_support/callbacks.rb:405:in `__run_callback'
activesupport (3.2.11) lib/active_support/callbacks.rb:385:in `_run_call_callbacks'
activesupport (3.2.11) lib/active_support/callbacks.rb:81:in `run_callbacks'
actionpack (3.2.11) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/reloader.rb:65:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/remote_ip.rb:31:in `call'
airbrake (3.1.8) lib/airbrake/rails/middleware.rb:15:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/debug_exceptions.rb:16:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/show_exceptions.rb:56:in `call'
railties (3.2.11) lib/rails/rack/logger.rb:32:in `call_app'
railties (3.2.11) lib/rails/rack/logger.rb:16:in `block in call'
activesupport (3.2.11) lib/active_support/tagged_logging.rb:22:in `tagged'
railties (3.2.11) lib/rails/rack/logger.rb:16:in `call'
quiet_assets (1.0.1) lib/quiet_assets.rb:20:in `call_with_quiet_assets'
actionpack (3.2.11) lib/action_dispatch/middleware/request_id.rb:22:in `call'
rack (1.4.5) lib/rack/methodoverride.rb:21:in `call'
rack (1.4.5) lib/rack/runtime.rb:17:in `call'
activesupport (3.2.11) lib/active_support/cache/strategy/local_cache.rb:72:in `call'
rack (1.4.5) lib/rack/lock.rb:15:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/static.rb:62:in `call'
airbrake (3.1.8) lib/airbrake/user_informer.rb:16:in `_call'
airbrake (3.1.8) lib/airbrake/user_informer.rb:12:in `call'
railties (3.2.11) lib/rails/engine.rb:479:in `call'
railties (3.2.11) lib/rails/application.rb:223:in `call'
rack (1.4.5) lib/rack/content_length.rb:14:in `call'
railties (3.2.11) lib/rails/rack/log_tailer.rb:17:in `call'
thin (1.5.0) lib/thin/connection.rb:81:in `block in pre_process'
thin (1.5.0) lib/thin/connection.rb:79:in `catch'
thin (1.5.0) lib/thin/connection.rb:79:in `pre_process'
thin (1.5.0) lib/thin/connection.rb:54:in `process'
thin (1.5.0) lib/thin/connection.rb:39:in `receive_data'
eventmachine (1.0.0) lib/eventmachine.rb:187:in `run_machine'
eventmachine (1.0.0) lib/eventmachine.rb:187:in `run'
thin (1.5.0) lib/thin/backends/base.rb:63:in `start'
thin (1.5.0) lib/thin/server.rb:159:in `start'
rack (1.4.5) lib/rack/handler/thin.rb:13:in `run'
rack (1.4.5) lib/rack/server.rb:268:in `start'
railties (3.2.11) lib/rails/commands/server.rb:70:in `start'
railties (3.2.11) lib/rails/commands.rb:55:in `block in <top (required)>'
railties (3.2.11) lib/rails/commands.rb:50:in `tap'
railties (3.2.11) lib/rails/commands.rb:50:in `<top (required)>'
script/rails:6:in `require'
script/rails:6:in `<main>'

I change the code as follow, then it works.

class MembersController < ApplicationController

  authorize_actions_for Admin
...
end

class AdminsController < MembersController
  authorize_actions_for Admin
end

So I think this code does not consider about the inheritance:

 def authority_resource
      return self.class.authority_resource       if self.class.authority_resource.is_a?(Class)
      send(self.class.authority_resource)
    rescue NoMethodError => e
      raise MissingResource.new(
          "Trying to authorize actions for '#{self.class.authority_resource}', but can't. \
          Must be either a resource class OR the name of a controller instance method that \
          returns one.".squeeze(' ')
      )
    end

handling nested resources - can_create?

I need to be able to ask whether a user can create a child object (class-level) for a given instance. So something like current_user.can_create?(@post, Comment) where I can check the post's instance data to determine whether to go ahead with the operation or not. Is this currently possible?

Authority: Passing options in controllers

I'd like to use authorize_actions_for on top of all my controllers to establish an authorization pattern at all my site. However I need to pass an option to authorizers and I can't do it using this method.

My system can be used for multiple schools. So, I need to authorize resources for an specific school. the argument is an school_id.

What's the right way to do it? Authority can accomplish it?

I don't want to rewrite authorize_action_for at every action.

Views of controllers that don't have a resource and passing options

Views of controllers that don't have a resource and passing options

At the wiki, we can see "Using authority on controllers that don't have a resource". But how about the view?

Simple example: I whould like to have a link, on a view, only if the user has rights to it.

Models:
User,Course,Role (from rolify)
Controllers: Course and Teacher

I have a controller where only the course's owner can manage the course's teachers.
I don't have a Teacher Model. A teacher is a teacher on a Course if it has a role teacher on that course.

Since I don't have a Model, I'm using the example at "Using authority on controllers that don't have a resource".

It that example, how can I create a link, on the view, only if the user has rights to acess it?

class TeachersController < ApplicationController 
  before_filter :authenticate_user!
  before_filter :load_course
  authorize_actions_for TeacherAuthorizer, :for => @course

So far so good, but how can I use it on the view?

= link_to 'Manage Teachers', course_teachers_path(@course) if current_user.can_create?(WHAT_HERE?)

I could use the User model, since the Teacher is a User, but it would require to set User.authorizer_name = TeacherAuthorizer.
But that doesn't fell right.

An other approach is to try to call the TeacherAuthorizer, in the view:

= link_to 'Manage Teachers', course_teachers_path(@course) if TeacherAuthorizer.creatable_by?(current_user, :for=>@course)

But I would get a "wrong number of arguments (2 for 1)".

I think, if we could, somehow say at TeacherAuthorizer:

class TeacherAuthorizer < Authority::Authorizer
  def self.auth_symbol = :teacher
  # Or gets from the Authorizer name of the class

And then, if we could:

= link_to 'Manage Teachers', course_teachers_path(@course) if current_user.can_create?(:teacher, :for=>@course)

In the controller, I'm doing this:

@course = Course.find(params[:course_id])
@course.authorizer_name = 'TeacherAuthorizer'
authorize_action_for @course

License missing from gemspec

RubyGems.org doesn't report a license for your gem. This is because it is not specified in the gemspec of your last release.

via e.g.

spec.license = 'MIT'
# or
spec.licenses = ['MIT', 'GPL-2']

Including a license in your gemspec is an easy way for rubygems.org and other tools to check how your gem is licensed. As you can imagine, scanning your repository for a LICENSE file or parsing the README, and then attempting to identify the license or licenses is much more difficult and more error prone. So, even for projects that already specify a license, including a license in your gemspec is a good practice. See, for example, how rubygems.org uses the gemspec to display the rails gem license.

There is even a License Finder gem to help companies/individuals ensure all gems they use meet their licensing needs. This tool depends on license information being available in the gemspec. This is an important enough issue that even Bundler now generates gems with a default 'MIT' license.

I hope you'll consider specifying a license in your gemspec. If not, please just close the issue with a nice message. In either case, I'll follow up. Thanks for your time!

Appendix:

If you need help choosing a license (sorry, I haven't checked your readme or looked for a license file), GitHub has created a license picker tool. Code without a license specified defaults to 'All rights reserved'-- denying others all rights to use of the code.
Here's a list of the license names I've found and their frequencies

p.s. In case you're wondering how I found you and why I made this issue, it's because I'm collecting stats on gems (I was originally looking for download data) and decided to collect license metadata,too, and make issues for gemspecs not specifying a license as a public service :). See the previous link or my blog post about this project for more information.

Can you add an example for a controller's index action that includes filtering by user?

I have an application where I need to limit the index on a controller to only return items that a user created. Right now I can't figure out how to use this gem to do that. It appears that I am still required to do something like..

current_user.widgets.all

I'd rather just set it up to apply permissions to filter the widgets and then do Widgets.all. In this way I don't have to worry about anyone forgetting filter the widgets to only current_user.

Limiting the scope of models by ability?

Say I have a list of users. I want to select users for editing in a view and the following rules apply.

A user can see and edit their own record (maybe with some exceptions like they can't change their salary).

A manager can see their employees records and can edit everything including the salary.

A regional manager can see and edit all users but only in their assigned region.

The HR manager can see and edit all records.

Is there an elegant way to get the users for the current_user?

More complex layering authorization

Hi,

i'm migrate from CanCan to Authority and i stuck on one thing.

My App has one more layer than only user and role authorizing. It is Account authorizing.

So every user should belongs to more accounts with different roles.

Account, User, Role
  has_many    :account_user_roles
end

AccountUserRole
  belongs_to :account
  belongs_to :user
  belongs_to :role
end

Account is layer, that separate clients against each other. Like

For example:
user 1 belongs to account 1 with role admin
user 1 belongs to account 2 with role author

User must be switched only in one account. So if i'm switched in Account 2 i have permissions only as author for that account.

Can you please hint me, how to do this?

In my controller i have helpers current_account, current_role

With CanCan it was ease, because there should be initialize with more arguments:

Ability.new(current_user, current_account, current_role, ...etc)

and in ability.rb do for example

can :manage, Media::Document, user_id: @current_user.id, account_id: @current_account.id

Thank you for help.

R.

Remove Rails dependency

Looking through the source, Authority isn't tied too tightly to Rails. ActiveSupport's the core Rails library being used.

While the Railtie and generators are nice, you could use a configuration or auto-detection mechanism to load those on demand/need.

ORM agnosticism is nice, but framework-agnosticism? That's great!

Plus an abstracted framework integration layer would make it easier for people (cough) to contribute new integrations.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.