mirror of https://github.com/harness/drone.git
[feat] pull/push over https - done (#47)
* pull/push impl done * Basic auth for harnessjobatzil/rename
parent
3f7fc109d0
commit
fb0e93394d
16
Makefile
16
Makefile
|
@ -37,6 +37,10 @@ mocks: $(mocks)
|
|||
|
||||
wire: cli/server/harness.wire_gen.go cli/server/standalone.wire_gen.go
|
||||
|
||||
force-wire:
|
||||
@sh ./scripts/wire/standalone.sh
|
||||
@sh ./scripts/wire/harness.sh
|
||||
|
||||
generate: $(mocks) wire mocks/mock_client.go proto
|
||||
@echo "Generating Code"
|
||||
|
||||
|
@ -121,18 +125,10 @@ lint: tools generate # lint the golang code
|
|||
# the source file has changed.
|
||||
###########################################
|
||||
cli/server/harness.wire_gen.go: cli/server/harness.wire.go ## Update the wire dependency injection if harness.wire.go has changed.
|
||||
@echo "Updating harness.wire_gen.go"
|
||||
@go run github.com/google/wire/cmd/wire gen -tags=harness -output_file_prefix="harness." github.com/harness/gitness/cli/server
|
||||
@perl -ni -e 'print unless /go:generate/' cli/server/harness.wire_gen.go
|
||||
@perl -i -pe's/\+build !wireinject/\+build !wireinject,harness/g' cli/server/harness.wire_gen.go
|
||||
@perl -i -pe's/go:build !wireinject/go:build !wireinject && harness/g' cli/server/harness.wire_gen.go
|
||||
|
||||
|
||||
cli/server/standalone.wire_gen.go: cli/server/standalone.wire.go ## Update the wire dependency injection if standalone.wire.go has changed.
|
||||
@echo "Updating standalone.wire_gen.go"
|
||||
@go run github.com/google/wire/cmd/wire gen -tags= -output_file_prefix="standalone." github.com/harness/gitness/cli/server
|
||||
@perl -ni -e 'print unless /go:generate/' cli/server/standalone.wire_gen.go
|
||||
@perl -i -pe's/\+build !wireinject/\+build !wireinject,!harness/g' cli/server/standalone.wire_gen.go
|
||||
@perl -i -pe's/go:build !wireinject/go:build !wireinject && !harness/g' cli/server/standalone.wire_gen.go
|
||||
@sh ./scripts/wire/standalone.sh
|
||||
|
||||
mocks/mock_client.go: internal/store/store.go client/client.go
|
||||
go generate mocks/mock.go
|
||||
|
|
|
@ -85,7 +85,7 @@ func initSystem(ctx context.Context, config *types.Config) (*system, error) {
|
|||
}
|
||||
repoController := repo.ProvideController(config, authorizer, spaceStore, repoStore, serviceAccountStore, gitrpcInterface)
|
||||
apiHandler := router.ProvideAPIHandler(systemStore, authenticator, accountClient, spaceController, repoController)
|
||||
gitHandler := router2.ProvideGitHandler(repoStore, authenticator)
|
||||
gitHandler := router2.ProvideGitHandler(repoStore, authenticator, gitrpcInterface)
|
||||
webHandler := router2.ProvideWebHandler(systemStore)
|
||||
routerRouter := router2.ProvideRouter(apiHandler, gitHandler, webHandler)
|
||||
serverServer := server.ProvideServer(config, routerRouter)
|
||||
|
|
|
@ -50,7 +50,7 @@ func initSystem(ctx context.Context, config *types.Config) (*system, error) {
|
|||
spaceController := space.NewController(authorizer, spaceStore, repoStore, serviceAccountStore)
|
||||
serviceaccountController := serviceaccount.NewController(authorizer, serviceAccountStore, spaceStore, repoStore, tokenStore)
|
||||
apiHandler := router.ProvideAPIHandler(systemStore, authenticator, repoController, spaceController, serviceaccountController, controller)
|
||||
gitHandler := router.ProvideGitHandler(repoStore, authenticator)
|
||||
gitHandler := router.ProvideGitHandler(repoStore, authenticator, gitrpcInterface)
|
||||
webHandler := router.ProvideWebHandler(systemStore)
|
||||
routerRouter := router.ProvideRouter(apiHandler, gitHandler, webHandler)
|
||||
serverServer := server.ProvideServer(config, routerRouter)
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
// Copyright 2022 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package repo
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/internal/gitrpc"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/hlog"
|
||||
)
|
||||
|
||||
type CtxRepoType string
|
||||
|
||||
const (
|
||||
CtxRepoKey CtxRepoType = "repo"
|
||||
)
|
||||
|
||||
func GetInfoRefs(client gitrpc.Interface) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
log := hlog.FromRequest(r)
|
||||
repo, ok := r.Context().Value(CtxRepoKey).(*types.Repository)
|
||||
if !ok {
|
||||
ctxKeyError(w, log)
|
||||
return
|
||||
}
|
||||
|
||||
// Clients MUST NOT reuse or revalidate a cached response.
|
||||
// Servers MUST include sufficient Cache-Control headers to prevent caching of the response.
|
||||
// https://git-scm.com/docs/http-protocol
|
||||
setHeaderNoCache(w)
|
||||
|
||||
service := getServiceType(r)
|
||||
log.Debug().Msgf("in GetInfoRefs: git service: %v", service)
|
||||
w.Header().Set("Content-Type", fmt.Sprintf("application/x-git-%s-advertisement", service))
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := client.GetInfoRefs(ctx, w, &gitrpc.InfoRefsParams{
|
||||
RepoUID: repo.GitUID,
|
||||
Service: service,
|
||||
Options: nil,
|
||||
GitProtocol: r.Header.Get("Git-Protocol"),
|
||||
}); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
log.Err(err).Msgf("in GetInfoRefs: error occurred in service %v", service)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func GetUploadPack(client gitrpc.Interface) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
const service = "upload-pack"
|
||||
log := hlog.FromRequest(r)
|
||||
repo, ok := r.Context().Value(CtxRepoKey).(*types.Repository)
|
||||
if !ok {
|
||||
ctxKeyError(w, log)
|
||||
return
|
||||
}
|
||||
|
||||
if err := serviceRPC(w, r, client, repo.GitUID, service, repo.CreatedBy); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func PostReceivePack(client gitrpc.Interface) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
const service = "receive-pack"
|
||||
log := hlog.FromRequest(r)
|
||||
repo, ok := r.Context().Value(CtxRepoKey).(*types.Repository)
|
||||
if !ok {
|
||||
ctxKeyError(w, log)
|
||||
return
|
||||
}
|
||||
|
||||
if err := serviceRPC(w, r, client, repo.GitUID, service, repo.CreatedBy); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func serviceRPC(
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
client gitrpc.Interface,
|
||||
repo, service string,
|
||||
principalID int64,
|
||||
) error {
|
||||
ctx := r.Context()
|
||||
log := hlog.FromRequest(r)
|
||||
defer func() {
|
||||
if err := r.Body.Close(); err != nil {
|
||||
log.Err(err).Msgf("serviceRPC: Close: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
w.Header().Set("Content-Type", fmt.Sprintf("application/x-git-%s-result", service))
|
||||
|
||||
var err error
|
||||
reqBody := r.Body
|
||||
|
||||
// Handle GZIP.
|
||||
if r.Header.Get("Content-Encoding") == "gzip" {
|
||||
reqBody, err = gzip.NewReader(reqBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return client.ServicePack(ctx, w, &gitrpc.ServicePackParams{
|
||||
RepoUID: repo,
|
||||
Service: service,
|
||||
Data: reqBody,
|
||||
Options: nil,
|
||||
PrincipalID: principalID,
|
||||
GitProtocol: r.Header.Get("Git-Protocol"),
|
||||
})
|
||||
}
|
||||
|
||||
func setHeaderNoCache(w http.ResponseWriter) {
|
||||
w.Header().Set("Expires", "Fri, 01 Jan 1980 00:00:00 GMT")
|
||||
w.Header().Set("Pragma", "no-cache")
|
||||
w.Header().Set("Cache-Control", "no-cache, max-age=0, must-revalidate")
|
||||
}
|
||||
|
||||
func getServiceType(r *http.Request) string {
|
||||
serviceType := r.FormValue("service")
|
||||
if !strings.HasPrefix(serviceType, "git-") {
|
||||
return ""
|
||||
}
|
||||
return strings.Replace(serviceType, "git-", "", 1)
|
||||
}
|
||||
|
||||
func ctxKeyError(w http.ResponseWriter, log *zerolog.Logger) {
|
||||
errMsg := "key 'repo' missing in context"
|
||||
http.Error(w, errMsg, http.StatusBadRequest)
|
||||
log.Error().Msg(errMsg)
|
||||
}
|
|
@ -122,6 +122,15 @@ func extractToken(r *http.Request) string {
|
|||
if bearer == "" {
|
||||
return r.FormValue("access_token")
|
||||
}
|
||||
// pull/push git operations will require auth using
|
||||
// Basic realm
|
||||
if strings.HasPrefix(bearer, "Basic") {
|
||||
_, tkn, ok := r.BasicAuth()
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return tkn
|
||||
}
|
||||
bearer = strings.TrimPrefix(bearer, "Bearer ")
|
||||
bearer = strings.TrimPrefix(bearer, "IdentityService ")
|
||||
return bearer
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/internal/gitrpc/rpc"
|
||||
|
@ -242,6 +243,7 @@ type Identity struct {
|
|||
type Client struct {
|
||||
conn *grpc.ClientConn
|
||||
repoService rpc.RepositoryServiceClient
|
||||
httpService rpc.SmartHTTPServiceClient
|
||||
}
|
||||
|
||||
func InitClient(remoteAddr string) (*Client, error) {
|
||||
|
@ -253,6 +255,7 @@ func InitClient(remoteAddr string) (*Client, error) {
|
|||
return &Client{
|
||||
conn: conn,
|
||||
repoService: rpc.NewRepositoryServiceClient(conn),
|
||||
httpService: rpc.NewSmartHTTPServiceClient(conn),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -519,12 +522,6 @@ func (c *Client) ListTreeNodes(ctx context.Context, params *ListTreeNodeParams)
|
|||
})
|
||||
}
|
||||
|
||||
// TODO: is this needed?
|
||||
err = stream.CloseSend()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to close stream")
|
||||
}
|
||||
|
||||
return &ListTreeNodeOutput{
|
||||
Nodes: nodes,
|
||||
}, nil
|
||||
|
@ -582,12 +579,6 @@ func (c *Client) ListCommits(ctx context.Context, params *ListCommitsParams) (*L
|
|||
output.Commits = append(output.Commits, *commit)
|
||||
}
|
||||
|
||||
// TODO: is this needed?
|
||||
err = stream.CloseSend()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to close stream")
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
||||
|
||||
|
@ -636,12 +627,6 @@ func (c *Client) ListBranches(ctx context.Context, params *ListBranchesParams) (
|
|||
output.Branches = append(output.Branches, *branch)
|
||||
}
|
||||
|
||||
// TODO: is this needed?
|
||||
err = stream.CloseSend()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to close stream")
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
||||
|
||||
|
@ -727,6 +712,122 @@ func mapToRPCListBranchesSortOption(o BranchSortOption) rpc.ListBranchesRequest_
|
|||
}
|
||||
}
|
||||
|
||||
type InfoRefsParams struct {
|
||||
// RepoUID is the uid of the git repository
|
||||
RepoUID string
|
||||
Service string
|
||||
Options []string // (key, value) pair
|
||||
GitProtocol string
|
||||
}
|
||||
|
||||
func (c *Client) GetInfoRefs(ctx context.Context, w io.Writer, params *InfoRefsParams) error {
|
||||
if w == nil {
|
||||
return errors.New("writer cannot be nil")
|
||||
}
|
||||
if params == nil {
|
||||
return ErrNoParamsProvided
|
||||
}
|
||||
stream, err := c.httpService.InfoRefs(ctx, &rpc.InfoRefsRequest{
|
||||
RepoUid: params.RepoUID,
|
||||
Service: params.Service,
|
||||
GitConfigOptions: params.Options,
|
||||
GitProtocol: params.GitProtocol,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error initializing GetInfoRefs() stream: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
response *rpc.InfoRefsResponse
|
||||
)
|
||||
for {
|
||||
response, err = stream.Recv()
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetInfoRefs() error receiving stream bytes: %w", err)
|
||||
}
|
||||
_, err = w.Write(response.GetData())
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetInfoRefs() error: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type ServicePackParams struct {
|
||||
// RepoUID is the uid of the git repository
|
||||
RepoUID string
|
||||
Service string
|
||||
GitProtocol string
|
||||
// PrincipalID used for git hooks in receive-pack service
|
||||
PrincipalID int64
|
||||
Data io.ReadCloser
|
||||
Options []string // (key, value) pair
|
||||
}
|
||||
|
||||
func (c *Client) ServicePack(ctx context.Context, w io.Writer, params *ServicePackParams) error {
|
||||
if w == nil {
|
||||
return errors.New("writer cannot be nil")
|
||||
}
|
||||
if params == nil {
|
||||
return ErrNoParamsProvided
|
||||
}
|
||||
stream, err := c.httpService.ServicePack(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// send basic information
|
||||
if err = stream.Send(&rpc.ServicePackRequest{
|
||||
RepoUid: params.RepoUID,
|
||||
Service: params.Service,
|
||||
GitConfigOptions: params.Options,
|
||||
GitProtocol: params.GitProtocol,
|
||||
PrincipalId: strconv.FormatInt(params.PrincipalID, 10),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// send body as stream
|
||||
stdout := NewWriter(func(p []byte) error {
|
||||
return stream.Send(&rpc.ServicePackRequest{
|
||||
Data: p,
|
||||
})
|
||||
})
|
||||
|
||||
_, err = io.Copy(stdout, params.Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("PostUploadPack() error copying reader: %w", err)
|
||||
}
|
||||
|
||||
if err = stream.CloseSend(); err != nil {
|
||||
return fmt.Errorf("PostUploadPack() error closing the stream: %w", err)
|
||||
}
|
||||
|
||||
// when we are done with inputs then we should expect
|
||||
// git data
|
||||
var (
|
||||
response *rpc.ServicePackResponse
|
||||
)
|
||||
for {
|
||||
response, err = stream.Recv()
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("PostUploadPack() error receiving stream bytes: %w", err)
|
||||
}
|
||||
_, err = w.Write(response.GetData())
|
||||
if err != nil {
|
||||
return fmt.Errorf("PostUploadPack() error: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func mapToRPCListCommitTagsSortOption(o TagSortOption) rpc.ListCommitTagsRequest_SortOption {
|
||||
switch o {
|
||||
case TagSortOptionName:
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2022 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package gitrpc
|
||||
|
||||
const (
|
||||
EnvRepoUID = "GITNESS_REPO_UID"
|
||||
EnvRepoName = "GITNESS_REPO_NAME"
|
||||
EnvPusherName = "GITNESS_PUSHER_NAME"
|
||||
EnvPusherID = "GITNESS_PUSHER_ID"
|
||||
EnvAppURL = "GITNESS_ROOT_URL" // base url for Gitness server
|
||||
EnvPusherEmail = "GITNESS_PUSHER_EMAIL"
|
||||
)
|
|
@ -219,7 +219,8 @@ func giteaParsePrettyFormatLogToList(giteaRepo *gitea.Repository, logs []byte) (
|
|||
// and includes the latest commit for all nodes if requested.
|
||||
// IMPORTANT: recursive and includeLatestCommit can't be used together.
|
||||
// Note: ref can be Branch / Tag / CommitSHA.
|
||||
//nolint:gocognit,goimports // refactor if needed
|
||||
//
|
||||
//nolint:gocognit // refactor if needed
|
||||
func (g giteaAdapter) ListTreeNodes(ctx context.Context, repoPath string,
|
||||
ref string, treePath string, recursive bool, includeLatestCommit bool) ([]treeNodeWithCommit, error) {
|
||||
if recursive && includeLatestCommit {
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
// Copyright 2022 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package gitrpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"github.com/harness/gitness/internal/gitrpc/rpc"
|
||||
"github.com/rs/zerolog/log"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
const (
|
||||
receivePack = "receive-pack"
|
||||
)
|
||||
|
||||
var safeGitProtocolHeader = regexp.MustCompile(`^[0-9a-zA-Z]+=[0-9a-zA-Z]+(:[0-9a-zA-Z]+=[0-9a-zA-Z]+)*$`)
|
||||
|
||||
type smartHTTPService struct {
|
||||
rpc.UnimplementedSmartHTTPServiceServer
|
||||
adapter gitAdapter
|
||||
reposRoot string
|
||||
}
|
||||
|
||||
func newHTTPService(adapter gitAdapter, gitRoot string) (*smartHTTPService, error) {
|
||||
reposRoot := filepath.Join(gitRoot, repoSubdirName)
|
||||
if _, err := os.Stat(reposRoot); errors.Is(err, os.ErrNotExist) {
|
||||
if err = os.MkdirAll(reposRoot, 0o700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &smartHTTPService{
|
||||
adapter: adapter,
|
||||
reposRoot: reposRoot,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *smartHTTPService) getFullPathForRepo(uid string) string {
|
||||
return filepath.Join(s.reposRoot, fmt.Sprintf("%s.%s", uid, gitRepoSuffix))
|
||||
}
|
||||
|
||||
func (s *smartHTTPService) InfoRefs(
|
||||
r *rpc.InfoRefsRequest,
|
||||
stream rpc.SmartHTTPService_InfoRefsServer,
|
||||
) error {
|
||||
environ := make([]string, 0)
|
||||
environ = append(os.Environ(), environ...)
|
||||
if r.GitProtocol != "" {
|
||||
environ = append(environ, "GIT_PROTOCOL="+r.GitProtocol)
|
||||
}
|
||||
|
||||
repoPath := s.getFullPathForRepo(r.GetRepoUid())
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
|
||||
w := NewWriter(func(p []byte) error {
|
||||
return stream.Send(&rpc.InfoRefsResponse{Data: p})
|
||||
})
|
||||
|
||||
cmd := &bytes.Buffer{}
|
||||
if err := git.NewCommand(ctx, r.GetService(), "--stateless-rpc", "--advertise-refs", ".").
|
||||
Run(&git.RunOpts{
|
||||
Env: environ,
|
||||
Dir: repoPath,
|
||||
Stdout: cmd,
|
||||
}); err != nil {
|
||||
return status.Errorf(codes.Internal, "InfoRefsUploadPack: cmd: %v", err)
|
||||
}
|
||||
if _, err := w.Write(packetWrite("# service=git-" + r.GetService() + "\n")); err != nil {
|
||||
return status.Errorf(codes.Internal, "InfoRefsUploadPack: pktLine: %v", err)
|
||||
}
|
||||
|
||||
if _, err := w.Write([]byte("0000")); err != nil {
|
||||
return status.Errorf(codes.Internal, "InfoRefsUploadPack: flush: %v", err)
|
||||
}
|
||||
|
||||
if _, err := io.Copy(w, cmd); err != nil {
|
||||
return status.Errorf(codes.Internal, "InfoRefsUploadPack: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *smartHTTPService) ServicePack(stream rpc.SmartHTTPService_ServicePackServer) error {
|
||||
ctx := stream.Context()
|
||||
// Get basic repo data
|
||||
req, err := stream.Recv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// if client sends data as []byte raise error, needs reader
|
||||
if req.Data != nil {
|
||||
return status.Errorf(codes.InvalidArgument, "PostUploadPack(): non-empty Data")
|
||||
}
|
||||
|
||||
if req.RepoUid == "" {
|
||||
return status.Errorf(codes.InvalidArgument, "PostUploadPack(): repository UID is missing")
|
||||
}
|
||||
|
||||
repoPath := s.getFullPathForRepo(req.GetRepoUid())
|
||||
|
||||
stdin := NewReader(func() ([]byte, error) {
|
||||
resp, streamErr := stream.Recv()
|
||||
return resp.GetData(), streamErr
|
||||
})
|
||||
|
||||
stdout := NewWriter(func(p []byte) error {
|
||||
return stream.Send(&rpc.ServicePackResponse{Data: p})
|
||||
})
|
||||
|
||||
return serviceRPC(ctx, stdin, stdout, req, repoPath)
|
||||
}
|
||||
|
||||
func serviceRPC(ctx context.Context, stdin io.Reader, stdout io.Writer, req *rpc.ServicePackRequest, dir string) error {
|
||||
protocol := req.GetGitProtocol()
|
||||
service := req.GetService()
|
||||
principalID := req.GetPrincipalId()
|
||||
repoUID := req.GetRepoUid()
|
||||
|
||||
environ := make([]string, 0)
|
||||
if service == receivePack && principalID != "" {
|
||||
environ = []string{
|
||||
EnvRepoUID + "=" + repoUID,
|
||||
EnvPusherID + "=" + principalID,
|
||||
}
|
||||
}
|
||||
// set this for allow pre-receive and post-receive execute
|
||||
environ = append(environ, "SSH_ORIGINAL_COMMAND="+service)
|
||||
|
||||
if protocol != "" && safeGitProtocolHeader.MatchString(protocol) {
|
||||
environ = append(environ, "GIT_PROTOCOL="+protocol)
|
||||
}
|
||||
|
||||
var (
|
||||
stderr bytes.Buffer
|
||||
)
|
||||
cmd := git.NewCommand(ctx, service, "--stateless-rpc", dir)
|
||||
cmd.SetDescription(fmt.Sprintf("%s %s %s [repo_path: %s]", git.GitExecutable, service, "--stateless-rpc", dir))
|
||||
err := cmd.Run(&git.RunOpts{
|
||||
Dir: dir,
|
||||
Env: append(os.Environ(), environ...),
|
||||
Stdout: stdout,
|
||||
Stdin: stdin,
|
||||
Stderr: &stderr,
|
||||
UseContextTimeout: true,
|
||||
})
|
||||
if err != nil && err.Error() != "signal: killed" {
|
||||
log.Err(err).Msgf("Fail to serve RPC(%s) in %s: %v - %s", service, dir, err, stderr.String())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func packetWrite(str string) []byte {
|
||||
s := strconv.FormatInt(int64(len(str)+4), 16)
|
||||
if len(s)%4 != 0 {
|
||||
s = strings.Repeat("0", 4-len(s)%4) + s
|
||||
}
|
||||
return []byte(s + str)
|
||||
}
|
|
@ -4,7 +4,10 @@
|
|||
|
||||
package gitrpc
|
||||
|
||||
import "context"
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
CreateRepository(ctx context.Context, params *CreateRepositoryParams) (*CreateRepositoryOutput, error)
|
||||
|
@ -15,6 +18,12 @@ type Interface interface {
|
|||
ListCommits(ctx context.Context, params *ListCommitsParams) (*ListCommitsOutput, error)
|
||||
ListBranches(ctx context.Context, params *ListBranchesParams) (*ListBranchesOutput, error)
|
||||
ListCommitTags(ctx context.Context, params *ListCommitTagsParams) (*ListCommitTagsOutput, error)
|
||||
|
||||
/*
|
||||
* HTTP services
|
||||
*/
|
||||
GetInfoRefs(ctx context.Context, w io.Writer, params *InfoRefsParams) error
|
||||
ServicePack(ctx context.Context, w io.Writer, params *ServicePackParams) error
|
||||
}
|
||||
|
||||
// gitAdapter for accessing git commands from gitea.
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
syntax = "proto3";
|
||||
package rpc;
|
||||
|
||||
option go_package = "github.com/harness/gitness/gitrpc/rpc";
|
||||
|
||||
// SmartHTTPService is a service that provides RPCs required for HTTP-based Git
|
||||
// clones via the smart HTTP protocol.
|
||||
service SmartHTTPService {
|
||||
// The response body for GET /info/refs?service=git-upload-pack
|
||||
// Will be invoked when the user executes a `git fetch`, meaning the server
|
||||
// will upload the packs to that user. The user doesn't upload new objects.
|
||||
rpc InfoRefs(InfoRefsRequest) returns (stream InfoRefsResponse) {}
|
||||
|
||||
// ServicePack is just upload-pack or receive-pack
|
||||
rpc ServicePack(stream ServicePackRequest) returns (stream ServicePackResponse) {}
|
||||
}
|
||||
|
||||
message InfoRefsRequest {
|
||||
string repo_uid = 1;
|
||||
// Service can be: upload-pack or receive-pack
|
||||
string service = 2;
|
||||
// Parameters to use with git -c (key=value pairs)
|
||||
repeated string git_config_options = 3;
|
||||
|
||||
// Git protocol version
|
||||
string git_protocol = 4;
|
||||
}
|
||||
|
||||
message InfoRefsResponse {
|
||||
bytes data = 1;
|
||||
}
|
||||
|
||||
message ServicePackRequest {
|
||||
// repository should only be present only in the first message of the stream
|
||||
string repo_uid = 1;
|
||||
// Service can be: upload-pack or receive-pack
|
||||
string service = 2;
|
||||
// Raw data to be copied to stdin of 'git upload-pack'
|
||||
bytes data = 3;
|
||||
// Parameters to use with git -c (key=value pairs)
|
||||
repeated string git_config_options = 4;
|
||||
|
||||
// Git protocol version
|
||||
string git_protocol = 5;
|
||||
|
||||
// user_id become env variable, used by the Git {pre,post}-receive
|
||||
// hooks. They should only be present in the first message of the stream.
|
||||
string principal_id = 6;
|
||||
}
|
||||
|
||||
message ServicePackResponse {
|
||||
// Raw data from stdout of 'git upload-pack'
|
||||
bytes data = 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,437 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.7
|
||||
// source: http.proto
|
||||
|
||||
package rpc
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type InfoRefsRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
RepoUid string `protobuf:"bytes,1,opt,name=repo_uid,json=repoUid,proto3" json:"repo_uid,omitempty"`
|
||||
// Service can be: upload-pack or receive-pack
|
||||
Service string `protobuf:"bytes,2,opt,name=service,proto3" json:"service,omitempty"`
|
||||
// Parameters to use with git -c (key=value pairs)
|
||||
GitConfigOptions []string `protobuf:"bytes,3,rep,name=git_config_options,json=gitConfigOptions,proto3" json:"git_config_options,omitempty"`
|
||||
// Git protocol version
|
||||
GitProtocol string `protobuf:"bytes,4,opt,name=git_protocol,json=gitProtocol,proto3" json:"git_protocol,omitempty"`
|
||||
}
|
||||
|
||||
func (x *InfoRefsRequest) Reset() {
|
||||
*x = InfoRefsRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_http_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *InfoRefsRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*InfoRefsRequest) ProtoMessage() {}
|
||||
|
||||
func (x *InfoRefsRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_http_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use InfoRefsRequest.ProtoReflect.Descriptor instead.
|
||||
func (*InfoRefsRequest) Descriptor() ([]byte, []int) {
|
||||
return file_http_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *InfoRefsRequest) GetRepoUid() string {
|
||||
if x != nil {
|
||||
return x.RepoUid
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *InfoRefsRequest) GetService() string {
|
||||
if x != nil {
|
||||
return x.Service
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *InfoRefsRequest) GetGitConfigOptions() []string {
|
||||
if x != nil {
|
||||
return x.GitConfigOptions
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *InfoRefsRequest) GetGitProtocol() string {
|
||||
if x != nil {
|
||||
return x.GitProtocol
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type InfoRefsResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (x *InfoRefsResponse) Reset() {
|
||||
*x = InfoRefsResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_http_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *InfoRefsResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*InfoRefsResponse) ProtoMessage() {}
|
||||
|
||||
func (x *InfoRefsResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_http_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use InfoRefsResponse.ProtoReflect.Descriptor instead.
|
||||
func (*InfoRefsResponse) Descriptor() ([]byte, []int) {
|
||||
return file_http_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *InfoRefsResponse) GetData() []byte {
|
||||
if x != nil {
|
||||
return x.Data
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ServicePackRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// repository should only be present only in the first message of the stream
|
||||
RepoUid string `protobuf:"bytes,1,opt,name=repo_uid,json=repoUid,proto3" json:"repo_uid,omitempty"`
|
||||
// Service can be: upload-pack or receive-pack
|
||||
Service string `protobuf:"bytes,2,opt,name=service,proto3" json:"service,omitempty"`
|
||||
// Raw data to be copied to stdin of 'git upload-pack'
|
||||
Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"`
|
||||
// Parameters to use with git -c (key=value pairs)
|
||||
GitConfigOptions []string `protobuf:"bytes,4,rep,name=git_config_options,json=gitConfigOptions,proto3" json:"git_config_options,omitempty"`
|
||||
// Git protocol version
|
||||
GitProtocol string `protobuf:"bytes,5,opt,name=git_protocol,json=gitProtocol,proto3" json:"git_protocol,omitempty"`
|
||||
// user_id become env variable, used by the Git {pre,post}-receive
|
||||
// hooks. They should only be present in the first message of the stream.
|
||||
PrincipalId string `protobuf:"bytes,6,opt,name=principal_id,json=principalId,proto3" json:"principal_id,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ServicePackRequest) Reset() {
|
||||
*x = ServicePackRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_http_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ServicePackRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ServicePackRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ServicePackRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_http_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ServicePackRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ServicePackRequest) Descriptor() ([]byte, []int) {
|
||||
return file_http_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *ServicePackRequest) GetRepoUid() string {
|
||||
if x != nil {
|
||||
return x.RepoUid
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ServicePackRequest) GetService() string {
|
||||
if x != nil {
|
||||
return x.Service
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ServicePackRequest) GetData() []byte {
|
||||
if x != nil {
|
||||
return x.Data
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ServicePackRequest) GetGitConfigOptions() []string {
|
||||
if x != nil {
|
||||
return x.GitConfigOptions
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ServicePackRequest) GetGitProtocol() string {
|
||||
if x != nil {
|
||||
return x.GitProtocol
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ServicePackRequest) GetPrincipalId() string {
|
||||
if x != nil {
|
||||
return x.PrincipalId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ServicePackResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Raw data from stdout of 'git upload-pack'
|
||||
Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ServicePackResponse) Reset() {
|
||||
*x = ServicePackResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_http_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ServicePackResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ServicePackResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ServicePackResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_http_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ServicePackResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ServicePackResponse) Descriptor() ([]byte, []int) {
|
||||
return file_http_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *ServicePackResponse) GetData() []byte {
|
||||
if x != nil {
|
||||
return x.Data
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_http_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_http_proto_rawDesc = []byte{
|
||||
0x0a, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x72, 0x70,
|
||||
0x63, 0x22, 0x97, 0x01, 0x0a, 0x0f, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x66, 0x73, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x75, 0x69,
|
||||
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x55, 0x69, 0x64,
|
||||
0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x67, 0x69,
|
||||
0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
|
||||
0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x67, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x67, 0x69, 0x74, 0x5f,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
|
||||
0x67, 0x69, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x26, 0x0a, 0x10, 0x49,
|
||||
0x6e, 0x66, 0x6f, 0x52, 0x65, 0x66, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64,
|
||||
0x61, 0x74, 0x61, 0x22, 0xd1, 0x01, 0x0a, 0x12, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50,
|
||||
0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x65,
|
||||
0x70, 0x6f, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65,
|
||||
0x70, 0x6f, 0x55, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12,
|
||||
0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64,
|
||||
0x61, 0x74, 0x61, 0x12, 0x2c, 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52,
|
||||
0x10, 0x67, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x73, 0x12, 0x21, 0x0a, 0x0c, 0x67, 0x69, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f,
|
||||
0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x67, 0x69, 0x74, 0x50, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61,
|
||||
0x6c, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x69, 0x6e,
|
||||
0x63, 0x69, 0x70, 0x61, 0x6c, 0x49, 0x64, 0x22, 0x29, 0x0a, 0x13, 0x53, 0x65, 0x72, 0x76, 0x69,
|
||||
0x63, 0x65, 0x50, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12,
|
||||
0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61,
|
||||
0x74, 0x61, 0x32, 0x97, 0x01, 0x0a, 0x10, 0x53, 0x6d, 0x61, 0x72, 0x74, 0x48, 0x54, 0x54, 0x50,
|
||||
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3b, 0x0a, 0x08, 0x49, 0x6e, 0x66, 0x6f, 0x52,
|
||||
0x65, 0x66, 0x73, 0x12, 0x14, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65,
|
||||
0x66, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x72, 0x70, 0x63, 0x2e,
|
||||
0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x66, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x00, 0x30, 0x01, 0x12, 0x46, 0x0a, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50,
|
||||
0x61, 0x63, 0x6b, 0x12, 0x17, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
|
||||
0x65, 0x50, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72,
|
||||
0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x61, 0x63, 0x6b, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x27, 0x5a, 0x25,
|
||||
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x72, 0x6e, 0x65,
|
||||
0x73, 0x73, 0x2f, 0x67, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x69, 0x74, 0x72, 0x70,
|
||||
0x63, 0x2f, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_http_proto_rawDescOnce sync.Once
|
||||
file_http_proto_rawDescData = file_http_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_http_proto_rawDescGZIP() []byte {
|
||||
file_http_proto_rawDescOnce.Do(func() {
|
||||
file_http_proto_rawDescData = protoimpl.X.CompressGZIP(file_http_proto_rawDescData)
|
||||
})
|
||||
return file_http_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_http_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||
var file_http_proto_goTypes = []interface{}{
|
||||
(*InfoRefsRequest)(nil), // 0: rpc.InfoRefsRequest
|
||||
(*InfoRefsResponse)(nil), // 1: rpc.InfoRefsResponse
|
||||
(*ServicePackRequest)(nil), // 2: rpc.ServicePackRequest
|
||||
(*ServicePackResponse)(nil), // 3: rpc.ServicePackResponse
|
||||
}
|
||||
var file_http_proto_depIdxs = []int32{
|
||||
0, // 0: rpc.SmartHTTPService.InfoRefs:input_type -> rpc.InfoRefsRequest
|
||||
2, // 1: rpc.SmartHTTPService.ServicePack:input_type -> rpc.ServicePackRequest
|
||||
1, // 2: rpc.SmartHTTPService.InfoRefs:output_type -> rpc.InfoRefsResponse
|
||||
3, // 3: rpc.SmartHTTPService.ServicePack:output_type -> rpc.ServicePackResponse
|
||||
2, // [2:4] is the sub-list for method output_type
|
||||
0, // [0:2] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_http_proto_init() }
|
||||
func file_http_proto_init() {
|
||||
if File_http_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_http_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*InfoRefsRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_http_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*InfoRefsResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_http_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ServicePackRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_http_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ServicePackResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_http_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 4,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_http_proto_goTypes,
|
||||
DependencyIndexes: file_http_proto_depIdxs,
|
||||
MessageInfos: file_http_proto_msgTypes,
|
||||
}.Build()
|
||||
File_http_proto = out.File
|
||||
file_http_proto_rawDesc = nil
|
||||
file_http_proto_goTypes = nil
|
||||
file_http_proto_depIdxs = nil
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v3.21.7
|
||||
// source: http.proto
|
||||
|
||||
package rpc
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// SmartHTTPServiceClient is the client API for SmartHTTPService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type SmartHTTPServiceClient interface {
|
||||
// The response body for GET /info/refs?service=git-upload-pack
|
||||
// Will be invoked when the user executes a `git fetch`, meaning the server
|
||||
// will upload the packs to that user. The user doesn't upload new objects.
|
||||
InfoRefs(ctx context.Context, in *InfoRefsRequest, opts ...grpc.CallOption) (SmartHTTPService_InfoRefsClient, error)
|
||||
// ServicePack is just upload-pack or receive-pack
|
||||
ServicePack(ctx context.Context, opts ...grpc.CallOption) (SmartHTTPService_ServicePackClient, error)
|
||||
}
|
||||
|
||||
type smartHTTPServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewSmartHTTPServiceClient(cc grpc.ClientConnInterface) SmartHTTPServiceClient {
|
||||
return &smartHTTPServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *smartHTTPServiceClient) InfoRefs(ctx context.Context, in *InfoRefsRequest, opts ...grpc.CallOption) (SmartHTTPService_InfoRefsClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &SmartHTTPService_ServiceDesc.Streams[0], "/rpc.SmartHTTPService/InfoRefs", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &smartHTTPServiceInfoRefsClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type SmartHTTPService_InfoRefsClient interface {
|
||||
Recv() (*InfoRefsResponse, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type smartHTTPServiceInfoRefsClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *smartHTTPServiceInfoRefsClient) Recv() (*InfoRefsResponse, error) {
|
||||
m := new(InfoRefsResponse)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *smartHTTPServiceClient) ServicePack(ctx context.Context, opts ...grpc.CallOption) (SmartHTTPService_ServicePackClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &SmartHTTPService_ServiceDesc.Streams[1], "/rpc.SmartHTTPService/ServicePack", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &smartHTTPServiceServicePackClient{stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type SmartHTTPService_ServicePackClient interface {
|
||||
Send(*ServicePackRequest) error
|
||||
Recv() (*ServicePackResponse, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type smartHTTPServiceServicePackClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *smartHTTPServiceServicePackClient) Send(m *ServicePackRequest) error {
|
||||
return x.ClientStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *smartHTTPServiceServicePackClient) Recv() (*ServicePackResponse, error) {
|
||||
m := new(ServicePackResponse)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// SmartHTTPServiceServer is the server API for SmartHTTPService service.
|
||||
// All implementations must embed UnimplementedSmartHTTPServiceServer
|
||||
// for forward compatibility
|
||||
type SmartHTTPServiceServer interface {
|
||||
// The response body for GET /info/refs?service=git-upload-pack
|
||||
// Will be invoked when the user executes a `git fetch`, meaning the server
|
||||
// will upload the packs to that user. The user doesn't upload new objects.
|
||||
InfoRefs(*InfoRefsRequest, SmartHTTPService_InfoRefsServer) error
|
||||
// ServicePack is just upload-pack or receive-pack
|
||||
ServicePack(SmartHTTPService_ServicePackServer) error
|
||||
mustEmbedUnimplementedSmartHTTPServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedSmartHTTPServiceServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedSmartHTTPServiceServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedSmartHTTPServiceServer) InfoRefs(*InfoRefsRequest, SmartHTTPService_InfoRefsServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method InfoRefs not implemented")
|
||||
}
|
||||
func (UnimplementedSmartHTTPServiceServer) ServicePack(SmartHTTPService_ServicePackServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method ServicePack not implemented")
|
||||
}
|
||||
func (UnimplementedSmartHTTPServiceServer) mustEmbedUnimplementedSmartHTTPServiceServer() {}
|
||||
|
||||
// UnsafeSmartHTTPServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to SmartHTTPServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeSmartHTTPServiceServer interface {
|
||||
mustEmbedUnimplementedSmartHTTPServiceServer()
|
||||
}
|
||||
|
||||
func RegisterSmartHTTPServiceServer(s grpc.ServiceRegistrar, srv SmartHTTPServiceServer) {
|
||||
s.RegisterService(&SmartHTTPService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _SmartHTTPService_InfoRefs_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(InfoRefsRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(SmartHTTPServiceServer).InfoRefs(m, &smartHTTPServiceInfoRefsServer{stream})
|
||||
}
|
||||
|
||||
type SmartHTTPService_InfoRefsServer interface {
|
||||
Send(*InfoRefsResponse) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type smartHTTPServiceInfoRefsServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *smartHTTPServiceInfoRefsServer) Send(m *InfoRefsResponse) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func _SmartHTTPService_ServicePack_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(SmartHTTPServiceServer).ServicePack(&smartHTTPServiceServicePackServer{stream})
|
||||
}
|
||||
|
||||
type SmartHTTPService_ServicePackServer interface {
|
||||
Send(*ServicePackResponse) error
|
||||
Recv() (*ServicePackRequest, error)
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type smartHTTPServiceServicePackServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *smartHTTPServiceServicePackServer) Send(m *ServicePackResponse) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *smartHTTPServiceServicePackServer) Recv() (*ServicePackRequest, error) {
|
||||
m := new(ServicePackRequest)
|
||||
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// SmartHTTPService_ServiceDesc is the grpc.ServiceDesc for SmartHTTPService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var SmartHTTPService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "rpc.SmartHTTPService",
|
||||
HandlerType: (*SmartHTTPServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "InfoRefs",
|
||||
Handler: _SmartHTTPService_InfoRefs_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "ServicePack",
|
||||
Handler: _SmartHTTPService_ServicePack_Handler,
|
||||
ServerStreams: true,
|
||||
ClientStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "http.proto",
|
||||
}
|
|
@ -27,11 +27,19 @@ func NewServer(bind string, gitRoot string) (*Server, error) {
|
|||
}
|
||||
s := grpc.NewServer()
|
||||
store := newLocalStore()
|
||||
// initialize services
|
||||
repoService, err := newRepositoryService(adapter, store, gitRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
httpService, err := newHTTPService(adapter, gitRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// register services
|
||||
rpc.RegisterRepositoryServiceServer(s, repoService)
|
||||
rpc.RegisterSmartHTTPServiceServer(s, httpService)
|
||||
|
||||
return &Server{
|
||||
Server: s,
|
||||
Bind: bind,
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
// Copyright 2022 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package gitrpc
|
||||
|
||||
import "io"
|
||||
|
||||
const MaxBufferSize = 128 * 1024
|
||||
|
||||
type writer struct {
|
||||
bufferSize int
|
||||
sender func([]byte) error
|
||||
}
|
||||
|
||||
type Option func(w *writer)
|
||||
|
||||
func WithBufferSize(size int) Option {
|
||||
return func(w *writer) {
|
||||
w.bufferSize = size
|
||||
}
|
||||
}
|
||||
|
||||
func NewWriter(sender func(p []byte) error, options ...Option) io.Writer {
|
||||
w := &writer{
|
||||
sender: sender,
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
option(w)
|
||||
}
|
||||
|
||||
if w.bufferSize == 0 || w.bufferSize > MaxBufferSize {
|
||||
w.bufferSize = MaxBufferSize
|
||||
}
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *writer) Write(p []byte) (int, error) {
|
||||
var sent int
|
||||
|
||||
for len(p) > 0 {
|
||||
chunkSize := len(p)
|
||||
if chunkSize > w.bufferSize {
|
||||
chunkSize = w.bufferSize
|
||||
}
|
||||
|
||||
if err := w.sender(p[:chunkSize]); err != nil {
|
||||
return sent, err
|
||||
}
|
||||
|
||||
sent += chunkSize
|
||||
p = p[chunkSize:]
|
||||
}
|
||||
|
||||
return sent, nil
|
||||
}
|
||||
|
||||
func NewReader(receiver func() ([]byte, error)) io.Reader {
|
||||
return &reader{receiver: receiver}
|
||||
}
|
||||
|
||||
type reader struct {
|
||||
receiver func() ([]byte, error)
|
||||
data []byte
|
||||
err error
|
||||
}
|
||||
|
||||
func (r *reader) Read(p []byte) (int, error) {
|
||||
if len(r.data) == 0 && r.err == nil {
|
||||
r.data, r.err = r.receiver()
|
||||
}
|
||||
|
||||
n := copy(p, r.data)
|
||||
r.data = r.data[n:]
|
||||
|
||||
if len(r.data) == 0 {
|
||||
return n, r.err
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
|
@ -5,11 +5,15 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"github.com/harness/gitness/internal/api/handler/repo"
|
||||
"github.com/harness/gitness/internal/gitrpc"
|
||||
|
||||
"github.com/harness/gitness/internal/api/middleware/accesslog"
|
||||
middleware_authn "github.com/harness/gitness/internal/api/middleware/authn"
|
||||
"github.com/harness/gitness/internal/api/middleware/encode"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
"github.com/harness/gitness/internal/auth/authn"
|
||||
|
@ -28,7 +32,9 @@ type GitHandler interface {
|
|||
// NewGitHandler returns a new GitHandler.
|
||||
func NewGitHandler(
|
||||
repoStore store.RepoStore,
|
||||
authenticator authn.Authenticator) GitHandler {
|
||||
authenticator authn.Authenticator,
|
||||
client gitrpc.Interface,
|
||||
) GitHandler {
|
||||
// Use go-chi router for inner routing.
|
||||
r := chi.NewRouter()
|
||||
|
||||
|
@ -43,13 +49,15 @@ func NewGitHandler(
|
|||
r.Use(accesslog.HlogHandler())
|
||||
|
||||
// for now always attempt auth - enforced per operation.
|
||||
r.Use(middleware_authn.Attempt(authenticator))
|
||||
|
||||
r.Use(HTTPGitEnabled)
|
||||
|
||||
r.Route(fmt.Sprintf("/{%s}", request.PathParamRepoRef), func(r chi.Router) {
|
||||
r.Use(BasicAuth("\".\"", authenticator, repoStore))
|
||||
// Write operations (need auth)
|
||||
r.Group(func(r chi.Router) {
|
||||
// middleware for authz?
|
||||
r.Handle("/git-upload-pack", stubGitHandler(repoStore))
|
||||
r.Handle("/git-upload-pack", repo.GetUploadPack(client))
|
||||
})
|
||||
|
||||
// Read operations (only need of it not public)
|
||||
|
@ -57,8 +65,8 @@ func NewGitHandler(
|
|||
// middleware for authz?
|
||||
|
||||
// handlers
|
||||
r.Post("/git-receive-pack", stubGitHandler(repoStore))
|
||||
r.Get("/info/refs", stubGitHandler(repoStore))
|
||||
r.Post("/git-receive-pack", repo.PostReceivePack(client))
|
||||
r.Get("/info/refs", repo.GetInfoRefs(client))
|
||||
r.Get("/HEAD", stubGitHandler(repoStore))
|
||||
r.Get("/objects/info/alternates", stubGitHandler(repoStore))
|
||||
r.Get("/objects/info/http-alternates", stubGitHandler(repoStore))
|
||||
|
@ -74,8 +82,60 @@ func NewGitHandler(
|
|||
return encode.GitPathBefore(r)
|
||||
}
|
||||
|
||||
func stubGitHandler(repoStore store.RepoStore) http.HandlerFunc {
|
||||
func HTTPGitEnabled(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if setting.Repository.DisableHTTPGit {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
_, _ = w.Write([]byte("Interacting with repositories by HTTP protocol is not allowed"))
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// BasicAuth implements a simple middleware handler for adding basic http auth to a route.
|
||||
func BasicAuth(realm string, auth authn.Authenticator, repoStore store.RepoStore) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
log := hlog.FromRequest(r)
|
||||
log.Debug().Msgf("BasicAuth middleware: validate path %v", r.URL.Path)
|
||||
repoPath, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
log.Err(err).Msgf("BasicAuth middleware: bad path %v", r.URL.Path)
|
||||
return
|
||||
}
|
||||
log.Debug().Msgf("BasicAuth middleware: find repo by path %v", r.URL.Path)
|
||||
repository, err := repoStore.FindByPath(r.Context(), repoPath)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Repo '%s' not found.", repoPath), http.StatusNotFound)
|
||||
log.Err(err).Msgf("BasicAuth middleware: repo not found %v", r.URL.Path)
|
||||
return
|
||||
}
|
||||
|
||||
if !repository.IsPublic {
|
||||
log.Debug().Msgf("BasicAuth middleware: repo %v is private", repository.Name)
|
||||
_, err = auth.Authenticate(r)
|
||||
if err != nil {
|
||||
basicAuthFailed(w, realm)
|
||||
log.Err(err).Msgf("BasicAuth middleware: authorization failed %v", r.URL.Path)
|
||||
return
|
||||
}
|
||||
}
|
||||
log.Debug().Msgf("BasicAuth middleware: serve next with CtxKey %v", repo.CtxRepoKey)
|
||||
ctx := context.WithValue(r.Context(), repo.CtxRepoKey, repository)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func basicAuthFailed(w http.ResponseWriter, realm string) {
|
||||
w.Header().Add("WWW-Authenticate", fmt.Sprintf(`Basic realm="%s"`, realm))
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
func stubGitHandler(repoStore store.RepoStore) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusTeapot)
|
||||
|
||||
repoPath, _ := request.GetRepoRefFromPath(r)
|
||||
|
@ -96,5 +156,5 @@ func stubGitHandler(repoStore store.RepoStore) http.HandlerFunc {
|
|||
r.URL.Path,
|
||||
r.URL.RawQuery,
|
||||
)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/harness/gitness/internal/api/controller/space"
|
||||
"github.com/harness/gitness/internal/api/controller/user"
|
||||
"github.com/harness/gitness/internal/auth/authn"
|
||||
"github.com/harness/gitness/internal/gitrpc"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
)
|
||||
|
||||
|
@ -30,8 +31,12 @@ func ProvideRouter(
|
|||
return NewRouter(api, git, web)
|
||||
}
|
||||
|
||||
func ProvideGitHandler(repoStore store.RepoStore, authenticator authn.Authenticator) GitHandler {
|
||||
return NewGitHandler(repoStore, authenticator)
|
||||
func ProvideGitHandler(
|
||||
repoStore store.RepoStore,
|
||||
authenticator authn.Authenticator,
|
||||
client gitrpc.Interface,
|
||||
) GitHandler {
|
||||
return NewGitHandler(repoStore, authenticator, client)
|
||||
}
|
||||
|
||||
func ProvideAPIHandler(
|
||||
|
|
|
@ -179,7 +179,7 @@ type (
|
|||
// Delete deletes the token with the given id.
|
||||
Delete(ctx context.Context, id int64) error
|
||||
|
||||
// Delete deletes all tokens for a specific principal
|
||||
// DeleteForPrincipal deletes all tokens for a specific principal
|
||||
DeleteForPrincipal(ctx context.Context, principalID int64) error
|
||||
|
||||
// List returns a list of tokens of a specific type for a specific principal.
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
echo "Updating harness.wire_gen.go"
|
||||
go run github.com/google/wire/cmd/wire gen -tags=harness -output_file_prefix="harness." github.com/harness/gitness/cli/server
|
||||
perl -ni -e 'print unless /go:generate/' cli/server/harness.wire_gen.go
|
||||
perl -i -pe's/\+build !wireinject/\+build !wireinject,harness/g' cli/server/harness.wire_gen.go
|
||||
perl -i -pe's/go:build !wireinject/go:build !wireinject && harness/g' cli/server/harness.wire_gen.go
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
echo "Updating standalone.wire_gen.go"
|
||||
go run github.com/google/wire/cmd/wire gen -tags= -output_file_prefix="standalone." github.com/harness/gitness/cli/server
|
||||
perl -ni -e 'print unless /go:generate/' cli/server/standalone.wire_gen.go
|
||||
perl -i -pe's/\+build !wireinject/\+build !wireinject,!harness/g' cli/server/standalone.wire_gen.go
|
||||
perl -i -pe's/go:build !wireinject/go:build !wireinject && !harness/g' cli/server/standalone.wire_gen.go
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
package enum
|
||||
|
||||
// Represents the type of the JWT token.
|
||||
// TokenType represents the type of the JWT token.
|
||||
type TokenType string
|
||||
|
||||
const (
|
||||
|
|
Loading…
Reference in New Issue