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