1package config
 2
 3import (
 4	"log"
 5	"strings"
 6
 7	gm "github.com/charmbracelet/wish/git"
 8	"github.com/gliderlabs/ssh"
 9)
10
11// Push registers Git push functionality for the given repo and key.
12func (cfg *Config) Push(repo string, pk ssh.PublicKey) {
13	err := cfg.Reload()
14	if err != nil {
15		log.Printf("error reloading after push: %s", err)
16	}
17	if cfg.Cfg.Callbacks != nil {
18		cfg.Cfg.Callbacks.Push(repo)
19	}
20}
21
22// Fetch registers Git fetch functionality for the given repo and key.
23func (cfg *Config) Fetch(repo string, pk ssh.PublicKey) {
24	if cfg.Cfg.Callbacks != nil {
25		cfg.Cfg.Callbacks.Fetch(repo)
26	}
27}
28
29// AuthRepo grants repo authorization to the given key.
30func (cfg *Config) AuthRepo(repo string, pk ssh.PublicKey) gm.AccessLevel {
31	return cfg.accessForKey(repo, pk)
32}
33
34// PasswordHandler returns whether or not password access is allowed.
35func (cfg *Config) PasswordHandler(ctx ssh.Context, password string) bool {
36	return (cfg.AnonAccess != "no-access") && cfg.AllowKeyless
37}
38
39// PublicKeyHandler returns whether or not the given public key may access the
40// repo.
41func (cfg *Config) PublicKeyHandler(ctx ssh.Context, pk ssh.PublicKey) bool {
42	return cfg.accessForKey("", pk) != gm.NoAccess
43}
44
45func (cfg *Config) accessForKey(repo string, pk ssh.PublicKey) gm.AccessLevel {
46	private := cfg.isPrivate(repo)
47	if repo == "config" {
48		private = true
49	}
50	for _, u := range cfg.Users {
51		for _, k := range u.PublicKeys {
52			apk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(strings.TrimSpace(k)))
53			if err != nil {
54				log.Printf("error: malformed authorized key: '%s'", k)
55				return gm.NoAccess
56			}
57			if ssh.KeysEqual(pk, apk) {
58				if u.Admin {
59					return gm.AdminAccess
60				}
61				for _, r := range u.CollabRepos {
62					if repo == r {
63						return gm.ReadWriteAccess
64					}
65				}
66				if !private {
67					return gm.ReadOnlyAccess
68				}
69			}
70		}
71	}
72	if private && (cfg.AnonAccess != "read-write") {
73		return gm.NoAccess
74	}
75	switch cfg.AnonAccess {
76	case "no-access":
77		return gm.NoAccess
78	case "read-only":
79		return gm.ReadOnlyAccess
80	case "read-write":
81		return gm.ReadWriteAccess
82	default:
83		return gm.NoAccess
84	}
85}