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
3686b73f
Commit
3686b73f
authored
Jan 06, 2017
by
Nick Thomas
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Disambiguate a number of routes shadowed by nested namespaces
parent
1c088017
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
96 additions
and
15 deletions
+96
-15
authorization_test.go
authorization_test.go
+1
-1
internal/api/api.go
internal/api/api.go
+16
-11
internal/upstream/routes.go
internal/upstream/routes.go
+12
-3
main_test.go
main_test.go
+67
-0
No files found.
authorization_test.go
View file @
3686b73f
...
@@ -78,7 +78,7 @@ func TestPreAuthorizeContentTypeFailure(t *testing.T) {
...
@@ -78,7 +78,7 @@ func TestPreAuthorizeContentTypeFailure(t *testing.T) {
t
,
ts
,
"/authorize"
,
t
,
ts
,
"/authorize"
,
regexp
.
MustCompile
(
`/authorize\z`
),
regexp
.
MustCompile
(
`/authorize\z`
),
""
,
""
,
200
,
5
00
)
200
,
2
00
)
}
}
func
TestPreAuthorizeJWT
(
t
*
testing
.
T
)
{
func
TestPreAuthorizeJWT
(
t
*
testing
.
T
)
{
...
...
internal/api/api.go
View file @
3686b73f
...
@@ -6,6 +6,7 @@ import (
...
@@ -6,6 +6,7 @@ import (
"fmt"
"fmt"
"io"
"io"
"io/ioutil"
"io/ioutil"
"mime"
"net/http"
"net/http"
"net/url"
"net/url"
"strconv"
"strconv"
...
@@ -198,14 +199,12 @@ func (api *API) PreAuthorize(suffix string, r *http.Request) (httpResponse *http
...
@@ -198,14 +199,12 @@ func (api *API) PreAuthorize(suffix string, r *http.Request) (httpResponse *http
}()
}()
requestsCounter
.
WithLabelValues
(
strconv
.
Itoa
(
httpResponse
.
StatusCode
),
authReq
.
Method
)
.
Inc
()
requestsCounter
.
WithLabelValues
(
strconv
.
Itoa
(
httpResponse
.
StatusCode
),
authReq
.
Method
)
.
Inc
()
if
httpResponse
.
StatusCode
!=
http
.
StatusOK
{
// This may be a false positive, e.g. for .../info/refs, rather than a
// failure, so pass the response back
if
httpResponse
.
StatusCode
!=
http
.
StatusOK
||
!
validResponseContentType
(
httpResponse
)
{
return
httpResponse
,
nil
,
nil
return
httpResponse
,
nil
,
nil
}
}
if
contentType
:=
httpResponse
.
Header
.
Get
(
"Content-Type"
);
contentType
!=
ResponseContentType
{
return
httpResponse
,
nil
,
fmt
.
Errorf
(
"preAuthorizeHandler: API responded with wrong content type: %v"
,
contentType
)
}
authResponse
=
&
Response
{}
authResponse
=
&
Response
{}
// The auth backend validated the client request and told us additional
// The auth backend validated the client request and told us additional
// request metadata. We must extract this information from the auth
// request metadata. We must extract this information from the auth
...
@@ -220,17 +219,18 @@ func (api *API) PreAuthorize(suffix string, r *http.Request) (httpResponse *http
...
@@ -220,17 +219,18 @@ func (api *API) PreAuthorize(suffix string, r *http.Request) (httpResponse *http
func
(
api
*
API
)
PreAuthorizeHandler
(
next
HandleFunc
,
suffix
string
)
http
.
Handler
{
func
(
api
*
API
)
PreAuthorizeHandler
(
next
HandleFunc
,
suffix
string
)
http
.
Handler
{
return
http
.
HandlerFunc
(
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
return
http
.
HandlerFunc
(
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
httpResponse
,
authResponse
,
err
:=
api
.
PreAuthorize
(
suffix
,
r
)
httpResponse
,
authResponse
,
err
:=
api
.
PreAuthorize
(
suffix
,
r
)
if
httpResponse
!=
nil
{
defer
httpResponse
.
Body
.
Close
()
}
if
err
!=
nil
{
if
err
!=
nil
{
helper
.
Fail500
(
w
,
r
,
err
)
helper
.
Fail500
(
w
,
r
,
err
)
return
return
}
}
if
httpResponse
!=
nil
{
defer
func
()
{
httpResponse
.
Body
.
Close
()
}()
}
if
httpResponse
.
StatusCode
!=
http
.
StatusOK
{
// The response couldn't be interpreted as a valid auth response, so
// pass it back (mostly) unmodified
if
httpResponse
!=
nil
&&
authResponse
==
nil
{
// NGINX response buffering is disabled on this path (with
// NGINX response buffering is disabled on this path (with
// X-Accel-Buffering: no) but we still want to free up the Unicorn worker
// X-Accel-Buffering: no) but we still want to free up the Unicorn worker
// that generated httpResponse as fast as possible. To do this we buffer
// that generated httpResponse as fast as possible. To do this we buffer
...
@@ -286,3 +286,8 @@ func bufferResponse(r io.Reader) (*bytes.Buffer, error) {
...
@@ -286,3 +286,8 @@ func bufferResponse(r io.Reader) (*bytes.Buffer, error) {
return
responseBody
,
nil
return
responseBody
,
nil
}
}
func
validResponseContentType
(
resp
*
http
.
Response
)
bool
{
parsed
,
_
,
err
:=
mime
.
ParseMediaType
(
resp
.
Header
.
Get
(
"Content-Type"
))
return
err
==
nil
&&
parsed
==
ResponseContentType
}
internal/upstream/routes.go
View file @
3686b73f
package
upstream
package
upstream
import
(
import
(
"mime"
"net/http"
"net/http"
"path"
"path"
"regexp"
"regexp"
...
@@ -63,6 +64,14 @@ func wsRoute(regexpStr string, handler http.Handler, matchers ...matcherFunc) ro
...
@@ -63,6 +64,14 @@ func wsRoute(regexpStr string, handler http.Handler, matchers ...matcherFunc) ro
}
}
}
}
// Creates matcherFuncs for a particular content type.
func
isContentType
(
contentType
string
)
func
(
*
http
.
Request
)
bool
{
return
func
(
r
*
http
.
Request
)
bool
{
parsed
,
_
,
err
:=
mime
.
ParseMediaType
(
r
.
Header
.
Get
(
"Content-Type"
))
return
err
==
nil
&&
contentType
==
parsed
}
}
func
(
ro
*
routeEntry
)
isMatch
(
cleanedPath
string
,
req
*
http
.
Request
)
bool
{
func
(
ro
*
routeEntry
)
isMatch
(
cleanedPath
string
,
req
*
http
.
Request
)
bool
{
if
ro
.
method
!=
""
&&
req
.
Method
!=
ro
.
method
{
if
ro
.
method
!=
""
&&
req
.
Method
!=
ro
.
method
{
return
false
return
false
...
@@ -115,9 +124,9 @@ func (u *Upstream) configureRoutes() {
...
@@ -115,9 +124,9 @@ func (u *Upstream) configureRoutes() {
u
.
Routes
=
[]
routeEntry
{
u
.
Routes
=
[]
routeEntry
{
// Git Clone
// Git Clone
route
(
"GET"
,
gitProjectPattern
+
`info/refs\z`
,
git
.
GetInfoRefsHandler
(
api
,
&
u
.
Config
)),
route
(
"GET"
,
gitProjectPattern
+
`info/refs\z`
,
git
.
GetInfoRefsHandler
(
api
,
&
u
.
Config
)),
route
(
"POST"
,
gitProjectPattern
+
`git-upload-pack\z`
,
contentEncodingHandler
(
git
.
PostRPC
(
api
))),
route
(
"POST"
,
gitProjectPattern
+
`git-upload-pack\z`
,
contentEncodingHandler
(
git
.
PostRPC
(
api
))
,
isContentType
(
"application/x-git-upload-pack-request"
)
),
route
(
"POST"
,
gitProjectPattern
+
`git-receive-pack\z`
,
contentEncodingHandler
(
git
.
PostRPC
(
api
))),
route
(
"POST"
,
gitProjectPattern
+
`git-receive-pack\z`
,
contentEncodingHandler
(
git
.
PostRPC
(
api
))
,
isContentType
(
"application/x-git-receive-pack-request"
)
),
route
(
"PUT"
,
gitProjectPattern
+
`gitlab-lfs/objects/([0-9a-f]{64})/([0-9]+)\z`
,
lfs
.
PutStore
(
api
,
proxy
)),
route
(
"PUT"
,
gitProjectPattern
+
`gitlab-lfs/objects/([0-9a-f]{64})/([0-9]+)\z`
,
lfs
.
PutStore
(
api
,
proxy
)
,
isContentType
(
"application/octet-stream"
)
),
// CI Artifacts
// CI Artifacts
route
(
"POST"
,
ciAPIPattern
+
`v1/builds/[0-9]+/artifacts\z`
,
contentEncodingHandler
(
artifacts
.
UploadArtifacts
(
api
,
proxy
))),
route
(
"POST"
,
ciAPIPattern
+
`v1/builds/[0-9]+/artifacts\z`
,
contentEncodingHandler
(
artifacts
.
UploadArtifacts
(
api
,
proxy
))),
...
...
main_test.go
View file @
3686b73f
...
@@ -624,6 +624,73 @@ func TestGetInfoRefsHandledLocallyDueToEmptyGitalySocketPath(t *testing.T) {
...
@@ -624,6 +624,73 @@ func TestGetInfoRefsHandledLocallyDueToEmptyGitalySocketPath(t *testing.T) {
}
}
}
}
func
TestAPIFalsePositivesAreProxied
(
t
*
testing
.
T
)
{
goodResponse
:=
[]
byte
(
`<html></html>`
)
ts
:=
testhelper
.
TestServerWithHandler
(
regexp
.
MustCompile
(
`.`
),
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
if
r
.
Header
.
Get
(
api
.
RequestHeader
)
!=
""
&&
r
.
Method
!=
"GET"
{
w
.
WriteHeader
(
500
)
w
.
Write
([]
byte
(
"non-GET request went through PreAuthorize handler"
))
}
else
{
w
.
Header
()
.
Set
(
"Content-Type"
,
"text/html"
)
if
_
,
err
:=
w
.
Write
(
goodResponse
);
err
!=
nil
{
t
.
Fatalf
(
"write upstream response: %v"
,
err
)
}
}
})
defer
ts
.
Close
()
ws
:=
startWorkhorseServer
(
ts
.
URL
)
defer
ws
.
Close
()
// Each of these cases is a specially-handled path in Workhorse that may
// actually be a request to be sent to gitlab-rails.
for
_
,
tc
:=
range
[]
struct
{
method
string
path
string
}{
{
"GET"
,
"/nested/group/project/blob/master/foo.git/info/refs"
},
{
"POST"
,
"/nested/group/project/blob/master/foo.git/git-upload-pack"
},
{
"POST"
,
"/nested/group/project/blob/master/foo.git/git-receive-pack"
},
{
"PUT"
,
"/nested/group/project/blob/master/foo.git/gitlab-lfs/objects/0000000000000000000000000000000000000000000000000000000000000000/0"
},
{
"GET"
,
"/nested/group/project/blob/master/environments/1/terminal.ws"
},
}
{
req
,
err
:=
http
.
NewRequest
(
tc
.
method
,
ws
.
URL
+
tc
.
path
,
nil
)
if
err
!=
nil
{
t
.
Logf
(
"Creating response for %+v failed: %v"
,
tc
,
err
)
t
.
Fail
()
continue
}
resp
,
err
:=
http
.
DefaultClient
.
Do
(
req
)
if
err
!=
nil
{
t
.
Logf
(
"Reading response from workhorse for %+v failed: %s"
,
tc
,
err
)
t
.
Fail
()
continue
}
defer
resp
.
Body
.
Close
()
respBody
,
err
:=
ioutil
.
ReadAll
(
resp
.
Body
)
if
err
!=
nil
{
t
.
Logf
(
"Reading response from workhorse for %+v failed: %s"
,
tc
,
err
)
t
.
Fail
()
}
if
resp
.
StatusCode
!=
http
.
StatusOK
{
t
.
Logf
(
"Expected HTTP 200 response for %+v, got %d. Body: %s"
,
tc
,
resp
.
StatusCode
,
string
(
respBody
))
t
.
Fail
()
}
if
resp
.
Header
.
Get
(
"Content-Type"
)
!=
"text/html"
{
t
.
Logf
(
"Unexpected response content type for %+v: %s"
,
tc
,
resp
.
Header
.
Get
(
"Content-Type"
))
t
.
Fail
()
}
if
bytes
.
Compare
(
respBody
,
goodResponse
)
!=
0
{
t
.
Logf
(
"Unexpected response body for %+v: %s"
,
tc
,
string
(
respBody
))
t
.
Fail
()
}
}
}
func
setupStaticFile
(
fpath
,
content
string
)
error
{
func
setupStaticFile
(
fpath
,
content
string
)
error
{
cwd
,
err
:=
os
.
Getwd
()
cwd
,
err
:=
os
.
Getwd
()
if
err
!=
nil
{
if
err
!=
nil
{
...
...
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