diff options
author | Douwe Maan <douwe@selenight.nl> | 2017-03-08 04:40:19 +0300 |
---|---|---|
committer | Douwe Maan <douwe@selenight.nl> | 2017-03-08 04:40:19 +0300 |
commit | 359f04e8a11e6714b9cc51df6c251f9c24eef264 (patch) | |
tree | 3edc9a1e06d63f7319e91c190f8d05476dd74cb4 /lib/gitlab/middleware | |
parent | 7f2819b778b055278a7fafe9c782d12d09dbd2ea (diff) |
Fix go-get support for projects in nested groups
Diffstat (limited to 'lib/gitlab/middleware')
-rw-r--r-- | lib/gitlab/middleware/go.rb | 66 |
1 files changed, 57 insertions, 9 deletions
diff --git a/lib/gitlab/middleware/go.rb b/lib/gitlab/middleware/go.rb index 5764ab15652..6023fa1820f 100644 --- a/lib/gitlab/middleware/go.rb +++ b/lib/gitlab/middleware/go.rb @@ -30,21 +30,69 @@ module Gitlab end def go_body(request) - base_url = Gitlab.config.gitlab.url - # Go subpackages may be in the form of namespace/project/path1/path2/../pathN - # We can just ignore the paths and leave the namespace/project - path_info = request.env["PATH_INFO"] - path_info.sub!(/^\//, '') - project_path = path_info.split('/').first(2).join('/') - request_url = URI.join(base_url, project_path) - domain_path = strip_url(request_url.to_s) + project_url = URI.join(Gitlab.config.gitlab.url, project_path(request)) + import_prefix = strip_url(project_url.to_s) - "<!DOCTYPE html><html><head><meta content='#{domain_path} git #{request_url}.git' name='go-import'></head></html>\n" + "<!DOCTYPE html><html><head><meta content='#{import_prefix} git #{project_url}.git' name='go-import'></head></html>\n" end def strip_url(url) url.gsub(/\Ahttps?:\/\//, '') end + + def project_path(request) + path_info = request.env["PATH_INFO"] + path_info.sub!(/^\//, '') + + # Go subpackages may be in the form of `namespace/project/path1/path2/../pathN`. + # In a traditional project with a single namespace, this would denote repo + # `namespace/project` with subpath `path1/path2/../pathN`, but with nested + # groups, this could also be `namespace/project/path1` with subpath + # `path2/../pathN`, for example. + + # We find all potential project paths out of the path segments + path_segments = path_info.split('/') + simple_project_path = path_segments.first(2).join('/') + + # If the path is at most 2 segments long, it is a simple `namespace/project` path and we're done + return simple_project_path if path_segments.length <= 2 + + project_paths = [] + begin + project_paths << path_segments.join('/') + path_segments.pop + end while path_segments.length >= 2 + + # We see if a project exists with any of these potential paths + project = project_for_paths(project_paths, request) + + if project + # If a project is found and the user has access, we return the full project path + project.full_path + else + # If not, we return the first two components as if it were a simple `namespace/project` path, + # so that we don't reveal the existence of a nested project the user doesn't have access to. + # This means that for an unauthenticated request to `group/subgroup/project/subpackage` + # for a private `group/subgroup/project` with subpackage path `subpackage`, GitLab will respond + # as if the user is looking for project `group/subgroup`, with subpackage path `project/subpackage`. + # Since `go get` doesn't authenticate by default, this means that + # `go get gitlab.com/group/subgroup/project/subpackage` will not work for private projects. + # `go get gitlab.com/group/subgroup/project.git/subpackage` will work, since Go is smart enough + # to figure that out. `import 'gitlab.com/...'` behaves the same as `go get`. + simple_project_path + end + end + + def project_for_paths(paths, request) + project = Project.where_full_path_in(paths).first + return unless Ability.allowed?(current_user(request), :read_project, project) + + project + end + + def current_user(request) + request.env['warden']&.authenticate + end end end end |