aishaq11
aishaq11

Reputation: 179

CanCanCan Rails Not Working

I am using Rails 4 with Devise and CanCanCan, and for some reason when I try to give users CRUD permissions only for products that they have made, none of the CanCanCan permission settings go through and users can only do read only. However, the moderator and admin permissions work as intended.

ability.rb

class Ability
  include CanCan::Ability

     def initialize(user)

       user ||= User.new

    if user.is? :admin
        can :manage, :all
      elsif user.is? :moderator
        can :read, :all
        can :manage, @products
      elsif user.is? :user
        can :read, :all
        can :create, :all
        can :manage, @products do |product|
          product.try(:user) == user
        end
      else
        can :read, :all
      end
    end
  end

user.rb

  has_many :products
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  before_save :assign_role

  Roles = [ :admin , :moderator , :user ]

  def is?( requested_role )
    self.role == requested_role.to_s
  end

  def assign_role
    self.role = Role.find_by role: "user" if self.role.nil?
  end

.html file

  <% if can? :update, @products %>
  <%= link_to "Edit", edit_product_path(@product) %>
  <% end %>
  <% if can? :destroy, @products %>
  <%= link_to "Delete", product_path(@product), method: :delete, data: { confirm: "Are you sure?"} %>
  <% end %>

controller

class ProductsController < ApplicationController
    respond_to :html, :json
    load_and_authorize_resource
    def create
        @product = current_user.products.build(product_params)
        @product.user_id = current_user.id
        if @product.save
            redirect_to products_path
        else
            render 'new'
        end
    end
        def product_params
            params.require(:product).permit(:product_name, :product_description, :user_id)
        end

EDIT:

users SQL table

+------------------------+--------------+------+-----+---------+----------------+
| Field                  | Type         | Null | Key | Default | Extra          |
+------------------------+--------------+------+-----+---------+----------------+
| id                     | int(11)      | NO   | PRI | NULL    | auto_increment |
| created_at             | datetime     | YES  |     | NULL    |                |
| updated_at             | datetime     | YES  |     | NULL    |                |
| email                  | varchar(255) | NO   | UNI |         |                |
| encrypted_password     | varchar(255) | NO   |     |         |                |
| reset_password_token   | varchar(255) | YES  | UNI | NULL    |                |
| reset_password_sent_at | datetime     | YES  |     | NULL    |                |
| remember_created_at    | datetime     | YES  |     | NULL    |                |
| sign_in_count          | int(11)      | NO   |     | 0       |                |
| current_sign_in_at     | datetime     | YES  |     | NULL    |                |
| last_sign_in_at        | datetime     | YES  |     | NULL    |                |
| current_sign_in_ip     | varchar(255) | YES  |     | NULL    |                |
| last_sign_in_ip        | varchar(255) | YES  |     | NULL    |                |
| role                   | varchar(255) | YES  |     | NULL    |                |
+------------------------+--------------+------+-----+---------+----------------+

products SQL table

+-----------------------------------+--------------+------+-----+---------+----------------+
| Field                             | Type         | Null | Key | Default | Extra          |
+-----------------------------------+--------------+------+-----+---------+----------------+
| id                                | int(11)      | NO   | PRI | NULL    | auto_increment |
| project_goal                      | int(11)      | YES  |     | NULL    |                |
| product_name                      | varchar(255) | YES  |     | NULL    |                |
| product_description               | varchar(255) | YES  |     | NULL    |                |
| project_category                  | varchar(255) | YES  |     | NULL    |                |
...
| expiration_date                   | datetime     | YES  |     | NULL    |                |
| created_at                        | datetime     | YES  |     | NULL    |                |
| updated_at                        | datetime     | YES  |     | NULL    |                |
| user_id                           | int(11)      | YES  |     | NULL    |                |
+-----------------------------------+--------------+------+-----+---------+----------------+

And here is my file structure

FileStructure

Upvotes: 0

Views: 1683

Answers (2)

BenKoshy
BenKoshy

Reputation: 35575

You seem to be using an instance variable in your abilities class. Try this and see if it works:

class Ability
  include CanCan::Ability

 def initialize(user)

   user ||= User.new

if user.is? :admin
    can :manage, :all
  elsif user.is? :moderator
    can :read, :all
    can :manage, Product # use class here not instance variable
  elsif user.is? :user
    can :read, :all
    can :create, :all
    can :manage, Product do |product| # <–-use the class here not instance variable.
      product.try(:user) == user # cancancan code examples general call ids you might wanna consider revisions?
    end
  else
    can :read, :all
  end
end
  end

Upvotes: 0

MarsAtomic
MarsAtomic

Reputation: 10673

Rather than setting up a block like this:

can :manage, @products do |product|
  product.try(:user) == user
end

you can try to establish permissions based on user_id:

can :manage, Product do |product|
  product.user_id == user.id
end

You can express this permission even more succinctly like so:

can :manage, Product, user_id: user.id

While the above syntax should work, if you'e having problems try this slightly more verbose version:

can :manage, Product, :user_id => user.id

This approach presumes, of course, that there's a relationship between User and Product (where products belong to users).

Also, keep in mind that :manage means "any operation," so it's redundant to define conditions for any of the CRUD operations and/or your custom methods on after specifying :manage.

As a final note, you might want to try using a different symbol for your standard access level, since the symbol :user can easily be mistaken for a user object when you actually mean user role.

Upvotes: 1

Related Questions