Commit babadf1e authored by Alexandru Croitor's avatar Alexandru Croitor

Use note access check though its policy

Usage of note.visible_for?(user) for read(view) permission check
is not complete. This change replaces that check with an actual
note policy check for actuall permission checks.
parent 5145de2d
...@@ -137,7 +137,7 @@ module IssuableActions ...@@ -137,7 +137,7 @@ module IssuableActions
end end
notes = prepare_notes_for_rendering(notes) notes = prepare_notes_for_rendering(notes)
notes = notes.select { |n| n.visible_for?(current_user) } notes = notes.select { |n| n.readable_by?(current_user) }
discussions = Discussion.build_collection(notes, issuable) discussions = Discussion.build_collection(notes, issuable)
......
...@@ -29,7 +29,7 @@ module NotesActions ...@@ -29,7 +29,7 @@ module NotesActions
end end
notes = prepare_notes_for_rendering(notes) notes = prepare_notes_for_rendering(notes)
notes = notes.select { |n| n.visible_for?(current_user) } notes = notes.select { |n| n.readable_by?(current_user) }
notes_json[:notes] = notes_json[:notes] =
if use_note_serializer? if use_note_serializer?
......
...@@ -20,6 +20,7 @@ class Discussion ...@@ -20,6 +20,7 @@ class Discussion
:to_ability_name, :to_ability_name,
:editable?, :editable?,
:visible_for?, :visible_for?,
:resource_parent,
to: :first_note to: :first_note
......
...@@ -339,12 +339,10 @@ class Note < ApplicationRecord ...@@ -339,12 +339,10 @@ class Note < ApplicationRecord
super super
end end
def cross_reference_not_visible_for?(user) # This method is to be used for checking read permissions on a note instead of `visible_for?`
cross_reference? && !all_referenced_mentionables_allowed?(user) def readable_by?(user)
end # note_policy accounts for #visible_for?(user) check when granting read access
Ability.allowed?(user, :read_note, self)
def visible_for?(user)
!cross_reference_not_visible_for?(user) && system_note_viewable_by?(user)
end end
def award_emoji? def award_emoji?
...@@ -504,6 +502,16 @@ class Note < ApplicationRecord ...@@ -504,6 +502,16 @@ class Note < ApplicationRecord
noteable.user_mentions.where(note: self) noteable.user_mentions.where(note: self)
end end
def cross_reference_not_visible_for?(user)
cross_reference? && !all_referenced_mentionables_allowed?(user)
end
# Using this method for checking `read` access on a note may result in incorrect permission granting
# check `spec/models/note_spec.rb` and `ee/spec/models/note_spec.rb` for corresponding specs
def visible_for?(user)
!cross_reference_not_visible_for?(user) && system_note_viewable_by?(user)
end
private private
# Using this method followed by a call to `save` may result in ActiveRecord::RecordNotUnique exception # Using this method followed by a call to `save` may result in ActiveRecord::RecordNotUnique exception
......
# frozen_string_literal: true # frozen_string_literal: true
class NotePolicy < BasePolicy class NotePolicy < BasePolicy
delegate { @subject.project } delegate { @subject.resource_parent }
delegate { @subject.noteable if DeclarativePolicy.has_policy?(@subject.noteable) } delegate { @subject.noteable if DeclarativePolicy.has_policy?(@subject.noteable) }
condition(:is_author) { @user && @subject.author == @user } condition(:is_author) { @user && @subject.author == @user }
......
...@@ -7,6 +7,7 @@ class PersonalSnippetPolicy < BasePolicy ...@@ -7,6 +7,7 @@ class PersonalSnippetPolicy < BasePolicy
rule { public_snippet }.policy do rule { public_snippet }.policy do
enable :read_snippet enable :read_snippet
enable :read_note
enable :create_note enable :create_note
end end
...@@ -14,11 +15,13 @@ class PersonalSnippetPolicy < BasePolicy ...@@ -14,11 +15,13 @@ class PersonalSnippetPolicy < BasePolicy
enable :read_snippet enable :read_snippet
enable :update_snippet enable :update_snippet
enable :admin_snippet enable :admin_snippet
enable :read_note
enable :create_note enable :create_note
end end
rule { internal_snippet & ~external_user }.policy do rule { internal_snippet & ~external_user }.policy do
enable :read_snippet enable :read_snippet
enable :read_note
enable :create_note enable :create_note
end end
......
- return unless note.author - return unless note.author
- return if note.cross_reference_not_visible_for?(current_user) - return if !note.readable_by?(current_user)
- show_image_comment_badge = local_assigns.fetch(:show_image_comment_badge, false) - show_image_comment_badge = local_assigns.fetch(:show_image_comment_badge, false)
- note_editable = can?(current_user, :admin_note, note) - note_editable = can?(current_user, :admin_note, note)
......
...@@ -87,7 +87,7 @@ module API ...@@ -87,7 +87,7 @@ module API
# They're not presented on Jira Dev Panel ATM. A comments count with a # They're not presented on Jira Dev Panel ATM. A comments count with a
# redirect link is presented. # redirect link is presented.
notes = paginate(noteable.notes.user.reorder(nil)) notes = paginate(noteable.notes.user.reorder(nil))
notes.select { |n| n.visible_for?(current_user) } notes.select { |n| n.readable_by?(current_user) }
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
...@@ -16,8 +16,9 @@ describe Note do ...@@ -16,8 +16,9 @@ describe Note do
let(:guest) { create(:group_member, :guest, group: group, user: create(:user)).user } let(:guest) { create(:group_member, :guest, group: group, user: create(:user)).user }
let(:reporter) { create(:group_member, :reporter, group: group, user: create(:user)).user } let(:reporter) { create(:group_member, :reporter, group: group, user: create(:user)).user }
let(:maintainer) { create(:group_member, :maintainer, group: group, user: create(:user)).user } let(:maintainer) { create(:group_member, :maintainer, group: group, user: create(:user)).user }
let(:non_member) { create(:user) }
let(:group) { create(:group) } let(:group) { create(:group, :public) }
let(:epic) { create(:epic, group: group, author: owner, created_at: 1.day.ago) } let(:epic) { create(:epic, group: group, author: owner, created_at: 1.day.ago) }
before do before do
...@@ -27,48 +28,50 @@ describe Note do ...@@ -27,48 +28,50 @@ describe Note do
context 'note created after epic' do context 'note created after epic' do
let(:note) { create(:system_note, noteable: epic, created_at: 1.minute.ago) } let(:note) { create(:system_note, noteable: epic, created_at: 1.minute.ago) }
it 'returns true for an owner' do it_behaves_like 'users with note access' do
expect(note.visible_for?(owner)).to be_truthy let(:users) { [owner, reporter, maintainer, guest, non_member, nil] }
end end
it 'returns true for a reporter' do context 'when group is private' do
expect(note.visible_for?(reporter)).to be_truthy let(:group) { create(:group, :private) }
end
it 'returns true for a maintainer' do it_behaves_like 'users with note access' do
expect(note.visible_for?(maintainer)).to be_truthy let(:users) { [owner, reporter, maintainer, guest] }
end end
it 'returns true for a guest user' do it 'returns visible but not readable for a non-member user' do
expect(note.visible_for?(guest)).to be_truthy expect(note.visible_for?(non_member)).to be_truthy
end expect(note.readable_by?(non_member)).to be_falsy
end
it 'returns true for a nil user' do it 'returns visible but not readable for a nil user' do
expect(note.visible_for?(nil)).to be_truthy expect(note.visible_for?(nil)).to be_truthy
expect(note.readable_by?(nil)).to be_falsy
end
end end
end end
context 'when note is older than epic' do context 'when note is older than epic' do
let(:older_note) { create(:system_note, noteable: epic, created_at: 2.days.ago) } let(:note) { create(:system_note, noteable: epic, created_at: 2.days.ago) }
it 'returns true for the owner' do it_behaves_like 'users with note access' do
expect(older_note.visible_for?(owner)).to be_truthy let(:users) { [owner, reporter, maintainer] }
end end
it 'returns true for a reporter' do it_behaves_like 'users without note access' do
expect(older_note.visible_for?(reporter)).to be_truthy let(:users) { [guest, non_member, nil] }
end end
it 'returns true for a maintainer' do context 'when group is private' do
expect(older_note.visible_for?(maintainer)).to be_truthy let(:group) { create(:group, :private) }
end
it 'returns false for a guest user' do it_behaves_like 'users with note access' do
expect(older_note.visible_for?(guest)).to be_falsy let(:users) { [owner, reporter, maintainer] }
end end
it 'returns false for a nil user' do it_behaves_like 'users without note access' do
expect(older_note.visible_for?(nil)).to be_falsy let(:users) { [guest, non_member, nil] }
end
end end
end end
end end
......
...@@ -239,7 +239,7 @@ module API ...@@ -239,7 +239,7 @@ module API
# because notes are redacted if they point to projects that # because notes are redacted if they point to projects that
# cannot be accessed by the user. # cannot be accessed by the user.
notes = prepare_notes_for_rendering(notes) notes = prepare_notes_for_rendering(notes)
notes.select { |n| n.visible_for?(current_user) } notes.select { |n| n.readable_by?(current_user) }
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
end end
......
...@@ -62,7 +62,7 @@ module API ...@@ -62,7 +62,7 @@ module API
def get_note(noteable, note_id) def get_note(noteable, note_id)
note = noteable.notes.with_metadata.find(note_id) note = noteable.notes.with_metadata.find(note_id)
can_read_note = note.visible_for?(current_user) can_read_note = note.readable_by?(current_user)
if can_read_note if can_read_note
present note, with: Entities::Note present note, with: Entities::Note
......
...@@ -45,7 +45,7 @@ module API ...@@ -45,7 +45,7 @@ module API
# array returned, but this is really a edge-case. # array returned, but this is really a edge-case.
notes = paginate(raw_notes) notes = paginate(raw_notes)
notes = prepare_notes_for_rendering(notes) notes = prepare_notes_for_rendering(notes)
notes = notes.select { |note| note.visible_for?(current_user) } notes = notes.select { |note| note.readable_by?(current_user) }
present notes, with: Entities::Note present notes, with: Entities::Note
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
...@@ -288,8 +288,14 @@ describe Note do ...@@ -288,8 +288,14 @@ describe Note do
describe "#visible_for?" do describe "#visible_for?" do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
let_it_be(:note) { create(:note) } let(:project) { create(:project, :public) }
let_it_be(:user) { create(:user) } let(:user) { create(:user) }
let(:guest) { create(:project_member, :guest, project: project, user: create(:user)).user }
let(:reporter) { create(:project_member, :reporter, project: project, user: create(:user)).user }
let(:maintainer) { create(:project_member, :maintainer, project: project, user: create(:user)).user }
let(:non_member) { create(:user) }
let(:note) { create(:note, project: project) }
where(:cross_reference_visible, :system_note_viewable, :result) do where(:cross_reference_visible, :system_note_viewable, :result) do
true | true | false true | true | false
...@@ -299,14 +305,39 @@ describe Note do ...@@ -299,14 +305,39 @@ describe Note do
with_them do with_them do
it "returns expected result" do it "returns expected result" do
expect(note).to receive(:cross_reference_not_visible_for?).and_return(cross_reference_visible) expect(note).to receive(:cross_reference_not_visible_for?).twice.and_return(cross_reference_visible)
unless cross_reference_visible unless cross_reference_visible
expect(note).to receive(:system_note_viewable_by?) expect(note).to receive(:system_note_viewable_by?).twice
.with(user).and_return(system_note_viewable) .with(user).and_return(system_note_viewable)
end end
expect(note.visible_for?(user)).to eq result expect(note.visible_for?(user)).to eq result
expect(note.readable_by?(user)).to eq result
end
end
context 'when project is public' do
it_behaves_like 'users with note access' do
let(:users) { [reporter, maintainer, guest, non_member, nil] }
end
end
context 'when group is private' do
let(:project) { create(:project, :private) }
it_behaves_like 'users with note access' do
let(:users) { [reporter, maintainer, guest] }
end
it 'returns visible but not readable for non-member user' do
expect(note.visible_for?(non_member)).to be_truthy
expect(note.readable_by?(non_member)).to be_falsy
end
it 'returns visible but not readable for a nil user' do
expect(note.visible_for?(nil)).to be_truthy
expect(note.readable_by?(nil)).to be_falsy
end end
end end
end end
......
# frozen_string_literal: true
shared_examples 'users with note access' do
it 'returns true' do
users.each do |user|
expect(note.visible_for?(user)).to be_truthy
expect(note.readable_by?(user)).to be_truthy
end
end
end
shared_examples 'users without note access' do
it 'returns false' do
users.each do |user|
expect(note.visible_for?(user)).to be_falsy
expect(note.readable_by?(user)).to be_falsy
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment