access.go

 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}