1package config
2
3import (
4 "github.com/charmbracelet/soft-serve/proto"
5 "github.com/gliderlabs/ssh"
6 gossh "golang.org/x/crypto/ssh"
7)
8
9var _ proto.Access = &Config{}
10
11// AuthRepo grants repo authorization to the given key.
12func (c *Config) AuthRepo(repo string, pk ssh.PublicKey) proto.AccessLevel {
13 return c.accessForKey(repo, pk)
14}
15
16// PasswordHandler returns whether or not password access is allowed.
17func (c *Config) PasswordHandler(ctx ssh.Context, password string) bool {
18 return (c.AnonAccess != proto.NoAccess) && c.SSH.AllowKeyless &&
19 c.SSH.AllowPassword && (c.SSH.Password == password)
20}
21
22// KeyboardInteractiveHandler returns whether or not keyboard interactive is allowed.
23func (c *Config) KeyboardInteractiveHandler(ctx ssh.Context, _ gossh.KeyboardInteractiveChallenge) bool {
24 return (c.AnonAccess != proto.NoAccess) && c.SSH.AllowKeyless
25}
26
27// PublicKeyHandler returns whether or not the given public key may access the
28// repo.
29func (c *Config) PublicKeyHandler(ctx ssh.Context, pk ssh.PublicKey) bool {
30 return c.accessForKey("", pk) != proto.NoAccess
31}
32
33// accessForKey returns the access level for the given repo.
34//
35// If repo doesn't exist, then access is based on user's admin privileges, or
36// config.AnonAccess.
37// If repo exists, and private, then admins and collabs are allowed access.
38// If repo exists, and not private, then access is based on config.AnonAccess.
39func (c *Config) accessForKey(repo string, pk ssh.PublicKey) proto.AccessLevel {
40 anon := c.AnonAccess
41 info, err := c.Metadata(repo)
42 if err != nil || info == nil {
43 return anon
44 }
45 private := info.IsPrivate()
46 collabs := info.Collabs()
47 if pk != nil {
48 for _, u := range collabs {
49 if u.IsAdmin() {
50 return proto.AdminAccess
51 }
52 for _, k := range u.PublicKeys() {
53 if ssh.KeysEqual(pk, k) {
54 if anon > proto.ReadWriteAccess {
55 return anon
56 }
57 return proto.ReadWriteAccess
58 }
59 }
60 if !private {
61 if anon > proto.ReadOnlyAccess {
62 return anon
63 }
64 return proto.ReadOnlyAccess
65 }
66 }
67 }
68 // Don't restrict access to private repos if no users are configured.
69 // Return anon access level.
70 if private && c.countUsers() > 0 {
71 return proto.NoAccess
72 }
73 return anon
74}
75
76func (c *Config) countUsers() int {
77 count, err := c.db.CountUsers()
78 if err != nil {
79 return 0
80 }
81 return count
82}