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