CanCan has_many through, specific abilities - ruby-on-rails-3

I'm currently making a project using devise and cancan.
To understand my problem i have this models :
User with some attributes and is_admin boolean to global access
Role belongs_to project and user with a specific ability for user on each project
Project has_many some others models user can edit or not (depend of it's role on the project)
So my question is how can I do this ?
Actually I have this Ability class :
class Ability
include CanCan::Ability
def initialize(user)
if user
can :read, :all # allow everyone to read everything
if user.is_admin?
can :manage, :all
end
end
end
end
I need to manage role in models depend on my Project model or something else ?
Thank you in advance.

class Ability
include CanCan::Ability
def initialize(user)
if user
can :read, :all # allow everyone to read everything
if user.is_admin?
can :manage, :all
end
can manage, Project do |project|
project.users.include? user
#projects.users return the list of the users in the the role table.
end
end
end
end
You can customize it but I think it's the good way to start.

Here's a cleaner way to organize your Ability.rb.
def initialize(user)
user ||= User.new # Set user to blank User.new if user isn't logged in.
# Everyone, including guests, can read everything
can :read, :all
# Will only let user manage if #project.admin_id == user.id
can :manage, Project, admin_id: user.id
end

Related

Applying cancan to custom actions

I'm working on a polling app and use Devise and Cancan for authentication and authorization. I have a User model generated by Devise and a Poll model. A poll belongs to a user. My problem is that I have a custom action in my Polls controller and I can't get Cancan to work with that custom action. This is what I tried to do in my code:
config/routes.rb:
match 'users/:user_id/polls' => 'polls#show_user'
Ability.rb:
def initialize(user)
user ||= User.new
if user.is? :admin
can :manage, :all
else # default
can :read, :all
can :manage, Poll, :user_id => user.id
can :show_user, Poll, :user_id => user.id
end # if else admin
end
polls_controller.rb:
class PollsController < ApplicationController
before_filter :authenticate_user!
load_and_authorize_resource :except => :show_user
def show_user
authorize! :show_user, user
#polls = Poll.find_all_by_user_id(params[:user_id])
render "index"
end
<...>
end
The idea is that a user's polls can be viewed only when the owner of the poll is signed in. However, with this code, when a poll's owner is signed in, that user gets kicked out of that page with a message that says authorization failed. If I remove the line authorize! :show_user, user, then a user who's signed in can view all other user's polls (the authorization doesn't work at all).
Can anyone see what I might be missing? Thanks in advance!
In abiltity.rb, you're verb/noun combination is :show_user and Poll, but in your controller you're using :show_user and user--you would need to use a Poll instead.
If, instead you want to allow the user to view all their own Polls, you might go with something like:
ability.rb:
can :show_polls_for, User, :id => user.id
polls_controller.rb:
def show_user
authorize! :show_polls_for, user
...
end

How to implement the dynamic authorization with CanCan?

i am following this tutorial http://eveloverails.wordpress.com/2011/04/02/183/. I have created the users through devise and i want when the user log in as an admin he should have the list of users in which it can edit the permissions of the users like edit destroy,etc.
Thanks in advance
you can add a field in devise model call role. then create cancan ability class in your model then define your authorization like
class Ability
include CanCan::Ability
def initialize(user)
# Define abilities for the passed in user here. For example:
user ||= Login.new # guest user (not logged in)
if user.role == 'admin'
can :manage, :all
else
can :read, :all
end
end
end
See the wiki for details: https://github.com/ryanb/cancan/wiki/Defining-Abilities

I don't want any specific role.I want to assign permission to any user from GUI by check boxes. So that user can do whatever permissions he have

i just want an application through which i can dynamically assign roles to users through check boxes.
Any answer will be appreciated.
Thanks
There is this gem "cancan" by Ryan Bates.
https://github.com/ryanb/cancan
It integrates nicely with Devise for authentication as well.
Read this 2 - part tutorial.
http://www.tonyamoyal.com/2010/07/28/rails-authentication-with-devise-and-cancan-customizing-devise-controllers/
class Ability
include CanCan::Ability
def initialize(user)
if user.admin?
can :manage, :all
else
user.permissions.each do |permission|
can permission.action_name.to_sym, permission.object_type.constantize # Getting the class name
end
end
end
end
This would now dynamically create permissions for the user. Does this help?

How to define Ability.rb (CanCan) correctly?

I trying to define my abilities as following:
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 == 'member'
can :manage, [User,Post] , :id => user.id
cannot :index, User # list users page
else
can :read, :all
end
end
end
And have included load_and_authorize_resource on top of my PostsController.
If I understand the definitions, guest users SHOULDN'T have access to the create action from PostsController but they do.
Any explanation for this behaviour?
EDIT
Solved!
Just realized that I have forgot to add an before_filter :authenticate_user! since I'm using Devise for authentication.
Solved!
Just realized that I have forgot to add an before_filter :authenticate_user! since I'm using Devise for authentication.

Ruby on Rails – CanCan ability to only let an admin view published blog posts

tl;dr
I use CanCan for authorization in a single-author blog. I want non-admin users to not be able to view unpublished posts. The following does not do the trick:
can :read, Post do |post|
post.published_at && post.published_at <= Time.zone.now
end
Why doesn't it work, and what can I do to make it work?
Thanks. ;-)
The long version
Hello World,
I have a single-user blogging application and use CanCan for authorization purposes. I want administrators (user.admin? # => true) to be able to do whatever they wish with everything (they are administrators after all…). I also want regular users (both those who are logged in, but does not have the admin role, and those who are not logged in) to be able to view blog posts that have been published. I do not want them to see those that are not published.
Blog posts (of the model Post) each have an attribute called published_at (which is a DateTime and nil by default). Needless to say: when published_at is nil, the post is not published, otherwise it is published at the set date and time.
I have the following in my Ability class:
class Ability
include CanCan::Ability
def initialize user
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, :all
else
can :read, Post do |post|
post.published_at && post.published_at <= Time.zone.now
end
end
end
end
However, this does not seem to work as I intend it to. I have read on the CanCan wiki that this might not always work. However, I believe it should work in my case here, as I do have an instance of the Post model called #post in my PostsController#show action:
class PostsController < ApplicationController
authorize_resource
respond_to :html, :json
# other actions omitted ...
def show
#post = Post.find params[:id]
respond_with #post
end
# other actions omitted ...
end
Even with this code I am able to visit the blog post through the show action and view. I have also tried removing the authorize_resource call from the PostsController, realizing it might override some abilities or something, but it didn't help.
I have figured out a temporary solution, although I find it ugly and really want to utilize the CanCan abilities. My ugly temporary solution checks internally in the PostsController#show if the user has access to view the resource:
def show
#post = Post.find params[:id]
unless #post.published_at
raise CanCan::AccessDenied unless current_user && current_user.admin?
end
respond_with #post
end
As I said, this works. But I don't really want to go with this solution, as I believe there's a better way of doing this as a CanCan ability.
I'd much appreciate an explanation of why my approach does not work as well as a good solution to the problem. Thanks in advance. :-)
At the point where authorize_resource is being called (before_filter) you don't have a post object to authorize.
Assuming CanCan 1.6 or later, try this..
In your Post model
class Post < ActiveRecord::Base
scope :published, lambda { where('published_at IS NOT NULL AND published_at <= ?', Time.zone.now) }
# the rest of your model code
end
In your Ability model
class Ability
include CanCan::Ability
def initialize user
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, :all
else
can :read, Post, Post.published do |post|
post.published_at && post.published_at <= Time.zone.now
end
end
end
end
In your controller
class PostsController < ApplicationController
load_and_authorize_resource
respond_to :html, :json
# other actions omitted ...
def show
respond_with #post
end
end