Как отфильтровать объект в рельсах, чтобы только пользователь, создавший его, или администратор мог уничтожить этот объект?

У меня есть простое приложение rails, в котором пользователи могут создавать цитаты (например, «Две вещи бесконечны: вселенная и человеческая глупость; и я не уверен насчет вселенной». — Альберт Эйнштейн и т. д.).

Я бы хотел, чтобы только пользователи, создавшие цитату, или администратор могли редактировать и удалять цитату.

В настоящее время у меня есть фильтр «до», который устанавливает пользователя, создавшего цитату, которая выглядит следующим образом:

before_action :correct_user, only: :destroy

Вот мой контроллер котировок:

class QuotesController < ApplicationController
  before_action :set_artist,   only: [:show, :edit, :update, :destroy]
  before_action :logged_in_user, only: [:create, :new, :destroy, :update, :edit ]
  before_action :correct_user,   only: :destroy

  def index
    @quotes = Quote.all.paginate(page: params[:page], per_page: 12)
  end

  def show
  end

  def new
    @quote = Quote.new
  end

  def create
    @quote = current_user.quotes.build(quote_params)
    if @quote.save
      flash[:success] = "Quote created!"
      redirect_to @quote
    else
      render :new
    end
  end

  def edit
  end

  def update
    if @quote.update(quote_params)
      flash[:success] = "Quote updated"
      redirect_to @quote
    else
      render :edit
    end
  end

  def destroy
    @quote.destroy
    flash[:success] = "Quote deleted"
    redirect_back(fallback_location: browse_path)
  end

  private

    def set_artist
      @quote = Quote.find(params[:id])
    end

    def quote_params
      params.require(:quote).permit(:content, :source, :topic_id, :speaker_id)
    end

    def correct_user
      @quote = current_user.quotes.find_by(id: params[:id])
      redirect_to root_url if @quote.nil?
    end
end

Каков идиоматически правильный способ сделать это в Rails? Должен ли я сделать что-то вроде этого:

def correct_user
  if user.admin?
    @quote = current_user.quotes.find_by(id: params[:id])
  else
   @quote = current_user.quotes.find_by(id: params[:id])
  end
  redirect_to root_url if @quote.nil?
end

Есть ли более краткий или Rails-способ сделать это, что мне не хватает? Кроме того, как вы гарантируете, что только пользователь, создавший предложение, может удалить или отредактировать его? Мой метод correct_user уже покрывает это?


person Lee McAlilly    schedule 11.02.2018    source источник


Ответы (2)


Я думаю, вы могли бы проверить, является ли пользователь администратором или user.id совпадает с quote.user_id, в таком случае вы возвращаете true, используя ||, вы возвращаете true, если какое-либо из двух выражений возвращает true, поэтому вы можете сделать что-то вроде:

def correct_user
  current_user.admin? || current_user.id == @quote.user_id
end

Таким образом, вы можете создать вспомогательный метод, который перенаправляет, если пользователь не является администратором или не является автором/владельцем цитаты:

before_action :check_permission, only: %i[edit destroy]

def correct_user
  current_user.admin? || current_user.id == @quote.user_id
end

def check_permission
  redirect_back(fallback_location: browse_path) unless correct_user
end

С обратным вызовом перед вы можете проверить редактирование и уничтожение и любые другие, если некоторые из этих двух выражений оцениваются как истинные.

person Sebastian Palma    schedule 11.02.2018

Я бы поставил два перед действиями.

before_action :resource, only: [:edit, :update, :destroy]
before_action :allow_admin, only: [:edit, :update, :destroy]

Сначала нужно найти ресурс цитаты

def resource
  @quote = current_user.quotes.find_by(id: params[:id])
end

Другие разрешат администратору доступ к ресурсу

def allow_admin
  if current_user.admin? && @quote.nil?
    @quote = Quote.find_by(id: params[:id])
    # Search all quotes, as admin has access to all
  elsif @quote.nil?
    redirect_to root_url
  end
end
person Nermin    schedule 11.02.2018