New: can_see_groups? method for better perf

New: can_see_groups? method for better perf

diff --git a/app/models/user_search.rb b/app/models/user_search.rb
index 469c4dc..7b9f0b9 100644
--- a/app/models/user_search.rb
+++ b/app/models/user_search.rb
@@ -15,7 +15,7 @@ class UserSearch
     @limit = opts[:limit] || 20
     @groups = opts[:groups]
     @guardian = Guardian.new(@searching_user)
-    @groups.each { |group| @guardian.ensure_can_see_group!(group) } if @groups
+    @guardian.ensure_can_see_groups!(@groups) if @groups
   end
 
   def scoped_users
diff --git a/lib/guardian.rb b/lib/guardian.rb
index 87565ba..27bdff3 100644
--- a/lib/guardian.rb
+++ b/lib/guardian.rb
@@ -208,6 +208,25 @@ class Guardian
     true
   end
 
+  def can_see_groups?(groups)
+    return false if groups.blank?
+    return true if groups.all? { |g| g.visibility_level == Group.visibility_levels[:public] }
+    return true if is_admin?
+    return true if is_staff? && groups.all? { |g| g.visibility_level == Group.visibility_levels[:staff] }
+    return false if user.blank?
+
+    memberships = GroupUser.where(group: groups, user_id: user.id).pluck(:owner)
+
+    return false if memberships.empty? || memberships.length < groups.size
+
+    if !memberships.all?
+      return false if groups.all? { |g| g.visibility_level == Group.visibility_levels[:owners] }
+      return false if groups.all? { |g| g.visibility_level == Group.visibility_levels[:staff] }
+    end
+
+    true
+  end
+
   # Can we impersonate this user?
   def can_impersonate?(target)
     target &&
diff --git a/spec/components/guardian_spec.rb b/spec/components/guardian_spec.rb
index 64c1eca..ae6b742 100644
--- a/spec/components/guardian_spec.rb
+++ b/spec/components/guardian_spec.rb
@@ -3038,6 +3038,115 @@ describe Guardian do
 
   end
 
+  describe '#can_see_groups?' do
+    it 'correctly handles owner visibile groups' do
+      group = Group.new(name: 'group', visibility_level: Group.visibility_levels[:owners])
+
+      member = Fabricate(:user)
+      group.add(member)
+      group.save!
+
+      owner = Fabricate(:user)
+      group.add_owner(owner)
+      group.reload
+
+      expect(Guardian.new(admin).can_see_groups?([group])).to eq(true)
+      expect(Guardian.new(moderator).can_see_groups?([group])).to eq(false)
+      expect(Guardian.new(member).can_see_groups?([group])).to eq(false)
+      expect(Guardian.new.can_see_groups?([group])).to eq(false)
+      expect(Guardian.new(owner).can_see_groups?([group])).to eq(true)
+    end
+
+    it 'correctly handles the case where the user does not own every group' do
+      group = Group.new(name: 'group', visibility_level: Group.visibility_levels[:owners])
+      group2 = Group.new(name: 'group2', visibility_level: Group.visibility_levels[:owners])
+      group2.save!
+
+      member = Fabricate(:user)
+      group.add(member)
+      group.save!
+
+      owner = Fabricate(:user)
+      group.add_owner(owner)
+      group.reload
+
+      expect(Guardian.new(admin).can_see_groups?([group, group2])).to eq(true)
+      expect(Guardian.new(moderator).can_see_groups?([group, group2])).to eq(false)
+      expect(Guardian.new(member).can_see_groups?([group, group2])).to eq(false)
+      expect(Guardian.new.can_see_groups?([group, group2])).to eq(false)
+      expect(Guardian.new(owner).can_see_groups?([group, group2])).to eq(false)
+    end
+
+    it 'correctly handles staff visibile groups' do
+      group = Group.new(name: 'group', visibility_level: Group.visibility_levels[:staff])
+
+      member = Fabricate(:user)
+      group.add(member)
+      group.save!
+
+      owner = Fabricate(:user)
+      group.add_owner(owner)
+      group.reload
+
+      expect(Guardian.new(member).can_see_groups?([group])).to eq(false)
+      expect(Guardian.new(admin).can_see_groups?([group])).to eq(true)
+      expect(Guardian.new(moderator).can_see_groups?([group])).to eq(true)
+      expect(Guardian.new(owner).can_see_groups?([group])).to eq(true)
+      expect(Guardian.new.can_see_groups?([group])).to eq(false)
+    end
+
+    it 'correctly handles member visibile groups' do
+      group = Group.new(name: 'group', visibility_level: Group.visibility_levels[:members])
+
+      member = Fabricate(:user)
+      group.add(member)
+      group.save!
+
+      owner = Fabricate(:user)
+      group.add_owner(owner)
+      group.reload
+
+      expect(Guardian.new(moderator).can_see_groups?([group])).to eq(false)
+      expect(Guardian.new.can_see_groups?([group])).to eq(false)
+      expect(Guardian.new(admin).can_see_groups?([group])).to eq(true)
+      expect(Guardian.new(member).can_see_groups?([group])).to eq(true)
+      expect(Guardian.new(owner).can_see_groups?([group])).to eq(true)
+    end
+
+    it 'correctly handles the case where the user is not a member of every group' do
+      group1 = Group.new(name: 'group', visibility_level: Group.visibility_levels[:members])
+      group2 = Group.new(name: 'group2', visibility_level: Group.visibility_levels[:members])
+      group2.save!
+
+      member = Fabricate(:user)
+      group1.add(member)
+      group1.save!
+
+      owner = Fabricate(:user)
+      group1.add_owner(owner)
+      group1.reload
+
+      expect(Guardian.new(moderator).can_see_groups?([group1, group2])).to eq(false)
+      expect(Guardian.new.can_see_groups?([group1, group2])).to eq(false)
+      expect(Guardian.new(admin).can_see_groups?([group1, group2])).to eq(true)
+      expect(Guardian.new(member).can_see_groups?([group1, group2])).to eq(false)
+      expect(Guardian.new(owner).can_see_groups?([group1, group2])).to eq(false)
+    end
+
+    it 'correctly handles public groups' do
+      group = Group.new(name: 'group', visibility_level: Group.visibility_levels[:public])
+
+      expect(Guardian.new.can_see_groups?([group])).to eq(true)
+    end
+
+    it 'correctly handles there case where not every group is public' do
+      group1 = Group.new(name: 'group', visibility_level: Group.visibility_levels[:public])
+      group2 = Group.new(name: 'group', visibility_level: Group.visibility_levels[:private])
+
+      expect(Guardian.new.can_see_groups?([group1, group2])).to eq(false)
+    end
+  end
+
   context 'topic featured link category restriction' do
     before { SiteSetting.topic_featured_link_enabled = true }
     let(:guardian) { Guardian.new }

GitHub sha: 2fa8df7c

1 Like