使用cancan进行Rails授权

时间:2016-02-01 09:21:47

标签: ruby-on-rails cancan

我关注此tutorial 我正在尝试仅授权用户如果用户是管理员,他应该能够看到所有帖子和评论,否则普通用户只能看到自己的帖子。我已经读过github页面但是很混乱


[post_controller.rb]

class PostsController < ApplicationController
    before_action :authenticate_user!, except: [:index, :show]

    def index
        @posts = Post.all.order('created_at DESC')
    end

    def new
        @post = Post.new
    end

    def show
        @post = Post.find(params[:id])
    end

    def create
        @post = Post.new(post_params)
        @post.user = current_user

        if @post.save
            redirect_to @post
        else
            render 'new'
        end
    end

    def edit
        @post = Post.find(params[:id])
    end

    def update
        @post = Post.find(params[:id])

        if @post.update(params[:post].permit(:title, :body))
            redirect_to @post
        else
            render 'edit'
        end
    end

    def destroy
        @post = Post.find(params[:id])
        @post.destroy

        redirect_to posts_path
    end

    private

    def post_params
        params.require(:post).permit(:title, :body)
    end
end


[comments_controller]

class CommentsController < ApplicationController
    def create
        @post = Post.find(params[:post_id])
        @comment = @post.comments.create(params[:comment].permit(:name, :body))
              @comment.user = current_user

   redirect_to post_path(@post)
    end

    def destroy
        @post = Post.find(params[:post_id])
        @comment = @post.comments.find(params[:id])
        @comment.destroy

        redirect_to post_path(@post)
    end
end


[ability.rb]

    class Ability


      include CanCan::Ability
   def initialize(user)
        unless user
        else
          case user.roles
          when 'admin'
            can :manage, Post
            can :manage, Comment
          when 'user' # or whatever role you assigned to a normal logged in user
            can :manage, Post, user_id: user.id
            can :manage, Comment, user_id: user.id
          end

   end


[comment.rb]

class Comment < ActiveRecord::Base
  belongs_to :post
end


[post.rb]

class Post < ActiveRecord::Base
    has_many :comments, dependent: :destroy
    validates :title, presence: true, length: {minimum: 5}
    validates :body,  presence: true
end


[user.rb]

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
end


[迁移]

class DeviseCreateUsers < ActiveRecord::Migration
  def change
    create_table(:users) do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      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.timestamps
    end
    add_index :users, :email,                unique: true
    add_index :users, :reset_password_token, unique: true
   end
end


[移民]

class CreateComments < ActiveRecord::Migration
  def change
    create_table :comments do |t|
      t.string :name
      t.text :body
      t.references :post, index: true

      t.timestamps
    end
  end
end


[移民]

class CreatePosts < ActiveRecord::Migration
  def change
    create_table :posts do |t|
      t.string :title
      t.text :body

      t.timestamps
    end
  end
end

4 个答案:

答案 0 :(得分:1)

#app/models/ability.rb
class Ability
  include CanCan::Ability

  def initialize(user)
      user ||= User.new # guest user (not logged in)
      case user.role
        when "admin"
           can :manage, :all
        else
           can :read, Post #-> cannot read comments
      end
  end
end

以上是ability类的外观。您可以将switch/case替换为if/else

-

您错过了evaluation个对象,特别是can?&amp; authorize方法:

#app/controllers/comments_controller.rb
class CommentsController < ApplicationController
   def create
      @post = Post.find params[:post_id]
      @comment = @post.comments.new comment_params
      @comment.save if authorize! :create, @comment

      redirect_to @post
   end

   def destroy
     @post = Post.find params[:post_id]
     @comment = @post.comments.find params[:id]
     @comment.destroy if authorize! :destroy, @comment

     redirect_to @post
   end

   private

   def comment_params
      params.require(:comment).permit(:name, :body)
   end
end

#app/controllers/posts_controller.rb
class PostsController < ApplicationController
   def show
      @post = Post.find params[:id]
   end
end

#app/views/posts/show.html.erb
<%= @post.title %>
<%= render @post.comments if can? :read, @post.comments %>

