BigW Consortium Gitlab
Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
G
gitlab-ce
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Forest Godfrey
gitlab-ce
Commits
0d975244
Commit
0d975244
authored
Jul 20, 2016
by
Jacob Vosmaer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add LFS controllers
parent
033e5423
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
420 additions
and
116 deletions
+420
-116
git_http_client_controller.rb
app/controllers/projects/git_http_client_controller.rb
+110
-0
git_http_controller.rb
app/controllers/projects/git_http_controller.rb
+3
-104
lfs_api_controller.rb
app/controllers/projects/lfs_api_controller.rb
+94
-0
lfs_storage_controller.rb
app/controllers/projects/lfs_storage_controller.rb
+106
-0
lfs_helper.rb
app/helpers/lfs_helper.rb
+66
-0
routes.rb
config/routes.rb
+16
-4
lfs_http_spec.rb
spec/requests/lfs_http_spec.rb
+25
-8
No files found.
app/controllers/projects/git_http_client_controller.rb
0 → 100644
View file @
0d975244
# This file should be identical in GitLab Community Edition and Enterprise Edition
class
Projects
::
GitHttpClientController
<
Projects
::
ApplicationController
include
ActionController
::
HttpAuthentication
::
Basic
include
KerberosSpnegoHelper
attr_reader
:user
# Git clients will not know what authenticity token to send along
skip_before_action
:verify_authenticity_token
skip_before_action
:repository
before_action
:authenticate_user
before_action
:ensure_project_found!
private
def
authenticate_user
if
project
&&
project
.
public?
&&
download_request?
return
# Allow access
end
if
allow_basic_auth?
&&
basic_auth_provided?
login
,
password
=
user_name_and_password
(
request
)
auth_result
=
Gitlab
::
Auth
.
find_for_git_client
(
login
,
password
,
project:
project
,
ip:
request
.
ip
)
if
auth_result
.
type
==
:ci
&&
download_request?
@ci
=
true
elsif
auth_result
.
type
==
:oauth
&&
!
download_request?
# Not allowed
else
@user
=
auth_result
.
user
end
if
ci?
||
user
return
# Allow access
end
elsif
allow_kerberos_spnego_auth?
&&
spnego_provided?
@user
=
find_kerberos_user
if
user
send_final_spnego_response
return
# Allow access
end
end
send_challenges
render
plain:
"HTTP Basic: Access denied
\n
"
,
status:
401
end
def
basic_auth_provided?
has_basic_credentials?
(
request
)
end
def
send_challenges
challenges
=
[]
challenges
<<
'Basic realm="GitLab"'
if
allow_basic_auth?
challenges
<<
spnego_challenge
if
allow_kerberos_spnego_auth?
headers
[
'Www-Authenticate'
]
=
challenges
.
join
(
"
\n
"
)
if
challenges
.
any?
end
def
ensure_project_found!
render_not_found
if
project
.
blank?
end
def
project
return
@project
if
defined?
(
@project
)
project_id
,
_
=
project_id_with_suffix
if
project_id
.
blank?
@project
=
nil
else
@project
=
Project
.
find_with_namespace
(
"
#{
params
[
:namespace_id
]
}
/
#{
project_id
}
"
)
end
end
# This method returns two values so that we can parse
# params[:project_id] (untrusted input!) in exactly one place.
def
project_id_with_suffix
id
=
params
[
:project_id
]
||
''
%w[.wiki.git .git]
.
each
do
|
suffix
|
if
id
.
end_with?
(
suffix
)
# Be careful to only remove the suffix from the end of 'id'.
# Accidentally removing it from the middle is how security
# vulnerabilities happen!
return
[
id
.
slice
(
0
,
id
.
length
-
suffix
.
length
),
suffix
]
end
end
# Something is wrong with params[:project_id]; do not pass it on.
[
nil
,
nil
]
end
def
repository
_
,
suffix
=
project_id_with_suffix
if
suffix
==
'.wiki.git'
project
.
wiki
.
repository
else
project
.
repository
end
end
def
render_not_found
render
plain:
'Not Found'
,
status: :not_found
end
def
ci?
@ci
.
present?
end
end
app/controllers/projects/git_http_controller.rb
View file @
0d975244
# This file should be identical in GitLab Community Edition and Enterprise Edition
class
Projects
::
GitHttpController
<
Projects
::
ApplicationController
include
ActionController
::
HttpAuthentication
::
Basic
include
KerberosSpnegoHelper
attr_reader
:user
# Git clients will not know what authenticity token to send along
skip_before_action
:verify_authenticity_token
skip_before_action
:repository
before_action
:authenticate_user
before_action
:ensure_project_found!
class
Projects
::
GitHttpController
<
Projects
::
GitHttpClientController
# GET /foo/bar.git/info/refs?service=git-upload-pack (git pull)
# GET /foo/bar.git/info/refs?service=git-receive-pack (git push)
def
info_refs
...
...
@@ -46,81 +35,8 @@ class Projects::GitHttpController < Projects::ApplicationController
private
def
authenticate_user
if
project
&&
project
.
public?
&&
upload_pack?
return
# Allow access
end
if
allow_basic_auth?
&&
basic_auth_provided?
login
,
password
=
user_name_and_password
(
request
)
auth_result
=
Gitlab
::
Auth
.
find_for_git_client
(
login
,
password
,
project:
project
,
ip:
request
.
ip
)
if
auth_result
.
type
==
:ci
&&
upload_pack?
@ci
=
true
elsif
auth_result
.
type
==
:oauth
&&
!
upload_pack?
# Not allowed
else
@user
=
auth_result
.
user
end
if
ci?
||
user
return
# Allow access
end
elsif
allow_kerberos_spnego_auth?
&&
spnego_provided?
@user
=
find_kerberos_user
if
user
send_final_spnego_response
return
# Allow access
end
end
send_challenges
render
plain:
"HTTP Basic: Access denied
\n
"
,
status:
401
end
def
basic_auth_provided?
has_basic_credentials?
(
request
)
end
def
send_challenges
challenges
=
[]
challenges
<<
'Basic realm="GitLab"'
if
allow_basic_auth?
challenges
<<
spnego_challenge
if
allow_kerberos_spnego_auth?
headers
[
'Www-Authenticate'
]
=
challenges
.
join
(
"
\n
"
)
if
challenges
.
any?
end
def
ensure_project_found!
render_not_found
if
project
.
blank?
end
def
project
return
@project
if
defined?
(
@project
)
project_id
,
_
=
project_id_with_suffix
if
project_id
.
blank?
@project
=
nil
else
@project
=
Project
.
find_with_namespace
(
"
#{
params
[
:namespace_id
]
}
/
#{
project_id
}
"
)
end
end
# This method returns two values so that we can parse
# params[:project_id] (untrusted input!) in exactly one place.
def
project_id_with_suffix
id
=
params
[
:project_id
]
||
''
%w[.wiki.git .git]
.
each
do
|
suffix
|
if
id
.
end_with?
(
suffix
)
# Be careful to only remove the suffix from the end of 'id'.
# Accidentally removing it from the middle is how security
# vulnerabilities happen!
return
[
id
.
slice
(
0
,
id
.
length
-
suffix
.
length
),
suffix
]
end
end
# Something is wrong with params[:project_id]; do not pass it on.
[
nil
,
nil
]
def
download_request?
upload_pack?
end
def
upload_pack?
...
...
@@ -143,27 +59,10 @@ class Projects::GitHttpController < Projects::ApplicationController
render
json:
Gitlab
::
Workhorse
.
git_http_ok
(
repository
,
user
)
end
def
repository
_
,
suffix
=
project_id_with_suffix
if
suffix
==
'.wiki.git'
project
.
wiki
.
repository
else
project
.
repository
end
end
def
render_not_found
render
plain:
'Not Found'
,
status: :not_found
end
def
render_not_allowed
render
plain:
download_access
.
message
,
status: :forbidden
end
def
ci?
@ci
.
present?
end
def
upload_pack_allowed?
return
false
unless
Gitlab
.
config
.
gitlab_shell
.
upload_pack
...
...
app/controllers/projects/lfs_api_controller.rb
0 → 100644
View file @
0d975244
class
Projects
::
LfsApiController
<
Projects
::
GitHttpClientController
include
LfsHelper
before_action
:lfs_enabled!
before_action
:lfs_check_access!
,
except:
[
:deprecated
]
def
batch
unless
objects
.
present?
render_lfs_not_found
return
end
if
download_request?
render
json:
{
objects:
download_objects!
}
elsif
upload_request?
render
json:
{
objects:
upload_objects!
}
else
raise
"Never reached"
end
end
def
deprecated
render
(
json:
{
message:
'Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.'
,
documentation_url:
"
#{
Gitlab
.
config
.
gitlab
.
url
}
/help"
,
},
status:
501
)
end
private
def
objects
(
params
[
:objects
]
||
[]).
to_a
end
def
existing_oids
@existing_oids
||=
begin
storage_project
.
lfs_objects
.
where
(
oid:
objects
.
map
{
|
o
|
o
[
'oid'
].
to_s
}).
pluck
(
:oid
)
end
end
def
download_objects!
objects
.
each
do
|
object
|
if
existing_oids
.
include?
(
object
[
:oid
])
object
[
:actions
]
=
download_actions
(
object
)
else
object
[
:error
]
=
{
code:
404
,
message:
"Object does not exist on the server or you don't have permissions to access it"
,
}
end
end
objects
end
def
upload_objects!
objects
.
each
do
|
object
|
object
[
:actions
]
=
upload_actions
(
object
)
unless
existing_oids
.
include?
(
object
[
:oid
])
end
objects
end
def
download_actions
(
object
)
{
download:
{
href:
"
#{
project
.
http_url_to_repo
}
/gitlab-lfs/objects/
#{
object
[
:oid
]
}
"
,
header:
{
Authorization
:
request
.
headers
[
'Authorization'
]
}.
compact
}
}
end
def
upload_actions
(
object
)
{
upload:
{
href:
"
#{
project
.
http_url_to_repo
}
/gitlab-lfs/objects/
#{
object
[
:oid
]
}
/
#{
object
[
:size
]
}
"
,
header:
{
Authorization
:
request
.
headers
[
'Authorization'
]
}.
compact
}
}
end
def
download_request?
params
[
:operation
]
==
'download'
end
def
upload_request?
params
[
:operation
]
==
'upload'
end
end
app/controllers/projects/lfs_storage_controller.rb
0 → 100644
View file @
0d975244
class
Projects
::
LfsStorageController
<
Projects
::
GitHttpClientController
include
LfsHelper
before_action
:lfs_enabled!
before_action
:lfs_check_access!
def
download
lfs_object
=
LfsObject
.
find_by_oid
(
oid
)
unless
lfs_object
&&
lfs_object
.
file
.
exists?
render_lfs_not_found
return
end
send_file
lfs_object
.
file
.
path
,
content_type:
"application/octet-stream"
end
def
upload_authorize
render
(
json:
{
StoreLFSPath
:
"
#{
Gitlab
.
config
.
lfs
.
storage_path
}
/tmp/upload"
,
LfsOid
:
oid
,
LfsSize
:
size
,
},
content_type:
'application/json; charset=utf-8'
)
end
def
upload_finalize
unless
tmp_filename
render_lfs_forbidden
return
end
if
store_file
(
oid
,
size
,
tmp_filename
)
head
200
else
render
plain:
'Unprocessable entity'
,
status:
422
end
end
private
def
download_request?
action_name
==
'download'
end
def
upload_request?
%w[upload_authorize upload_finalize]
.
include?
action_name
end
def
oid
params
[
:oid
].
to_s
end
def
size
params
[
:size
].
to_i
end
def
tmp_filename
name
=
request
.
headers
[
'X-Gitlab-Lfs-Tmp'
]
if
name
.
present?
name
.
gsub!
(
/^.*(\\|\/)/
,
''
)
name
=
name
.
match
(
/[0-9a-f]{73}/
)
name
[
0
]
if
name
else
nil
end
end
def
store_file
(
oid
,
size
,
tmp_file
)
tmp_file_path
=
File
.
join
(
"
#{
Gitlab
.
config
.
lfs
.
storage_path
}
/tmp/upload"
,
tmp_file
)
object
=
LfsObject
.
find_or_create_by
(
oid:
oid
,
size:
size
)
if
object
.
file
.
exists?
success
=
true
else
success
=
move_tmp_file_to_storage
(
object
,
tmp_file_path
)
end
if
success
success
=
link_to_project
(
object
)
end
success
ensure
# Ensure that the tmp file is removed
FileUtils
.
rm_f
(
tmp_file_path
)
end
def
move_tmp_file_to_storage
(
object
,
path
)
File
.
open
(
path
)
do
|
f
|
object
.
file
=
f
end
object
.
file
.
store!
object
.
save
end
def
link_to_project
(
object
)
if
object
&&
!
object
.
projects
.
exists?
(
storage_project
.
id
)
object
.
projects
<<
storage_project
object
.
save
end
end
end
app/helpers/lfs_helper.rb
0 → 100644
View file @
0d975244
module
LfsHelper
def
lfs_enabled!
return
if
Gitlab
.
config
.
lfs
.
enabled
render
(
json:
{
message:
'Git LFS is not enabled on this GitLab server, contact your admin.'
,
documentation_url:
"
#{
Gitlab
.
config
.
gitlab
.
url
}
/help"
,
},
status:
501
)
end
def
lfs_check_access!
return
if
download_request?
&&
lfs_download_access?
return
if
upload_request?
&&
lfs_upload_access?
if
project
.
public?
||
(
user
&&
user
.
can?
(
:read_project
,
project
))
render_lfs_forbidden
else
render_lfs_not_found
end
end
def
lfs_download_access?
project
.
public?
||
ci?
||
(
user
&&
user
.
can?
(
:download_code
,
project
))
end
def
lfs_upload_access?
user
&&
user
.
can?
(
:push_code
,
project
)
end
def
render_lfs_forbidden
render
(
json:
{
message:
'Access forbidden. Check your access level.'
,
documentation_url:
"
#{
Gitlab
.
config
.
gitlab
.
url
}
/help"
,
},
content_type:
"application/vnd.git-lfs+json"
,
status:
403
)
end
def
render_lfs_not_found
render
(
json:
{
message:
'Not found.'
,
documentation_url:
"
#{
Gitlab
.
config
.
gitlab
.
url
}
/help"
,
},
content_type:
"application/vnd.git-lfs+json"
,
status:
404
)
end
def
storage_project
@storage_project
||=
begin
result
=
project
while
result
.
forked?
do
result
=
result
.
forked_from_project
end
result
end
end
end
config/routes.rb
View file @
0d975244
...
...
@@ -85,9 +85,6 @@ Rails.application.routes.draw do
# Health check
get
'health_check(/:checks)'
=>
'health_check#index'
,
as: :health_check
# Enable Grack support (for LFS only)
mount
Grack
::
AuthSpawner
,
at:
'/'
,
constraints:
lambda
{
|
request
|
/[-\/\w\.]+\.git\/(info\/lfs|gitlab-lfs)/
.
match
(
request
.
path_info
)
},
via:
[
:get
,
:post
,
:put
]
# Help
get
'help'
=>
'help#index'
get
'help/shortcuts'
=>
'help#shortcuts'
...
...
@@ -483,11 +480,26 @@ Rails.application.routes.draw do
end
scope
module: :projects
do
# Git HTTP clients ('git clone' etc.)
scope
constraints:
{
id:
/.+\.git/
,
format:
nil
}
do
# Git HTTP clients ('git clone' etc.)
get
'/info/refs'
,
to:
'git_http#info_refs'
post
'/git-upload-pack'
,
to:
'git_http#git_upload_pack'
post
'/git-receive-pack'
,
to:
'git_http#git_receive_pack'
# Git LFS API (metadata)
post
'/info/lfs/objects/batch'
,
to:
'lfs_api#batch'
post
'/info/lfs/objects'
,
to:
'lfs_api#deprecated'
get
'/info/lfs/objects/*oid'
,
to:
'lfs_api#deprecated'
# GitLab LFS object storage
scope
constraints:
{
oid:
/[a-f0-9]{64}/
}
do
get
'/gitlab-lfs/objects/*oid'
,
to:
'lfs_storage#download'
scope
constraints:
{
size:
/[0-9]+/
}
do
put
'/gitlab-lfs/objects/*oid/*size/authorize'
,
to:
'lfs_storage#upload_authorize'
put
'/gitlab-lfs/objects/*oid/*size'
,
to:
'lfs_storage#upload_finalize'
end
end
end
# Allow /info/refs, /info/refs?service=git-upload-pack, and
...
...
spec/requests/lfs_http_spec.rb
View file @
0d975244
...
...
@@ -31,6 +31,7 @@ describe Gitlab::Lfs::Router do
'operation'
=>
'upload'
}
end
let
(
:authorization
)
{
authorize_user
}
before
do
allow
(
Gitlab
.
config
.
lfs
).
to
receive
(
:enabled
).
and_return
(
false
)
...
...
@@ -71,6 +72,7 @@ describe Gitlab::Lfs::Router do
end
context
'when handling lfs request using deprecated API'
do
let
(
:authorization
)
{
authorize_user
}
before
do
post_json
"
#{
project
.
http_url_to_repo
}
/info/lfs/objects"
,
nil
,
headers
end
...
...
@@ -118,8 +120,8 @@ describe Gitlab::Lfs::Router do
project
.
lfs_objects
<<
lfs_object
end
it
'responds with status 40
3
'
do
expect
(
response
).
to
have_http_status
(
40
3
)
it
'responds with status 40
4
'
do
expect
(
response
).
to
have_http_status
(
40
4
)
end
end
...
...
@@ -147,8 +149,8 @@ describe Gitlab::Lfs::Router do
context
'without required headers'
do
let
(
:authorization
)
{
authorize_user
}
it
'responds with status 40
3
'
do
expect
(
response
).
to
have_http_status
(
40
3
)
it
'responds with status 40
4
'
do
expect
(
response
).
to
have_http_status
(
40
4
)
end
end
end
...
...
@@ -304,10 +306,10 @@ describe Gitlab::Lfs::Router do
end
context
'when user does is not member of the project'
do
let
(
:
role
)
{
:guest
}
let
(
:
update_user_permissions
)
{
nil
}
it
'responds with 40
3
'
do
expect
(
response
).
to
have_http_status
(
40
3
)
it
'responds with 40
4
'
do
expect
(
response
).
to
have_http_status
(
40
4
)
end
end
...
...
@@ -510,6 +512,7 @@ describe Gitlab::Lfs::Router do
describe
'unsupported'
do
let
(
:project
)
{
create
(
:empty_project
)
}
let
(
:authorization
)
{
authorize_user
}
let
(
:body
)
do
{
'operation'
=>
'other'
,
'objects'
=>
[
...
...
@@ -557,7 +560,7 @@ describe Gitlab::Lfs::Router do
end
it
'does not recognize it as a valid lfs command'
do
expect
(
response
).
to
have_http_status
(
40
3
)
expect
(
response
).
to
have_http_status
(
40
1
)
end
end
end
...
...
@@ -582,6 +585,16 @@ describe Gitlab::Lfs::Router do
expect
(
response
).
to
have_http_status
(
403
)
end
end
context
'and request is sent with a malformed headers'
do
before
do
put_finalize
(
'cat /etc/passwd'
)
end
it
'does not recognize it as a valid lfs command'
do
expect
(
response
).
to
have_http_status
(
403
)
end
end
end
describe
'to one project'
do
...
...
@@ -627,6 +640,10 @@ describe Gitlab::Lfs::Router do
end
describe
'and user does not have push access'
do
before
do
project
.
team
<<
[
user
,
:reporter
]
end
it_behaves_like
'forbidden'
end
end
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment