I want to disable access to a Pages controller for users having role "author", using cancan (by Ryan Bates).
The PagesController is as follows
class PagesController < ApplicationController
def new
#page = Page.new
authorize! :update, #page
...
end
...
end
This is returning uninitialized constant CanCan::Ability::I18n
Note that the same thing happens when I use
load_and_authorize_resource
filter instead of
authorize! :update, #page
I am using Rails 2.2.3.
Has anyone encountered a similar issue?
Thanks
Adding the ability.rb code:
class Ability
include CanCan::Ability
def initialize(current_user)
user = User.find(:first, :conditions => ["username = ?", current_user])
user ||= User.new # guest user
if user.role?('admin')
can :manage, :all
can :manage, WpArticle
elsif user.role?('moderator')
can :manage, :all
elsif user.role?('author')
can :create, WpArticle
can :update, WpArticle
can :read, WpArticle
end
end
end
You need to install the i18n gem. Once installing, it should hopefully work.
Related
In my app I have a model called User that has_one Talent.
In CanCanCan I have this ability:
class Ability
include CanCan::Ability
def initialize(user)
if user.nil?
can :read, User
can :read, Talent, is_public?: true
else
can :read, Talent, is_public?: true
end
My page is being rendered by the ProfilesController#show. Like this:
class ProfilesController < ApplicationController
before_action :check_ability, except: [:show]
def show
#user = User.find(params[:id])
authorize! :read, #user
authorize! :read, #user.talent
if current_user
sent_connections = current_user.sent_connections
connections = sent_connections + current_user.all_connections
#is_connected = !(connections.select { |c| c.user.id == #user.id }.empty?)
end
#top_5_photos = #user.top_5_photos
end
Well. Im trying to render a profile that the method: is_public returns false. But the page is being rendered correctly, while I expected was that the user cant see the page because of the rule:
can :read, Talent, is_public?: true
What Im missing here?
If I remember it correctly,
can :read, Talent, is_public?: true
^ is_public? above is expected to be an attribute by Cancancan.
But because is_public? is a custom method, then can you try the following instead?
can :read, Talent do |talent|
talent.is_public?
end
I have 2 types of roles for my user at the moment [:member , :admin], members can CRUD most post created by them . :admin can do CRUD any post period. Now im trying to create a moderator that can only View and update all posts. i have added :moderator to my enum role:. I also included
before_action :moderator_user, except: [:index, :show] and
def authorize_user
unless current_user.admin?
flash[:alert] = "You must be an admin to do that."
redirect_to topics_path
end
end
def moderator_user
unless current_user.moderator?
flash[:alert] = "You must be an moderator to do that."
redirect_to topics_path
end
end
but seem to be interfering with my before_action :authorize_user, except: [:index, :show] because it causes my rspec tests to fail.
Im trying to figure out how to create a moderator role which will be in between member and admin but without affecting either.
helper.rb :
def user_is_authorized_for_topics?
current_user && current_user.admin?
end
def user_is_moderator_for_topics?
current_user && current_user.moderator?
end
This is a perfect case for one of the authorization gems -- Pundit or CanCanCan. CanCanCan is probably the best for user-centric implementations...
#Gemfile
gem 'cancancan', '~> 1.13'
#app/models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in) #-> looks for "current_user"
case true
when user.admin?
can :manage, Post #-> CRUD all
when user.moderator?
can [:read, :update], Post #-> update/read posts
when user.member?
can :manage, Post, user_id: user.id #-> CRUD their posts
end
end
end
The above will give you the ability to use the can? and authorize methods in your controller & views:
#app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
load_and_authorize_resource
end
#app/views/articles/index.html.erb
<% #articles.each do |article| %>
<% if can? :update, article %>
<%= link_to "Edit", edit_article_path(article) %>
<% end %>
<% end %>
The above should do well for you.
The load_and_authorize_resource filter should provide you with scoped data:
As of 1.4 the index action will load the collection resource using accessible_by.
def index
# #products automatically set to Product.accessible_by(current_ability)
end
--
There is a great Railscast about this here. The creator of Railscasts authored CanCan before getting burned out, so a new community took it up with CanCanCan.
I can't understand what I've missed.
ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
can :read, Post
end
end
post_controller.rb
class PostController < ApplicationController
before_filter :authenticate_user!
def index
#posts = Post.all
authorize! :read, #posts
end
end
index.html.haml
- if can? :read, #posts
you can!
- else
you cannot!
Using this code, I always get CanCan::AccessDenied in PostController#index exception. It says there's something wrong at the line #8: authorize! :read, #posts
1.
If I change code in the post_controller.rb like this:
post_controller.rb
class PostController < ApplicationController
before_filter :authenticate_user!
load_and_authorize_resource
def index
#posts = Post.all
end
end
The exception is gone, but I get you cannot! from my view. I expect to get you can! message.
2. If I change can :read, Post to can :read, :all in the ability.rb, I get you can! message as expected. But that's not what I want to use.
What's wrong here?
Actually, either you use can :read, Post or you use can :read, post while looping #posts.
There is no in between.
btw, if you use load_and_authorize_resource, no need to add #posts = Post.all.
They are automatically loaded.
PS: why do you check in your controller AND in your view?
Trying to get Cancan securing a few models in an application and curious why it's not working the way I thought it would. I had thought you could can? on the specific instance as opposed to the entire class so, not in this example but, you could enable abilities on a per instance basis as a list of posts are displayed?!?
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.role? :admin
can :manage, :all
elsif user.role? :moderator
can :manage, Post
else
can :read, :all
end
end
end
# posts/index.html.haml
...
- if can? :update, #post <- doesn't work
- if can? :update, Post <- works
Edit: add PostsController.rb
#posts_controller.rb
class PostsController < ApplicationController
before_filter :login_required, :except => [:index, :show]
load_and_authorize_resource :except => [:create]
def index
# #posts = Post.all ## <- handled by Cancan's load_and_authorize_resource
#events = Event.where("end_date <= :today", :today => Date.today)
#next_event = Event.next
respond_to do |format|
format.html # index.html.erb
format.json { render json: #posts }
end
end
...
end
This line:
- if can? :update, #post <- doesn't work
Is asking CanCan "can I update this specific post." You defined the ability in terms of all posts. If you had done:
can :update, Post, :user_id => user.id
Then your "if can?" would work, and the user would only be able to update their own posts. So you want to use the specific resource version ("#post") if something about this instance of the resource determines the permission, and you want to use the class version ("Post") if the user has the ability for all instances of the class.
I am using authlogic and cancan on a rails 3 application, I want to allow all logged in users to access the users index page, i have tried something like this but it dosent seem to be working:
ability class:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
can :index, User if UserSession.find
can :read, User if UserSession.find
end
Controller:
def index
#users = User.search(params[:search]).order('username').page(params[:page]).per(1)
authorize! :index, #users
end
def show
#user = User.find(params[:id])
authorize! :read, #user
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #user }
end
end
thanks
I find it's easier to use load_and_authorize_resource at the top of my controllers. Then your ability class contains all the ability logic instead of having it strewn about your controllers.
ability.rb
class Ability
include CanCan::Ability
def initialize(user)
if user
can :index, User
can [:show, :edit, :update, :destroy], User, :id => user.id
end
end
end
users_controller.rb
class UsersController < ApplicationController
load_and_authorize_resource
def index
#users = User.search(params[:search]).order('username').page(params[:page]).per(1)
end
def show
end
...
end
I haven't used authlogic in a while as I tend to use devise now, so I'm not sure if my sample code is authlogic ready. If you don't want to use load_and_authorize_resource, my code shows how to limit what users can see in the ability class, but in your code I'd change :read to :show.
Continuing from my comment, the problem was in the following code
authorize! :index, #users
Here, you're passing an Array of users to the CanCan's method, while your can :index, User declaration defines the authorization for a User object.