Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authorization #13

Merged
merged 6 commits into from
Aug 2, 2020
Merged

Authorization #13

merged 6 commits into from
Aug 2, 2020

Conversation

hunter298
Copy link
Owner

Реализованы все проверки в проекте на наличие прав (например, такие, что только автор может редактировать вопрос/ответ и другие правила авторизации) у пользователя через CanCan, покрыт тестами класс Ability.

@@ -7,23 +7,23 @@ class AnswersController < ApplicationController

def create
@answer = question.answers.create(answer_params.merge(user: current_user))
authorize! :create, Answer

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

авторизовыывать после того, как объект создан смысла-то уже нет. Авторизация всегда должна идти ДО основнонго действия, которое мы авторизуем. Сейчас, допустим, окажется, что прав на создание нет и что тогда? ответ ведь уже был создан и сохранен в базе, какой смысл проверять после того, как все уже сделано? как говорится, "после драки калаками не машут" ))

@@ -2,8 +2,16 @@ class ApplicationController < ActionController::Base
before_action :authenticate_user!
before_action :gon_params

check_authorization unless: :devise_controller?

rescue_from CanCan::AccessDenied do |exception|

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

тут надо обработку сделать еще и для js и json форматов, т.к. редирект возможен только в html-формате. а в js и json лучше возвращать 403 ошибку и обрабатывать ее уже на клиенте

path = current_user ? root_path : new_user_session_path
redirect_to path, alert: exception.message
end

def delete_file_attached

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

так, я это, видимо, пропустил. что делает этот экшн в AppicationController? Нужно создать AttachamentsController с экшеном destroy и через него удалять аттачменты.

@@ -3,5 +3,6 @@ class BadgesController < ApplicationController

def index
@badges = current_user.badges
authorize! :read, @badges

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

то же самое: авторизация должна выполняться ДО действия

@@ -6,6 +6,7 @@ class CommentsController < ApplicationController

def create
@comment = @commentable.comments.new(comment_params.merge(user: current_user))
authorize! :create, @comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

вот здесь нормально, потому что тут new, т.е. объект хоть в паямти и инстанциирован, но еще не сохранен. а в ответах у тебя create, там авторизация должна быть ДО действия. Но на самом деле, для создания, обычно в правила авторизации нужно только класс передавать, поэтому тут тоже можно авторизацию сделать первой

@@ -2,6 +2,8 @@ module Voted
extend ActiveSupport::Concern
included do
before_action :set_ratable, only: %i[upvote downvote]

load_and_authorize_resource

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

лучше использовать authorize! в самих экшенах, иначе может быть двойная загрузка

@@ -1,29 +1,31 @@
class QuestionsController < ApplicationController

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

тут та же проблема: сначала авторизация, потом действия, никак не наоборот

@@ -0,0 +1,10 @@
class ActiveStorage::AttachmentsController < ApplicationController

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не обязательно даже неймспейс вводить здесь (тем более в системном неймспейсе создавать свой контроллер). Контроллер - это просто класс с методами (экшенами), в каждом экшене ты можешь написать любой код, который может обращаться к любым моделям, имя контроллера и моделей никак между собой не связаны. Мы называем их похоже чисто для понятности и логичности, но это необязательно.
Здесь достаточно было бы просто AttachmentsController

def destroy
@file = ActiveStorage::Attachment.find(params[:id])
authorize! :destroy, @file
if current_user&.author_of?(@file.record)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

у тебя предыдущая строка должна выполнить эту проверку, в этом и суть авторизации. ранее мы вручную это проверяли, поэтому нужен был вызов метода author_of?, теперь везде эти вызовы надо менять на вызов авторизации.

path = current_user ? root_path : new_user_session_path
redirect_to path, alert: exception.message
end
format.json { render json: {error: exception.message}, status: :unauthorized }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

почему в json один статус, а в js другой? здесь в обоих случаях должен быть forbidden (403), т.к. unauthorized (401), несмотря на название, на самом деле означает, что юзер не залогинен.

can :downvote, [Question, Answer]
cannot :downvote, [Question, Answer], user_id: user.id
can :best, Answer, question_id: user.question_ids
can :destroy, ActiveStorage::Attachment, record_id: user.question_ids

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

вот такая запись очень неоптимальная, ты каждый раз коллекцию передаешь. да и просто по id в полиморфных ассоциациях нельзя четко определить объект, нужен еще и тип. более того, у тебя следующее правило перезапишет это (т.е. они не объединятся, а одно заменит другое) и у тебя сможет юзер удалять аттачменты у объектов, id которых совпадает с id ответов юзера. А ведь это может быть и ответ и вопрос, причем, вопрос может быть чужой.

Тут нужно использовать правило с блоком. В нем можно написать любой код и если блок вернет true, значит, юзер сможет выполнить действие. Внутри блока можно вызвать метод author_of?, например

@hunter298 hunter298 merged commit a58c6a0 into master Aug 2, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants