1package config
2
3import (
4 "log"
5 "strings"
6
7 gm "github.com/charmbracelet/wish/git"
8 "github.com/gliderlabs/ssh"
9 gossh "golang.org/x/crypto/ssh"
10)
11
12// Push registers Git push functionality for the given repo and key.
13func (cfg *Config) Push(repo string, pk ssh.PublicKey) {
14 go func() {
15 err := cfg.Reload()
16 if err != nil {
17 log.Printf("error reloading after push: %s", err)
18 }
19 if cfg.Cfg.Callbacks != nil {
20 cfg.Cfg.Callbacks.Push(repo)
21 }
22 r, err := cfg.Source.GetRepo(repo)
23 if err != nil {
24 log.Printf("error getting repo after push: %s", err)
25 return
26 }
27 err = r.UpdateServerInfo()
28 if err != nil {
29 log.Printf("error updating server info after push: %s", err)
30 }
31 }()
32}
33
34// Fetch registers Git fetch functionality for the given repo and key.
35func (cfg *Config) Fetch(repo string, pk ssh.PublicKey) {
36 if cfg.Cfg.Callbacks != nil {
37 cfg.Cfg.Callbacks.Fetch(repo)
38 }
39}
40
41// AuthRepo grants repo authorization to the given key.
42func (cfg *Config) AuthRepo(repo string, pk ssh.PublicKey) gm.AccessLevel {
43 return cfg.accessForKey(repo, pk)
44}
45
46// PasswordHandler returns whether or not password access is allowed.
47func (cfg *Config) PasswordHandler(ctx ssh.Context, password string) bool {
48 return (cfg.AnonAccess != "no-access") && cfg.AllowKeyless
49}
50
51// KeyboardInteractiveHandler returns whether or not keyboard interactive is allowed.
52func (cfg *Config) KeyboardInteractiveHandler(ctx ssh.Context, _ gossh.KeyboardInteractiveChallenge) bool {
53 return (cfg.AnonAccess != "no-access") && cfg.AllowKeyless
54}
55
56// PublicKeyHandler returns whether or not the given public key may access the
57// repo.
58func (cfg *Config) PublicKeyHandler(ctx ssh.Context, pk ssh.PublicKey) bool {
59 return cfg.accessForKey("", pk) != gm.NoAccess
60}
61
62func (cfg *Config) anonAccessLevel() gm.AccessLevel {
63 switch cfg.AnonAccess {
64 case "no-access":
65 return gm.NoAccess
66 case "read-only":
67 return gm.ReadOnlyAccess
68 case "read-write":
69 return gm.ReadWriteAccess
70 case "admin-access":
71 return gm.AdminAccess
72 default:
73 return gm.NoAccess
74 }
75}
76
77// accessForKey returns the access level for the given repo.
78//
79// If repo doesn't exist, then access is based on user's admin privileges, or
80// config.AnonAccess.
81// If repo exists, and private, then admins and collabs are allowed access.
82// If repo exists, and not private, then access is based on config.AnonAccess.
83func (cfg *Config) accessForKey(repo string, pk ssh.PublicKey) gm.AccessLevel {
84 anon := cfg.anonAccessLevel()
85 private := cfg.isPrivate(repo)
86 // Find user
87 for _, user := range cfg.Users {
88 for _, k := range user.PublicKeys {
89 apk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(strings.TrimSpace(k)))
90 if err != nil {
91 log.Printf("error: malformed authorized key: '%s'", k)
92 return gm.NoAccess
93 }
94 if ssh.KeysEqual(pk, apk) {
95 if user.Admin {
96 return gm.AdminAccess
97 }
98 u := user
99 if cfg.isCollab(repo, &u) {
100 if anon > gm.ReadWriteAccess {
101 return anon
102 }
103 return gm.ReadWriteAccess
104 }
105 if !private {
106 if anon > gm.ReadOnlyAccess {
107 return anon
108 }
109 return gm.ReadOnlyAccess
110 }
111 }
112 }
113 }
114 // Don't restrict access to private repos if no users are configured.
115 // Return anon access level.
116 if private && len(cfg.Users) > 0 {
117 return gm.NoAccess
118 }
119 return anon
120}
121
122func (cfg *Config) findRepo(repo string) *RepoConfig {
123 for _, r := range cfg.Repos {
124 if r.Repo == repo {
125 return &r
126 }
127 }
128 return nil
129}
130
131func (cfg *Config) isPrivate(repo string) bool {
132 if r := cfg.findRepo(repo); r != nil {
133 return r.Private
134 }
135 return false
136}
137
138func (cfg *Config) isCollab(repo string, user *User) bool {
139 if user != nil {
140 for _, r := range user.CollabRepos {
141 if r == repo {
142 return true
143 }
144 }
145 if r := cfg.findRepo(repo); r != nil {
146 for _, c := range r.Collabs {
147 if c == user.Name {
148 return true
149 }
150 }
151 }
152 }
153 return false
154}