james2m / canard Goto Github PK
View Code? Open in Web Editor NEWMakes role based authorization in Rails really simple. Wraps CanCan and RoleModel up with a smattering of syntactic sugar, some generators and scopes.
License: MIT License
Makes role based authorization in Rails really simple. Wraps CanCan and RoleModel up with a smattering of syntactic sugar, some generators and scopes.
License: MIT License
I ended up not using Canard for a recent project because the "additive" nature of abilities does not include guest abilities.
I think the behavior should be, for example: guest, user, manager, admin
Instead it is: user, manager, admin
So any abilities defined for guest have to be duplicated in user. Not very DRY. While I realize this is a breaking change, to be perfectly honest, I can't think of any real world examples where a user would be denied from doing something that a guest can do.
I'm using the canard gem to set up user roles and am running into a test_unit[not found] error. I tried adding test-unit to the Gemfile but it still fails. Disclaimer: I'm still relatively novice at Rails.
$ rails g canard:ability user can:create:post
conflict app/abilities/users.rb
Overwrite /home/ubuntu/project/app/abilities/users.rb? (enter "h" for help) [Ynaqdh] Y
force app/abilities/users.rb
error test_unit [not found]
Great job wrapping up role_model! This is actually a role_model suggestion but I guess I want to point that the example:
class User < ActiveRecord::Base
acts_as_user :roles => :manager, :admin
end
suggests that abilities are checked in the following order:
app/abilities/users.rb
app/abilities/manager.rb
app/abilities/admin.rb
And indeed, I confirmed that /users.rb is called for users with empty roles, and yet of course has_role? :user returns false. Since, of course, you're keeping canard consistent with role_model, then an end-user work around would explicitly specify the :user role in the list of roles and explicitly set each new user to be a :user,
acts_as_user :roles => :user, :manager, :admin
Of course, while it's easy to add a :user role later and add the :user role to every existing user if someone really feels the need to do this, maybe it would be worth documenting a sentence or two about this inconsistency.
Hi there,
Thanks for this gem, I like it quite a bit. I'm curious about support for cancancan. I noticed that it seems to be in the gemspec on github, but perhaps the latest code is not released as a gem? For now I've specified the master branch of this repo on my gemfile and then cancancan was installed as a dependency of canard. Any plans to release those 6 month old changes to gem land? Or perhaps I'm just doing something wrong since I was still seeing cancan installed when just using gem 'canard'
in my gemfile.
Thanks!
I really like how canard simplifies authorization and roles. However, for my particular application I have been agonizing over role_model's inability to scope a role to a particular resource. I also have nightmares in my sleep thinking about when I need to add a new role and migrate all the bitmasks over. shudder
I'd like to try out rolify but I don't want to give up canard!
After looking things over, I think it might be possible to support rolify in canard. For example, most of the stuff in ::acts_as_user
and the Adapters aren't necessary, because rolify already defines scopes for ActiveRecord and Mongoid. This stuff could conditionally be loaded only if the model is_a? RoleModel
or perhaps via a configuration option.
If this sounds plausible/interesting to you, I'd be willing to give it a shot.
Hello,
First of all I would like to say thank you for great and well tested open source project.
I have trouble with getting out of my problem so I decided to ask for help. Currently I am trying to make system with couple separate interfaces but with one User model. There is Admin, Partner and User interface. User could have many global roles (cancard fit there very good) but User could have many roles as PartnerUser (model which contains relation user_id, partner_id, role). I dont know how to set up before_filter in my Partner base namespace to add this separate context (role of user for selected partner).
Please checkout my relations:
class PartnerUser < ActiveRecord::Base
ROLES = {
owner: 0,
employee: 1
}
belongs_to :user
belongs_to :partner
has_many :transactions, class_name: "Transaction"
validate :role, inclussion: { in: ROLES }
class Partner < ActiveRecord::Base
has_many :partner_users, include: :user
has_many :users, through: :partner_users
class User < ActiveRecord::Base
ALLOWED_ROLES = [:manager, :admin, :reporter]
acts_as_user roles: ALLOWED_ROLES
# Partner
has_many :partner_users
has_many :partners, through: :partner_users
Partner base controller
# -*- encoding : utf-8 -*-
class Partner::BaseController < InheritedResources::Base
layout 'admin/index'
before_filter :authenticate_user!
before_filter :authorize_for_subdomain!
helper_method :current_user_role
helper_method :current_partner
rescue_from ActiveRecord::RecordNotFound, with: :partner_not_found
add_crumb(I18n.t('partner.dashboard.singular')) { |instance| instance.send :partner_root_path }
def authorize_for_subdomain!
@subdomain = request.subdomain.to_s.split('.').first
@current_partner = Partner.find_by_url(@subdomain)
raise ActiveRecord::RecordNotFound unless @current_partner.present?
@current_partner_user = @current_partner.partner_users.where(user_id: @current_user.id).try(:first)
raise CanCan::AccessDenied unless @current_partner_user.present?
@current_user_role = @current_partner_user.current_role || :employee
current_user
end
private
def current_user_role
authorize_for_subdomain! unless @current_user_role.present?
@current_user_role
end
def current_partner
authorize_for_subdomain! unless @current_partner.present?
@current_partner
end
protected
def partner_not_found
flash[:error] = "Poszukiwana strona nie istnieje"
redirect_to root_path
end
def begin_of_association_chain
current_partner
end
def set_current_user_for_acl
@current_ability ||= Ability.new(current_admin)
end
end
Thanks in advantage for any suggestions :).
Have a nice day!
Though one can assign multiple roles to user, to try to keep the abilities DRY, we would prefer to get the same affect when assigning a single role to a user. For example, if I had the following roles
Instead of making sure that the Supervisor user also was assigned to all of the preceding roles, I would like to define the Supervisor's abilities so that it inherited from all of the preceding abilities.
I am thinking that at the top of an abilities definition, I include a call like:
includes_abilities_of :user, :writer, :editor, :manager
Then add the following to the Ability class:
def includes_abilities_of(*other_roles)
other_roles.each { |other_role| append_abilities(other_role) }
end
If this makes sense to you, i am happy to issue a pull request
This stems from the require 'rails'
on line 4 of the gemspec. I see why you're doing that, so you can check whether to use cancan or cancancan, but it doesn't seem like this is safe. I even added a runtime_dependency to the gemspec for rails and it still didn't try to install rails before trying to install canard. I'm sure there is a way, but I'm not sure what it is. Can you help?
There was a LoadError while loading canard.gemspec:
cannot load such file -- rails from
/rails/github/canard/canard.gemspec:4:in `<main>'
This is from my local branch, which is why the path is not maybe what you'd expect.
Thanks!
I believe that the dependency in the gemspec should be >= 1.1
rather than ~> 1
, so projects happy using CanCanCan v 2.x can continue to do so.
I'm trying to get the available roles for the user model defined, so I can display them in my view. I know with the role model gem I can call User.valid_roles to return all the available roles declared. Is there a way to do this with canard? If I do use ".valid_roles" on my user object it gives me an undefined method error.
Help with this will be appreciated
EDIT - my mistake, saw I was calling it on the user object instead of on the User model - is working now.
Is it possible to create roles that don't inherit abilities from "lesser" roles? I need two separate roles that are disjoint, and it seems that's not possible atm.
It would be awesome to have multiple ability definitions for applications that have multiple "apps" with different permissions (primary example being an admin app vs the standard app).
I know this is a relatively common use-case in plain old CanCan(Can), but was wondering how you'd suggest doing so with Canard?
I have a rudimentary solution working, but it requires me to create a custom Ability
class from yours (partly due to the methods being private
over protected
, partly to provide a hook)
class CustomAbility < Ability
def initialize(object=nil)
@user = object.respond_to?(:user) ? object.user : object
setup_abilities
end
protected
def setup_abilities
end
def add_role_abilities(roles)
roles = [roles] unless roles.is_a? Array
roles.each{ |role| self.send(:append_abilities, role) }
end
def get_ability_key(class_name)
self.send(:ability_key, class_name)
end
end
and then for my "custom" ablities, inheriting from the new class, and adjusting as necessary (currently by just simply appending admin_
to the front, just to see if it'd work)
class AdminAbility < CustomAbility
protected
def setup_abilities
return unless @user
user_class_name = String(@user.class.name)
add_role_abilities get_ability_key("admin_#{user_class_name}") unless user_class_name.empty?
# If user has roles get those abilities
add_role_abilities @user.roles.map{ |role| role.to_s.prepend('admin_').to_sym } if @user.respond_to?(:roles)
end
end
With Rails 4.0 beta1, devise loads the user model which, in turn, loads canard which checks it for a role matrix. This is fine if the database exists, but as many of us know from painful experience Heroku does not bring the database up before running the precompile. As a result you see:
Preparing app for Rails asset pipeline
Running: rake assets:precompile
rake aborted!
could not connect to server: Connection refused
Is the server running on host "127.0.0.1" and accepting
TCP/IP connections on port 5432?
/tmp/build_2sfjhhr6toicw/vendor/bundle/ruby/2.0.0/gems/activerecord-4.0.0.beta1/lib/active_record/connection_adapters/postgresql_adapter.rb:771:in `initialize'
/tmp/build_2sfjhhr6toicw/vendor/bundle/ruby/2.0.0/gems/activerecord-4.0.0.beta1/lib/active_record/connection_adapters/postgresql_adapter.rb:771:in `new'
/tmp/build_2sfjhhr6toicw/vendor/bundle/ruby/2.0.0/gems/activerecord-4.0.0.beta1/lib/active_record/connection_adapters/postgresql_adapter.rb:771:in `connect'
/tmp/build_2sfjhhr6toicw/vendor/bundle/ruby/2.0.0/gems/activerecord-4.0.0.beta1/lib/active_record/connection_adapters/postgresql_adapter.rb:493:in `initialize'
/tmp/build_2sfjhhr6toicw/vendor/bundle/ruby/2.0.0/gems/activerecord-4.0.0.beta1/lib/active_record/connection_adapters/postgresql_adapter.rb:41:in `new'
/tmp/build_2sfjhhr6toicw/vendor/bundle/ruby/2.0.0/gems/activerecord-4.0.0.beta1/lib/active_record/connection_adapters/postgresql_adapter.rb:41:in `postgresql_c
onnection'
/tmp/build_2sfjhhr6toicw/vendor/bundle/ruby/2.0.0/gems/activerecord-4.0.0.beta1/lib/active_record/connection_adapters/abstract/connection_pool.rb:446:in `new_c
onnection'
/tmp/build_2sfjhhr6toicw/vendor/bundle/ruby/2.0.0/gems/activerecord-4.0.0.beta1/lib/active_record/connection_adapters/abstract/connection_pool.rb:456:in `check
out_new_connection'
/tmp/build_2sfjhhr6toicw/vendor/bundle/ruby/2.0.0/gems/activerecord-4.0.0.beta1/lib/active_record/connection_adapters/abstract/connection_pool.rb:427:in `acqui
re_connection'
/tmp/build_2sfjhhr6toicw/vendor/bundle/ruby/2.0.0/gems/activerecord-4.0.0.beta1/lib/active_record/connection_adapters/abstract/connection_pool.rb:364:in `block
in checkout'
/tmp/build_2sfjhhr6toicw/vendor/bundle/ruby/2.0.0/gems/activerecord-4.0.0.beta1/lib/active_record/connection_adapters/abstract/connection_pool.rb:363:in `check
out'
/tmp/build_2sfjhhr6toicw/vendor/bundle/ruby/2.0.0/gems/activerecord-4.0.0.beta1/lib/active_record/connection_adapters/abstract/connection_pool.rb:273:in `block
in connection'
/tmp/build_2sfjhhr6toicw/vendor/bundle/ruby/2.0.0/gems/activerecord-4.0.0.beta1/lib/active_record/connection_adapters/abstract/connection_pool.rb:272:in `conne
ction'
/tmp/build_2sfjhhr6toicw/vendor/bundle/ruby/2.0.0/gems/activerecord-4.0.0.beta1/lib/active_record/connection_adapters/abstract/connection_pool.rb:552:in `retri
eve_connection'
/tmp/build_2sfjhhr6toicw/vendor/bundle/ruby/2.0.0/gems/activerecord-4.0.0.beta1/lib/active_record/connection_handling.rb:79:in `retrieve_connection'
/tmp/build_2sfjhhr6toicw/vendor/bundle/ruby/2.0.0/gems/activerecord-4.0.0.beta1/lib/active_record/connection_handling.rb:53:in `connection'
/tmp/build_2sfjhhr6toicw/vendor/bundle/ruby/2.0.0/gems/activerecord-4.0.0.beta1/lib/active_record/model_schema.rb:203:in `table_exists?'
/tmp/build_2sfjhhr6toicw/vendor/bundle/ruby/2.0.0/gems/canard-0.4.1/lib/canard/adapters/active_record.rb:30:in `active_record_table?'
/tmp/build_2sfjhhr6toicw/vendor/bundle/ruby/2.0.0/gems/canard-0.4.1/lib/canard/adapters/active_record.rb:35:in `has_roles_mask_accessors?'
/tmp/build_2sfjhhr6toicw/vendor/bundle/ruby/2.0.0/gems/canard-0.4.1/lib/canard/user_model.rb:74:in `acts_as_user'
/tmp/build_2sfjhhr6toicw/app/models/user.rb:12:in `<class:User>'
/tmp/build_2sfjhhr6toicw/app/models/user.rb:1:in `<top (required)>'
The workaround that got Heroku back up and running was to only run the canard helper if the connection is present.
# must test for the connection because of a precompile problem on Heroku
acts_as_user :roles => [:timekeeper, :admin] if ActiveRecord::Base.connected?
Not sure if anything can be done on the code side but if it was built in to the matrix check that would help. Possible? Necessary? Thanks so much for the wonderful work on this gem. I rolled my own at first and then appreciated the hard work that went into this gem all the more.
HI,
One question about instalation. Should I define any column for storing role attributes? I could not find any details about this in readme and sourcecode.
Hi,
I am having a code like can? :access, :project in my views. Based on that I am showing the link.
So that is defined in the abilities file as usual which is working fine.
i.e., eg:
app/abilities/admin.rb
I am using can :access, :project
app/abilities/employee.rb
I am using cannot :access, :project
Now I wanted to test in the following way in cucumber with the help of step_definition:
So this means I wanted to dynamically change the ability and ensure the test pass.
How DO I do this?
I have done something like this which is not working.
@user.ability.stub(:can?).with(:access, :project).and_return(false)
The reason behind this is tomorrow we might change employee ability also to can :access, :project hence i want this dynamic behavior.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.