2017-10-06 4 views
0

J'utilise le pundit gem afin de donner des permissions à trois utilisateurs différents (Admin, vendeur, spectateur). Actuellement, j'ai tout ce qui fonctionne, l'administrateur a accès à tout, le vendeur à ses propres produits et le spectateur peut simplement voir les produits.Rails pundit gem, permettre la vue aux utilisateurs non-signés

Le seul problème que j'ai, c'est que je voudrais que les utilisateurs de non signed_up/signed_in puissent voir les produits via les résultats de recherche. Actuellement, les utilisateurs non sign_up/signed_in peuvent voir les résultats de la recherche mais n'ont pas accès à la vue du diaporama.

Voici la configuration que j'ai:

class ItemPolicy < ApplicationPolicy 
    attr_reader :item 

    def initialize(user, record) 
    super(user, record) 
    @user = user 
    @item = record 
    end 

    def update? 
    @user.is_a?(Admin) ? item.all : @user.items 
    end 

    def index? 
    @user.is_a?(Admin) ? item.all : @user.items 
    end 

    def show? 
    @user.is_a?(Admin) ? item.all : @user.items 
    end 

    def create? 
    @user.is_a?(Admin) ? item.all : @user.items 
    end 

    def new? 
    @user.is_a?(Admin) ? item.all : @user.items 
    end 

    def edit? 
    @user.is_a?(Admin) ? item.all : @user.items 
    end 

    def destroy? 
    @user.is_a?(Admin) ? item.all : @user.items 
    end 

    class Scope < Struct.new(:user, :scope) 
    def resolve 
     if user.is_a?(Admin) 
     scope.where(:parent_id => nil) 
     elsif user.is_a?(Seller) 
     scope.where(:id => user.items) 
     end 
    end 

    def show? 
     return true if user.is_a?(Admin) 
     return true if user.seller_id == seller.id && user.is_a?(Seller) 
    false 
    end 
    end 
end 

le contrôleur:

class ItemsController < ApplicationController 
    before_action :set_item, only: [:show, :edit, :update, :destroy] 


    def index 
    authorize Item 
    @items = policy_scope(Item) 
    end 

    def search 
    if params[:term] 
     @items = Item.search(params[:term]).order("created_at DESC") 
    else 
     @items = [] 
    end 
    end 


    def show 
    @comments = Comment.where(item_id: @item).order("created_at DESC") 
    @items = policy_scope(Item).find(params[:id]) 
    authorize @item 
    end 


    def new 
    @item = Item.new 
    authorize @item 
    @categories = Category.order(:name) 
    end 


    def edit 
    authorize @item 
    @categories = Category.order(:name) 
    end 


    def create 
    @item = Item.new(item_params) 
    authorize @item 

    respond_to do |format| 
     if @item.save 
     format.html { redirect_to @item, notice: 'Item was successfully created.' } 
     format.json { render :show, status: :created, location: @item } 
     else 
     format.html { render :new } 
     format.json { render json: @item.errors, status: :unprocessable_entity } 
     end 
    end 
    end 


    def update 
    authorize @item 
    respond_to do |format| 
     if @item.update(item_params) 
     format.html { redirect_to @item, notice: 'Item was successfully updated.' } 
     format.json { render :show, status: :ok, location: @item } 
     else 
     format.html { render :edit } 
     format.json { render json: @item.errors, status: :unprocessable_entity } 
     end 
    end 
    end 


    def destroy 
    authorize @item 
    @item.destroy 
    respond_to do |format| 
     format.html { redirect_to items_url, notice: 'Item was successfully destroyed.' } 
     format.json { head :no_content } 
    end 
    end 

    private 
    def set_item 
     @item = Item.find(params[:id]) 
     authorize @item 
    end 


    def item_params 
     params.require(:item).permit(:title, :description, :image, :price, :category_id) 
    end 
end 

application_contoller.rb

