- commit
- 9e07ba1
- parent
- 271b447
- author
- Eric Bower
- date
- 2024-04-28 11:14:56 -0400 EDT
upload handler
4 files changed,
+147,
-46
M
go.mod
+6,
-1
1@@ -6,11 +6,13 @@ require (
2 github.com/charmbracelet/soft-serve v0.7.4
3 github.com/charmbracelet/ssh v0.0.0-20240301204039-e79ff702f5b3
4 github.com/charmbracelet/wish v1.3.2
5- github.com/picosh/ptun v0.0.0-20240313192814-d0ca401937fe
6+ github.com/picosh/send v0.0.0-20240217194807-77b972121e63
7 )
8
9 require (
10+ github.com/DavidGamba/go-getoptions v0.29.0 // indirect
11 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
12+ github.com/antoniomika/go-rsync-receiver v0.0.0-20231110145728-c94949e1ab7d // indirect
13 github.com/aymanbagabas/git-module v1.8.4-0.20231101154130-8d27204ac6d2 // indirect
14 github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
15 github.com/caarlos0/env/v10 v10.0.0 // indirect
16@@ -31,6 +33,7 @@ require (
17 github.com/go-logfmt/logfmt v0.6.0 // indirect
18 github.com/gobwas/glob v0.2.3 // indirect
19 github.com/golang-jwt/jwt/v5 v5.1.0 // indirect
20+ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
21 github.com/google/uuid v1.4.0 // indirect
22 github.com/jmoiron/sqlx v1.3.5 // indirect
23 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
24@@ -40,10 +43,12 @@ require (
25 github.com/mattn/go-localereader v0.0.1 // indirect
26 github.com/mattn/go-runewidth v0.0.15 // indirect
27 github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75 // indirect
28+ github.com/mmcloughlin/md4 v0.1.2 // indirect
29 github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
30 github.com/muesli/cancelreader v0.2.2 // indirect
31 github.com/muesli/reflow v0.3.0 // indirect
32 github.com/muesli/termenv v0.15.2 // indirect
33+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
34 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
35 github.com/rivo/uniseg v0.4.7 // indirect
36 github.com/rubyist/tracerx v0.0.0-20170927163412-787959303086 // indirect
M
go.sum
+10,
-2
1@@ -1,5 +1,9 @@
2+github.com/DavidGamba/go-getoptions v0.29.0 h1:cU8MjOyfAyPZke4hrgEuiGBJHS9PFYPAHve2fhDhdDk=
3+github.com/DavidGamba/go-getoptions v0.29.0/go.mod h1:zE97E3PR9P3BI/HKyNYgdMlYxodcuiC6W68KIgeYT84=
4 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
5 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
6+github.com/antoniomika/go-rsync-receiver v0.0.0-20231110145728-c94949e1ab7d h1:NyzUTxebDLLdtNu1gY5hn/amdAEnKG9DOawz82LwNTY=
7+github.com/antoniomika/go-rsync-receiver v0.0.0-20231110145728-c94949e1ab7d/go.mod h1:zmqePVIo1hp+WEKxERLLGHJBDOr8/z/T4eFqXgWIw1w=
8 github.com/aymanbagabas/git-module v1.8.4-0.20231101154130-8d27204ac6d2 h1:3w5KT+shE3hzWhORGiu2liVjEoaCEXm9uZP47+Gw4So=
9 github.com/aymanbagabas/git-module v1.8.4-0.20231101154130-8d27204ac6d2/go.mod h1:d4gQ7/3/S2sPq4NnKdtAgUOVr6XtLpWFtxyVV5/+76U=
10 github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
11@@ -56,6 +60,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
12 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
13 github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
14 github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
15+github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
16+github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
17 github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
18 github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
19 github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
20@@ -88,6 +94,8 @@ github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwp
21 github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
22 github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75 h1:Pijfgr7ZuvX7QIQiEwLdRVr3RoMG+i0SbBO1Qu+7yVk=
23 github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo=
24+github.com/mmcloughlin/md4 v0.1.2 h1:kGYl+iNbxhyz4u76ka9a+0TXP9KWt/LmnM0QhZwhcBo=
25+github.com/mmcloughlin/md4 v0.1.2/go.mod h1:AAxFX59fddW0IguqNzWlf1lazh1+rXeIt/Bj49cqDTQ=
26 github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
27 github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
28 github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
29@@ -96,8 +104,8 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
30 github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
31 github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
32 github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
33-github.com/picosh/ptun v0.0.0-20240313192814-d0ca401937fe h1:FmDSB2z/4iQ1m1WEVWQaDEwXiXlehd9Csqb8evWCMpY=
34-github.com/picosh/ptun v0.0.0-20240313192814-d0ca401937fe/go.mod h1:uQfDebjN3JJPsI3PAx8T5rmJwdpfmjvdRe7fXY33Kbw=
35+github.com/picosh/send v0.0.0-20240217194807-77b972121e63 h1:VSSbAejFzj2KBThfVnMcNXQwzHmwjPUridgi29LxihU=
36+github.com/picosh/send v0.0.0-20240217194807-77b972121e63/go.mod h1:1JCq0NVOdTDenQ0/Kd8e4rP80lu06UHJJ+6dQxhcpew=
37 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
38 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
39 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+82,
-0
1@@ -0,0 +1,82 @@
2+package git
3+
4+import (
5+ "fmt"
6+ "io"
7+ "log/slog"
8+ "os"
9+ "path/filepath"
10+
11+ "github.com/charmbracelet/ssh"
12+ "github.com/picosh/send/send/utils"
13+)
14+
15+type UploadHandler struct {
16+ Cfg *GitCfg
17+ Logger *slog.Logger
18+}
19+
20+func NewUploadHandler(cfg *GitCfg, logger *slog.Logger) *UploadHandler {
21+ return &UploadHandler{
22+ Cfg: cfg,
23+ Logger: logger,
24+ }
25+}
26+
27+func (h *UploadHandler) Read(s ssh.Session, entry *utils.FileEntry) (os.FileInfo, utils.ReaderAtCloser, error) {
28+ cleanFilename := filepath.Base(entry.Filepath)
29+
30+ if cleanFilename == "" || cleanFilename == "." {
31+ return nil, nil, os.ErrNotExist
32+ }
33+
34+ return nil, nil, os.ErrNotExist
35+}
36+
37+func (h *UploadHandler) List(s ssh.Session, fpath string, isDir bool, recursive bool) ([]os.FileInfo, error) {
38+ var fileList []os.FileInfo
39+ cleanFilename := filepath.Base(fpath)
40+
41+ if cleanFilename == "" || cleanFilename == "." || cleanFilename == "/" {
42+ name := cleanFilename
43+ if name == "" {
44+ name = "/"
45+ }
46+
47+ fileList = append(fileList, &utils.VirtualFile{
48+ FName: name,
49+ FIsDir: true,
50+ })
51+ } else {
52+ }
53+
54+ return fileList, nil
55+}
56+
57+func (h *UploadHandler) GetLogger() *slog.Logger {
58+ return h.Logger
59+}
60+
61+func (h *UploadHandler) Validate(s ssh.Session) error {
62+ return nil
63+}
64+
65+func (h *UploadHandler) Write(s ssh.Session, entry *utils.FileEntry) (string, error) {
66+ logger := h.GetLogger()
67+ user := s.User()
68+
69+ filename := filepath.Base(entry.Filepath)
70+ logger = logger.With(
71+ "user", user,
72+ "filename", filename,
73+ )
74+
75+ var text []byte
76+ if b, err := io.ReadAll(entry.Reader); err == nil {
77+ text = b
78+ }
79+
80+ fmt.Println(text)
81+
82+ return "", nil
83+}
R main.go =>
ssh.go
+49,
-43
1@@ -7,7 +7,6 @@ import (
2 "os"
3 "os/signal"
4 "path/filepath"
5- "slices"
6 "syscall"
7 "time"
8
9@@ -15,65 +14,66 @@ import (
10 "github.com/charmbracelet/soft-serve/pkg/utils"
11 "github.com/charmbracelet/ssh"
12 "github.com/charmbracelet/wish"
13+ "github.com/picosh/send/list"
14+ wishrsync "github.com/picosh/send/send/rsync"
15+ "github.com/picosh/send/send/scp"
16 )
17
18 func authHandler(ctx ssh.Context, key ssh.PublicKey) bool {
19 return true
20 }
21
22-var cmdAllowlist = []string{
23- "git-receive-pack",
24- "git-upload-pack",
25+func gitServiceCommands(sesh ssh.Session, cfg *GitCfg, cmd, repo string) error {
26+ name := utils.SanitizeRepo(repo)
27+ // git bare repositories should end in ".git"
28+ // https://git-scm.com/docs/gitrepository-layout
29+ repoDir := name + ".git"
30+ reposDir := filepath.Join(cfg.DataPath, "repos")
31+ err := git.EnsureWithin(reposDir, repoDir)
32+ if err != nil {
33+ return err
34+ }
35+ repoPath := filepath.Join(reposDir, repoDir)
36+ serviceCmd := git.ServiceCommand{
37+ Stdin: sesh,
38+ Stdout: sesh,
39+ Stderr: sesh.Stderr(),
40+ Dir: repoPath,
41+ Env: sesh.Environ(),
42+ }
43+
44+ if cmd == "git-receive-pack" {
45+ err := git.ReceivePack(sesh.Context(), serviceCmd)
46+ if err != nil {
47+ return err
48+ }
49+ } else if cmd == "git-upload-pack" {
50+ err := git.UploadPack(sesh.Context(), serviceCmd)
51+ if err != nil {
52+ return err
53+ }
54+ }
55+
56+ return nil
57 }
58
59 func GitServerMiddleware(cfg *GitCfg) wish.Middleware {
60 return func(next ssh.Handler) ssh.Handler {
61 return func(sesh ssh.Session) {
62- _, _, activePty := sesh.Pty()
63- if activePty {
64- next(sesh)
65- return
66- }
67-
68 args := sesh.Command()
69 cmd := args[0]
70
71- if !slices.Contains(cmdAllowlist, cmd) {
72- wish.Fatalf(sesh, "%s not a valid command", cmd)
73- return
74- }
75-
76- name := utils.SanitizeRepo(args[1])
77- // git bare repositories should end in ".git"
78- // https://git-scm.com/docs/gitrepository-layout
79- repoDir := name + ".git"
80- reposDir := filepath.Join(cfg.DataPath, "repos")
81- err := git.EnsureWithin(reposDir, repoDir)
82- if err != nil {
83- wish.Fatal(sesh, err.Error())
84- }
85- repoPath := filepath.Join(reposDir, repoDir)
86- serviceCmd := git.ServiceCommand{
87- Stdin: sesh,
88- Stdout: sesh,
89- Stderr: sesh.Stderr(),
90- Dir: repoPath,
91- Env: sesh.Environ(),
92- }
93-
94- if cmd == "git-receive-pack" {
95- err := git.ReceivePack(sesh.Context(), serviceCmd)
96- if err != nil {
97- wish.Fatal(sesh, err.Error())
98- return
99- }
100- return
101- } else if cmd == "git-upload-pack" {
102- err := git.UploadPack(sesh.Context(), serviceCmd)
103+ if cmd == "git-receive-pack" || cmd == "git-upload-pack" {
104+ repoName := args[1]
105+ err := gitServiceCommands(sesh, cfg, cmd, repoName)
106 if err != nil {
107 wish.Fatal(sesh, err.Error())
108 return
109 }
110+ } else if cmd == "help" {
111+ wish.Println(sesh, "commands: [help, git-receive-pack, git-upload-pack]")
112+ } else {
113+ next(sesh)
114 return
115 }
116 }
117@@ -102,12 +102,18 @@ func GitSshServer() {
118
119 cfg := NewGitCfg()
120 logger := slog.Default()
121+ handler := NewUploadHandler(cfg, logger)
122
123 s, err := wish.NewServer(
124 wish.WithAddress(fmt.Sprintf("%s:%s", host, port)),
125 wish.WithHostKeyPath(filepath.Join(cfg.DataPath, "term_info_ed25519")),
126 wish.WithPublicKeyAuth(authHandler),
127- wish.WithMiddleware(GitServerMiddleware(cfg)),
128+ wish.WithMiddleware(
129+ list.Middleware(handler),
130+ scp.Middleware(handler),
131+ wishrsync.Middleware(handler),
132+ GitServerMiddleware(cfg),
133+ ),
134 )
135
136 if err != nil {