J'utilise Pundit avec Rails, et j'ai un contrôleur que je dois complètement restreindre à partir d'un rôle d'utilisateur spécifique. Mes rôles sont «Personnel» et «Consommateur». Le personnel doit avoir un accès complet au contrôleur, mais les consommateurs ne doivent pas y avoir accès.
Existe-t-il un moyen de le faire qui soit plus SEC que de restreindre chaque action une par une?
exemple, voici ma politique:
class MaterialsController < ApplicationController before_action :set_material, only: [:show, :edit, :update, :destroy] # GET /materials def index @materials = Material.all authorize @materials end # GET /materials/1 def show authorize @material end # GET /materials/new def new @material = Material.new authorize @material end # GET /materials/1/edit def edit authorize @material end # POST /materials def create @material = Material.new(material_params) authorize @material respond_to do |format| if @material.save format.html { redirect_to @material, notice: 'Material was successfully created.' } else format.html { render :new } end end end # PATCH/PUT /materials/1 def update authorize @material respond_to do |format| if @material.update(material_params) format.html { redirect_to @material, notice: 'Material was successfully updated.' } else format.html { render :edit } end end end # DELETE /materials/1 def destroy authorize @material @material.destroy respond_to do |format| format.html { redirect_to materials_url, notice: 'Material was successfully destroyed.' } end end private # Use callbacks to share common setup or constraints between actions. def set_material @material = Material.find(params[:id]) end # Never trust parameters from the scary internet, only allow the white list through. def material_params params.require(:material).permit(:name) end end
Et mon contrôleur:
class MaterialPolicy < ApplicationPolicy attr_reader :user, :material def initialize(user, material) @user = user @material = material end def index? user.staff? end def show? index? end def new? index? end def edit? index? end def create? index? end def update? create? end def destroy? update? end end
Y a-t-il un moyen de faire ceci que je Je ne comprends pas, ou est-ce ainsi que Pundit est conçu pour exiger que vous soyez explicite?
3 Réponses :
Utilisez le deuxième argument de la méthode authorize
. Par exemple:
authorize @material, :index?
Vous pouvez maintenant supprimer toutes les autres méthodes qui appellent simplement index?
Voulez-vous dire dans la politique? Existe-t-il un moyen de restreindre l'accès à un contrôleur dans son ensemble et de ne pas avoir à appeler autoriser pour chaque action?
@LeeMcAlilly Je pense que vous pouvez utiliser before_action
car vous n'avez besoin que de l'utilisateur
Merci une action avant a fait l'affaire pour le contrôleur, mais ce fichier de politique semble toujours faux. N'y a-t-il pas une manière plus concise d'exprimer cela?
Pundit ne vous oblige pas à être explicite, mais il le permet. Si la méthode index?
de votre stratégie n'a pas été dupliquée, vous voudrez que la capacité soit explicite.
Vous pouvez commencer par envisager de déplacer certaines des vérifications d'autorisation dans la méthode set_material
, qui réduit plus de la moitié des vérifications.
L'autre moitié pourrait être extraite dans d'autres méthodes privées si vous le souhaitez, mais je pense qu'elles sont parfaites telles quelles.
Vous pouvez également envisager d'ajouter un rappel before_action pour appeler l'autorisation en fonction du nom de l'action, après avoir mémorisé @material
via votre autre rappel, mais la lisibilité en souffrira probablement. p>
Merci. Ça a du sens. Existe-t-il un moyen de rendre la classe de politiques plus concise? Pour le moment, la seule façon de le faire fonctionner est de répertorier explicitement chaque action dans la stratégie et de déclarer qui est en mesure d'accéder à cette action. N'y a-t-il pas un moyen de les définir tous à la fois dans la politique? Je jouais avec Scope mais je n'arrive pas à le faire fonctionner.
Vous pouvez utiliser quelque chose comme alias
ou alias_method
. Nous rédigeons nos politiques comme vous l'avez rédigée. Cela facilite leur modification plus tard. Les portées sont un peu plus délicates; vous devez utiliser policy_scope
ou appeler la méthode de résolution de la politique explicitement (par exemple, MaterialPolicy :: Scope.new (current_user, Material) .resolve`. Tant que votre politique hérite d'une ApplicationPolicy de base en suivant l'exemple ou en implémentant la même structure elle-même, cela devrait "simplement fonctionner" github.com/varvet/pundit #scopes . Si vous rencontrez toujours des problèmes avec la portée, déposez une question avec ce que vous avez
La première étape consiste simplement à déplacer l'appel à autoriser vers votre rappel:
class StaffPolicy < ApplicationPolicy %i[ show? index? new? create? edit? update? delete? ].each do |name| define_method name do user.staff? end end end class MaterialPolicy < StaffPolicy # this is how you would add additional restraints in a subclass def show? super && some_other_condition end end
Vous pouvez également écrire @material = autoriser Material.find (params [: id])
si votre version de Pundit est à jour (les versions précédentes renvoyaient vrai / faux au lieu de l'enregistrement).
Pundit a une énorme flexibilité dans la façon dont vous choisissez de l'utiliser. Vous pouvez par exemple créer une politique sans tête distincte :
class MaterialsController < ApplicationController before_action :authorize_staff # ... def authorize_staff authorize :staff, :access? end end