简体   繁体   中英

CanCanCan not authorizing admin

I'm using cancancan with rails_admin and devise gems. Even though the user has the role admin cancan shows the error You are not authorized to access this page. when trying to go to /admin route after logging in.

Here's my ability.rb

#models/ability.rb
class Ability
  include CanCan::Ability
  def initialize(user)
    can :read, :all                   # allow everyone to read everything
    if user && user.has_role?(:admin)
      can :access, :rails_admin       # only allow admin users to access Rails Admin
      can :dashboard                  # allow access to dashboard
      if user.role? :admin
        can :manage, :all             # allow superadmins to do anything
      end
    end
  end
end

Here's my user model

#models/user.rb
class User < ActiveRecord::Base

    ROLES = %i[admin moderator banned]

    def roles=(roles)
        roles = [*roles].map { |r| r.to_sym }
        self.roles_mask = (roles & ROLES).map { |r| 2**ROLES.index(r) }.inject(0, :+)
    end

    def roles
        ROLES.reject do |r|
            ((roles_mask.to_i || 0) & 2**ROLES.index(r)).zero?
        end
    end

    def has_role?(role)
        roles.include?(role)
    end

    def role?(base_role)
        ROLES.index(base_role.to_s) <= ROLES.index(role)
    end

    def self.find_first_by_auth_conditions(warden_conditions)
        conditions = warden_conditions.dup
        if login = conditions.delete(:login)
            where(conditions).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
        else
            if conditions[:username].nil?
                where(conditions).first
            else
                where(username: conditions[:username]).first
            end
        end
    end
    validate :validate_username

    def validate_username
        if User.where(email: username).exists?
            errors.add(:username, :invalid)
        end
    end
    validates_format_of :username, with: /^[a-zA-Z0-9_\.]*$/, :multiline => true
  # Include default devise modules. Others available are:
  # , :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :confirmable, :registerable,
  :recoverable, :rememberable, :trackable, :validatable, :authentication_keys => [:login]
  attr_accessor :login
  def login=(login)
    @login = login
  end

  def login
    @login || self.username || self.email
  end
end

Here's the rails_admin.rb

#config/initializers/rails_admin.rb
RailsAdmin.config do |config|

  ## == Devise ==
   config.authenticate_with do
     warden.authenticate! scope: :user
   end
   config.current_user_method(&:current_user)

  ## == Cancan ==
   config.authorize_with :cancan

  config.actions do
    dashboard                     # mandatory
    index                         # mandatory
    new
    export
    bulk_delete
    show
    edit
    delete
    show_in_app

    ## With an audit adapter, you can add:
    # history_index
    # history_show
  end
end

Here's the user in my schema.rb

create_table "users", force: :cascade do |t|
  t.string   "email",                  default: "", null: false
  t.string   "encrypted_password",     default: "", null: false
  t.string   "reset_password_token"
  t.datetime "reset_password_sent_at"
  t.datetime "remember_created_at"
  t.integer  "sign_in_count",          default: 0,  null: false
  t.datetime "current_sign_in_at"
  t.datetime "last_sign_in_at"
  t.string   "current_sign_in_ip"
  t.string   "last_sign_in_ip"
  t.string   "confirmation_token"
  t.datetime "confirmed_at"
  t.datetime "confirmation_sent_at"
  t.string   "unconfirmed_email"
  t.datetime "created_at",                          null: false
  t.datetime "updated_at",                          null: false
  t.string   "name"
  t.string   "username"
  t.string   "role"
  t.integer  "roles_mask"
end

add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
add_index "users", ["username"], name: "index_users_on_username", unique: true

I created a regular user and changed it's role attribute to admin using User.first.update_attribute :role, 'admin'

I don't exactly understand the use of roles_mask . Do I need to use both role and roles_mask in my database?

  1. I don't fully understand what's going on in the User#roles but after arbitrarily assigning a role_mask of 0(the index of admin), I got an empty array. I doubt that this is the behaviour you anticipated.

  2. I see that the check for roles( has_role? ) uses the role_mask which you may not have assigned after the creation of your users.

To reduce some of these boilerplate code, I think it would be convenient to use ActiveRecord.enum .

#models/user.rb
class User < AR::Base
  enum roles: %i[admin moderator banned]
end

note that to assign a default role, you could set that on your db or after_create .

#models/ability.rb
class Ability
  include CanCan::Ability
  def initialize(user)
    can :read, :all                   # allow everyone to read everything
    if user && user.admin?
      can :access, :rails_admin       # only allow admin users to access Rails Admin
      can :dashboard                  # allow access to dashboard
      if user.admin?
        can :manage, :all             # allow superadmins to do anything
      end
    end
  end
end

You can also assign role to a particular user by user.admin!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM