Skip to content

Commit

Permalink
refactor: guardian
Browse files Browse the repository at this point in the history
  • Loading branch information
pangbo13 committed Apr 13, 2024
1 parent f79941d commit 7b07710
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 65 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
Gemfile.lock
/.vscode
38 changes: 13 additions & 25 deletions app/controllers/retorts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,24 @@ def update
end

exist_record = Retort.find_by(post_id: post.id, user_id: current_user.id, emoji: emoji)
if exist_record
if exist_record.present?
if exist_record.deleted?
# Record has been deleted, try to create again
if exist_record.can_recover?
exist_record.recover!
DiscourseEvent.trigger(:create_retort,post,current_user,emoji)
else
respond_with_unprocessable(I18n.t("retort.error.guardian_fail"))
return
end
guardian.ensure_can_recover_retort!(exist_record)
exist_record.recover!
DiscourseEvent.trigger(:create_retort,post,current_user,emoji)
else
if exist_record.can_withdraw?
exist_record.withdraw!
DiscourseEvent.trigger(:withdraw_retort,post,current_user,emoji)
else
respond_with_unprocessable(I18n.t("retort.error.exceed_withdraw_limit"))
return
end
guardian.ensure_can_delete_retort!(exist_record)
exist_record.withdraw!
DiscourseEvent.trigger(:withdraw_retort,post,current_user,emoji)
end
else
if !Retort.can_create?(current_user,post,emoji)
respond_with_unprocessable(I18n.t("retort.error.guardian_fail"))
return
else
begin
exist_record = Retort.create(post_id: post.id, user_id: current_user.id, emoji: emoji)
DiscourseEvent.trigger(:create_retort,post,current_user,emoji) unless exist_record.nil?
rescue ActiveRecord::RecordNotUnique
# Concurrent creation, ignore
end
guardian.ensure_can_create!(Retort, post)
begin
exist_record = Retort.create(post_id: post.id, user_id: current_user.id, emoji: emoji)
DiscourseEvent.trigger(:create_retort,post,current_user,emoji) unless exist_record.nil?
rescue ActiveRecord::RecordNotUnique
# Concurrent creation, ignore
end
end

Expand Down
35 changes: 1 addition & 34 deletions app/models/retort.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,42 +35,9 @@ def recover!
self.save!
end

def can_recover?
# If it cannot be created, it must not be recoverd
return false if !Retort.can_create?(self.user,self.post,self.emoji)
return true if self.user.staff? || self.user.trust_level == 4
# withdrawn by self, can be recoverd
return true if self.deleted_at && self.deleted_by == self.user.id
false
end

def can_withdraw?
return true if self.user.staff? || self.user.trust_level == 4
return true if self.updated_at > SiteSetting.retort_withdraw_tolerance.second.ago
false
end

def can_toggle?
return false if self.deleted_at && !Retort.can_create?(user,self.post,self.emoji)
# staff can do anything
return true if self.user.staff? || self.user.trust_level == 4
# deleted retort can be recovered
return true if self.deleted_at && self.deleted_by != user.id
# cannot delete old retort
self.updated_at > SiteSetting.retort_withdraw_tolerance.second.ago
end

def self.can_create?(user,post,emoji)
return false if user.silenced? || SiteSetting.retort_disabled_users.split("|").include?(user.username)
return false if SiteSetting.retort_disabled_emojis.split("|").include?(emoji)
return true if user.staff? || user.trust_level == 4
return false if post.topic.archived?
true
end

def self.remove_retort(post_id, emoji, actor_id)
exist_record = Retort.where(post_id: post_id, emoji: emoji)
if exist_record
if exist_record.present?
exist_record.update_all(deleted_at: Time.now, deleted_by: actor_id)
Retort.clear_cache(post_id)
return true
Expand Down
42 changes: 42 additions & 0 deletions lib/guardian/retort_guardian.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# frozen_string_literal: true

# mixin for all guardian methods dealing with retort permissions
module DiscourseRetort::RetortGuardian
def can_create_retort?
return false if SiteSetting.retort_disabled_users.split("|").include?(@user.username)
return false if @user.silenced?
true
end

def can_create_retort_on_topic?(topic)
return false if topic.archived?
return false if SiteSetting.retort_disabled_categories.split("|").map(&:to_i).include?(topic.category_id)
true
end

def can_create_retort_on_post?(post)
# discourse already checked `can_see?` at app/lib/guardian.rb#L192
can_create_retort? && can_create_retort_on_topic?(post.topic)
end

def can_recover_retort?(retort)
return false if retort.nil?
return false unless is_my_own?(retort)
return false unless can_create_retort_on_post?(retort.post)
return false if @user.id != retort.deleted_by
true
end

def can_delete_retort?(retort)
return false if retort.nil?
return false unless is_my_own?(retort)
return false if retort.updated_at < SiteSetting.retort_withdraw_tolerance.second.ago
true
end

def can_moderate_retort?(retort)
return false if retort.nil?
return true if is_staff?
false
end
end
5 changes: 3 additions & 2 deletions plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ class Engine < ::Rails::Engine
register_editable_user_custom_field :hide_ignored_retorts
register_editable_user_custom_field :disable_retorts

require_relative "lib/guardian/retort_guardian.rb"
::Guardian.prepend DiscourseRetort::RetortGuardian

register_stat("retort", show_in_ui: true, expose_via_api: true) do
{
:last_day => Retort.where("created_at > ?", 1.days.ago).count,
Expand Down Expand Up @@ -87,6 +90,4 @@ def check_react
end
end
end

DiscourseEvent.trigger(:plugin_retort_initialized)
end
6 changes: 2 additions & 4 deletions spec/requests/controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,7 @@
sign_in(user)
post "/retorts/#{first_post.id}.json", params: { retort: emoji }
end
expect(response.status).to eq(422)
expect(JSON.parse(response.body)["error"]).to eq I18n.t("retort.error.exceed_withdraw_limit")
expect(response.status).to eq(403)
retort = Retort.find_by(post_id: first_post.id, user_id: user.id, emoji: emoji)
expect(retort.deleted_at).to be_nil
expect(retort.deleted_by).to be_nil
Expand Down Expand Up @@ -193,8 +192,7 @@
sign_in(user)
post "/retorts/#{first_post.id}.json", params: { retort: emoji }
end
expect(response.status).to eq(422)
expect(JSON.parse(response.body)["error"]).to eq I18n.t("retort.error.exceed_withdraw_limit")
expect(response.status).to eq(403)
retort = Retort.find_by(post_id: first_post.id, user_id: user.id, emoji: emoji)
expect(retort.deleted_at).to be_nil
expect(retort.deleted_by).to be_nil
Expand Down

0 comments on commit 7b07710

Please sign in to comment.