答案 1 :(得分:1)

您似乎尚未与userpost建立comment关系,以确定用户是否拥有/创建了评论/帖子

执行命令

rails generate migration AddUserToPost user:belongs_to
rails generate migration AddUserToComment user:belongs_to
bundle exec rake db:migrate

然后添加关联关系:

post.rb

class Post < ActiveRecord::Base
  belongs_to :user
  # ..
end

comment.rb

class Comment < ActiveRecord::Base
  belongs_to :user
  # ..
end

user.rb

class User < ActiveRecord::Base
  has_many :posts
  has_many :comments
  # ..
end

现在,您可以识别谁拥有帖子/评论,以及用户拥有/创建的帖子/评论,例如以下伪代码:

# rails console
post = Post.find(1)
post_owner = post.user

comment = Comment.find(1)
comment_owner = comment.user

user = User.find(1)
user_comments = user.comments
user_posts = user.posts

现在,下一步是将登录用户自动关联到新创建的帖子/评论。这是通过控制器完成的:

posts_controller.rb

class PostsController < ApplicationController
  authorize_resource
  # ..

  def create
    @post = Post.new(post_params)
    @post.user = current_user # I assume you have a variable current_user, or if you are using Devise current_user is already accessible

    if @post.save
      redirect_to @post
    else
      render :new
    end
  end
end

comments_controller.rb

class CommentsController < Application
  authorize_resource
  # ..

  def create
    @post = Post.find(params[:post_id])
    @comment = @post.comments.build(params[:comment].permit(:name, :body))
        #puts "hhhhhhhhhh#{@comment}"
    @comment.user = current_user # I assume you have a variable current_user, or if you are using Devise current_user is already accessible

    @comment.save

    redirect_to post_path(@post)
  end 
end

现在,此时此刻。每当创建帖子/评论时,登录用户就会自动与其关联(作为所有者)。

最后,我们只需将Ability类更新为仅授权用户:edit:update:show:destroy操作,如果{ {1}}(登录用户)。

ability.rb

user_id: current_user

上述class Ability include CanCan::Ability def initialize(user) # if not logged in (Guest) unless user # cant do anything unless you add more `can` here # else if logged in else case user.role when 'admin' can :manage, Post can :manage, Comment when 'normal' # or whatever role you assigned to a normal logged in user can :manage, Post, user_id: user.id can :manage, Comment, user_id: user.id # If you don't have a role name for a normal user, then use the else condition like Rich Peck's answer. Uncomment the following instead, and then comment the `when 'normal' block of code just above # else # can :manage, Post, user_id: user.id # can :manage, Comment, user_id: user.id end end end end 的最后有用信息:

  • Ability

    这只是一个等于的简写:

    can :manage, Post, user_id: user.id

    您会注意到can [:show, :edit, :update, :destroy], Post, user_id: user.id can [:index, :new, :create], Post user_id: user.id:index未考虑:new,因为这些是:create方法,而不是{{} 1}}方法。更多信息here

  • 如果您想要可读性和可自定义性,您可以选择使用上面较长的一个而不是速记:collection

答案 2 :(得分:0)

1)在PostsController中更改此行,删除此条件:[index,show]除外。或者用户可以在未经授权的情况下查看页面。

before_action :authenticate_user!

2)使用此样式更改索引操作和其他操作。使用 - current_user。

def index
  if current_user.has_role? :admin        
    @posts = Post.all.order('created_at DESC')
  else
    @posts = current_user.posts.order('created_at DESC')
  end
end

答案 3 :(得分:0)

你可以用这种方式写你的能力

class Ability
  include CanCan::Ability

  def initialize(user)
      user ||= User.new # guest user (not logged in)
      case user.role
        when "admin"
           can :manage, :all
        else
           can :read, Post, :user_id => user.id
      end
  end
end

并且只使用能力资源加载帖子的资源,以便它只加载当前用户的帖子(如果不是admin

class CommentsController < Application
  load_and_authorize_resource
    def index
     @posts = @posts
    end
end