git.go

  1package config
  2
  3import (
  4	"log"
  5	"strings"
  6
  7	"github.com/charmbracelet/soft-serve/config"
  8	gm "github.com/charmbracelet/wish/git"
  9	"github.com/gliderlabs/ssh"
 10)
 11
 12type HookOption = config.GitHookOption
 13
 14// PreReceive implements GitHooks interface. It is called before a git push is
 15// performed.
 16func (cfg *Config) PreReceive(repo string, args []HookOption) {
 17}
 18
 19// Update implements GitHooks interface. It is called during a git push once for
 20// each reference.
 21func (cfg *Config) Update(repo string, arg HookOption) {
 22}
 23
 24// PostReceive implements GitHooks interface. It is called after a git push is
 25// performed.
 26func (cfg *Config) PostReceive(repo string, args []HookOption) {
 27}
 28
 29// Push registers Git push functionality for the given repo and key.
 30func (cfg *Config) Push(repo string, pk ssh.PublicKey) {
 31	go func() {
 32		err := cfg.Reload()
 33		if err != nil {
 34			log.Printf("error reloading after push: %s", err)
 35		}
 36		if cfg.Cfg.Callbacks != nil {
 37			cfg.Cfg.Callbacks.Push(repo)
 38		}
 39		r, err := cfg.Source.GetRepo(repo)
 40		if err != nil {
 41			log.Printf("error getting repo after push: %s", err)
 42			return
 43		}
 44		err = r.UpdateServerInfo()
 45		if err != nil {
 46			log.Printf("error updating server info after push: %s", err)
 47		}
 48	}()
 49}
 50
 51// Fetch registers Git fetch functionality for the given repo and key.
 52func (cfg *Config) Fetch(repo string, pk ssh.PublicKey) {
 53	if cfg.Cfg.Callbacks != nil {
 54		cfg.Cfg.Callbacks.Fetch(repo)
 55	}
 56}
 57
 58// AuthRepo grants repo authorization to the given key.
 59func (cfg *Config) AuthRepo(repo string, pk ssh.PublicKey) gm.AccessLevel {
 60	return cfg.accessForKey(repo, pk)
 61}
 62
 63// PasswordHandler returns whether or not password access is allowed.
 64func (cfg *Config) PasswordHandler(ctx ssh.Context, password string) bool {
 65	return (cfg.AnonAccess != "no-access") && cfg.AllowKeyless
 66}
 67
 68// PublicKeyHandler returns whether or not the given public key may access the
 69// repo.
 70func (cfg *Config) PublicKeyHandler(ctx ssh.Context, pk ssh.PublicKey) bool {
 71	return cfg.accessForKey("", pk) != gm.NoAccess
 72}
 73
 74func (cfg *Config) accessForKey(repo string, pk ssh.PublicKey) gm.AccessLevel {
 75	private := cfg.isPrivate(repo)
 76	for _, u := range cfg.Users {
 77		for _, k := range u.PublicKeys {
 78			apk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(strings.TrimSpace(k)))
 79			if err != nil {
 80				log.Printf("error: malformed authorized key: '%s'", k)
 81				return gm.NoAccess
 82			}
 83			if ssh.KeysEqual(pk, apk) {
 84				if u.Admin {
 85					return gm.AdminAccess
 86				}
 87				for _, r := range u.CollabRepos {
 88					if repo == r {
 89						return gm.ReadWriteAccess
 90					}
 91				}
 92				if !private {
 93					return gm.ReadOnlyAccess
 94				}
 95			}
 96		}
 97	}
 98	if private && len(cfg.Users) > 0 {
 99		return gm.NoAccess
100	}
101	switch cfg.AnonAccess {
102	case "no-access":
103		return gm.NoAccess
104	case "read-only":
105		return gm.ReadOnlyAccess
106	case "read-write":
107		return gm.ReadWriteAccess
108	case "admin-access":
109		return gm.AdminAccess
110	default:
111		return gm.NoAccess
112	}
113}