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