- commit
- b815c22
- parent
- 2e01b84
- author
- Antonio Mika
- date
- 2025-04-30 20:48:41 -0400 EDT
Improvements
6 files changed,
+94,
-39
+1,
-1
1@@ -97,7 +97,7 @@ func StartSshServer() {
2 Session: sesh,
3 Cfg: cfg,
4 Dbpool: handler.DBPool,
5- Logger: cfg.Logger,
6+ Logger: pssh.GetLogger(sesh),
7 }
8 return pssh.PtyMdw(createTui(shrd), 200*time.Millisecond)(next)(sesh)
9 }
+47,
-10
1@@ -12,41 +12,60 @@ type ctxUserKey struct{}
2
3 type FindUserInterface interface {
4 FindUser(string) (*db.User, error)
5+ FindUserByPubkey(string) (*db.User, error)
6 }
7
8 type GetLoggerInterface interface {
9 GetLogger(s *SSHServerConnSession) *slog.Logger
10 }
11
12-func LogMiddleware(getLogger GetLoggerInterface, db FindUserInterface) SSHServerMiddleware {
13+func LogMiddleware(getLogger GetLoggerInterface, database FindUserInterface) SSHServerMiddleware {
14 return func(sshHandler SSHServerHandler) SSHServerHandler {
15 return func(s *SSHServerConnSession) error {
16 ct := time.Now()
17
18 logger := GetLogger(s)
19- if logger == slog.Default() {
20+ if logger == slog.Default() || logger == s.Logger {
21 logger = getLogger.GetLogger(s)
22
23 user := GetUser(s)
24 if user == nil {
25- userID, ok := s.Permissions().Extensions["user_id"]
26- if ok {
27- user, err := db.FindUser(userID)
28+ _, impersonated := s.Permissions().Extensions["imp_id"]
29+
30+ var user *db.User
31+ var err error
32+ var found bool
33+
34+ if !impersonated {
35+ pubKey, ok := s.Permissions().Extensions["pubkey"]
36+ if ok {
37+ user, err = database.FindUserByPubkey(pubKey)
38+ found = true
39+ }
40+ } else {
41+ userID, ok := s.Permissions().Extensions["user_id"]
42+ if ok {
43+ user, err = database.FindUser(userID)
44+ found = true
45+ }
46+ }
47+
48+ if found {
49 if err == nil && user != nil {
50 logger = logger.With(
51 "user", user.Name,
52 "userId", user.ID,
53 "ip", s.RemoteAddr().String(),
54 )
55- s.SetValue(ctxUserKey{}, user)
56+
57+ SetUser(s, user)
58+ } else {
59+ logger.Error("`user` not set in permissions", "err", err)
60 }
61- } else {
62- logger.Error("`user_id` not set in permissions")
63 }
64-
65 }
66
67- s.SetValue(ctxLoggerKey{}, logger)
68+ SetLogger(s, logger)
69 }
70
71 pty, _, ok := s.Pty()
72@@ -101,6 +120,8 @@ func GetLogger(s *SSHServerConnSession) *slog.Logger {
73 return logger
74 }
75
76+ logger = s.Logger
77+
78 if v, ok := s.Context().Value(ctxLoggerKey{}).(*slog.Logger); ok {
79 return v
80 }
81@@ -108,6 +129,14 @@ func GetLogger(s *SSHServerConnSession) *slog.Logger {
82 return logger
83 }
84
85+func SetLogger(s *SSHServerConnSession, logger *slog.Logger) {
86+ if s == nil {
87+ return
88+ }
89+
90+ s.SetValue(ctxLoggerKey{}, logger)
91+}
92+
93 func GetUser(s *SSHServerConnSession) *db.User {
94 if v, ok := s.Context().Value(ctxUserKey{}).(*db.User); ok {
95 return v
96@@ -115,3 +144,11 @@ func GetUser(s *SSHServerConnSession) *db.User {
97
98 return nil
99 }
100+
101+func SetUser(s *SSHServerConnSession, user *db.User) {
102+ if s == nil {
103+ return
104+ }
105+
106+ s.SetValue(ctxUserKey{}, user)
107+}
+5,
-2
1@@ -361,10 +361,13 @@ func (s *SSHServer) HandleConn(conn net.Conn) error {
2
3 newLogger := s.Logger.With(
4 "remoteAddr", conn.RemoteAddr().String(),
5- "user", sshConn.User(),
6- "pubkey", sshConn.Permissions.Extensions["pubkey"],
7+ "sshUser", sshConn.User(),
8 )
9
10+ if pubKey, ok := sshConn.Permissions.Extensions["pubkey"]; ok {
11+ newLogger = newLogger.With("pubkey", pubKey)
12+ }
13+
14 newConn := NewSSHServerConn(
15 s.Ctx,
16 newLogger,
1@@ -10,6 +10,8 @@ import (
2 "golang.org/x/crypto/ssh"
3 )
4
5+const adminPrefix = "admin__"
6+
7 type SshAuthHandler struct {
8 DB AuthFindUser
9 Logger *slog.Logger
10@@ -47,32 +49,32 @@ func (r *SshAuthHandler) PubkeyAuthHandler(conn ssh.ConnMetadata, key ssh.Public
11 }
12
13 // impersonation
14- impID := user.ID
15- adminPrefix := "admin__"
16+ var impID string
17 usr := conn.User()
18 if strings.HasPrefix(usr, adminPrefix) {
19 ff, err := r.DB.FindFeature(user.ID, "admin")
20- if err != nil {
21- return nil, fmt.Errorf("only admins can impersonate a user: %w", err)
22- }
23- if !ff.IsValid() {
24- return nil, fmt.Errorf("expired admin feature flag, cannot impersonate a user")
25- }
26-
27- impersonate := strings.Replace(usr, adminPrefix, "", 1)
28- user, err = r.DB.FindUserByName(impersonate)
29- if err != nil {
30- return nil, err
31+ if err == nil && ff.IsValid() {
32+ impersonate := strings.TrimPrefix(usr, adminPrefix)
33+ impersonatedUser, err := r.DB.FindUserByName(impersonate)
34+ if err == nil {
35+ impID = user.ID
36+ user = impersonatedUser
37+ }
38 }
39 }
40
41- return &ssh.Permissions{
42+ perms := &ssh.Permissions{
43 Extensions: map[string]string{
44- "imp_id": impID,
45 "user_id": user.ID,
46 "pubkey": pubkey,
47 },
48- }, nil
49+ }
50+
51+ if impID != "" {
52+ perms.Extensions["imp_id"] = impID
53+ }
54+
55+ return perms, nil
56 }
57
58 func FindPlusFF(dbpool db.DB, cfg *ConfigSite, userID string) *db.FeatureFlag {
+12,
-0
1@@ -9,6 +9,7 @@ import (
2 "git.sr.ht/~rockorager/vaxis/vxfw/richtext"
3 "git.sr.ht/~rockorager/vaxis/vxfw/text"
4 "github.com/picosh/pico/pkg/db"
5+ "github.com/picosh/pico/pkg/pssh"
6 "github.com/picosh/utils"
7 "golang.org/x/crypto/ssh"
8 )
9@@ -56,7 +57,18 @@ func (m *SignupPage) CaptureEvent(ev vaxis.Event) (vxfw.Command, error) {
10 m.err = err
11 return vxfw.RedrawCmd{}, nil
12 }
13+
14 m.shared.User = user
15+
16+ pssh.SetUser(m.shared.Session, user)
17+
18+ m.shared.Logger = m.shared.Logger.With(
19+ "user", user.Name,
20+ "userId", user.ID,
21+ )
22+
23+ pssh.SetLogger(m.shared.Session, m.shared.Logger)
24+
25 m.shared.App.PostEvent(Navigate{To: HOME})
26 }
27 }
+11,
-10
1@@ -15,7 +15,10 @@ import (
2 "github.com/picosh/utils"
3 )
4
5-var HOME = "dash"
6+const (
7+ adminPrefix = "admin__"
8+ HOME = "dash"
9+)
10
11 type SharedModel struct {
12 Logger *slog.Logger
13@@ -286,21 +289,19 @@ func FindUser(shrd *SharedModel) (*db.User, error) {
14 // no user and not error indicates we need to create an account
15 return nil, nil
16 }
17+
18 origUserName := user.Name
19
20 // impersonation
21- adminPrefix := "admin__"
22 if strings.HasPrefix(usr, adminPrefix) {
23 hasFeature := shrd.Dbpool.HasFeatureForUser(user.ID, "admin")
24- if !hasFeature {
25- return nil, fmt.Errorf("only admins can impersonate a user")
26- }
27- impersonate := strings.Replace(usr, adminPrefix, "", 1)
28- user, err = shrd.Dbpool.FindUserByName(impersonate)
29- if err != nil {
30- return nil, err
31+ if hasFeature {
32+ impersonate := strings.TrimPrefix(usr, adminPrefix)
33+ user, err = shrd.Dbpool.FindUserByName(impersonate)
34+ if err == nil {
35+ shrd.Impersonator = origUserName
36+ }
37 }
38- shrd.Impersonator = origUserName
39 }
40
41 return user, nil