From 9d8fbcc03847820eeda61e9d765693161f3619c5 Mon Sep 17 00:00:00 2001
From: Patricio Cano <suprnova32@gmail.com>
Date: Wed, 24 Aug 2016 17:08:23 -0500
Subject: [PATCH] Added project specific enable/disable setting for LFS

---
 app/controllers/projects_controller.rb        |   2 +-
 app/helpers/lfs_helper.rb                     |   4 +
 app/helpers/projects_helper.rb                |  12 ++
 app/models/project.rb                         |   4 +
 app/views/admin/projects/show.html.haml       |   5 +
 app/views/projects/edit.html.haml             |   8 ++
 ...160823213309_add_enable_lfs_to_projects.rb |  29 +++++
 db/schema.rb                                  |   1 +
 spec/requests/lfs_http_spec.rb                | 107 ++++++++++++++++++
 9 files changed, 171 insertions(+), 1 deletion(-)
 create mode 100644 db/migrate/20160823213309_add_enable_lfs_to_projects.rb

diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index fc52cd2f367..678b56b5d9b 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -309,7 +309,7 @@ class ProjectsController < Projects::ApplicationController
       :issues_tracker_id, :default_branch,
       :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar,
       :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex,
-      :public_builds, :only_allow_merge_if_build_succeeds, :request_access_enabled
+      :public_builds, :only_allow_merge_if_build_succeeds, :request_access_enabled, :enable_lfs
     )
   end
 
diff --git a/app/helpers/lfs_helper.rb b/app/helpers/lfs_helper.rb
index eb651e3687e..5d82abfca79 100644
--- a/app/helpers/lfs_helper.rb
+++ b/app/helpers/lfs_helper.rb
@@ -23,10 +23,14 @@ module LfsHelper
   end
 
   def lfs_download_access?
+    return false unless project.lfs_enabled?
+
     project.public? || ci? || (user && user.can?(:download_code, project))
   end
 
   def lfs_upload_access?
+    return false unless project.lfs_enabled?
+
     user && user.can?(:push_code, project)
   end
 
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 356f27f2d5d..a5ae9f8668e 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -187,6 +187,18 @@ module ProjectsHelper
     nav_tabs.flatten
   end
 
+  def project_lfs_status(project)
+    if project.lfs_enabled?
+      content_tag(:span, class: 'vs-private') do
+        'Enabled'
+      end
+    else
+      content_tag(:span, class: 'vs-internal') do
+        'Disabled'
+      end
+    end
+  end
+
   def git_user_name
     if current_user
       current_user.name
diff --git a/app/models/project.rb b/app/models/project.rb
index c34064f96ce..c271448946c 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -390,6 +390,10 @@ class Project < ActiveRecord::Base
     end
   end
 
+  def lfs_enabled?
+    (Gitlab.config.lfs.enabled && enable_lfs) || (enable_lfs.nil? && Gitlab.config.lfs.enabled)
+  end
+
   def repository_storage_path
     Gitlab.config.repositories.storages[repository_storage]
   end
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index b2c607361b3..f65322cc12f 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -73,6 +73,11 @@
             %span.light last commit:
             %strong
               = last_commit(@project)
+
+          %li
+            %span.light LFS status:
+            %strong
+              = project_lfs_status(@project)
         - else
           %li
             %span.light repository:
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index b282aa52b25..8aa2db197a3 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -80,6 +80,14 @@
                 %strong Snippets
                 %br
                 %span.descr Share code pastes with others out of git repository
+          - if Gitlab.config.lfs.enabled && current_user.admin?
+            .form-group
+              .checkbox
+                = f.label :enable_lfs do
+                  = f.check_box :enable_lfs, checked: (true if @project.enable_lfs || @project.enable_lfs.nil?)
+                  %strong LFS
+                  %br
+                  %span.descr Git Large File Storage
           - if Gitlab.config.registry.enabled
             .form-group
               .checkbox
