Używam gem Pundit (z Devise i Rolify), aby ograniczyć dostęp do informacji na podstawie zalogowanych ról użytkownika.Implementowanie zakresów w Pundit
W tej chwili mam zdefiniowane trzy role dla mojego modelu użytkownika: Admin, Client Admin i Customer Admin.
Użytkownik należy do klienta. Klient ma wielu użytkowników.
Pomyślnie zaimplementowałem politykę Pundit, gdy indeksowanie modelu klienta. Administratorzy i administratorzy klienta widzą wszystkich klientów. Administrator klienta może zobaczyć tylko swój rekord OWN.
Problem polega na tym, że próbuję ograniczyć metodę klienta kontrolera show. Administratorzy i administratorzy klienta widzą wszystkich klientów. Jednak administrator klienta powinien mieć dostęp tylko do własnego rekordu. Ale w tej postaci Administrator klienta może wprowadzić dowolny identyfikator w adresie URL i zobaczyć dowolny rekord klienta.
Jestem niewyraźny w sprawie zakresu. Rozumiem, że metody Polityki (tj. Index? I show?) Mają ograniczać WHO do wykonywania tych akcji, a metody Scopingu ograniczają, DO KTÓRYCH można uzyskać RECORDS. Mam problem z komponowaniem poprawnego zakresu dla powyższego scenariusza.
Oto kontroler klienta:
class CustomersController < ApplicationController
before_action :set_customer, only: [:show, :edit, :update, :destroy]
after_action :verify_authorized
# GET /customers
# GET /customers.json
def index
@customers = policy_scope(Customer)
authorize Customer
end
# GET /customers/1
# GET /customers/1.json
def show
authorize @customer
end
# GET /customers/new
def new
@customer = Customer.new
authorize @customer
end
# GET /customers/1/edit
def edit
authorize @customer
end
# POST /customers
# POST /customers.json
def create
@customer = Customer.new(customer_params)
authorize @customer
respond_to do |format|
if @customer.save
format.html { redirect_to @customer, notice: 'Customer was successfully created.' }
format.json { render :show, status: :created, location: @customer }
else
format.html { render :new }
format.json { render json: @customer.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /customers/1
# PATCH/PUT /customers/1.json
def update
authorize @customer
respond_to do |format|
if @customer.update(customer_params)
format.html { redirect_to @customer, notice: 'Customer was successfully updated.' }
format.json { render :show, status: :ok, location: @customer }
else
format.html { render :edit }
format.json { render json: @customer.errors, status: :unprocessable_entity }
end
end
end
# DELETE /customers/1
# DELETE /customers/1.json
def destroy
authorize @customer
@customer.destroy
respond_to do |format|
format.html { redirect_to customers_url, notice: 'Customer was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_customer
@customer = Customer.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def customer_params
params.require(:customer).permit(:name, :parent_customer_id, :customer_type, :active, :currency)
end
end
A oto polityka klienta:
class CustomerPolicy < ApplicationPolicy
def index?
# Admins, ClientAdmins, and CustomerAdmins can index customers (see Scope class for filters)
@user.has_role? :admin or @user.has_role? :client_admin or @user.has_role? :customer_admin
end
def show?
# Admins, ClientAdmins, and CustomerAdmins can see any customer details
@user.has_role? :admin or @user.has_role? :client_admin or @user.has_role? :customer_admin
end
def update?
# Only Admins and ClientAdmins can update customer details
@user.has_role? :admin or @user.has_role? :client_admin
end
def destroy?
@user.has_role? :admin or @user.has_role? :client_admin
end
class Scope < Struct.new(:user, :scope)
def resolve
if (user.has_role? :admin or user.has_role? :client_admin)
# Admins and ClientAdmins can see all Customers
scope.where(:parent_id => nil)
elsif user.has_role? :customer_admin
# Customer Admins can only see their own Customer
scope.where(:id => user.customer) # THIS DOES NOT APPEAR TO GET INVOKED BY THE SHOW METHOD OF THE CONTROLLER
end
end
def show?
# NOT SURE WHAT TO PUT IN HERE
end
end
end
Sukces !! Dzięki tej niespodzianki, którą dostałem od railscard, sztuczka polegała na modyfikacji show? metoda w pliku zasad klienta jak następuje:
def show?
# Admins, ClientAdmins, and CustomerAdmins can see any customer details
# Students cannot see customer details
return true if user.has_role?(:admin) || user.has_role?(:client_admin)
return true if user.customer_id == @record.id && user.has_role?(:customer_admin)
false
end
Zauważ, że musiałem użyć zmiennej instancji @record, jako że to właśnie klasa polityka aplikacja używa w odniesieniu do rekordu są przekazywane w metodą autoryzacji.
Dzięki!
Z góry dziękujemy za pomoc. Rozumiem, co mówisz - zakres powinien być używany, gdy oczekuję zwrotu kolekcji. Ale nie jestem pewien, gdzie umieścić tę metodę, którą opisałeś. Czy to by było w polityce klienta? Przepraszam. To sprawia, że czuję się zupełnie głupio. –
Aby było jasne: Próbowałem wstawić metodę do pliku zasad klienta i nie widzę żadnej różnicy w zachowaniu. Chyba nie jestem pewien, co wywołałoby tę metodę. –
GOT IT! Jesteś moim bohaterem, dziękuję bardzo. W pytaniu opublikuję poprawiony kod, ale była to właściwie właściwa odpowiedź. –