Commit 4c44c5ef9a569c65bca8e70078205ef2ce7d6396
Committed by
Stephen Lottermoser
1 parent
4f5aae1d
Exists in
master
and in
4 other branches
Internally public projects
Public projects listed in the public section will be linked to the actual project's page. Public projects now give any user Guest permissions to the project, allowing them to download the code, read and create issues, and view anything else in the project's pages. Ample access tests have been added to the project_access_spec to verify correct permissions and behavior on public projects. - Visitors to the site who are not logged in still cannot view the project's pages. - Logged-in users visiting a public project where they are not a team member can create issues, but not snippets. They can view the projects code, issues, merge requests, etc, just as if they were a Guest member of the project. - Since this is a public project, the user is also granted :download_code permissions, a permission normally reserved for Reporters, since they can clone the repo anyways and browse commits and branches locally.
Showing
8 changed files
with
273 additions
and
4 deletions
Show diff stats
app/controllers/application_controller.rb
@@ -88,7 +88,7 @@ class ApplicationController < ActionController::Base | @@ -88,7 +88,7 @@ class ApplicationController < ActionController::Base | ||
88 | end | 88 | end |
89 | 89 | ||
90 | def authorize_code_access! | 90 | def authorize_code_access! |
91 | - return access_denied! unless can?(current_user, :download_code, project) | 91 | + return access_denied! unless can?(current_user, :download_code, project) or project.public? |
92 | end | 92 | end |
93 | 93 | ||
94 | def authorize_create_team! | 94 | def authorize_create_team! |
app/models/ability.rb
@@ -37,7 +37,7 @@ class Ability | @@ -37,7 +37,7 @@ class Ability | ||
37 | elsif team.reporters.include?(user) | 37 | elsif team.reporters.include?(user) |
38 | rules << project_report_rules | 38 | rules << project_report_rules |
39 | 39 | ||
40 | - elsif team.guests.include?(user) | 40 | + elsif team.guests.include?(user) or project.public? |
41 | rules << project_guest_rules | 41 | rules << project_guest_rules |
42 | end | 42 | end |
43 | 43 |
app/views/projects/_form.html.haml
@@ -48,7 +48,7 @@ | @@ -48,7 +48,7 @@ | ||
48 | Public mode: | 48 | Public mode: |
49 | .control-group | 49 | .control-group |
50 | = f.label :public, class: 'control-label' do | 50 | = f.label :public, class: 'control-label' do |
51 | - %span Public clone access | 51 | + %span Public access |
52 | .controls | 52 | .controls |
53 | = f.check_box :public | 53 | = f.check_box :public |
54 | %span.descr | 54 | %span.descr |
@@ -56,6 +56,8 @@ | @@ -56,6 +56,8 @@ | ||
56 | %em without any | 56 | %em without any |
57 | authentication. | 57 | authentication. |
58 | It will also be listed on the #{link_to "public access directory", public_root_path}. | 58 | It will also be listed on the #{link_to "public access directory", public_root_path}. |
59 | + %em Any | ||
60 | + user will have #{link_to "Guest", help_permissions_path} permissions on the repository. | ||
59 | 61 | ||
60 | %fieldset.features | 62 | %fieldset.features |
61 | %legend | 63 | %legend |
app/views/public/projects/index.html.haml
@@ -9,7 +9,7 @@ | @@ -9,7 +9,7 @@ | ||
9 | %li.clearfix | 9 | %li.clearfix |
10 | %h5 | 10 | %h5 |
11 | %i.icon-share | 11 | %i.icon-share |
12 | - = project.name_with_namespace | 12 | + = link_to_project project |
13 | .pull-right | 13 | .pull-right |
14 | %pre.dark.tiny git clone #{project.http_url_to_repo} | 14 | %pre.dark.tiny git clone #{project.http_url_to_repo} |
15 | %p.description | 15 | %p.description |
features/steps/shared/paths.rb
@@ -263,6 +263,14 @@ module SharedPaths | @@ -263,6 +263,14 @@ module SharedPaths | ||
263 | visit project_wiki_path(@project, :home) | 263 | visit project_wiki_path(@project, :home) |
264 | end | 264 | end |
265 | 265 | ||
266 | + # ---------------------------------------- | ||
267 | + # Public Projects | ||
268 | + # ---------------------------------------- | ||
269 | + | ||
270 | + Given 'I visit the public projects area' do | ||
271 | + visit public_root_path | ||
272 | + end | ||
273 | + | ||
266 | def root_ref | 274 | def root_ref |
267 | @project.repository.root_ref | 275 | @project.repository.root_ref |
268 | end | 276 | end |
spec/features/security/project_access_spec.rb
@@ -229,4 +229,246 @@ describe "Application access" do | @@ -229,4 +229,246 @@ describe "Application access" do | ||
229 | it { should be_denied_for :visitor } | 229 | it { should be_denied_for :visitor } |
230 | end | 230 | end |
231 | end | 231 | end |
232 | + | ||
233 | + | ||
234 | + describe "PublicProject" do | ||
235 | + let(:project) { create(:project) } | ||
236 | + | ||
237 | + let(:master) { create(:user) } | ||
238 | + let(:guest) { create(:user) } | ||
239 | + let(:reporter) { create(:user) } | ||
240 | + | ||
241 | + let(:admin) { create(:user) } | ||
242 | + | ||
243 | + before do | ||
244 | + # public project | ||
245 | + project.public = true | ||
246 | + project.save! | ||
247 | + | ||
248 | + # full access | ||
249 | + project.team << [master, :master] | ||
250 | + | ||
251 | + # readonly | ||
252 | + project.team << [reporter, :reporter] | ||
253 | + | ||
254 | + end | ||
255 | + | ||
256 | + describe "Project should be public" do | ||
257 | + subject { project } | ||
258 | + | ||
259 | + its(:public?) { should be_true } | ||
260 | + end | ||
261 | + | ||
262 | + describe "GET /project_code" do | ||
263 | + subject { project_path(project) } | ||
264 | + | ||
265 | + it { should be_allowed_for master } | ||
266 | + it { should be_allowed_for reporter } | ||
267 | + it { should be_allowed_for admin } | ||
268 | + it { should be_allowed_for guest } | ||
269 | + it { should be_allowed_for :user } | ||
270 | + it { should be_denied_for :visitor } | ||
271 | + end | ||
272 | + | ||
273 | + describe "GET /project_code/tree/master" do | ||
274 | + subject { project_tree_path(project, project.repository.root_ref) } | ||
275 | + | ||
276 | + it { should be_allowed_for master } | ||
277 | + it { should be_allowed_for reporter } | ||
278 | + it { should be_allowed_for :admin } | ||
279 | + it { should be_allowed_for guest } | ||
280 | + it { should be_allowed_for :user } | ||
281 | + it { should be_denied_for :visitor } | ||
282 | + end | ||
283 | + | ||
284 | + describe "GET /project_code/commits/master" do | ||
285 | + subject { project_commits_path(project, project.repository.root_ref, limit: 1) } | ||
286 | + | ||
287 | + it { should be_allowed_for master } | ||
288 | + it { should be_allowed_for reporter } | ||
289 | + it { should be_allowed_for :admin } | ||
290 | + it { should be_allowed_for guest } | ||
291 | + it { should be_allowed_for :user } | ||
292 | + it { should be_denied_for :visitor } | ||
293 | + end | ||
294 | + | ||
295 | + describe "GET /project_code/commit/:sha" do | ||
296 | + subject { project_commit_path(project, project.repository.commit) } | ||
297 | + | ||
298 | + it { should be_allowed_for master } | ||
299 | + it { should be_allowed_for reporter } | ||
300 | + it { should be_allowed_for :admin } | ||
301 | + it { should be_allowed_for guest } | ||
302 | + it { should be_allowed_for :user } | ||
303 | + it { should be_denied_for :visitor } | ||
304 | + end | ||
305 | + | ||
306 | + describe "GET /project_code/compare" do | ||
307 | + subject { project_compare_index_path(project) } | ||
308 | + | ||
309 | + it { should be_allowed_for master } | ||
310 | + it { should be_allowed_for reporter } | ||
311 | + it { should be_allowed_for :admin } | ||
312 | + it { should be_allowed_for guest } | ||
313 | + it { should be_allowed_for :user } | ||
314 | + it { should be_denied_for :visitor } | ||
315 | + end | ||
316 | + | ||
317 | + describe "GET /project_code/team" do | ||
318 | + subject { project_team_index_path(project) } | ||
319 | + | ||
320 | + it { should be_allowed_for master } | ||
321 | + it { should be_allowed_for reporter } | ||
322 | + it { should be_allowed_for :admin } | ||
323 | + it { should be_allowed_for guest } | ||
324 | + it { should be_allowed_for :user } | ||
325 | + it { should be_denied_for :visitor } | ||
326 | + end | ||
327 | + | ||
328 | + describe "GET /project_code/wall" do | ||
329 | + subject { project_wall_path(project) } | ||
330 | + | ||
331 | + it { should be_allowed_for master } | ||
332 | + it { should be_allowed_for reporter } | ||
333 | + it { should be_allowed_for :admin } | ||
334 | + it { should be_allowed_for guest } | ||
335 | + it { should be_allowed_for :user } | ||
336 | + it { should be_denied_for :visitor } | ||
337 | + end | ||
338 | + | ||
339 | + describe "GET /project_code/blob" do | ||
340 | + before do | ||
341 | + commit = project.repository.commit | ||
342 | + path = commit.tree.contents.select { |i| i.is_a?(Grit::Blob)}.first.name | ||
343 | + @blob_path = project_blob_path(project, File.join(commit.id, path)) | ||
344 | + end | ||
345 | + | ||
346 | + it { @blob_path.should be_allowed_for master } | ||
347 | + it { @blob_path.should be_allowed_for reporter } | ||
348 | + it { @blob_path.should be_allowed_for :admin } | ||
349 | + it { @blob_path.should be_allowed_for guest } | ||
350 | + it { @blob_path.should be_allowed_for :user } | ||
351 | + it { @blob_path.should be_denied_for :visitor } | ||
352 | + end | ||
353 | + | ||
354 | + describe "GET /project_code/edit" do | ||
355 | + subject { edit_project_path(project) } | ||
356 | + | ||
357 | + it { should be_allowed_for master } | ||
358 | + it { should be_denied_for reporter } | ||
359 | + it { should be_denied_for :admin } | ||
360 | + it { should be_denied_for guest } | ||
361 | + it { should be_denied_for :user } | ||
362 | + it { should be_denied_for :visitor } | ||
363 | + end | ||
364 | + | ||
365 | + describe "GET /project_code/deploy_keys" do | ||
366 | + subject { project_deploy_keys_path(project) } | ||
367 | + | ||
368 | + it { should be_allowed_for master } | ||
369 | + it { should be_denied_for reporter } | ||
370 | + it { should be_denied_for :admin } | ||
371 | + it { should be_denied_for guest } | ||
372 | + it { should be_denied_for :user } | ||
373 | + it { should be_denied_for :visitor } | ||
374 | + end | ||
375 | + | ||
376 | + describe "GET /project_code/issues" do | ||
377 | + subject { project_issues_path(project) } | ||
378 | + | ||
379 | + it { should be_allowed_for master } | ||
380 | + it { should be_allowed_for reporter } | ||
381 | + it { should be_allowed_for :admin } | ||
382 | + it { should be_allowed_for guest } | ||
383 | + it { should be_allowed_for :user } | ||
384 | + it { should be_denied_for :visitor } | ||
385 | + end | ||
386 | + | ||
387 | + describe "GET /project_code/snippets" do | ||
388 | + subject { project_snippets_path(project) } | ||
389 | + | ||
390 | + it { should be_allowed_for master } | ||
391 | + it { should be_allowed_for reporter } | ||
392 | + it { should be_allowed_for :admin } | ||
393 | + it { should be_allowed_for guest } | ||
394 | + it { should be_allowed_for :user } | ||
395 | + it { should be_denied_for :visitor } | ||
396 | + end | ||
397 | + | ||
398 | + describe "GET /project_code/snippets/new" do | ||
399 | + subject { new_project_snippet_path(project) } | ||
400 | + | ||
401 | + it { should be_allowed_for master } | ||
402 | + it { should be_allowed_for reporter } | ||
403 | + it { should be_denied_for :admin } | ||
404 | + it { should be_denied_for guest } | ||
405 | + it { should be_denied_for :user } | ||
406 | + it { should be_denied_for :visitor } | ||
407 | + end | ||
408 | + | ||
409 | + describe "GET /project_code/merge_requests" do | ||
410 | + subject { project_merge_requests_path(project) } | ||
411 | + | ||
412 | + it { should be_allowed_for master } | ||
413 | + it { should be_allowed_for reporter } | ||
414 | + it { should be_allowed_for :admin } | ||
415 | + it { should be_allowed_for guest } | ||
416 | + it { should be_allowed_for :user } | ||
417 | + it { should be_denied_for :visitor } | ||
418 | + end | ||
419 | + | ||
420 | + describe "GET /project_code/repository" do | ||
421 | + subject { project_repository_path(project) } | ||
422 | + | ||
423 | + it { should be_allowed_for master } | ||
424 | + it { should be_allowed_for reporter } | ||
425 | + it { should be_allowed_for :admin } | ||
426 | + it { should be_allowed_for guest } | ||
427 | + it { should be_allowed_for :user } | ||
428 | + it { should be_denied_for :visitor } | ||
429 | + end | ||
430 | + | ||
431 | + describe "GET /project_code/repository/branches" do | ||
432 | + subject { branches_project_repository_path(project) } | ||
433 | + | ||
434 | + before do | ||
435 | + # Speed increase | ||
436 | + Project.any_instance.stub(:branches).and_return([]) | ||
437 | + end | ||
438 | + | ||
439 | + it { should be_allowed_for master } | ||
440 | + it { should be_allowed_for reporter } | ||
441 | + it { should be_allowed_for :admin } | ||
442 | + it { should be_allowed_for guest } | ||
443 | + it { should be_allowed_for :user } | ||
444 | + it { should be_denied_for :visitor } | ||
445 | + end | ||
446 | + | ||
447 | + describe "GET /project_code/repository/tags" do | ||
448 | + subject { tags_project_repository_path(project) } | ||
449 | + | ||
450 | + before do | ||
451 | + # Speed increase | ||
452 | + Project.any_instance.stub(:tags).and_return([]) | ||
453 | + end | ||
454 | + | ||
455 | + it { should be_allowed_for master } | ||
456 | + it { should be_allowed_for reporter } | ||
457 | + it { should be_allowed_for :admin } | ||
458 | + it { should be_allowed_for guest } | ||
459 | + it { should be_allowed_for :user } | ||
460 | + it { should be_denied_for :visitor } | ||
461 | + end | ||
462 | + | ||
463 | + describe "GET /project_code/hooks" do | ||
464 | + subject { project_hooks_path(project) } | ||
465 | + | ||
466 | + it { should be_allowed_for master } | ||
467 | + it { should be_allowed_for reporter } | ||
468 | + it { should be_allowed_for :admin } | ||
469 | + it { should be_allowed_for guest } | ||
470 | + it { should be_allowed_for :user } | ||
471 | + it { should be_denied_for :visitor } | ||
472 | + end | ||
473 | + end | ||
232 | end | 474 | end |