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}