我正在努力为与我的用户模型没有直接关系的模型授权索引。实际上,我正在努力围绕Pundit范围的想法。
我知道我无法在SitePolicy中授权@sites,而且从我所做的所有阅读中,我相信我只需要在SitePolicy类的范围内进行,但我不确定如何执行此操作。
这就是我所拥有的:
模型
User
has_one :business
has_many :locations, :through => :business
end
Business
belongs_to :user
has_many :locations
end
Location
extend FriendlyId
belongs_to :business
has_one :user, :through => :business
has_many :sites, dependent: :destroy
friendly_id :custom_url, use: :slugged
end
Site
belongs_to :location
end
的routes.rb
resources :locations do
resources :sites
end
sites_controller.rb
class SitesController < ApplicationController
before_action :set_site, only: [:show, :edit, :update, :destroy]
before_action :set_location, only: [:new, :show, :edit, :index, :update, :destroy]
def index
@sites = @location.sites.all
authorize Site
end
private
def set_site
@site = Site.find(params[:id])
end
def set_location
@location = Location.friendly.find(params[:location_id])
end
def site_params
params.require(:site).permit(:location_id, :site, :url, :review_site_id, :number_of_reviews, :average_rating, :extra_data)
end
end
site_policy.rb
class SitePolicy < ApplicationPolicy
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
if user.has_role? :admin
scope.all
else
scope.where(scope.location.user == user)
end
end
end
def index?
return true if user.present? and user.has_role? :admin
end
...
schema.rb
ActiveRecord::Schema.define(version: 2018_05_28_085645) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "businesses", force: :cascade do |t|
t.string "name"
t.text "description"
t.bigint "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "more_than_one_location"
t.boolean "signed_up"
t.string "slug"
t.index ["more_than_one_location"], name: "index_businesses_on_more_than_one_location"
t.index ["user_id"], name: "index_businesses_on_user_id"
end
create_table "friendly_id_slugs", force: :cascade do |t|
t.string "slug", null: false
t.integer "sluggable_id", null: false
t.string "sluggable_type", limit: 50
t.string "scope"
t.datetime "created_at"
t.index ["slug", "sluggable_type", "scope"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type_and_scope", unique: true
t.index ["slug", "sluggable_type"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type"
t.index ["sluggable_id"], name: "index_friendly_id_slugs_on_sluggable_id"
t.index ["sluggable_type"], name: "index_friendly_id_slugs_on_sluggable_type"
end
create_table "locations", force: :cascade do |t|
t.string "location_name"
t.string "address_line_1"
t.string "address_line_2"
t.string "city"
t.string "region"
t.string "country"
t.string "postal_code"
t.string "website"
t.string "phone_number"
t.string "location_contact_email"
t.bigint "business_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "slug", null: false
t.string "custom_url", null: false
t.index ["business_id"], name: "index_locations_on_business_id"
t.index ["custom_url"], name: "index_locations_on_custom_url", unique: true
t.index ["slug"], name: "index_locations_on_slug", unique: true
end
create_table "roles", force: :cascade do |t|
t.string "name"
t.string "resource_type"
t.bigint "resource_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["name", "resource_type", "resource_id"], name: "index_roles_on_name_and_resource_type_and_resource_id"
t.index ["resource_type", "resource_id"], name: "index_roles_on_resource_type_and_resource_id"
end
create_table "sites", force: :cascade do |t|
t.bigint "location_id"
t.string "site"
t.string "url"
t.string "review_site_id"
t.integer "number_of_reviews"
t.decimal "average_rating"
t.jsonb "extra_data", default: {}, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["extra_data"], name: "index_sites_on_extra_data", using: :gin
t.index ["location_id"], name: "index_sites_on_location_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
create_table "users_roles", id: false, force: :cascade do |t|
t.bigint "user_id"
t.bigint "role_id"
t.index ["role_id"], name: "index_users_roles_on_role_id"
t.index ["user_id", "role_id"], name: "index_users_roles_on_user_id_and_role_id"
t.index ["user_id"], name: "index_users_roles_on_user_id"
end
add_foreign_key "businesses", "users"
add_foreign_key "locations", "businesses"
add_foreign_key "sites", "locations"
end
答案 0 :(得分:3)
要授权记录集合,通常使用范围执行此操作,而不是授权操作本身。换句话说,而不是:
def index
@sites = @location.sites.all
authorize Site
end
你需要这样做:
def index
@sites = policy_scope(@location.sites)
end
由于站点和用户之间的链接经历了多个模型,因此您不幸需要JOIN
一直回到用户,以便在SQL中执行此查询:
class SitePolicy < ApplicationPolicy
class Scope < Scope
def resolve
if user.has_role? :admin
scope.all
else
scope.joins(location: :business)
.where(locations: {businesses: {user: user}})
end
end
end
end
遵循此设计模式是Pundit recommends向您的应用程序添加以下代码的原因:
class ApplicationController < ActionController::Base
include Pundit
after_action :verify_authorized, except: :index
after_action :verify_policy_scoped, only: :index
end