Skip to content

Commit 62c9a05

Browse files
committed
Fix a bug when uploading file via lfs ssh command
1 parent f63822f commit 62c9a05

File tree

3 files changed

+50
-19
lines changed

3 files changed

+50
-19
lines changed

cmd/serv.go

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -246,11 +246,7 @@ func runServ(c *cli.Context) error {
246246
return fail(ctx, "Too few arguments", "Too few arguments in cmd: %s", cmd)
247247
}
248248

249-
verb := words[0]
250249
repoPath := strings.TrimPrefix(words[1], "/")
251-
252-
var lfsVerb string
253-
254250
rr := strings.SplitN(repoPath, "/", 2)
255251
if len(rr) != 2 {
256252
return fail(ctx, "Invalid repository path", "Invalid repository path: %v", repoPath)
@@ -286,22 +282,25 @@ func runServ(c *cli.Context) error {
286282
}()
287283
}
288284

289-
if allowedCommands.Contains(verb) {
290-
if allowedCommandsLfs.Contains(verb) {
291-
if !setting.LFS.StartServer {
292-
return fail(ctx, "LFS Server is not enabled", "")
293-
}
294-
if verb == verbLfsTransfer && !setting.LFS.AllowPureSSH {
295-
return fail(ctx, "LFS SSH transfer is not enabled", "")
296-
}
297-
if len(words) > 2 {
298-
lfsVerb = words[2]
299-
}
300-
}
301-
} else {
285+
verb := words[0]
286+
var lfsVerb string
287+
288+
if !allowedCommands.Contains(verb) {
302289
return fail(ctx, "Unknown git command", "Unknown git command %s", verb)
303290
}
304291

292+
if allowedCommandsLfs.Contains(verb) {
293+
if !setting.LFS.StartServer {
294+
return fail(ctx, "LFS Server is not enabled", "")
295+
}
296+
if verb == verbLfsTransfer && !setting.LFS.AllowPureSSH {
297+
return fail(ctx, "LFS SSH transfer is not enabled", "")
298+
}
299+
if len(words) > 2 {
300+
lfsVerb = words[2]
301+
}
302+
}
303+
305304
requestedMode := getAccessMode(verb, lfsVerb)
306305

307306
results, extra := private.ServCommand(ctx, keyID, username, reponame, requestedMode, verb, lfsVerb)

routers/private/serv.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ func ServCommand(ctx *context.PrivateContext) {
8181
ownerName := ctx.PathParam("owner")
8282
repoName := ctx.PathParam("repo")
8383
mode := perm.AccessMode(ctx.FormInt("mode"))
84+
verb := ctx.FormString("verb") // there should be two verbs, but we only care about the first one
8485

8586
// Set the basic parts of the results to return
8687
results := private.ServCommandResults{
@@ -296,7 +297,7 @@ func ServCommand(ctx *context.PrivateContext) {
296297
}
297298
} else {
298299
// Because of the special ref "refs/for" we will need to delay write permission check
299-
if git.DefaultFeatures().SupportProcReceive && unitType == unit.TypeCode {
300+
if git.DefaultFeatures().SupportProcReceive && verb == "git-receive-pack" && unitType == unit.TypeCode {
300301
mode = perm.AccessModeRead
301302
}
302303

tests/integration/git_general_test.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
package integration
55

66
import (
7+
"bytes"
78
"encoding/hex"
89
"fmt"
910
"io"
1011
mathRand "math/rand/v2"
1112
"net/http"
1213
"net/url"
1314
"os"
15+
"os/exec"
1416
"path"
1517
"path/filepath"
1618
"strconv"
@@ -30,6 +32,7 @@ import (
3032
api "code.gitea.io/gitea/modules/structs"
3133
"code.gitea.io/gitea/tests"
3234

35+
"github.com/kballard/go-shellquote"
3336
"github.com/stretchr/testify/assert"
3437
"github.com/stretchr/testify/require"
3538
)
@@ -105,7 +108,11 @@ func testGitGeneral(t *testing.T, u *url.URL) {
105108

106109
// Setup key the user ssh key
107110
withKeyFile(t, keyname, func(keyFile string) {
108-
t.Run("CreateUserKey", doAPICreateUserKey(sshContext, "test-key", keyFile))
111+
var keyID int64
112+
t.Run("CreateUserKey", doAPICreateUserKey(sshContext, "test-key", keyFile, func(t *testing.T, key api.PublicKey) {
113+
keyID = key.ID
114+
}))
115+
assert.Greater(t, keyID, int64(0))
109116

110117
// Setup remote link
111118
// TODO: get url from api
@@ -132,10 +139,34 @@ func testGitGeneral(t *testing.T, u *url.URL) {
132139
})
133140

134141
t.Run("PushCreate", doPushCreate(sshContext, sshURL))
142+
t.Run("LFSUploadTest", doLFSUploadTest(sshContext, keyID))
135143
})
136144
})
137145
}
138146

147+
func doLFSUploadTest(ctx APITestContext, keyID int64) func(*testing.T) {
148+
return func(t *testing.T) {
149+
// This is set in withKeyFile and ensure correctly
150+
sshCommand := os.Getenv("GIT_SSH_COMMAND")
151+
152+
// We really have to split on the arguments and pass them individually.
153+
sshOptions, err := shellquote.Split(sshCommand)
154+
assert.NoError(t, err)
155+
156+
sshOptions = append(sshOptions, "-p "+strconv.Itoa(setting.SSH.ListenPort), "git@"+setting.SSH.ListenHost)
157+
// user2's key upload lfs file to user5/repo4
158+
sshOptions = append(sshOptions, "git-lfs-authenticate", "user5/repo4.git", "upload")
159+
160+
cmd := exec.CommandContext(t.Context(), sshOptions[0], sshOptions[1:]...)
161+
stderr := bytes.Buffer{}
162+
cmd.Stderr = &stderr
163+
err = cmd.Run()
164+
165+
assert.ErrorContains(t, err, "exit status 1")
166+
assert.Contains(t, stderr.String(), fmt.Sprintf("User: 2:user2 with Key: %d:test-key is not authorized to write to user5/repo4.", keyID))
167+
}
168+
}
169+
139170
func ensureAnonymousClone(t *testing.T, u *url.URL) {
140171
dstLocalPath := t.TempDir()
141172
t.Run("CloneAnonymous", doGitClone(dstLocalPath, u))

0 commit comments

Comments
 (0)