li 的 Blog

Go Module 与 私有库 (二)

Feb 27, 2020

现状

问题

背景

go get 查找 repo root 方式

go get -u -v gitlab.supos.ai/supos/datalake/common/log
get "gitlab.supos.ai/supos/datalake": found meta tag get.metaImport{Prefix:"gitlab.supos.ai/supos/datalake", VCS:"git", RepoRoot:"http://gitlab.supos.ai/supos/datalake.git"} at //gitlab.supos.ai/supos/datalake?go-get=1
get "gitlab.supos.ai/supos/datalake/common": found meta tag get.metaImport{Prefix:"gitlab.supos.ai/supos/datalake/common", VCS:"git", RepoRoot:"http://gitlab.supos.ai/supos/datalake/common.git"} at //gitlab.supos.ai/supos/datalake/common?go-get=1
get "gitlab.supos.ai/supos/datalake/common/log": found meta tag get.metaImport{Prefix:"gitlab.supos.ai/supos/datalake/common", VCS:"git", RepoRoot:"http://gitlab.supos.ai/supos/datalake/common.git"} at //gitlab.supos.ai/supos/datalake/common/log?go-get=1
get "gitlab.supos.ai/supos/datalake/common/log": verifying non-authoritative meta tag
go: finding gitlab.supos.ai/supos/datalake/common/log latest
gitlab.supos.ai/supos/datalake/common/log

go get 携带认证信息的方式

源码位置

// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package auth provides access to user-provided authentication credentials.
package auth

import "net/http"

// AddCredentials fills in the user's credentials for req, if any.
// The return value reports whether any matching credentials were found.
func AddCredentials(req *http.Request) (added bool) {
	// TODO(golang.org/issue/26232): Support arbitrary user-provided credentials.
	netrcOnce.Do(readNetrc)
	for _, l := range netrc {
		if l.machine == req.URL.Host {
			req.SetBasicAuth(l.login, l.password)
			return true
		}
	}

	return false
}

源码位置

func (r *Request) SetBasicAuth(username, password string) {
	r.Header.Set("Authorization", "Basic "+basicAuth(username, password))
}

源码位置

func basicAuth(username, password string) string {
	auth := username + ":" + password
	return base64.StdEncoding.EncodeToString([]byte(auth))
}

GitLab 处理 ?go-get=1 请求的逻辑

源码位置

 def project_path(request)
		...
        # 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)
        authenticator = Gitlab::Auth::RequestAuthenticator.new(request)
        user = authenticator.find_user_from_access_token || authenticator.find_user_from_warden

        return unless user&.can?(:access_api)

        # Right now, the `api` scope is the only one that should be able to determine private project existence.
        return unless authenticator.valid_access_token?(scopes: [:api])

        user
      end

源码位置


      def valid_access_token?(scopes: [])
        validate_access_token!(scopes: scopes)

源码位置

      def validate_access_token!(scopes: [])
        return unless access_token

源码位置

      def access_token
        strong_memoize(:access_token) do
          find_oauth_access_token || find_personal_access_token
        end
      end

      def find_personal_access_token
        token =
          current_request.params[PRIVATE_TOKEN_PARAM].presence ||
          current_request.env[PRIVATE_TOKEN_HEADER].presence

        return unless token

        # Expiration, revocation and scopes are verified in `validate_access_token!`
        PersonalAccessToken.find_by(token: token) || raise(UnauthorizedError)
      end

      def find_oauth_access_token
        token = Doorkeeper::OAuth::Token.from_request(current_request, *Doorkeeper.configuration.access_token_methods)
        return unless token

        # Expiration, revocation and scopes are verified in `validate_access_token!`
        oauth_token = OauthAccessToken.by_token(token)
        raise UnauthorizedError unless oauth_token

        oauth_token.revoke_previous_refresh_token!
        oauth_token
      end

GitLab 10.6.5 Go Midware 行为

根因