Rails 康康授权
Rails authorization with cancan
我正在关注这个 tutorial
我正在尝试仅授权用户如果用户是管理员,他应该能够看到所有 post 和评论,否则普通用户只能看到自己的 post。我已经阅读了 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
#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
class 的外观。您可以将 switch/case
替换为 if/else
。
--
您缺少对象的 evaluation,特别是 can?
和 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) 更改PostsController中的这一行,删除这个条件:except [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
您似乎还没有与 post
和 comment
建立 user
关系,您需要在其中确定用户 owns/created 是否是 comment/post
运行:
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
现在您可以识别谁拥有 post/comment,以及 posts/comments 用户 owned/created 的内容,类似于以下伪代码:
# 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/comments。这是通过控制器完成的:
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
现在,在这一点上。每当创建 post/comment 时,登录用户都会自动关联到它(作为所有者)。
最后,我们可以将 Ability
class 更新为仅授权用户 :edit
、:update
、:show
和 :destroy
操作,如果 user_id: current_user
(已登录用户)。
ability.rb
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
这只是一个 shorthand 等于:
can [:show, :edit, :update, :destroy], Post, user_id: user.id
can [:index, :new, :create], Post
您会注意到 user_id: user.id
未被考虑用于 :index
、:new
和 :create
,因为这些是 :collection
方法,而不是:member
方法。更多信息 here
如果你想要可读性和可定制性,你可以选择使用上面较长的而不是 shorthand :manage
。
你可以这样写你的能力
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
并且仅使用能力资源加载 post 的资源,以便它仅加载当前用户的 post,如果不是管理员
class CommentsController < Application
load_and_authorize_resource
def index
@posts = @posts
end
end
我正在关注这个 tutorial 我正在尝试仅授权用户如果用户是管理员,他应该能够看到所有 post 和评论,否则普通用户只能看到自己的 post。我已经阅读了 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
#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
class 的外观。您可以将 switch/case
替换为 if/else
。
--
您缺少对象的 evaluation,特别是 can?
和 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) 更改PostsController中的这一行,删除这个条件:except [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
您似乎还没有与 post
和 comment
建立 user
关系,您需要在其中确定用户 owns/created 是否是 comment/post
运行:
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
现在您可以识别谁拥有 post/comment,以及 posts/comments 用户 owned/created 的内容,类似于以下伪代码:
# 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/comments。这是通过控制器完成的:
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
现在,在这一点上。每当创建 post/comment 时,登录用户都会自动关联到它(作为所有者)。
最后,我们可以将 Ability
class 更新为仅授权用户 :edit
、:update
、:show
和 :destroy
操作,如果 user_id: current_user
(已登录用户)。
ability.rb
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
这只是一个 shorthand 等于:
can [:show, :edit, :update, :destroy], Post, user_id: user.id can [:index, :new, :create], Post
您会注意到
user_id: user.id
未被考虑用于:index
、:new
和:create
,因为这些是:collection
方法,而不是:member
方法。更多信息 here如果你想要可读性和可定制性,你可以选择使用上面较长的而不是 shorthand
:manage
。
你可以这样写你的能力
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
并且仅使用能力资源加载 post 的资源,以便它仅加载当前用户的 post,如果不是管理员
class CommentsController < Application
load_and_authorize_resource
def index
@posts = @posts
end
end