From 4c44c5ef9a569c65bca8e70078205ef2ce7d6396 Mon Sep 17 00:00:00 2001 From: Stephen Lottermoser Date: Wed, 1 May 2013 23:52:05 -0700 Subject: [PATCH 1/3] 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. --- app/controllers/application_controller.rb | 2 +- app/models/ability.rb | 2 +- app/views/projects/_form.html.haml | 4 +- app/views/public/projects/index.html.haml | 2 +- features/project/public_projects.feature | 8 + features/steps/project/public_projects.rb | 9 + features/steps/shared/paths.rb | 8 + spec/features/security/project_access_spec.rb | 242 ++++++++++++++++++ 8 files changed, 273 insertions(+), 4 deletions(-) create mode 100644 features/project/public_projects.feature create mode 100644 features/steps/project/public_projects.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 32b1246601d..c2ca23fae53 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -88,7 +88,7 @@ class ApplicationController < ActionController::Base end def authorize_code_access! - return access_denied! unless can?(current_user, :download_code, project) + return access_denied! unless can?(current_user, :download_code, project) or project.public? end def authorize_create_team! diff --git a/app/models/ability.rb b/app/models/ability.rb index 5b49104da8a..c5e4524b8ce 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -37,7 +37,7 @@ class Ability elsif team.reporters.include?(user) rules << project_report_rules - elsif team.guests.include?(user) + elsif team.guests.include?(user) or project.public? rules << project_guest_rules end diff --git a/app/views/projects/_form.html.haml b/app/views/projects/_form.html.haml index 4d635e3dc68..0e1fd238005 100644 --- a/app/views/projects/_form.html.haml +++ b/app/views/projects/_form.html.haml @@ -48,7 +48,7 @@ Public mode: .control-group = f.label :public, class: 'control-label' do - %span Public clone access + %span Public access .controls = f.check_box :public %span.descr @@ -56,6 +56,8 @@ %em without any authentication. It will also be listed on the #{link_to "public access directory", public_root_path}. + %em Any + user will have #{link_to "Guest", help_permissions_path} permissions on the repository. %fieldset.features %legend diff --git a/app/views/public/projects/index.html.haml b/app/views/public/projects/index.html.haml index 3d0d793b2d2..e66851ead5b 100644 --- a/app/views/public/projects/index.html.haml +++ b/app/views/public/projects/index.html.haml @@ -9,7 +9,7 @@ %li.clearfix %h5 %i.icon-share - = project.name_with_namespace + = link_to_project project .pull-right %pre.dark.tiny git clone #{project.http_url_to_repo} %p.description diff --git a/features/project/public_projects.feature b/features/project/public_projects.feature new file mode 100644 index 00000000000..c5a9da14c54 --- /dev/null +++ b/features/project/public_projects.feature @@ -0,0 +1,8 @@ +Feature: Public Projects + Background: + Given I sign in as a user + + Scenario: I should see the list of public projects + When I visit the public projects area + Then I should see the list of public projects + diff --git a/features/steps/project/public_projects.rb b/features/steps/project/public_projects.rb new file mode 100644 index 00000000000..7063e7d56ae --- /dev/null +++ b/features/steps/project/public_projects.rb @@ -0,0 +1,9 @@ +class PublicProjects < Spinach::FeatureSteps + include SharedAuthentication + include SharedProject + include SharedPaths + + Then 'I should see the list of public projects' do + page.should have_content "Public Projects" + end +end diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index 27ca65b22dd..38730cc2cd6 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -263,6 +263,14 @@ module SharedPaths visit project_wiki_path(@project, :home) end + # ---------------------------------------- + # Public Projects + # ---------------------------------------- + + Given 'I visit the public projects area' do + visit public_root_path + end + def root_ref @project.repository.root_ref end diff --git a/spec/features/security/project_access_spec.rb b/spec/features/security/project_access_spec.rb index cfbb8f135ab..a00b2b0375a 100644 --- a/spec/features/security/project_access_spec.rb +++ b/spec/features/security/project_access_spec.rb @@ -229,4 +229,246 @@ describe "Application access" do it { should be_denied_for :visitor } end end + + + describe "PublicProject" do + let(:project) { create(:project) } + + let(:master) { create(:user) } + let(:guest) { create(:user) } + let(:reporter) { create(:user) } + + let(:admin) { create(:user) } + + before do + # public project + project.public = true + project.save! + + # full access + project.team << [master, :master] + + # readonly + project.team << [reporter, :reporter] + + end + + describe "Project should be public" do + subject { project } + + its(:public?) { should be_true } + end + + describe "GET /project_code" do + subject { project_path(project) } + + it { should be_allowed_for master } + it { should be_allowed_for reporter } + it { should be_allowed_for admin } + it { should be_allowed_for guest } + it { should be_allowed_for :user } + it { should be_denied_for :visitor } + end + + describe "GET /project_code/tree/master" do + subject { project_tree_path(project, project.repository.root_ref) } + + it { should be_allowed_for master } + it { should be_allowed_for reporter } + it { should be_allowed_for :admin } + it { should be_allowed_for guest } + it { should be_allowed_for :user } + it { should be_denied_for :visitor } + end + + describe "GET /project_code/commits/master" do + subject { project_commits_path(project, project.repository.root_ref, limit: 1) } + + it { should be_allowed_for master } + it { should be_allowed_for reporter } + it { should be_allowed_for :admin } + it { should be_allowed_for guest } + it { should be_allowed_for :user } + it { should be_denied_for :visitor } + end + + describe "GET /project_code/commit/:sha" do + subject { project_commit_path(project, project.repository.commit) } + + it { should be_allowed_for master } + it { should be_allowed_for reporter } + it { should be_allowed_for :admin } + it { should be_allowed_for guest } + it { should be_allowed_for :user } + it { should be_denied_for :visitor } + end + + describe "GET /project_code/compare" do + subject { project_compare_index_path(project) } + + it { should be_allowed_for master } + it { should be_allowed_for reporter } + it { should be_allowed_for :admin } + it { should be_allowed_for guest } + it { should be_allowed_for :user } + it { should be_denied_for :visitor } + end + + describe "GET /project_code/team" do + subject { project_team_index_path(project) } + + it { should be_allowed_for master } + it { should be_allowed_for reporter } + it { should be_allowed_for :admin } + it { should be_allowed_for guest } + it { should be_allowed_for :user } + it { should be_denied_for :visitor } + end + + describe "GET /project_code/wall" do + subject { project_wall_path(project) } + + it { should be_allowed_for master } + it { should be_allowed_for reporter } + it { should be_allowed_for :admin } + it { should be_allowed_for guest } + it { should be_allowed_for :user } + it { should be_denied_for :visitor } + end + + describe "GET /project_code/blob" do + before do + commit = project.repository.commit + path = commit.tree.contents.select { |i| i.is_a?(Grit::Blob)}.first.name + @blob_path = project_blob_path(project, File.join(commit.id, path)) + end + + it { @blob_path.should be_allowed_for master } + it { @blob_path.should be_allowed_for reporter } + it { @blob_path.should be_allowed_for :admin } + it { @blob_path.should be_allowed_for guest } + it { @blob_path.should be_allowed_for :user } + it { @blob_path.should be_denied_for :visitor } + end + + describe "GET /project_code/edit" do + subject { edit_project_path(project) } + + it { should be_allowed_for master } + it { should be_denied_for reporter } + it { should be_denied_for :admin } + it { should be_denied_for guest } + it { should be_denied_for :user } + it { should be_denied_for :visitor } + end + + describe "GET /project_code/deploy_keys" do + subject { project_deploy_keys_path(project) } + + it { should be_allowed_for master } + it { should be_denied_for reporter } + it { should be_denied_for :admin } + it { should be_denied_for guest } + it { should be_denied_for :user } + it { should be_denied_for :visitor } + end + + describe "GET /project_code/issues" do + subject { project_issues_path(project) } + + it { should be_allowed_for master } + it { should be_allowed_for reporter } + it { should be_allowed_for :admin } + it { should be_allowed_for guest } + it { should be_allowed_for :user } + it { should be_denied_for :visitor } + end + + describe "GET /project_code/snippets" do + subject { project_snippets_path(project) } + + it { should be_allowed_for master } + it { should be_allowed_for reporter } + it { should be_allowed_for :admin } + it { should be_allowed_for guest } + it { should be_allowed_for :user } + it { should be_denied_for :visitor } + end + + describe "GET /project_code/snippets/new" do + subject { new_project_snippet_path(project) } + + it { should be_allowed_for master } + it { should be_allowed_for reporter } + it { should be_denied_for :admin } + it { should be_denied_for guest } + it { should be_denied_for :user } + it { should be_denied_for :visitor } + end + + describe "GET /project_code/merge_requests" do + subject { project_merge_requests_path(project) } + + it { should be_allowed_for master } + it { should be_allowed_for reporter } + it { should be_allowed_for :admin } + it { should be_allowed_for guest } + it { should be_allowed_for :user } + it { should be_denied_for :visitor } + end + + describe "GET /project_code/repository" do + subject { project_repository_path(project) } + + it { should be_allowed_for master } + it { should be_allowed_for reporter } + it { should be_allowed_for :admin } + it { should be_allowed_for guest } + it { should be_allowed_for :user } + it { should be_denied_for :visitor } + end + + describe "GET /project_code/repository/branches" do + subject { branches_project_repository_path(project) } + + before do + # Speed increase + Project.any_instance.stub(:branches).and_return([]) + end + + it { should be_allowed_for master } + it { should be_allowed_for reporter } + it { should be_allowed_for :admin } + it { should be_allowed_for guest } + it { should be_allowed_for :user } + it { should be_denied_for :visitor } + end + + describe "GET /project_code/repository/tags" do + subject { tags_project_repository_path(project) } + + before do + # Speed increase + Project.any_instance.stub(:tags).and_return([]) + end + + it { should be_allowed_for master } + it { should be_allowed_for reporter } + it { should be_allowed_for :admin } + it { should be_allowed_for guest } + it { should be_allowed_for :user } + it { should be_denied_for :visitor } + end + + describe "GET /project_code/hooks" do + subject { project_hooks_path(project) } + + it { should be_allowed_for master } + it { should be_allowed_for reporter } + it { should be_allowed_for :admin } + it { should be_allowed_for guest } + it { should be_allowed_for :user } + it { should be_denied_for :visitor } + end + end end From a7ba81ea5d3f2c9b0085db68e880e89cec960f6f Mon Sep 17 00:00:00 2001 From: Stephen Lottermoser Date: Thu, 2 May 2013 00:53:54 -0700 Subject: [PATCH 2/3] Fix internally public projects tests Takes into account 98bea4b1ff and 3b88636d3c. --- spec/features/security/project_access_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/features/security/project_access_spec.rb b/spec/features/security/project_access_spec.rb index a00b2b0375a..8c65af061e0 100644 --- a/spec/features/security/project_access_spec.rb +++ b/spec/features/security/project_access_spec.rb @@ -232,7 +232,7 @@ describe "Application access" do describe "PublicProject" do - let(:project) { create(:project) } + let(:project) { create(:project_with_code) } let(:master) { create(:user) } let(:guest) { create(:user) } @@ -356,7 +356,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_denied_for reporter } - it { should be_denied_for :admin } + it { should be_allowed_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -367,7 +367,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_denied_for reporter } - it { should be_denied_for :admin } + it { should be_allowed_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -400,7 +400,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_denied_for :admin } + it { should be_allowed_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } From 8589f0f4000095583a3a536f4986dd85a379669d Mon Sep 17 00:00:00 2001 From: Stephen Lottermoser Date: Thu, 2 May 2013 13:25:31 -0700 Subject: [PATCH 3/3] Non-logged in users see public project names as static text In the public area, project names are shown as static text for non-logged in users, while logged-in users are given project names as links they can follow to the project's page. --- app/views/public/projects/index.html.haml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/views/public/projects/index.html.haml b/app/views/public/projects/index.html.haml index e66851ead5b..c31fcfd15de 100644 --- a/app/views/public/projects/index.html.haml +++ b/app/views/public/projects/index.html.haml @@ -9,7 +9,10 @@ %li.clearfix %h5 %i.icon-share - = link_to_project project + - if current_user + = link_to_project project + - else + = project.name_with_namespace .pull-right %pre.dark.tiny git clone #{project.http_url_to_repo} %p.description