class ApplicationController < ActionController::Base 
    include Pundit 
    protect_from_forgery prepend: true 

    rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized 

    def pundit_user 
    current_seller || current_admin || current_viewer 
    end 

    private 

    def user_not_authorized(exception) 
    policy_name = exception.policy.class.to_s.underscore 
    flash[:warning] = t "#{policy_name}.#{exception.query}", scope: "pundit", default: :default 
    redirect_to(request.referrer || root_path) 
    end 
end 

Update 1

class ApplicationPolicy 
    attr_reader :user, :record 

    def initialize(user, record) 
    raise Pundit::NotAuthorizedError, "must be logged in" unless user 
    @user = user 
    @record = record 
    end 

    def index? 
    true # anybody can view 
    end 

    def show? 
    true # anybody can view 
    end 

    def search? 
    index? 
    end 

    def new? 
    create? 
    end 

    def edit? 
    update? 
    end 

    def destroy? 
    update? 
    end 

    private 
    # don't repeat yourself 
    def admin? 
    user.is_a?(Admin) 
    end 

    def seller? 
    user.is_a?(Seller) 
    end 
end 


class ItemPolicy < ApplicationPolicy 
    attr_reader :item 

    class Scope < Struct.new(:user, :scope) 
    def resolve 
     if admin? 
     scope.where(parent_id: nil) 
     elsif seller? 
     # avoids a query for user.items 
     scope.where(seller: user) 
     end 
    end 
    end 

    def initialize(user, record) 
    super(user, record) 
    @user = user 
    @item = record 
    end 

    def update? 
    admin? || is_owner? 
    end 

    def create? 
    # just guessing here 
    admin? || seller? 
    end 

    private 

    def is_owner? 
    # or whatever the association between the item and its owner is 
    item.seller == user 
    end 
end 

Répondre

1

Vous êtes en train de confondre les étendues et les méthodes d'autorisation dans Pundit. Les méthodes new?, show? etc doivent renvoyer un booléen indiquant si l'utilisateur est autorisé à effectuer l'action du tout.

Pour permettre aux utilisateurs non autorisés d'effectuer une action, vous renvoyez simplement true. Les étendues servent à affiner les enregistrements auxquels l'utilisateur a accès. Ils ont seulement une méthode resolve.

class ApplicationPolicy 
    # ... 

    private 
    # don't repeat yourself 
    def admin? 
    user.is_a?(Admin) 
    end 

    def seller? 
    user.is_a?(Seller) 
    end 
end 


class ItemPolicy < ApplicationPolicy 
    attr_reader :item 

    class Scope < Struct.new(:user, :scope) 
    def resolve 
     if admin? 
     scope.where(parent_id: nil) 
     elsif seller? 
     # avoids a query for user.items 
     scope.where(seller: user) 
     end 
    end 
    end 

    def initialize(user, record) 
    super(user, record) 
    @user = user 
    @item = record 
    end 

    def update? 
    admin? || is_owner? 
    end 

    def index? 
    true # anybody can view 
    end 

    def show? 
    true # anybody can view 
    end 

    def search? 
    index? 
    end 

    def create? 
    # just guessing here 
    admin? || seller? 
    end 

    def new? 
    create? 
    end 

    def edit? 
    update? 
    end 

    def destroy? 
    update? 
    end 

    private 

    def is_owner? 
    # or whatever the association between the item and its owner is 
    item.seller == user 
    end 
end 

Au lieu de vous répéter (les conditions), vous pouvez de raccourci plusieurs des actions depuis les autorisations pour l'édition par exemple, est la même que la mise à jour. Vous pouvez même le faire dans le ApplicationPolicy afin que vous n'avez pas de le répéter dans chaque classe politique:

class ApplicationPolicy 
    # ... 

    def index? 
    true # anybody can view 
    end 

    def show? 
    true # anybody can view 
    end 

    def search? 
    index? 
    end 

    def new? 
    create? 
    end 

    def edit? 
    update? 
    end 

    def destroy? 
    update? 
    end 

    private 
    # don't repeat yourself 
    def admin? 
    user.is_a?(Admin) 
    end 

    def seller? 
    user.is_a?(Seller) 
    end 