diff --git a/db/migrate/20160823213309_add_enable_lfs_to_projects.rb b/db/migrate/20160823213309_add_enable_lfs_to_projects.rb
new file mode 100644
index 00000000000..9df1a5078fa
--- /dev/null
+++ b/db/migrate/20160823213309_add_enable_lfs_to_projects.rb
@@ -0,0 +1,29 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddEnableLfsToProjects < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  # Set this constant to true if this migration requires downtime.
+  DOWNTIME = false
+
+  # When a migration requires downtime you **must** uncomment the following
+  # constant and define a short and easy to understand explanation as to why the
+  # migration requires downtime.
+  # DOWNTIME_REASON = ''
+
+  # When using the methods "add_concurrent_index" or "add_column_with_default"
+  # you must disable the use of transactions as these methods can not run in an
+  # existing transaction. When using "add_concurrent_index" make sure that this
+  # method is the _only_ method called in the migration, any other changes
+  # should go in a separate migration. This ensures that upon failure _only_ the
+  # index creation fails and can be retried or reverted easily.
+  #
+  # To disable transactions uncomment the following line and remove these
+  # comments:
+  # disable_ddl_transaction!
+
+  def change
+    add_column :projects, :enable_lfs, :boolean
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 227e10294e4..28711294746 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -825,6 +825,7 @@ ActiveRecord::Schema.define(version: 20160824103857) do
     t.string   "repository_storage",                 default: "default", null: false
     t.boolean  "request_access_enabled",             default: true,      null: false
     t.boolean  "has_external_wiki"
+    t.boolean  "enable_lfs"
   end
 
   add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index 4c9b4a8ba42..2d39f3808d5 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -44,6 +44,113 @@ describe 'Git LFS API and storage' do
     end
   end
 
+  context 'project specific LFS settings' do
+    let(:project) { create(:empty_project) }
+    let(:body) do
+      {
+        'objects' => [
+          { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+            'size' => 1575078
+          },
+          { 'oid' => sample_oid,
+            'size' => sample_size
+          }
+        ],
+        'operation' => 'upload'
+      }
+    end
+    let(:authorization) { authorize_user }
+
+    context 'with LFS disabled globally' do
+      before do
+        project.team << [user, :master]
+        allow(Gitlab.config.lfs).to receive(:enabled).and_return(false)
+      end
+
+      describe 'LFS disabled in project' do
+        before do
+          project.update_attribute(:enable_lfs, false)
+        end
+
+        it 'responds with a 501 message on upload' do
+          post_lfs_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
+
+          expect(response).to have_http_status(501)
+        end
+
+        it 'responds with a 501 message on download' do
+          get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
+
+          expect(response).to have_http_status(501)
+        end
+      end
+
+      describe 'LFS enabled in project' do
+        before do
+          project.update_attribute(:enable_lfs, true)
+        end
+
+        it 'responds with a 501 message on upload' do
+          post_lfs_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
+
+          expect(response).to have_http_status(501)
+        end
+
+        it 'responds with a 501 message on download' do
+          get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
+
+          expect(response).to have_http_status(501)
+        end
+      end
+    end
+
+    context 'with LFS enabled globally' do
+      before do
+        project.team << [user, :master]
+        enable_lfs
+      end
+
+      describe 'LFS disabled in project' do
+        before do
+          project.update_attribute(:enable_lfs, false)
+        end
+
+        it 'responds with a 403 message on upload' do
+          post_lfs_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
+
+          expect(response).to have_http_status(403)
+          expect(json_response).to include('message' => 'Access forbidden. Check your access level.')
+        end
+
+        it 'responds with a 403 message on download' do
+          get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
+
+          expect(response).to have_http_status(403)
+          expect(json_response).to include('message' => 'Access forbidden. Check your access level.')
+        end
+      end
+
+      describe 'LFS enabled in project' do
+        before do
+          project.update_attribute(:enable_lfs, true)
+        end
+
+        it 'responds with a 200 message on upload' do
+          post_lfs_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
+
+          expect(response).to have_http_status(200)
+          expect(json_response['objects'].first['size']).to eq(1575078)
+        end
+
+        it 'responds with a 200 message on download' do
+          get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
+
+          expect(response).to have_http_status(200)
+        end
+      end
+    end
+  end
+
   describe 'deprecated API' do
     let(:project) { create(:empty_project) }
 
-- 
GitLab