1# frozen_string_literal: true
2
3require 'spec_helper'
4
5RSpec.describe NotePolicy do
6  describe '#rules', :aggregate_failures do
7    let(:user) { create(:user) }
8    let(:project) { create(:project, :public) }
9    let(:issue) { create(:issue, project: project) }
10    let(:noteable) { issue }
11    let(:policy) { described_class.new(user, note) }
12    let(:note) { create(:note, noteable: noteable, author: user, project: project) }
13
14    shared_examples_for 'user cannot read or act on the note' do
15      specify do
16        expect(policy).to be_disallowed(:admin_note, :reposition_note, :resolve_note, :read_note, :award_emoji)
17      end
18    end
19
20    shared_examples_for 'a discussion with a private noteable' do
21      context 'when the note author can no longer see the noteable' do
22        it_behaves_like 'user cannot read or act on the note'
23      end
24
25      context 'when the note author can still see the noteable' do
26        before do
27          project.add_developer(user)
28        end
29
30        it 'can edit the note' do
31          expect(policy).to be_allowed(:admin_note)
32          expect(policy).to be_allowed(:reposition_note)
33          expect(policy).to be_allowed(:resolve_note)
34          expect(policy).to be_allowed(:read_note)
35          expect(policy).to be_allowed(:award_emoji)
36        end
37      end
38    end
39
40    shared_examples_for 'a note on a public noteable' do
41      it 'can only read and award emoji on the note' do
42        expect(policy).to be_allowed(:read_note, :award_emoji)
43        expect(policy).to be_disallowed(:reposition_note, :admin_note, :resolve_note)
44      end
45    end
46
47    context 'when the noteable is a deleted commit' do
48      let(:commit) { nil }
49      let(:note) { create(:note_on_commit, commit_id: '12345678', author: user, project: project) }
50
51      it 'allows to read' do
52        expect(policy).to be_allowed(:read_note)
53        expect(policy).to be_disallowed(:admin_note)
54        expect(policy).to be_disallowed(:reposition_note)
55        expect(policy).to be_disallowed(:resolve_note)
56        expect(policy).to be_disallowed(:award_emoji)
57      end
58    end
59
60    context 'when the noteable is a commit' do
61      let(:commit) { project.repository.head_commit }
62      let(:note) { create(:note_on_commit, commit_id: commit.id, author: user, project: project) }
63
64      context 'when the project is private' do
65        let(:project) { create(:project, :private, :repository) }
66
67        it_behaves_like 'a discussion with a private noteable'
68      end
69
70      context 'when the project is public' do
71        context 'when repository access level is private' do
72          let(:project) { create(:project, :public, :repository, :repository_private) }
73
74          it_behaves_like 'a discussion with a private noteable'
75        end
76      end
77    end
78
79    context 'when the noteable is a Design' do
80      include DesignManagementTestHelpers
81
82      let(:note) { create(:note, noteable: noteable, project: project) }
83      let(:noteable) { create(:design, issue: issue) }
84
85      before do
86        enable_design_management
87      end
88
89      it 'can read, award emoji and reposition the note' do
90        expect(policy).to be_allowed(:reposition_note, :read_note, :award_emoji)
91        expect(policy).to be_disallowed(:admin_note, :resolve_note)
92      end
93
94      context 'when project is private' do
95        let(:project) { create(:project, :private) }
96
97        it_behaves_like 'user cannot read or act on the note'
98      end
99    end
100
101    context 'when the noteable is a personal snippet' do
102      let(:noteable) { create(:personal_snippet, :public) }
103      let(:note) { create(:note, noteable: noteable) }
104
105      it_behaves_like 'a note on a public noteable'
106
107      context 'when user is the author of the personal snippet' do
108        let(:note) { create(:note, noteable: noteable, author: user) }
109
110        it 'can edit note' do
111          expect(policy).to be_allowed(:read_note, :award_emoji, :admin_note, :reposition_note, :resolve_note)
112        end
113
114        context 'when it is private' do
115          let(:noteable) { create(:personal_snippet, :private) }
116
117          it_behaves_like 'user cannot read or act on the note'
118        end
119      end
120    end
121
122    context 'when the project is public' do
123      context 'when user is not the author of the note' do
124        let(:note) { create(:note, noteable: noteable, project: project) }
125
126        it_behaves_like 'a note on a public noteable'
127      end
128
129      context 'when the note author is not a project member' do
130        it 'can edit a note' do
131          expect(policy).to be_allowed(:admin_note)
132          expect(policy).to be_allowed(:reposition_note)
133          expect(policy).to be_allowed(:resolve_note)
134          expect(policy).to be_allowed(:read_note)
135        end
136      end
137
138      context 'when the noteable is a project snippet' do
139        let(:noteable) { create(:project_snippet, :public, project: project) }
140
141        it 'can edit note' do
142          expect(policy).to be_allowed(:admin_note)
143          expect(policy).to be_allowed(:reposition_note)
144          expect(policy).to be_allowed(:resolve_note)
145          expect(policy).to be_allowed(:read_note)
146        end
147
148        context 'when it is private' do
149          let(:noteable) { create(:project_snippet, :private, project: project) }
150
151          it_behaves_like 'a discussion with a private noteable'
152        end
153      end
154
155      context 'when a discussion is confidential' do
156        before do
157          issue.update_attribute(:confidential, true)
158        end
159
160        it_behaves_like 'a discussion with a private noteable'
161      end
162
163      context 'when a discussion is locked' do
164        before do
165          issue.update_attribute(:discussion_locked, true)
166        end
167
168        context 'when the note author is a project member' do
169          before do
170            project.add_developer(user)
171          end
172
173          it 'can edit a note' do
174            expect(policy).to be_allowed(:admin_note)
175            expect(policy).to be_allowed(:reposition_note)
176            expect(policy).to be_allowed(:resolve_note)
177            expect(policy).to be_allowed(:read_note)
178          end
179        end
180
181        context 'when the note author is not a project member' do
182          it 'can not edit a note' do
183            expect(policy).to be_disallowed(:admin_note)
184            expect(policy).to be_disallowed(:reposition_note)
185            expect(policy).to be_disallowed(:resolve_note)
186          end
187
188          it 'can read a note' do
189            expect(policy).to be_allowed(:read_note)
190          end
191        end
192      end
193
194      context 'for discussions' do
195        let(:policy) { described_class.new(user, note.discussion) }
196
197        it 'allows the author to manage the discussion' do
198          expect(policy).to be_allowed(:admin_note)
199          expect(policy).to be_allowed(:reposition_note)
200          expect(policy).to be_allowed(:resolve_note)
201          expect(policy).to be_allowed(:read_note)
202          expect(policy).to be_allowed(:award_emoji)
203        end
204
205        context 'when the user does not have access to the noteable' do
206          before do
207            noteable.update_attribute(:confidential, true)
208          end
209
210          it_behaves_like 'a discussion with a private noteable'
211        end
212      end
213
214      context 'when it is a system note' do
215        let(:developer) { create(:user) }
216        let(:any_user) { create(:user) }
217
218        shared_examples_for 'user can read the note' do
219          it 'allows the user to read the note' do
220            expect(policy).to be_allowed(:read_note)
221          end
222        end
223
224        shared_examples_for 'user can act on the note' do
225          it 'allows the user to read the note' do
226            expect(policy).to be_disallowed(:admin_note)
227            expect(policy).to be_disallowed(:reposition_note)
228            expect(policy).to be_allowed(:resolve_note)
229            expect(policy).to be_allowed(:award_emoji)
230          end
231        end
232
233        context 'when noteable is a public issue' do
234          let(:note) { create(:note, system: true, noteable: noteable, author: user, project: project) }
235
236          before do
237            project.add_developer(developer)
238          end
239
240          context 'when user is project member' do
241            let(:policy) { described_class.new(developer, note) }
242
243            it_behaves_like 'user can read the note'
244            it_behaves_like 'user can act on the note'
245          end
246
247          context 'when user is not project member' do
248            let(:policy) { described_class.new(any_user, note) }
249
250            it_behaves_like 'user can read the note'
251          end
252
253          context 'when user is anonymous' do
254            let(:policy) { described_class.new(nil, note) }
255
256            it_behaves_like 'user can read the note'
257          end
258        end
259
260        context 'when it is a system note referencing a confidential issue' do
261          let(:confidential_issue) { create(:issue, :confidential, project: project) }
262          let(:note) { create(:note, system: true, noteable: issue, author: user, project: project, note: "mentioned in issue #{confidential_issue.to_reference(project)}") }
263
264          before do
265            project.add_developer(developer)
266          end
267
268          context 'when user is project member' do
269            let(:policy) { described_class.new(developer, note) }
270
271            it_behaves_like 'user can read the note'
272            it_behaves_like 'user can act on the note'
273          end
274
275          context 'when user is not project member' do
276            let(:policy) { described_class.new(any_user, note) }
277
278            it_behaves_like 'user cannot read or act on the note'
279          end
280
281          context 'when user is anonymous' do
282            let(:policy) { described_class.new(nil, note) }
283
284            it_behaves_like 'user cannot read or act on the note'
285          end
286        end
287      end
288
289      context 'with confidential notes' do
290        def permissions(user, note)
291          described_class.new(user, note)
292        end
293
294        let(:reporter) { create(:user) }
295        let(:developer) { create(:user) }
296        let(:maintainer) { create(:user) }
297        let(:guest) { create(:user) }
298        let(:non_member) { create(:user) }
299        let(:author) { create(:user) }
300        let(:assignee) { create(:user) }
301        let(:admin) { create(:admin) }
302
303        before do
304          project.add_reporter(reporter)
305          project.add_developer(developer)
306          project.add_maintainer(maintainer)
307          project.add_guest(guest)
308        end
309
310        shared_examples_for 'confidential notes permissions' do
311          it 'does not allow non members to read confidential notes and replies' do
312            expect(permissions(non_member, confidential_note)).to be_disallowed(:read_note, :admin_note, :reposition_note, :resolve_note, :award_emoji)
313          end
314
315          it 'does not allow guests to read confidential notes and replies' do
316            expect(permissions(guest, confidential_note)).to be_disallowed(:read_note, :admin_note, :reposition_note, :resolve_note, :award_emoji)
317          end
318
319          it 'allows reporter to read all notes but not resolve and admin them' do
320            expect(permissions(reporter, confidential_note)).to be_allowed(:read_note, :award_emoji)
321            expect(permissions(reporter, confidential_note)).to be_disallowed(:admin_note, :reposition_note, :resolve_note)
322          end
323
324          it 'allows developer to read and resolve all notes' do
325            expect(permissions(developer, confidential_note)).to be_allowed(:read_note, :award_emoji, :resolve_note)
326            expect(permissions(developer, confidential_note)).to be_disallowed(:admin_note, :reposition_note)
327          end
328
329          it 'allows maintainers to read all notes and admin them' do
330            expect(permissions(maintainer, confidential_note)).to be_allowed(:read_note, :admin_note, :reposition_note, :resolve_note, :award_emoji)
331          end
332
333          context 'when admin mode is enabled', :enable_admin_mode do
334            it 'allows admins to read all notes and admin them' do
335              expect(permissions(admin, confidential_note)).to be_allowed(:read_note, :admin_note, :reposition_note, :resolve_note, :award_emoji)
336            end
337          end
338
339          context 'when admin mode is disabled' do
340            it 'does not allow non members to read confidential notes and replies' do
341              expect(permissions(admin, confidential_note)).to be_disallowed(:read_note, :admin_note, :reposition_note, :resolve_note, :award_emoji)
342            end
343          end
344
345          it 'allows noteable author to read and resolve all notes' do
346            expect(permissions(author, confidential_note)).to be_allowed(:read_note, :resolve_note, :award_emoji)
347            expect(permissions(author, confidential_note)).to be_disallowed(:admin_note, :reposition_note)
348          end
349        end
350
351        context 'for issues' do
352          let(:issue) { create(:issue, project: project, author: author, assignees: [assignee]) }
353          let(:confidential_note) { create(:note, :confidential, project: project, noteable: issue) }
354
355          it_behaves_like 'confidential notes permissions'
356
357          it 'allows noteable assignees to read all notes' do
358            expect(permissions(assignee, confidential_note)).to be_allowed(:read_note, :award_emoji)
359            expect(permissions(assignee, confidential_note)).to be_disallowed(:admin_note, :reposition_note, :resolve_note)
360          end
361        end
362
363        context 'for merge requests' do
364          let(:merge_request) { create(:merge_request, source_project: project, author: author, assignees: [assignee]) }
365          let(:confidential_note) { create(:note, :confidential, project: project, noteable: merge_request) }
366
367          it_behaves_like 'confidential notes permissions'
368
369          it 'allows noteable assignees to read all notes' do
370            expect(permissions(assignee, confidential_note)).to be_allowed(:read_note, :award_emoji)
371            expect(permissions(assignee, confidential_note)).to be_disallowed(:admin_note, :reposition_note, :resolve_note)
372          end
373        end
374
375        context 'for project snippets' do
376          let(:project_snippet) { create(:project_snippet, project: project, author: author) }
377          let(:confidential_note) { create(:note, :confidential, project: project, noteable: project_snippet) }
378
379          it_behaves_like 'confidential notes permissions'
380        end
381
382        context 'for personal snippets' do
383          let(:personal_snippet) { create(:personal_snippet, author: author) }
384          let(:confidential_note) { create(:note, :confidential, project: nil, noteable: personal_snippet) }
385
386          it 'allows snippet author to read and resolve all notes' do
387            expect(permissions(author, confidential_note)).to be_allowed(:read_note, :resolve_note, :award_emoji)
388            expect(permissions(author, confidential_note)).to be_disallowed(:admin_note, :reposition_note)
389          end
390
391          it 'does not allow maintainers to read confidential notes and replies' do
392            expect(permissions(maintainer, confidential_note)).to be_disallowed(:read_note, :admin_note, :reposition_note, :resolve_note, :award_emoji)
393          end
394        end
395      end
396    end
397  end
398end
399