diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 32b1246601de7fbd3eca5fce5938fdb29a352713..c2ca23fae53d2a88d57d1f2901142a423b503f95 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -88,7 +88,7 @@ def authorize_project!(action)
   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 5b49104da8a8fe949c6a3ef30521ca551cbfb8fd..c5e4524b8ceb70ca56dd229098bca0fe9efd6f90 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -37,7 +37,7 @@ def project_abilities(user, project)
       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 4d635e3dc68b9b78e5a751e5ff356f5f8a567d80..0e1fd2380053e32e68f630ecc1d8f10702119eff 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 3d0d793b2d2edee6e0eac2982ec0d7f7a3595257..e66851ead5b3ecc66c1121d2aed9e8f51a7a9365 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 0000000000000000000000000000000000000000..c5a9da14c541653fb6afdaa7f83f9672bd4d21a6
--- /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 0000000000000000000000000000000000000000..7063e7d56aefcc27c7312af6750dfbfaade94c04
--- /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 27ca65b22dd662d13695d0d89307e36bca73aabc..38730cc2cd61a017dfc5cfd96ea217bde3f88549 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 cfbb8f135ab0c05fb8d373732cf6f0eb2cc26138..a00b2b0375ab7d01bcf92ed22f6a1c7921a2a3e8 100644
--- a/spec/features/security/project_access_spec.rb
+++ b/spec/features/security/project_access_spec.rb
@@ -229,4 +229,246 @@
       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