Предотвращение доступа других зарегистрированных пользователей к странице «редактировать»

#ruby-on-rails

#ruby-on-rails

Вопрос:

Я создаю небольшое веб-приложение, которое позволяет пользователям перечислять свои цели. Я хочу, чтобы пользователи могли редактировать только свой собственный контент. У меня уже есть функция аутентификации в качестве before_filter, которая проверяет, что кто-то вошел в систему, но она не проверяет, является ли пользователь создателем контента. Я попытался создать второй before_filter с именем correct_user, который имеет следующий код:

 def correct_user
  @user = User.find(params[:id])
  redirect_to(user_path(current_user)) unless current_user?(@user)
end 
  

Кроме того, вот результат выполнения сервером запроса get для редактирования моего собственного контента

 Started GET "/goals/31/edit" for 127.0.0.1 at 2011-05-18 15:22:38 -0400
  Processing by GoalsController#edit as HTML
  Parameters: {"id"=>"31"}
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE ("users"."id" = 101) LIMIT 1
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE ("users"."id" = 31) LIMIT 1
Redirected to http://localhost:3000/users/101 Completed 302 Found in 49ms
Completed 302 Found in 49ms
  

Для наглядности используемый мной идентификатор пользователя равен 101, а идентификатор цели, который я пытаюсь отредактировать, равен 31. Кто-нибудь может точно объяснить, что происходит?

Кроме того, я знаю, что вы можете решить эту проблему, используя драгоценный камень под названием CanCan (поскольку на аналогичный вопрос был дан ответ по этому поводу), но есть ли способ сделать это без использования драгоценного камня? Кажется, что моя простая маленькая функция должна работать, но не мог бы кто-нибудь объяснить, почему это не так?

Ответ №1:

params является ли хэш всех параметров, отправляемых (через URL или поля формы и т.д.) вашему действию. Имя параметра, если оно присутствует в URL, определено в вашем файле routes. Для ваших маршрутов контроллера целей у вас, вероятно (предположительно), есть:

 goals_path: /goals/
goal_path: /goals/:id
edit_goal_path: /goals/:id/edit
  

Поскольку вы получаете /goals/31/edit , params[:id] равен 31, идентификатор цели, которую вы редактируете. Первая строка в correct_user — это поиск пользователя, идентификатор которого совпадает с идентификатором в хэше параметров (goal_id). Итак, на самом деле, вы должны делать что-то вроде этого:

 def correct_user
  user = Goal.find(params[:id]).user if params[:id]
  redirect_to user_path(current_user) unless current_user?(user)
end
  

Здесь говорится: найдите цель, которую кто-то хочет отредактировать (из параметров [:id]), и дайте мне пользователя, связанного с ней (вы не опубликовали свою модель цели, я предполагаю, что цель принадлежит_пользователю, но вы, возможно, назвали ее «создатель» или «владелец» вместо этого). Перенаправление, если пользователь не совпадает с текущим пользователем, вошедшим в систему. Ваш предыдущий код пытался найти пользователя с тем же идентификатором, что и редактируемая цель.

Комментарии:

1. Это многое проясняет, спасибо! Но почему у вас в конце первой строки добавлено «if params [:id]»? Каковы последствия отключения этой функции?

2. Зависит от того, как вы определяете свой before_filter. Если фильтр «до» выполняется для каждого действия, он будет генерировать исключение RecordNotFound для действий с коллекцией (index, new). Если ваш фильтр «до» запускается только перед редактированием, обновлением, уничтожением, созданием, то, вероятно, в этом нет необходимости. По сути, это приводит к тому, что первая строка не выполняется, если она не может найти идентификатор в хэше параметров, вместо выполнения и выдачи исключения.

3. Да, у меня это работает только при определенных действиях. Еще раз спасибо!

Ответ №2:

Учитывая следующие предположения:

  1. Модель пользователя: has_many :goals
  2. Целевая модель: belongs_to :user
  3. Путь к цели редактирования: /goals/:id/edit
  4. Контроллер целей, действие редактирования аутентифицировано (так что у вас обязательно будет current_user )

Вы должны иметь возможность получить доступ к цели следующим образом:

 def edit
  @goal = current_user.goals.find(params[:id]) rescue redirect_to(user_path current_user)
end
  

Это позволит ограничить поиск целями, принадлежащими current_user , поэтому @goal они всегда будут принадлежать правильному пользователю.

Ответ №3:

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

Вероятно, вы хотите сделать что-то вроде

 def correct_user
   @goal = Goal.find(params[:id])
   redirect_to(user_path(current_user)) unless current_user?(@goal.user)
end
  

Комментарии:

1. @goal = User.find ? Возможно, там опечатка! 🙂

Ответ №4:

Другие ответили на вашу основную проблему, но я хочу сказать вот что: я бы настоятельно рекомендовал использовать CanCan даже для очень небольшого проекта. Это очень простое в использовании средство, которое вам очень поможет, и у вас будет потрясающий чистый код

например, в вашей ситуации вы могли бы ввести эту строку ability.rb для управления обновлением целей пользователя

can :update, Goal, :user_id => user.id

и в вашем контроллере просто сделайте load_and_authorize_resource вверху. Никаких ручных before_filters, никакой проверки каких-либо условий или чего-либо подобного.

и везде, где вам нужно будет указать что-то о редактировании цели, например, в индексном представлении, при перечислении ссылок вы просто добавите что-то вроде link_to_if(can?(:update, goal) , "edit goal", goal_path(goal) ){}