cancan
Authorization Gem for Ruby on Rails.
I was trying to test a simple index view, which has following code inside:
- if can? :destroy, MyModel
%th Options
MyModelsController
has following options (Inherited Resources + CanCan + Devise):
class MyModelsController < ApplicationController
inherit_resources
nested_belongs_to :mymodel
before_filter :authenticate_user!
load_and_authorize_resource :project
load_and_authorize_resource :mymodel, :through => :project
When running specs, it crashes at the line - if can? :destroy, MyModel
Failure/Error: render
ActionView::Template::Error:
undefined method `authenticate' for nil:NilClass
There's no traceback, nothing to base on...
I thought that maybe I'm not authorized and signed when testing views, but Devise::TestHelpers
should only be included in controller tests (and that's how I have it).
I was trying to override method can? in both Ability
and the controller, but that gave no effect.
Source: (StackOverflow)
I have a Post model with a :published
attribute (boolean) and a User model with a role
attribute (string). There are three roles: ROLES = %w[admin publisher author]
I don't want users whose role is author to be capable of setting, or editing, the :published
field on the Post model.
I'm using CanCan (and RailsAdmin gem) and my simplified Ability.rb file looks like this:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.role? :admin
can :manage, :all
elsif user.role? :publisher
can :manage, Post
elsif user.role? :author
# I want to prevent these guys from setting the :published attribute
end
end
end
Anyone got any tips for doing this sort of thing?
Source: (StackOverflow)
I have a standard RESTful controller that uses strong parameters.
class UsersController < ApplicationController
respond_to :html, :js
def index
@users = User.all
end
def show
@user = User.find(params[:id])
end
def new
@user = User.new
end
def edit
@user = User.find(params[:id])
end
def create
@user = User.new(safe_params)
if @user.save
redirect_to @user, notice: t('users.controller.create.success')
else
render :new
end
end
def update
@user = User.find(params[:id])
if @user.update_attributes(safe_params)
redirect_to @user, notice: t('users.controller.update.success')
else
render :edit
end
end
def destroy
@user = User.find(params[:id])
if current_user != @user
@user.destroy
else
flash[:error] = t('users.controller.destroy.prevent_self_destroy')
end
redirect_to users_url
end
private
def safe_params
safe_attributes =
[
:first_name,
:last_name,
:email,
:password,
:password_confirmation,
]
if current_user.is?(:admin)
safe_attributes += [:role_ids]
end
params.require(:user).permit(*safe_attributes)
end
end
In my config/initializers
I have the file strong_parameters.rb
ActiveRecord::Base.send(:include, ActiveModel::ForbiddenAttributesProtection)
When I add a simple call to CanCan's load_and_authorize_resource
I get
1) UsersController POST create with invalid params re-renders the 'new' template
Failure/Error: post :create, user: @attr
ActiveModel::ForbiddenAttributes:
ActiveModel::ForbiddenAttributes
# ./spec/controllers/users_controller_spec.rb:128:in `block (4 levels) in <top (required)>'
Where @attr
in the test is defined as
before(:each) do
@attr =
{
first_name: "John",
last_name: "Doe",
email: "user@example.com",
password: "foobar",
password_confirmation: "foobar"
}
end
In the tests I have it all setup properly to login the user and give them the necessary roles for being an administrator so I know it's not that. I don't know why this is causing ForbiddenAttributes to trigger. I'm sure it's something simple I've overlooked. Has anyone else encountered this problem and found a solution to it?
Source: (StackOverflow)
When it comes to Authorization/Authentication devise + cancan are usually my gems of choice.
After the release of Rails4's strong parameters I've been looking into using the cancan_strong_parameters gem.
I can't shake the feeling that this approach seems a bit 'hacky'.
The other options seems to be TheRole gem or simply rolling my own auth from scratch.
Was hoping anyone with first hand experience here could give a few pointers on how they tackled the problem, what problems the faced and where each approach fell short (if anywhere).
I know this isn't a clean cut StackOverflow typed question, but there doesn't seem to be much info regarding this subject when Googling.
Thanks.
Source: (StackOverflow)
I want to use CanCan to handle my permissions. My site has many different permissions levels, and most of them are context aware. For instance, Here are the relations in my 3 main models:
class User < ActiveRecord::Base
has_many :league_relations
has_many :leagues, :through => :league_relations
end
class League < ActiveRecord::Base
has_many :league_relations
has_many :users, :through => :league_relations
end
class LeagueRelation < ActiveRecord::Base
belongs_to :user
belongs_to :league
end
Note, LeagueRelations is a nested resource of Leagues. What I want to do is allow a user to modify leagues, and gauge each user's authorization based off of data stored in league_relation. I would then like a user to modify league relation, based only the data stored in the user model.
To be succinct: I basically want LeagueRelations to be used to authorize League actions, and Users to be used to authorize LeagueRelations actions. i.e. league_relation.owner = true to delete a League, but user.owner? must be true to delete a LeagueRelation. How can I authorize based on the attributes of league_relation when inside the league controller, and authorize other actions in other controllers on other models. Please leave a comment if you need more clarification.
Thanks.
Source: (StackOverflow)
I'm working to implement CanCan. For some reason CanCan keeps giving me Access Denied when I try to get specific about model permissions. And I can't figure out why.
Is there a way to get CanCan to be specific, perhaps in the logs or in development about Why Access is denied? something like, No Read Ability to XXX Model.
That would be helpful for debugging.
Thanks
Source: (StackOverflow)
I have a requests spec for interactions with the User model. I want to make sure that Users with the Admin role can create/edit/destroy Users. I'm having a problem right now where the Edit action does not update the user. Everything works properly when I manually go through the actions on the site itself, but the tests fail to update the user.
Here's my spec:
it 'edits a user' do
@user = FactoryGirl.create(:user)
visit new_user_session_path unless current_path == new_user_session_path
fill_in "Email", :with => @user.email
fill_in "Password", :with => @user.password
click_button "Sign In"
user_to_edit = FactoryGirl.create(:user, first_name: "John", last_name: "Smith")
visit edit_user_path(user_to_edit) unless current_path == edit_user_path(user_to_edit)
fill_in 'user_last_name', with: "Changed"
expect{
click_button "Do it"
}.to change { user_to_edit.last_name }.from("Smith").to("Changed")
page.should have_content "John Changed"
end
The error that I get is:
Failure/Error: expect{
result should have been changed to "Changed", but is now "Smith"
If I change the last few lines of the test to this:
fill_in 'user_last_name', with: "Changed"
click_button "Do it"
page.should have_content "John Changed"
Then the test succeeds. This doesn't seem right, since the page should not display "John Changed" if user_to_edit
was not updated.
My Delete request spec works fine:
it "deletes a user" do
@user = FactoryGirl.create(:user)
visit new_user_session_path unless current_path == new_user_session_path
fill_in "Email", :with => @user.email
fill_in "Password", :with => @user.password
click_button "Sign In"
user_to_delete = FactoryGirl.create(:user, first_name: "John", last_name: "Smith")
visit users_path unless current_path == users_path
expect{
within ".user_#{user_to_delete.id}" do
click_link 'Delete'
end
}.to change(User,:count).by(-1)
page.should_not have_content "John Smith"
end
I have a user model:
class User < ActiveRecord::Base
ROLES = %w[renter landlord admin]
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation :first_name, :last_name, :role
validates :password, :presence => true, :on => :create
validates :first_name, :presence => true
validates :last_name, :presence => true
before_save :set_phones
def set_phones
self.fax = Phoner::Phone.parse(self.fax).format("%a%n") unless self.fax.blank?
self.land_phone = Phoner::Phone.parse(self.land_phone).format("%a%n") unless land_phone.blank?
self.mobile_phone = Phoner::Phone.parse(self.mobile_phone).format("%a%n") unless mobile_phone.blank?
end
end
I have this factory:
require 'faker'
FactoryGirl.define do
factory :user do |f|
f.first_name { Faker::Name.first_name }
f.last_name { Faker::Name.last_name }
f.email {Faker::Internet.email}
f.password { "oq2847hrowihgfoigq278o4r7qgo4" }
f.role { "admin" }
end
end
I have these actions in my user controller:
def edit
@user = User.find_by_id(params[:id])
respond_to do |format|
format.html
end
end
def update
if params[:user][:password].blank?
[:password,:password_confirmation].collect{|p| params[:user].delete(p) }
end
respond_to do |format|
if @user.errors[:base].empty? and @user.update_attributes(params[:user])
flash.now[:notice] = "Your account has been updated"
format.html { render :action => :show }
else
format.html { render :action => :edit, :status => :unprocessable_entity }
end
end
end
The routes.rb
file is also relevant, since I'm using Devise and have a custom Users Controller:
devise_for :users, :skip => [:sessions, :registrations]
devise_scope :user do
get "login" => "devise/sessions#new", :as => :new_user_session
post 'login' => 'devise/sessions#create', :as => :user_session
delete "logout" => "devise/sessions#destroy", :as => :destroy_user_session
get "signup" => "devise/registrations#new", :as => :new_user_registration
put "update-registration" => "devise/registrations#update", :as => :update_user_registration
delete "delete-registration" => "devise/registrations#destroy", :as => :delete_user_registration
get "edit-registration" => "devise/registrations#edit", :as => :edit_user_registration
get "cancel-registration" => "devise/registrations#cancel", :as => :cancel_user_registration
post "create-registration" => "devise/registrations#create", :as => :user_registration
end
resources :users, :controller => "users"
Source: (StackOverflow)
A have a bunch of controllers w/ the Admin namespace. I want to restrict access to these unless the user is an admin. Is there a way to do this using CanCan without having to call unautorized! in every method of every controller?
Source: (StackOverflow)
How do I serialize permissions with active_model_serializers? I don't have access to current_user
or the can?
method in models and serializers.
Source: (StackOverflow)
I'm working on a rails project in which I use CanCan to authorize my resources. When a user is not signed in and tries to submit a "talk" (via an ajax form submission), CanCan correctly raises a 401 with {"status":"error","message":"You must be logged in to do that!"}
as the response (I have verified this in the browser using firebug). However, in my tests get a 302 response code rather than a 401:
class TalksController < ApplicationController
authorize_resource
def create
@talk = current_user.talks.build(params[:talk])
respond_to do |format|
if @talk.save
response = { :redirect => talk_path(@talk) }
format.html { redirect_to @talk, notice: 'Talk was successfully created.' }
format.json { render json: response, status: :created, }
else
format.html { render action: "new" }
format.json { render json: @talk.errors, status: :unprocessable_entity }
end
end
end
end
talks_controller_spec.rb:
describe TalksController do
describe "POST create" do
context "when not signed in" do
it "should not assign talk" do
post :create
assigns[:talk].should be_nil
end
it "should respond with a 401" do
post :create
response.response_code.should == 401
end
end
end
end
The first example included here is successful (assigns[:talk] does not get assigned), but the second is not:
1) TalksController POST create when not signed in should respond with a 401
Failure/Error: response.response_code.should == 401
expected: 401
got: 302 (using ==)
# ./spec/controllers/talks_controller_spec.rb:53:in `block (4 levels) in <top (required)>'
I'm not sure exactly what's going on. Is there a way I can test for the actual response code returned to the browser? Or a better way I can test the authorization?
Source: (StackOverflow)
I was wondering how I can define an ability class and serve that ability class depending on the user that has logged in.
I am using Active Admin, Can Can and Devise and I have successfully created a User and an AdminUser models.
I have this in my ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if (user)
can :manage, Item
end
end
end
Now I have used this wiki entry to determine that we can indeed define a custom ability file and use that instead of the ability.rb:
https://github.com/ryanb/cancan/wiki/changing-defaults
But what I wanted to do is, be able to use ability.rb if a "non-admin user" is signed in and a custom abilty if a user admin is signed in.
Side Question: Could it be done such that I don't need a custom one and I could set permissions in one ability.rb file?
Source: (StackOverflow)
I have an admin controller and I want that only users that are defined as admin would have access to that controller.
my ability class:
class Ability
include CanCan::Ability
def initialize(user)
if user.admin?
can :manage, :all
else
can :read, :all
end
end
end
my admin controller:
class AdminController < ApplicationController
load_and_authorize_resource
def index
end
def users_list
end
end
when i try to access /admin/users_list
(either with an admin user or without) i get the following error: uninitialized constant Admin
What am I doing wrong? Is that the right way to restrict access to a controller?
Source: (StackOverflow)
According to all documentation, the :read
action is aliased to both :index
and :show
:
alias_action :index, show, :to => :read
However, consider the following scenario with nested resources:
resources :posts
resources :comments
end
If I define abilities like this:
# ability.rb
can :read, Post
can :show, Comment
# comments_controller.rb
load_and_authorize_resource :organization, :find_by => :permalink
load_and_authorize_resource :membership, :through => :organization
things work as expected. However, if I change the :read
action to [:index, :show]:
# ability.rb
can [:index, :show], Post
can :show, Comment
# comments_controller.rb
load_and_authorize_resource :organization, :find_by => :permalink
load_and_authorize_resource :membership, :through => :organization
I am unauthorized to access /posts/:post_id/comments
, /posts/:post_id/comments/:id
, etc. I still, however, can access both :index
and :show
for the posts_controller
.
How is possible that these actions are "aliased", if they behave differently?
In my fiddling, I also came across the following. Changing load_and_authorize_resource
to the following allowed access:
# ability.rb
can [:index, :show], Post
can :show, Comment
# comments_controller.rb
load__resource :organization, :find_by => :permalink
load_and_authorize_resource :membership, :through => :organization
Can someone explain what's going on here?
Source: (StackOverflow)
In the documentation of cancan it shows how to fetch all accessible records (in http://wiki.github.com/ryanb/cancan/fetching-records) in this way:
@articles = Article.accessible_by(current_ability)
but what is current_ability
? I've tried passing the current user which I'm using for authentication and authorization, but I've got this error:
NoMethodError: undefined method `conditions' for #<User:0x1092a3b90>
Any ideas what should I pass to accessible_by
or what's wrong here?
Source: (StackOverflow)
I'm trying to test how a not logged in user behaves like this
describe "not logged in user" do
user_no_rights
it "can't access action index" do
expect(get :index).to raise_error(CanCan::AccessDenied)
end
end
The output when i run rspec
Failure/Error: expect(get :index).to raise_error("CanCan::AccessDenied:You are not authorized to access this page.")
CanCan::AccessDenied:
You are not authorized to access this page.
So it looks like the correct execption is raised, but why is the spec not passing?
Source: (StackOverflow)