From 8bb1931ef2d25ee5dfc5352a3932b948656ddf94 Mon Sep 17 00:00:00 2001
From: Patricio Cano <suprnova32@gmail.com>
Date: Wed, 10 Aug 2016 19:04:25 -0500
Subject: [PATCH] Deny Git over HTTP access to users that have 2FA enabled,
 unless they use a Personal Access Token.

---
 .../personal_access_tokens/index.html.haml    |  4 ++
 spec/requests/git_http_spec.rb                | 41 +++++++++++++++++++
 2 files changed, 45 insertions(+)

diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml
index 71ac367830d..03ee682b343 100644
--- a/app/views/profiles/personal_access_tokens/index.html.haml
+++ b/app/views/profiles/personal_access_tokens/index.html.haml
@@ -7,6 +7,10 @@
       = page_title
     %p
       You can generate a personal access token for each application you use that needs access to the GitLab API.
+    %p
+      You can also use personal access tokens to authenticate against Git over HTTP. Use them specially when you
+      have 2FA enabled.
+
   .col-lg-9
 
     - if flash[:personal_access_token]
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 8537c252b58..37c2586bbe2 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -198,6 +198,47 @@ describe 'Git HTTP requests', lib: true do
               end
             end
 
+            context 'when user has 2FA enabled' do
+              before do
+                @user = create(:user, :two_factor)
+                project.team << [@user, :master]
+              end
+
+              context 'when username and password are provided' do
+                it 'rejects the clone attempt' do
+                  download("#{project.path_with_namespace}.git", user: @user.username, password: @user.password) do |response|
+                    expect(response).to have_http_status(401)
+                    expect(response.body).to include('You have 2FA enabled, please use a personal access token for Git over HTTP')
+                  end
+                end
+
+                it 'rejects the push attempt' do
+                  upload("#{project.path_with_namespace}.git", user: @user.username, password: @user.password) do |response|
+                    expect(response).to have_http_status(401)
+                    expect(response.body).to include('You have 2FA enabled, please use a personal access token for Git over HTTP')
+                  end
+                end
+              end
+
+              context 'when username and personal access token are provided' do
+                before do
+                  @token = create(:personal_access_token, user: @user)
+                end
+
+                it 'allows clones' do
+                  download("#{project.path_with_namespace}.git", user: @user.username, password: @token.token) do |response|
+                    expect(response).to have_http_status(200)
+                  end
+                end
+
+                it 'allows pushes' do
+                  upload("#{project.path_with_namespace}.git", user: @user.username, password: @token.token) do |response|
+                    expect(response).to have_http_status(200)
+                  end
+                end
+              end
+            end
+
             context "when blank password attempts follow a valid login" do
               def attempt_login(include_password)
                 password = include_password ? user.password : ""
-- 
GitLab