2014-07-07 16 views
5

Utilizzo la gemma Pundit (con Devise e Rolify) per limitare l'accesso alle informazioni in base ai ruoli utente registrati.Implementazione di ambiti in Pundit

In questo momento sono definiti tre ruoli per il modello Utente: Amministratore, Amministratore cliente e Amministratore cliente.

Un utente appartiene a un cliente. Il cliente ha molti utenti.

Ho implementato correttamente un criterio Pundit quando indicizza il modello Cliente. Amministratori e amministratori clienti possono vedere tutti i clienti. L'amministratore del cliente può vedere solo il proprio record PROPRIO.

Il problema si trova quando si tenta di limitare il metodo show del controller del cliente. Amministratori e amministratori clienti possono vedere tutti i clienti. Tuttavia, l'amministratore del cliente dovrebbe essere in grado di vedere solo il proprio record. Ma così com'è l'amministratore del cliente può inserire qualsiasi id nell'URL e vedere qualsiasi record del cliente.

Sono confuso sullo scoping. Sono a conoscenza del fatto che i metodi della politica (cioè l'indice e lo spettacolo?) Limitano l'OMS in grado di eseguire queste azioni e che i metodi di Scoping limitano QUALI RECORD possono essere ottenuti. Ho difficoltà a comporre l'ambito corretto per lo scenario sopra.

Ecco il controller clienti:

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 

E qui è la politica del cliente:

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 

Successo !! Grazie all'avvio da me fornito dalla railscard, il trucco era modificare lo spettacolo? metodo nel file politica di cliente come il seguente:

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 

Nota che ho dovuto utilizzare la variabile di istanza @record, come è quello che utilizza la classe politica di applicazione di fare riferimento al record di essere passato dal metodo di autorizzare.

Grazie !!

risposta

4

Penso che non sia necessario l'ambito per limitare l'accesso all'azione show.

def show? 
    return true if user.has_role? :admin || user.has_role? :client_admin 
    return true if user.customer_id == customer.id && user.has_role? :customer_admin 
    false 
end 

Ambiti di ambito solitamente utilizzati per recuperare un elenco di record a cui l'utente ha accesso. In caso di show metodo (o qualsiasi altro metodo di controllo, dove si chiama authorize) Pundit un'istanza della classe politica con l'utente corrente e determinato cliente e poi semplicemente chiama show? metodo per controllare i permessi degli utenti, vale a dire CustomerPolicy.new(current_user, @customer).show?

+0

Grazie in anticipo per l'aiuto. Capisco cosa stai dicendo - lo scope dovrebbe essere usato quando mi aspetto di restituire una collezione. Ma non sono sicuro di dove mettere questo metodo che hai descritto. Sarebbe nella politica del cliente? Scusate. Questo mi sta facendo sentire completamente stupido. –

+0

Per essere chiari: ho provato a inserire il metodo nel file dei criteri cliente e non vedo alcuna differenza di comportamento. Immagino di non essere sicuro di cosa scatenerebbe quel metodo. –

+0

OTTENUTO! Sei il mio eroe grazie mille. Inserirò il codice revisionato nella domanda, ma questa era quasi la risposta giusta. –

5

Per ottenere scoping del Pundit lavoro per l'azione show, è possibile utilizzare l'helper Pundit policy_scope (o policy_scope!), o dal numero ApplicationPolicy generato, è possibile ereditare lo show?.

L'azione index utilizza già policy_scope correttamente, abbiamo solo bisogno di fare qualcosa di simile per l'azione show.Ecco alcune opzioni:

Opzione 1: modificare l'azione show a

def show 
    # Also remove :show from the :only option where 
    # before_action :set_customer, only: ... is called. 
    @customer = policy_scope(Customer).find(params[:id]) 
    authorize @customer 
end 

O

Opzione 2: Modificare set_customer per

def set_customer 
    @customer = policy_scope(Customer).find(params[:id]) 
end 

O

Opzione 3: modifica Visualizza clientePolitica? a

def show? 
    # scope call here will return the 
    # result of CustomerPolicy::Scope#resolve 
    # This is the same implementation generated 
    # in the default ApplicationPolicy so you could 
    # just delete this method here and inherit instead. 
    scope.where(:id => record.id).exists? 
end 

Here's the code che genera il metodo predefinito ApplicationPolicy#show?.

Vedere la sezione README di Pundit su Scopes per ulteriori dettagli.

Penso che si possa tranquillamente cancellare il metodo show? vuoto che si ha in CustomerPolicy::Scope, non credo che verrà chiamato.

Problemi correlati