Rails:隐藏 "Edit" 和 "Destroy" 除非以管理员身份登录
Rails: Hide "Edit" and "Destroy" unless logged in as Admin
我是 rails 的新手,我正在使用 Rails 5 以及 gems Devise 和 Pundit 创建一个简单的词汇表应用程序。我用 Devise 生成了一个 Admin 模型;那是唯一需要登录的角色。我安装了 Pundit 来创建允许我隐藏 "Edit," "Destroy," 和 "New" 按钮的策略,除非以管理员身份登录。
Glossary App Index
将以下策略代码添加到我的 index.html.erb 文件以隐藏 'Edit' 和 'Destroy' 按钮后,我收到 "Undefined method 'current_user'" 错误。
<tbody>
<% @terms.each do |term| %>
<tr>
<td><%= term.name %></td>
<td><%= term.category %></td>
<td><%= term.definition %></td>
<td><%= link_to 'Show', term, class: 'btn btn-mini' %></td>
<td>
<% if policy(@term).edit? %>
<%= link_to 'Edit', edit_term_path(term), class: 'btn btn-mini' %>
<% end %>
</td>
<td>
<% if policy(@term).destroy? %>
<%= link_to 'Destroy', term, method: :delete, class: 'btn btn-mini', data: { confirm: 'Are you sure?' } %>
<% end %>
</td>
</tr>
<% end %>
</tbody>
由于我没有使用 Devise 生成 "User" 模型,而是生成了 "Admin" 模型,因此错误指的是我的 "user" 一词似乎合乎逻辑新政策。所以我在 application_policy.rb 和 terms_policy.rb 中用 "admin" 替换了 "user"。显然我不明白 "user" 在此错误中的含义,因为我仍然明白。
我不知道你到底需要看什么,下面是我的模型、控制器和策略:
application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end
admin.rb
class Admin < ApplicationRecord
has_many :terms
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :trackable, :timeoutable, :lockable
end
term.rb
class Term < ApplicationRecord
belongs_to :admin
def self.search(search)
if search
where(["name LIKE ?","%#{search}%"])
else
all
end
end
end
application_controller.rb
class ApplicationController < ActionController::Base
include Pundit
protect_from_forgery with: :exception
before_action :set_current_user
def set_current_user
Term.current_user = current_user
end
end
terms_controller.rb
class TermsController < ApplicationController
before_action :set_term, only: [:show, :edit, :update, :destroy]
before_action :authenticate_admin!, :only => [:new, :edit, :create, :destroy]
# GET /terms
# GET /terms.json
def index
@terms = Term.search(params[:search])
end
# GET /terms/1
# GET /terms/1.json
def show
end
# GET /terms/new
def new
@term = Term.new
end
# GET /terms/1/edit
def edit
@hide_edit_button = true
end
# POST /terms
# POST /terms.json
def create
@term = Term.new(term_params)
respond_to do |format|
if @term.save
format.html { redirect_to @term, notice: 'Term was successfully created.' }
format.json { render :show, status: :created, location: @term }
else
format.html { render :new }
format.json { render json: @term.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /terms/1
# PATCH/PUT /terms/1.json
def update
respond_to do |format|
if @term.update(term_params)
format.html { redirect_to @term, notice: 'Term was successfully updated.' }
format.json { render :show, status: :ok, location: @term }
else
format.html { render :edit }
format.json { render json: @term.errors, status: :unprocessable_entity }
end
end
end
# DELETE /terms/1
# DELETE /terms/1.json
def destroy
@term.destroy
respond_to do |format|
format.html { redirect_to terms_url, notice: 'Term was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_term
@term = Term.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def term_params
params.require(:term).permit(:name, :category, :definition)
end
def verify_is_admin
(current_admin.nil?) ? redirect_to(root_path) :
(redirect_to(root_path) unless current_admin.admin?)
end
end
application_policy.rb
class ApplicationPolicy
attr_reader :admin, :record
def initialize(admin, record)
@admin = admin
@record = record
end
def index?
false
end
def show?
scope.where(:id => record.id).exists?
end
def create?
false
end
def new?
create?
end
def update?
false
end
def edit?
update?
end
def destroy?
false
end
def scope
Pundit.policy_scope!(admin, record.class)
end
class Scope
attr_reader :admin, :scope
def initialize(admin, scope)
@admin = admin
@scope = scope
end
def resolve
scope
end
end
end
terms_policy.rb
class TermPolicy < ApplicationPolicy
def index?
true
end
def create?
user.present?
end
def update?
return true if user.present?
end
def edit?
user.admin?
end
def destroy?
user.admin?
end
end
我已经尝试实施来自 Access to current_user from within a model in Ruby on Rails, , https://code.tutsplus.com/tutorials/authorization-with-pundit--cms-28202 的建议以及一些其他来源的建议。我确信这些都是很好的资源,但在这个阶段我需要一些更有针对性的项目和对 Rails.
的熟悉程度
让我知道我还能提供什么。感谢您的帮助!
您好,您可以尝试添加一些
before_action :ensure_admin, except: [:show, :edit, :update]
before_action :ensure_admin_or_user, only: [:edit, :update, :account]
在你的控制器中
使用方法=>
def ensure_admin
if current_user.nil? || current_user.is_at_least?(:manager) == false
flash[:notice] = I18n.t('must_be_admin')
redirect_to root_path
return true
end
false
end
希望对您有所帮助!
Pundit 在您的控制器中查找 current_user
以获取用户记录。您可以 configure 使用 pundit_user
方法。在 application_controller.rb
中将其覆盖到 return 您的管理员记录。
def pundit_user
Admin.find_however_you're_doing_that
end
就其价值而言,如果您打算添加其他角色(非管理员用户),您可能会采用这种痛苦的方式。您可能需要一个将角色定义为属性的 Devise 模型。
我是 rails 的新手,我正在使用 Rails 5 以及 gems Devise 和 Pundit 创建一个简单的词汇表应用程序。我用 Devise 生成了一个 Admin 模型;那是唯一需要登录的角色。我安装了 Pundit 来创建允许我隐藏 "Edit," "Destroy," 和 "New" 按钮的策略,除非以管理员身份登录。 Glossary App Index
将以下策略代码添加到我的 index.html.erb 文件以隐藏 'Edit' 和 'Destroy' 按钮后,我收到 "Undefined method 'current_user'" 错误。
<tbody>
<% @terms.each do |term| %>
<tr>
<td><%= term.name %></td>
<td><%= term.category %></td>
<td><%= term.definition %></td>
<td><%= link_to 'Show', term, class: 'btn btn-mini' %></td>
<td>
<% if policy(@term).edit? %>
<%= link_to 'Edit', edit_term_path(term), class: 'btn btn-mini' %>
<% end %>
</td>
<td>
<% if policy(@term).destroy? %>
<%= link_to 'Destroy', term, method: :delete, class: 'btn btn-mini', data: { confirm: 'Are you sure?' } %>
<% end %>
</td>
</tr>
<% end %>
</tbody>
由于我没有使用 Devise 生成 "User" 模型,而是生成了 "Admin" 模型,因此错误指的是我的 "user" 一词似乎合乎逻辑新政策。所以我在 application_policy.rb 和 terms_policy.rb 中用 "admin" 替换了 "user"。显然我不明白 "user" 在此错误中的含义,因为我仍然明白。
我不知道你到底需要看什么,下面是我的模型、控制器和策略:
application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end
admin.rb
class Admin < ApplicationRecord
has_many :terms
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :trackable, :timeoutable, :lockable
end
term.rb
class Term < ApplicationRecord
belongs_to :admin
def self.search(search)
if search
where(["name LIKE ?","%#{search}%"])
else
all
end
end
end
application_controller.rb
class ApplicationController < ActionController::Base
include Pundit
protect_from_forgery with: :exception
before_action :set_current_user
def set_current_user
Term.current_user = current_user
end
end
terms_controller.rb
class TermsController < ApplicationController
before_action :set_term, only: [:show, :edit, :update, :destroy]
before_action :authenticate_admin!, :only => [:new, :edit, :create, :destroy]
# GET /terms
# GET /terms.json
def index
@terms = Term.search(params[:search])
end
# GET /terms/1
# GET /terms/1.json
def show
end
# GET /terms/new
def new
@term = Term.new
end
# GET /terms/1/edit
def edit
@hide_edit_button = true
end
# POST /terms
# POST /terms.json
def create
@term = Term.new(term_params)
respond_to do |format|
if @term.save
format.html { redirect_to @term, notice: 'Term was successfully created.' }
format.json { render :show, status: :created, location: @term }
else
format.html { render :new }
format.json { render json: @term.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /terms/1
# PATCH/PUT /terms/1.json
def update
respond_to do |format|
if @term.update(term_params)
format.html { redirect_to @term, notice: 'Term was successfully updated.' }
format.json { render :show, status: :ok, location: @term }
else
format.html { render :edit }
format.json { render json: @term.errors, status: :unprocessable_entity }
end
end
end
# DELETE /terms/1
# DELETE /terms/1.json
def destroy
@term.destroy
respond_to do |format|
format.html { redirect_to terms_url, notice: 'Term was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_term
@term = Term.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def term_params
params.require(:term).permit(:name, :category, :definition)
end
def verify_is_admin
(current_admin.nil?) ? redirect_to(root_path) :
(redirect_to(root_path) unless current_admin.admin?)
end
end
application_policy.rb
class ApplicationPolicy
attr_reader :admin, :record
def initialize(admin, record)
@admin = admin
@record = record
end
def index?
false
end
def show?
scope.where(:id => record.id).exists?
end
def create?
false
end
def new?
create?
end
def update?
false
end
def edit?
update?
end
def destroy?
false
end
def scope
Pundit.policy_scope!(admin, record.class)
end
class Scope
attr_reader :admin, :scope
def initialize(admin, scope)
@admin = admin
@scope = scope
end
def resolve
scope
end
end
end
terms_policy.rb
class TermPolicy < ApplicationPolicy
def index?
true
end
def create?
user.present?
end
def update?
return true if user.present?
end
def edit?
user.admin?
end
def destroy?
user.admin?
end
end
我已经尝试实施来自 Access to current_user from within a model in Ruby on Rails,
让我知道我还能提供什么。感谢您的帮助!
您好,您可以尝试添加一些
before_action :ensure_admin, except: [:show, :edit, :update]
before_action :ensure_admin_or_user, only: [:edit, :update, :account]
在你的控制器中
使用方法=>
def ensure_admin
if current_user.nil? || current_user.is_at_least?(:manager) == false
flash[:notice] = I18n.t('must_be_admin')
redirect_to root_path
return true
end
false
end
希望对您有所帮助!
Pundit 在您的控制器中查找 current_user
以获取用户记录。您可以 configure 使用 pundit_user
方法。在 application_controller.rb
中将其覆盖到 return 您的管理员记录。
def pundit_user
Admin.find_however_you're_doing_that
end
就其价值而言,如果您打算添加其他角色(非管理员用户),您可能会采用这种痛苦的方式。您可能需要一个将角色定义为属性的 Devise 模型。