2013-07-29 6 views
5

Sto tentando di nascondere parti delle mie viste a seconda del ruolo Utente.Come nascondere parti della vista dato un ruolo utente su Rails 4

Quindi diciamo che voglio che solo gli amministratori siano in grado di distruggere i Prodotti. Oltre il codice nel controller per prevenire gli utenti regolari di distruggere record, vorrei fare quanto segue nella visualizzazione:

<% if current_user.admin? %> 
    <%= link_to 'Delete', product, method: :delete %> 
<% end %> 

Il codice precedente funziona, ma è soggetto a errori di omissione, che possono causare gli utenti regolari di vedere link a azioni che non sono autorizzati a eseguire.

Inoltre, se decido in seguito che un nuovo ruolo (ad esempio "moderatore") può cancellare prodotti, avrei dovuto trovare il punto di vista che visualizzano un collegamento eliminare e aggiungere la logica che consente moderatori per vederlo.

E se ci sono molti modelli che possono essere eliminati solo dagli utenti amministratori (ad es. Promozione, utente) la manutenzione di tutti i if sarebbe piuttosto impegnativa.

C'è un modo migliore di farlo? Forse usando aiutanti o qualcosa di simile? Sto cercando qualcosa di forse come questo:

<%= destroy_link 'Delete', product %> # Only admins can see it 
<%= edit_link 'Edit', promotion %> # Again, only admins see this link 
<%= show_link 'Show', comment %> # Everyone sees this one 

Ho trovato queste due domande che sono simili al mio, ma nessuno di loro risposto alla mia domanda:

Show and hide based on user role in rails

Ruby on Rails (3) hiding parts of the view

risposta

6

Consiglio vivamente lo pundit.

Permette di creare "politiche" per ogni modello. Per il vostro modello di Product si potrebbe avere un ProductPolicy che sembra qualcosa di simile

class ProductPolicy < ApplicationPolicy 
    def delete? 
    user.admin? 
    end 
end 

Secondo lei si può fare qualcosa di simile

<% if policy(@post).delete? %> 
    <%= link_to 'Delete', product, method: :delete %> 
<% end %> 

Se in seguito si desidera aggiungere un ruolo moderator, basta modificare il metodo della politica

class ProductPolicy < ApplicationPolicy 
    def delete? 
    user.admin? || user.moderator? 
    end 
end 
+1

+1 per [pundit] (https://github.com/elabs/pundit); confronto con "mainstream" [CanCan] (https: // github.com/ryanb/cancan) più facile da capire come esplicita, stessa facilità d'uso nelle viste e logica dei controller, e molto più flessibile (non complicato quando usato insieme a [rolify] (https://github.com/EppO/rolify)) Fortemente consigliato anche qui! – CloudRide

+0

Scegli questo come risposta corretta. Non mi è piaciuta molto la mia risposta. : P –

1

CanCan è un altro gioiello che consente di definire "Abilità" per ogni ruolo utente. Nelle viste è possibile utilizzare qualcosa come if can? :delete, @post per verificare se l'utente può eliminare quel post specifico.

1

Quindi ho immaginato un modo per spostare gli IF fuori dalla vista. In primo luogo, sovrascrivo l'helper link_to nel mio application_helper.rb:

def link_to(text, path, options={}) 
    super(text, path, options) unless options[:admin] and !current_user.admin? 
end 

Poi, il mio punto di vista lo uso come:

<%= link_to 'Edit Product', product, admin: true, ... %> 

Questo impedisce agli utenti regolari di vedere link di amministrazione, ma per gli altri tag html con contenuto all'interno, come div, tabelle ecc., se sarebbe ancora necessario.

0

Utilizzando le gemme CanCan e Role, ciò che è ancora necessario è un modo per controllare il percorso e vedere se "current_user" ha le autorizzazioni per accedere a quella rotta in base al ruolo o ai ruoli, quindi mostrare/nascondere in base a ciò.

Ciò consente di risparmiare il clic dell'utente sulle cose e sempre detto che non possono vederlo - oppure dover scrivere per-voce "se" logica specificare quali ruoli possono vedere ciò che list-elementi (quali il cliente cambia periodicamente, come i ruoli vengono modificati/perfezionati) attorno a ogni singolo link nel menu di uno (considera un menu di bootstrap con oltre 50 elementi nidificati in gruppi con formattazione HTML, ecc.), che è folle.

Se dobbiamo mettere la logica if in ogni voce di menu, usiamo la stessa logica per ogni elemento controllando il ruolo/le autorizzazioni che abbiamo già definito nel file Abilità.

Ma nella nostra lista di menu, abbiamo helper di route - non informazioni "controller/metodo", quindi come testare la capacità dell'utente di colpire l'azione del controller specificata per il "percorso" in ciascun collegamento?

Per ottenere il controller e il metodo (azione) di un percorso (i miei esempi si utilizza il 'users_path' itinerario-helper) ...

Rails.application.routes.recognize_path(app.users_path) 
     => {:controller=>"users", :action=>"index"} 

ottenere solo il nome del controller

Rails.application.routes.recognize_path(app.users_path)[:controller] 
     => "users" 

abilità usa il modello per la sua ripartizione, in modo da convertire da nome del controllore ad esso è il modello (assumendo la denominazione di default usata) ...

Rails.application.routes.recognize_path(app.users_path)[:controller].classify 
     => "User" 

ottenere solo l'azione nome

Rails.application.routes.recognize_path(app.users_path)[:action] 
     => "index" 

E poiché il "può?" metodo ha bisogno di un simbolo per l'azione, e Constant per il modello, per ogni voce di menu otteniamo questo:

path_hash = Rails.application.routes.recognize_path(app.users_path) 
    model = path_hash[:controller].classify.constantize 
    action = path_hash[:action].to_sym 

Poi utilizzare il nostro sistema abilty esistente per verificare se il current_user può accedervi, dobbiamo passare il azione come un simbolo e il modello come una costante, in modo da ...

<% if can? action model %> 
     <%= link_to "Users List", users_path %> 
    <% end %> 

Ora siamo in grado di cambiare chi può vedere questa risorsa e il link dal file Ability, senza mai fare scherzi con il menu, ancora una volta. Ma per rendere questo un po 'più pulita, ho estratto la ricerca per ogni voce di menu con questo in app-Controller:

def get_path_parts(path) 
    path_hash = Rails.application.routes.recognize_path(path) 
    model_name = path_hash[:controller].classify.constantize 
    action_name = path_hash[:action].to_sym 
    return [model_name, action_name] 
end 
helper_method :get_path_parts 

... così ho potuto fare questo nella vista (ho tirato fuori tutto il html-formattazione da collegamenti per la semplicità, qui):

<% path_parts = get_path_parts(users_path); if can?(path_parts[1], path_parts[0]) %> 
    <%= link_to "Users Listing", users_path %> 
<% end %> 

... e per fare questo ci vuole tutto il giorno digitando questi per-voci di menu IF-impacchi, ho usato regex trova/sostituisci con la cattura e caratteri jolly per avvolgere questo elenco attorno ad ogni elemento della lista di menu in un unico passaggio.

Non è l'ideale, e potrei fare molto di più per renderlo molto migliore, ma non ho tempo libero per scrivere il resto di questo pezzo mancante del sistema Role/CanCan. Spero che questa parte aiuti qualcuno.

Problemi correlati