Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
941e578c
Commit
941e578c
authored
Jun 28, 2016
by
Jacob Vosmaer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Roll back Grack::Auth to fix Git HTTP SPNEGO
parent
80a16d49
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
374 additions
and
11 deletions
+374
-11
config/routes.rb
config/routes.rb
+2
-9
lib/gitlab/backend/grack_auth.rb
lib/gitlab/backend/grack_auth.rb
+59
-1
lib/gitlab/backend/shell_env.rb
lib/gitlab/backend/shell_env.rb
+28
-0
spec/lib/gitlab/backend/grack_auth_spec.rb
spec/lib/gitlab/backend/grack_auth_spec.rb
+284
-0
spec/requests/git_http_spec.rb
spec/requests/git_http_spec.rb
+1
-1
No files found.
config/routes.rb
View file @
941e578c
...
...
@@ -93,8 +93,8 @@ 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
]
# Enable Grack support
mount
Grack
::
AuthSpawner
,
at:
'/'
,
constraints:
lambda
{
|
request
|
/[-\/\w\.]+\.git\//
.
match
(
request
.
path_info
)
},
via:
[
:get
,
:post
,
:put
]
# Help
get
'help'
=>
'help#index'
...
...
@@ -502,13 +502,6 @@ Rails.application.routes.draw do
end
scope
module: :projects
do
# Git HTTP clients ('git clone' etc.)
scope
constraints:
{
id:
/.+\.git/
,
format:
nil
}
do
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'
end
# Allow /info/refs, /info/refs?service=git-upload-pack, and
# /info/refs?service=git-receive-pack, but nothing else.
#
...
...
lib/gitlab/backend/grack_auth.rb
View file @
941e578c
require_relative
'shell_env'
module
Grack
class
AuthSpawner
def
self
.
call
(
env
)
...
...
@@ -39,7 +41,10 @@ module Grack
lfs_response
=
Gitlab
::
Lfs
::
Router
.
new
(
project
,
@user
,
@request
).
try_call
return
lfs_response
unless
lfs_response
.
nil?
if
@user
.
nil?
&&
!
@ci
if
project
&&
authorized_request?
# Tell gitlab-workhorse the request is OK, and what the GL_ID is
render_grack_auth_ok
elsif
@user
.
nil?
&&
!
@ci
unauthorized
else
render_not_found
...
...
@@ -114,6 +119,11 @@ module Grack
@user
=
authenticate_user
(
login
,
password
)
end
if
@user
Gitlab
::
ShellEnv
.
set_env
(
@user
)
@env
[
'REMOTE_USER'
]
=
@auth
.
username
end
end
def
ci_request?
(
login
,
password
)
...
...
@@ -186,6 +196,36 @@ module Grack
user
end
def
authorized_request?
return
true
if
@ci
case
git_cmd
when
*
Gitlab
::
GitAccess
::
DOWNLOAD_COMMANDS
if
!
Gitlab
.
config
.
gitlab_shell
.
upload_pack
false
elsif
user
Gitlab
::
GitAccess
.
new
(
user
,
project
).
download_access_check
.
allowed?
elsif
project
.
public?
# Allow clone/fetch for public projects
true
else
false
end
when
*
Gitlab
::
GitAccess
::
PUSH_COMMANDS
if
!
Gitlab
.
config
.
gitlab_shell
.
receive_pack
false
elsif
user
# Skip user authorization on upload request.
# It will be done by the pre-receive hook in the repository.
true
else
false
end
else
false
end
end
def
git_cmd
if
@request
.
get?
@request
.
params
[
'service'
]
...
...
@@ -212,6 +252,24 @@ module Grack
end
end
def
render_grack_auth_ok
repo_path
=
if
@request
.
path_info
=~
/^([\w\.\/-]+)\.wiki\.git/
ProjectWiki
.
new
(
project
).
repository
.
path_to_repo
else
project
.
repository
.
path_to_repo
end
[
200
,
{
"Content-Type"
=>
"application/json"
},
[
JSON
.
dump
({
'GL_ID'
=>
Gitlab
::
ShellEnv
.
gl_id
(
@user
),
'RepoPath'
=>
repo_path
,
})]
]
end
def
render_not_found
[
404
,
{
"Content-Type"
=>
"text/plain"
},
[
"Not Found"
]]
end
...
...
lib/gitlab/backend/shell_env.rb
0 → 100644
View file @
941e578c
module
Gitlab
# This module provide 2 methods
# to set specific ENV variables for GitLab Shell
module
ShellEnv
extend
self
def
set_env
(
user
)
# Set GL_ID env variable
if
user
ENV
[
'GL_ID'
]
=
gl_id
(
user
)
end
end
def
reset_env
# Reset GL_ID env variable
ENV
[
'GL_ID'
]
=
nil
end
def
gl_id
(
user
)
if
user
.
present?
"user-
#{
user
.
id
}
"
else
# This empty string is used in the render_grack_auth_ok method
""
end
end
end
end
spec/lib/gitlab/backend/grack_auth_spec.rb
0 → 100644
View file @
941e578c
require
"spec_helper"
describe
Grack
::
Auth
,
lib:
true
do
let
(
:user
)
{
create
(
:user
)
}
let
(
:project
)
{
create
(
:project
)
}
let
(
:app
)
{
lambda
{
|
env
|
[
200
,
{},
"Success!"
]
}
}
let
(
:env
)
do
{
'rack.input'
=>
''
,
'REQUEST_METHOD'
=>
'GET'
,
'QUERY_STRING'
=>
'service=git-upload-pack'
}
end
let
(
:status
)
{
Grack
::
AuthSpawner
::
call
(
env
).
first
}
describe
"#call"
do
context
"when the project doesn't exist"
do
before
do
env
[
"PATH_INFO"
]
=
"doesnt/exist.git"
end
context
"when no authentication is provided"
do
it
"responds with status 401"
do
expect
(
status
).
to
eq
(
401
)
end
end
context
"when username and password are provided"
do
context
"when authentication fails"
do
before
do
env
[
"HTTP_AUTHORIZATION"
]
=
ActionController
::
HttpAuthentication
::
Basic
.
encode_credentials
(
user
.
username
,
"nope"
)
end
it
"responds with status 401"
do
expect
(
status
).
to
eq
(
401
)
end
end
context
"when authentication succeeds"
do
before
do
env
[
"HTTP_AUTHORIZATION"
]
=
ActionController
::
HttpAuthentication
::
Basic
.
encode_credentials
(
user
.
username
,
user
.
password
)
end
it
"responds with status 404"
do
expect
(
status
).
to
eq
(
404
)
end
end
end
end
context
"when the Wiki for a project exists"
do
before
do
@wiki
=
ProjectWiki
.
new
(
project
)
env
[
"PATH_INFO"
]
=
"
#{
@wiki
.
repository
.
path_with_namespace
}
.git/info/refs"
project
.
update_attribute
(
:visibility_level
,
Project
::
PUBLIC
)
end
it
"responds with the right project"
do
response
=
Grack
::
AuthSpawner
::
call
(
env
)
json_body
=
ActiveSupport
::
JSON
.
decode
(
response
[
2
][
0
])
expect
(
response
.
first
).
to
eq
(
200
)
expect
(
json_body
[
'RepoPath'
]).
to
include
(
@wiki
.
repository
.
path_with_namespace
)
end
end
context
"when the project exists"
do
before
do
env
[
"PATH_INFO"
]
=
project
.
path_with_namespace
+
".git"
end
context
"when the project is public"
do
before
do
project
.
update_attribute
(
:visibility_level
,
Project
::
PUBLIC
)
end
it
"responds with status 200"
do
expect
(
status
).
to
eq
(
200
)
end
end
context
"when the project is private"
do
before
do
project
.
update_attribute
(
:visibility_level
,
Project
::
PRIVATE
)
end
context
"when no authentication is provided"
do
it
"responds with status 401"
do
expect
(
status
).
to
eq
(
401
)
end
end
context
"when Kerberos token is provided"
do
before
do
allow_any_instance_of
(
Grack
::
Auth
).
to
receive
(
:allow_kerberos_auth?
).
and_return
(
true
)
env
[
"HTTP_AUTHORIZATION"
]
=
"Negotiate
#{
::
Base64
.
strict_encode64
(
'opaque_request_token'
)
}
"
end
shared_examples
"RFC4559 compliance"
do
it
"complies with RFC4559"
do
allow_any_instance_of
(
Grack
::
Auth
::
Request
).
to
receive
(
:spnego_response_token
).
and_return
(
"opaque_response_token"
)
headers
=
Grack
::
AuthSpawner
::
call
(
env
)[
1
]
expect
(
headers
[
'WWW-Authenticate'
].
split
(
"
\n
"
)).
to
include
(
"Negotiate
#{
::
Base64
.
strict_encode64
(
'opaque_response_token'
)
}
"
)
end
end
context
"when authentication fails because of invalid Kerberos token"
do
before
do
allow_any_instance_of
(
Grack
::
Auth
::
Request
).
to
receive
(
:spnego_credentials!
).
and_return
(
nil
)
end
it
"responds with status 401"
do
expect
(
status
).
to
eq
(
401
)
end
end
context
"when authentication fails because of unknown Kerberos identity"
do
before
do
allow_any_instance_of
(
Grack
::
Auth
::
Request
).
to
receive
(
:spnego_credentials!
).
and_return
(
"mylogin@FOO.COM"
)
end
it
"responds with status 401"
do
expect
(
status
).
to
eq
(
401
)
end
end
context
"when authentication succeeds"
do
before
do
allow_any_instance_of
(
Grack
::
Auth
::
Request
).
to
receive
(
:spnego_credentials!
).
and_return
(
"mylogin@FOO.COM"
)
user
.
identities
.
build
(
provider:
"kerberos"
,
extern_uid
:"mylogin@FOO.COM"
).
save
end
context
"when the user has access to the project"
do
before
do
project
.
team
<<
[
user
,
:master
]
end
context
"when the user is blocked"
do
before
do
user
.
block
project
.
team
<<
[
user
,
:master
]
end
it
"responds with status 404"
do
expect
(
status
).
to
eq
(
404
)
end
end
context
"when the user isn't blocked"
do
it
"responds with status 200"
do
expect
(
status
).
to
eq
(
200
)
end
end
include_examples
"RFC4559 compliance"
end
context
"when the user doesn't have access to the project"
do
it
"responds with status 404"
do
expect
(
status
).
to
eq
(
404
)
end
include_examples
"RFC4559 compliance"
end
end
end
context
"when username and password are provided"
do
context
"when authentication fails"
do
before
do
env
[
"HTTP_AUTHORIZATION"
]
=
ActionController
::
HttpAuthentication
::
Basic
.
encode_credentials
(
user
.
username
,
"nope"
)
end
it
"responds with status 401"
do
expect
(
status
).
to
eq
(
401
)
end
context
"when the user is IP banned"
do
before
do
expect
(
Rack
::
Attack
::
Allow2Ban
).
to
receive
(
:filter
).
and_return
(
true
)
allow_any_instance_of
(
Rack
::
Request
).
to
receive
(
:ip
).
and_return
(
'1.2.3.4'
)
end
it
"responds with status 401"
do
expect
(
status
).
to
eq
(
401
)
end
end
end
context
"when authentication succeeds"
do
before
do
env
[
"HTTP_AUTHORIZATION"
]
=
ActionController
::
HttpAuthentication
::
Basic
.
encode_credentials
(
user
.
username
,
user
.
password
)
end
context
"when the user has access to the project"
do
before
do
project
.
team
<<
[
user
,
:master
]
end
context
"when the user is blocked"
do
before
do
user
.
block
project
.
team
<<
[
user
,
:master
]
end
it
"responds with status 404"
do
expect
(
status
).
to
eq
(
404
)
end
end
context
"when the user isn't blocked"
do
before
do
expect
(
Rack
::
Attack
::
Allow2Ban
).
to
receive
(
:reset
)
end
it
"responds with status 200"
do
expect
(
status
).
to
eq
(
200
)
end
end
context
"when blank password attempts follow a valid login"
do
let
(
:options
)
{
Gitlab
.
config
.
rack_attack
.
git_basic_auth
}
let
(
:maxretry
)
{
options
[
:maxretry
]
-
1
}
let
(
:ip
)
{
'1.2.3.4'
}
before
do
allow_any_instance_of
(
Rack
::
Request
).
to
receive
(
:ip
).
and_return
(
ip
)
Rack
::
Attack
::
Allow2Ban
.
reset
(
ip
,
options
)
end
after
do
Rack
::
Attack
::
Allow2Ban
.
reset
(
ip
,
options
)
end
def
attempt_login
(
include_password
)
password
=
include_password
?
user
.
password
:
""
env
[
"HTTP_AUTHORIZATION"
]
=
ActionController
::
HttpAuthentication
::
Basic
.
encode_credentials
(
user
.
username
,
password
)
Grack
::
AuthSpawner
::
call
(
env
).
first
end
it
"repeated attempts followed by successful attempt"
do
maxretry
.
times
.
each
do
expect
(
attempt_login
(
false
)).
to
eq
(
401
)
end
expect
(
attempt_login
(
true
)).
to
eq
(
200
)
expect
(
Rack
::
Attack
::
Allow2Ban
.
banned?
(
ip
)).
to
be_falsey
maxretry
.
times
.
each
do
expect
(
attempt_login
(
false
)).
to
eq
(
401
)
end
end
end
end
context
"when the user doesn't have access to the project"
do
it
"responds with status 404"
do
expect
(
status
).
to
eq
(
404
)
end
end
end
end
context
"when a gitlab ci token is provided"
do
let
(
:token
)
{
"123"
}
let
(
:project
)
{
FactoryGirl
.
create
:empty_project
}
before
do
project
.
update_attributes
(
runners_token:
token
,
builds_enabled:
true
)
env
[
"HTTP_AUTHORIZATION"
]
=
ActionController
::
HttpAuthentication
::
Basic
.
encode_credentials
(
"gitlab-ci-token"
,
token
)
end
it
"responds with status 200"
do
expect
(
status
).
to
eq
(
200
)
end
end
end
end
end
end
spec/requests/git_http_spec.rb
View file @
941e578c
...
...
@@ -97,7 +97,7 @@ describe 'Git HTTP requests', lib: true do
allow
(
Gitlab
.
config
.
gitlab_shell
).
to
receive
(
:upload_pack
).
and_return
(
false
)
download
(
path
,
{})
do
|
response
|
expect
(
response
.
status
).
to
eq
(
40
4
)
expect
(
response
.
status
).
to
eq
(
40
1
)
end
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