枚举角色专家

Pundit for enum roles

上下文

在我的应用程序中,一个 user.admin 可以创建多个酒店。

问题

错误信息

网络浏览器中的消息:

localhost redirected you too many times.
Try clearing your cookies.
ERR_TOO_MANY_REDIRECTS

控制台

Started GET "/" for ::1 at 2019-11-07 09:27:16 +0100
Processing by PagesController#home as HTML
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" =  ORDER BY "users"."id" ASC LIMIT   [["id", 20], ["LIMIT", 1]]
  ↳ app/controllers/pages_controller.rb:6
  Hotel Exists (0.5ms)  SELECT  1 AS one FROM "hotels" INNER JOIN "user_hotels" ON "hotels"."id" = "user_hotels"."hotel_id" WHERE "user_hotels"."user_id" =  LIMIT   [["user_id", 20], ["LIMIT", 1]]
  ↳ app/controllers/pages_controller.rb:7
  Hotel Load (0.2ms)  SELECT  "hotels".* FROM "hotels" WHERE "hotels"."id" IS NULL LIMIT   [["LIMIT", 1]]
  ↳ app/controllers/pages_controller.rb:8
  Hotel Load (0.2ms)  SELECT  "hotels".* FROM "hotels" INNER JOIN "user_hotels" ON "hotels"."id" = "user_hotels"."hotel_id" WHERE "user_hotels"."user_id" =  ORDER BY "hotels"."id" DESC LIMIT   [["user_id", 20], ["LIMIT", 1]]
  ↳ app/controllers/pages_controller.rb:12
Redirected to http://localhost:3000/hotels/9
Completed 302 Found in 6ms (ActiveRecord: 1.2ms)

代码

路线

Rails.application.routes.draw do

  devise_for :users

  resources :hotels do
devise_for :users, :controllers => { :invitations => 'users/invitations' }
  end
end

application_controller

class ApplicationController < ActionController::Base
  before_action :set_locale
  before_action :configure_permitted_parameters, if: :devise_controller?

  def set_locale
    I18n.locale = params.fetch(:locale, I18n.default_locale).to_sym
  end

  def default_url_options
    { locale: I18n.locale == I18n.default_locale ? nil : I18n.locale }
  end

  protect_from_forgery with: :exception
  before_action :authenticate_user!
  include Pundit

  # Pundit: white-list approach.
  after_action :verify_authorized, except: :index, unless: :skip_pundit?
  after_action :verify_policy_scoped, only: :index, unless: :skip_pundit?

  # Uncomment when you *really understand* Pundit!
  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

  private
  def user_not_authorized
    flash[:alert] = "You are not authorized to perform this action."
    redirect_to(root_path)
  end

  def skip_pundit?
    devise_controller? || params[:controller] =~ /(^(rails_)?admin)|(^pages$)/
  end

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:invite, keys: [:role, :user_parks_attributes])
  end
end

页数#home

class PagesController < ApplicationController
  skip_before_action :authenticate_user!, :raise => false
  skip_after_action :verify_authorized

  def home
    if !current_user.nil?
      if current_user.hotels.any?
        if Hotel.find_by_id params[:id]
          @hotel = Hotel.find(params[:id])
          redirect_to hotel_path(@hotel)
        else 
          @hotel = current_user.hotels.last
          redirect_to hotel_path(@hotel)
          # redirect_to :controller => 'hotels' , :action => 'show',  :id => @hotel.id
        end
      else
        redirect_to hotels_path
      end
    else
      redirect_to pages_landing_page_path
    end
  end
end

hotel_policy

class HotelPolicy < ApplicationPolicy
  class Scope < Scope
    def resolve
      if user.admin? || user.employee?
        scope.joins(hotel: :user_hotels).where(user_hotels: { user_id: user.id })
      else
        raise Pundit::NotAuthorizedError
      end
    end
  end

  def show?
    user.admin? || user.employee?
  end
end

型号

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  has_many :user_hotels, dependent: :destroy
  has_many :hotels, through: :user_hotels
  accepts_nested_attributes_for :user_hotels
  enum role: [:owner, :admin, :employee]
  after_initialize :set_default_role, :if => :new_record?

  def set_default_role
    self.role ||= :admin
  end

  devise :invitable, :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :invitable
end

class UserHotel < ApplicationRecord
  belongs_to :hotel
  belongs_to :user
end

class Hotel < ApplicationRecord
  has_many :user_hotels, dependent: :destroy
  has_many :users, through: :user_hotels
  accepts_nested_attributes_for :users, allow_destroy: true, reject_if: ->(attrs) { attrs['email'].blank? || attrs['role'].blank?}
end

hotels_controller

class HotelsController < ApplicationController
def show
    if Hotel.find_by_id params[:id]
      @hotel = Hotel.find(params[:id])
    else
      @hotel = current_user.hotels.last
    end
    authorize @hotel
    @reservations = @hotel.reservations
  end
end

原来是一个愚蠢的错误,这个错误在我的 application_policy.rb 中应该包含 user.employee 在初始化中。

def initialize(user, record)
    raise Pundit::NotAuthorizedError, "must be logged in with a user account" unless (user.admin? || user.employee?)
    @user = user
    @record = record
  end