repos / pico

pico services mono repo
git clone https://github.com/picosh/pico.git

commit
b815c22
parent
2e01b84
author
Antonio Mika
date
2025-04-30 20:48:41 -0400 EDT
Improvements
6 files changed,  +94, -39
M pkg/apps/pico/ssh.go
+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 				}
M pkg/pssh/logger.go
+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+}
M pkg/pssh/server.go
+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,
M pkg/shared/ssh.go
+18, -16
 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 {
M pkg/tui/signup.go
+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 	}
M pkg/tui/ui.go
+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