Eric Bower
·
2025-03-28
ssh.go
1package shared
2
3import (
4 "fmt"
5 "log/slog"
6 "strings"
7
8 "github.com/picosh/pico/pkg/db"
9 "github.com/picosh/utils"
10 "golang.org/x/crypto/ssh"
11)
12
13type SshAuthHandler struct {
14 DB AuthFindUser
15 Logger *slog.Logger
16}
17
18type AuthFindUser interface {
19 FindUserByPubkey(key string) (*db.User, error)
20 FindUserByName(name string) (*db.User, error)
21 FindFeature(userID, name string) (*db.FeatureFlag, error)
22}
23
24func NewSshAuthHandler(dbh AuthFindUser, logger *slog.Logger) *SshAuthHandler {
25 return &SshAuthHandler{
26 DB: dbh,
27 Logger: logger,
28 }
29}
30
31func (r *SshAuthHandler) PubkeyAuthHandler(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
32 pubkey := utils.KeyForKeyText(key)
33 user, err := r.DB.FindUserByPubkey(pubkey)
34 if err != nil {
35 r.Logger.Error(
36 "could not find user for key",
37 "keyType", key.Type(),
38 "key", string(key.Marshal()),
39 "err", err,
40 )
41 return nil, err
42 }
43
44 if user.Name == "" {
45 r.Logger.Error("username is not set")
46 return nil, fmt.Errorf("username is not set")
47 }
48
49 // impersonation
50 impID := user.ID
51 adminPrefix := "admin__"
52 usr := conn.User()
53 if strings.HasPrefix(usr, adminPrefix) {
54 ff, err := r.DB.FindFeature(user.ID, "admin")
55 if err != nil {
56 return nil, fmt.Errorf("only admins can impersonate a user: %w", err)
57 }
58 if !ff.IsValid() {
59 return nil, fmt.Errorf("expired admin feature flag, cannot impersonate a user")
60 }
61
62 impersonate := strings.Replace(usr, adminPrefix, "", 1)
63 user, err = r.DB.FindUserByName(impersonate)
64 if err != nil {
65 return nil, err
66 }
67 }
68
69 return &ssh.Permissions{
70 Extensions: map[string]string{
71 "imp_id": impID,
72 "user_id": user.ID,
73 "pubkey": pubkey,
74 },
75 }, nil
76}
77
78func FindPlusFF(dbpool db.DB, cfg *ConfigSite, userID string) *db.FeatureFlag {
79 ff, _ := dbpool.FindFeature(userID, "plus")
80 // we have free tiers so users might not have a feature flag
81 // in which case we set sane defaults
82 if ff == nil {
83 ff = db.NewFeatureFlag(
84 userID,
85 "plus",
86 cfg.MaxSize,
87 cfg.MaxAssetSize,
88 cfg.MaxSpecialFileSize,
89 )
90 }
91 // this is jank
92 ff.Data.StorageMax = ff.FindStorageMax(cfg.MaxSize)
93 ff.Data.FileMax = ff.FindFileMax(cfg.MaxAssetSize)
94 ff.Data.SpecialFileMax = ff.FindSpecialFileMax(cfg.MaxSpecialFileSize)
95 return ff
96}