Rails 4: "Pundit::NotAuthorizedError"

Rails 4: "Pundit::NotAuthorizedError"

在我的Rails4个应用中,有5个模型:

class User < ActiveRecord::Base
  has_many :administrations
  has_many :calendars, through: :administrations
end

class Calendar < ActiveRecord::Base
  has_many :administrations
  has_many :users, through: :administrations
  has_many :posts
end

class Administration < ActiveRecord::Base
  belongs_to :user
  belongs_to :calendar
end

class Post < ActiveRecord::Base
  belongs_to :calendar
end

class Comment < ActiveRecord::Base
  belongs_to :post
  belongs_to :user
end

我使用 Devise 实现了身份验证(因此我们可以访问 current_user)。

现在,我正在尝试使用 Pundit(第一个定时器)实现授权。

documentation 之后,我安装了 gem 和 运行 rails g pundit:install 生成器。

然后,我创建了一个CalendarPolicy,如下:

class CalendarPolicy < ApplicationPolicy

  attr_reader :user, :calendar

  def initialize(user, calendar)
    @user = user
    @calendar = calendar
  end

  def index?
    user.owner? || user.editor? || user.viewer?
  end

  def show?
    user.owner? || user.editor? || user.viewer?
  end

  def update?
    user.owner? || user.editor?
  end

  def edit?
    user.owner? || user.editor?
  end

  def destroy?
    user.owner?
  end

end

我还使用以下方法更新了我的 User 模型:

def owner?
  Administration.find_by(user_id: params[:user_id], calendar_id: params[:calendar_id]).role == "Owner"
end

def editor?
  Administration.find_by(user_id: params[:user_id], calendar_id: params[:calendar_id]).role == "Editor"
end

def viewer?
  Administration.find_by(user_id: params[:user_id], calendar_id: params[:calendar_id]).role == "Viewer"
end

我用 authorize @calendar 更新了我的 CalendarsController 操作,如下:

  def index
    @user = current_user
    @calendars = @user.calendars.all
  end

  # GET /calendars/1
  # GET /calendars/1.json
  def show
    @user = current_user
    @calendar = @user.calendars.find(params[:id])
    authorize @calendar
  end

  # GET /calendars/new
  def new
    @user = current_user
    @calendar = @user.calendars.new
    authorize @calendar
  end

  # GET /calendars/1/edit
  def edit
    @user = current_user
    authorize @calendar
  end

  # POST /calendars
  # POST /calendars.json
def create
  @user = current_user
  @calendar = @user.calendars.create(calendar_params)
  authorize @calendar
  respond_to do |format|
    if @calendar.save
      current_user.set_default_role(@calendar.id, 'Owner')
      format.html { redirect_to calendar_path(@calendar), notice: 'Calendar was successfully created.' }
      format.json { render :show, status: :created, location: @calendar }
    else
      format.html { render :new }
      format.json { render json: @calendar.errors, status: :unprocessable_entity }
    end
  end
end

  # PATCH/PUT /calendars/1
  # PATCH/PUT /calendars/1.json
  def update
    @user = current_user
    @calendar = Calendar.find(params[:id])
    authorize @calendar
    respond_to do |format|
      if @calendar.update(calendar_params)
        format.html { redirect_to calendar_path(@calendar), notice: 'Calendar was successfully updated.' }
        format.json { render :show, status: :ok, location: @calendar }
      else
        format.html { render :edit }
        format.json { render json: @calendar.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /calendars/1
  # DELETE /calendars/1.json
  def destroy
    @user = current_user
    @calendar.destroy
    authorize @calendar
    respond_to do |format|
      format.html { redirect_to calendars_url, notice: 'Calendar was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

并且我在 ApplicationController 中加入了 after_action :verify_authorized, :except => :index

现在,当我登录时,我可以访问 http://localhost:3000/calendars/,但是当我尝试访问 http://localhost:3000/calendars/new 时,出现以下错误:

Pundit::NotAuthorizedError in CalendarsController#new
not allowed to new? this #<Calendar id: nil, name: nil, created_at: nil, updated_at: nil>

  @user = current_user
  @calendar = @user.calendars.new
  authorize @calendar
end

显然,我一定是做错了什么。

问题:我不知道是什么。

有什么想法吗?

您无权访问模型中的参数,除非您传递它们。您应该将日历传递给模型实例函数,并且您已经可以访问用户。

user.editor?(calendar)

def editor?(calendar)
  Administration.find_by(user_id: self.id, calendar_id: calendar.id).role == "Editor"
end

问题是我没有在 CalendarPolicy 中定义 create 操作。

由于 CalendarPolicy 继承自 ApplicationPolicy — CalendarPolicy < ApplicationPolicy — 并且 ApplicationPolicy 中的 create 操作默认设置为 false,因此我遇到了错误。

只需将以下代码添加到 CalendarPolicy 即可解决问题:

def create?
  true
end

额外提示:无需向CalendarPolicy添加新操作,因为我们在ApplicationPolicy中已有以下代码:

def new?
  create?
end