Diff
1diff --git a/server/server_test.go b/server/server_test.go
2index d7dcf9b8b21cedaba940b88c709c172b94bd9bfc..6f126ce9bbd81b60f608d936f05996d57914339f 100644
3--- a/server/server_test.go
4+++ b/server/server_test.go
5@@ -99,7 +99,9 @@ func setupServer(t *testing.T) *Server {
6 go func() {
7 s.Start()
8 }()
9- defer s.Close()
10+ t.Cleanup(func() {
11+ s.Close()
12+ })
13 return s
14 }
15
16diff --git a/server/session.go b/server/session.go
17index 96aeba4a919ecf6afa290219cb4efdd74b2da904..a645c09c97948ba19ddcd231e427b241f2b44875 100644
18--- a/server/session.go
19+++ b/server/session.go
20@@ -6,11 +6,14 @@ import (
21 "github.com/aymanbagabas/go-osc52"
22 tea "github.com/charmbracelet/bubbletea"
23 appCfg "github.com/charmbracelet/soft-serve/config"
24+ cm "github.com/charmbracelet/soft-serve/server/cmd"
25 "github.com/charmbracelet/soft-serve/ui"
26 "github.com/charmbracelet/soft-serve/ui/common"
27 "github.com/charmbracelet/soft-serve/ui/keymap"
28 "github.com/charmbracelet/soft-serve/ui/styles"
29+ "github.com/charmbracelet/wish"
30 bm "github.com/charmbracelet/wish/bubbletea"
31+ gm "github.com/charmbracelet/wish/git"
32 "github.com/gliderlabs/ssh"
33 zone "github.com/lrstanley/bubblezone"
34 )
35@@ -26,6 +29,11 @@ func SessionHandler(ac *appCfg.Config) bm.ProgramHandler {
36 initialRepo := ""
37 if len(cmd) == 1 {
38 initialRepo = cmd[0]
39+ auth := ac.AuthRepo(initialRepo, s.PublicKey())
40+ if auth < gm.ReadOnlyAccess {
41+ wish.Fatalln(s, cm.ErrUnauthorized)
42+ return nil
43+ }
44 }
45 if ac.Cfg.Callbacks != nil {
46 ac.Cfg.Callbacks.Tui("new session")
47diff --git a/server/session_test.go b/server/session_test.go
48new file mode 100644
49index 0000000000000000000000000000000000000000..3257be89c852af1612b7b8a8349fed8e2c4a0001
50--- /dev/null
51+++ b/server/session_test.go
52@@ -0,0 +1,77 @@
53+package server
54+
55+import (
56+ "bytes"
57+ "errors"
58+ "os"
59+ "strings"
60+ "testing"
61+ "time"
62+
63+ appCfg "github.com/charmbracelet/soft-serve/config"
64+ cm "github.com/charmbracelet/soft-serve/server/cmd"
65+ "github.com/charmbracelet/soft-serve/server/config"
66+ bm "github.com/charmbracelet/wish/bubbletea"
67+ "github.com/charmbracelet/wish/testsession"
68+ "github.com/gliderlabs/ssh"
69+ "github.com/matryer/is"
70+ "github.com/muesli/termenv"
71+ gossh "golang.org/x/crypto/ssh"
72+)
73+
74+func TestSession(t *testing.T) {
75+ is := is.New(t)
76+ t.Run("unauthorized repo access", func(t *testing.T) {
77+ var out bytes.Buffer
78+ s := setup(t)
79+ s.Stderr = &out
80+ defer s.Close()
81+ err := s.RequestPty("xterm", 80, 40, nil)
82+ is.NoErr(err)
83+ err = s.Run("config")
84+ // Session writes error and exits
85+ is.True(strings.Contains(out.String(), cm.ErrUnauthorized.Error()))
86+ var ee *gossh.ExitError
87+ is.True(errors.As(err, &ee) && ee.ExitStatus() == 1)
88+ })
89+ t.Run("authorized repo access", func(t *testing.T) {
90+ s := setup(t)
91+ s.Stderr = os.Stderr
92+ defer s.Close()
93+ err := s.RequestPty("xterm", 80, 40, nil)
94+ is.NoErr(err)
95+ go func() {
96+ time.Sleep(1 * time.Second)
97+ s.Signal(gossh.SIGTERM)
98+ // FIXME: exit with code 0 instead of forcibly closing the session
99+ s.Close()
100+ }()
101+ err = s.Run("test")
102+ var ee *gossh.ExitMissingError
103+ is.True(errors.As(err, &ee))
104+ })
105+}
106+
107+func setup(tb testing.TB) *gossh.Session {
108+ is := is.New(tb)
109+ tb.Helper()
110+ cfg.RepoPath = tb.TempDir()
111+ ac, err := appCfg.NewConfig(&config.Config{
112+ Port: 22226,
113+ KeyPath: tb.TempDir(),
114+ RepoPath: tb.TempDir(),
115+ InitialAdminKeys: []string{
116+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMJlb/qf2B2kMNdBxfpCQqI2ctPcsOkdZGVh5zTRhKtH",
117+ },
118+ })
119+ ac.AnonAccess = "read-only"
120+ is.NoErr(err)
121+ return testsession.New(tb, &ssh.Server{
122+ Handler: bm.MiddlewareWithProgramHandler(SessionHandler(ac), termenv.ANSI256)(func(s ssh.Session) {
123+ _, _, active := s.Pty()
124+ tb.Logf("PTY active %v", active)
125+ tb.Log(s.Command())
126+ s.Exit(0)
127+ }),
128+ }, nil)
129+}