end 

class ItemPolicy < ApplicationPolicy 
    attr_reader :item 

    class Scope < Struct.new(:user, :scope) 
    def resolve 
     if admin? 
     scope.where(parent_id: nil) 
     elsif seller? 
     # avoids a query for user.items 
     scope.where(seller: user) 
     end 
    end 
    end 

    def initialize(user, record) 
    super(user, record) 
    @user = user 
    @item = record 
    end 

    def update? 
    admin? || is_owner? 
    end 

    def create? 
    # just guessing here 
    admin? || seller? 
    end 

    private 

    def is_owner? 
    # or whatever the association between the item and its owner is 
    item.seller == user 
    end 
end 

Vous autorisez également à l'utilisateur deux fois à de nombreux endroits dans votre contrôleur car il est comme déjà réalisée par le set_item callback:

class ItemsController < ApplicationController 
    before_action :set_item, only: [:show, :edit, :update, :destroy] 

    def index 
    authorize Item 
    @items = policy_scope(Item) 
    end 

    def search 
    if params[:term] 
     @items = Item.search(params[:term]).order("created_at DESC") 
    else 
     @items = Item.none # Don't use [] as @items.none? for example would blow up. 
    end 
    end 


    def show 
    @comments = Comment.where(item_id: @item).order("created_at DESC") 
    authorize @item 
    end 


    def new 
    @item = authorize(Item.new) 
    @categories = Category.order(:name) 
    end 


    def edit 
    @categories = Category.order(:name) 
    end 


    def create 
    @item = authorize(Item.new(item_params)) 

    respond_to do |format| 
     if @item.save 
     format.html { redirect_to @item, notice: 'Item was successfully created.' } 
     format.json { render :show, status: :created, location: @item } 
     else 
     format.html { render :new } 
     format.json { render json: @item.errors, status: :unprocessable_entity } 
     end 
    end 
    end 

    def update 
    respond_to do |format| 
     if @item.update(item_params) 
     format.html { redirect_to @item, notice: 'Item was successfully updated.' } 
     format.json { render :show, status: :ok, location: @item } 
     else 
     format.html { render :edit } 
     format.json { render json: @item.errors, status: :unprocessable_entity } 
     end 
    end 
    end 


    def destroy 
    @item.destroy 
    respond_to do |format| 
     format.html { redirect_to items_url, notice: 'Item was successfully destroyed.' } 
     format.json { head :no_content } 
    end 
    end 

    private 
    def set_item 
     @item = authorize(Item.find(params[:id])) 
    end 


    def item_params 
     params.require(:item).permit(:title, :description, :image, :price, :category_id) 
    end 
end 
+0

Merci pour l'info et aide encore une fois @max !!! J'ai mis à jour mon code en fonction de votre réponse, mais je ne peux toujours pas obtenir les «utilisateurs non autorisés» pour voir la page d'affichage ... même lorsqu'un utilisateur est connecté, il peut voir et modifier tous les éléments maintenant. J'ai ajouté la mise à jour 1 à ma question afin que vous puissiez voir ce que j'ai fait jusqu'à présent. – Theopap

+0

Un administrateur peut voir et modifier tous les éléments. Si ce n'est pas ce que vous voulez changer la logique dans le #update? méthode. Aussi, pour permettre aux utilisateurs non autorisés d'accéder à une ressource, vous devez ignorer le 'authorize_user' de Devise. (Ou quoi que ce soit) rappel. – max

+0

Oui c'est exactement je veux, l'administrateur d'avoir accès à tous, le vendeur seulement son sur les articles, le spectateur seulement l'index des résultats de recherche et de montrer .... mais actuellement avec vos modifications ... un vendeur connecté peut modifier/afficher/supprimer tous les éléments. – Theopap