How to get a permission from a relationship using CanCanCan? - ruby-on-rails

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

Related

CanCanCan is not blocking access for users index page

I want to stop users who aren't logged in from accessing the URL using CanCanCan
http://localhost:3000/users
My Ability model is
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, :all
elsif user.roles.size > 0
can :manage, User, :id => user.id
else
can :read, :all
cannot :read, :User
end
end
end
And my Users controller is
class UsersController < ApplicationController
load_and_authorize_resource
def index
#users = User.paginate(page: params[:page],:per_page => 5)
end
def new
# #user = User.new
end
...
end
When I access the page as an guest user. I see the users index page instead of being redirected to login by this code in my application controller
rescue_from CanCan::AccessDenied do |exception|
if user_signed_in?
flash[:error] = "Access denied!"
redirect_to root_url
else
flash[:error] = "Please Sign in"
redirect_to new_user_session_path
end
end
CanCanCan works and stops access to the other actions in the controller just not for index.
In my users controller I was missing
before_filter :authenticate_user!, :except => [:new, :create]
This was allowing the guest user to access the page.

CanCan works wrong

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?

User not being denied access in Active Admin using CanCan

I'm using Active Admin's CanCan authorization adapter, along with Rolify, to manage authorization on an admin site. I have a model, company, that has_many :manuals, and another model, manuals, that has_many :parts.
If a user does not have access to read admin/manuals/1 and types it into the address bar, they are redirected properly and presented with the unauthorized message. However, if the user types in admin/manuals/1/parts they are not denied access. They are taken to that page, except all the parts are hidden from them. They should be getting redirected to the dashboard with an unauthorized message.
Here is my configuration. Thanks in advance for any advice you can offer.
config/routes.rb
ActiveAdmin.routes(self)
models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
can :read, ActiveAdmin::Page, :name => "Dashboard"
if user.has_role? :admin
can :manage, :all
elsif user.has_role? :moderator
can :manage, Part, :manual => { :company_id => user.company_id }
else
can :read, Part, :manual => { :company_id => user.company_id }
end
end
end
I've also overwritten the default authorization methods in controllers/application_controller.rb
rescue_from CanCan::AccessDenied do |exception|
redirect_to root_url, :alert => exception.message
end
def authenticate_admin_user!
authenticate_user!
unless user_signed_in?
flash[:alert] = "You are not authorized to view this page"
redirect_to root_path
end
end
def current_admin_user #use predefined method name
return nil unless user_signed_in?
current_user
end
def after_sign_in_path_for(user)
if current_user.has_role? :admin
admin_dashboard_path
elsif current_user.has_role? :moderator
admin_manuals_path
else
company_path(user.company)
end
end
Did you add the method load_and_authorize_resource to your controller?
Like this:
class SomeController < ApplicationController
load_and_authorize_resource
...
end
Check Abilities & Authorization

Understanding Cancan abilities

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.

Rails & CanCan: If user is logged in then allow him/her to view index page?

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.

Resources