Skip to content

Commit 3f1b68f

Browse files
committed
fix
1 parent 44aadc3 commit 3f1b68f

File tree

4 files changed

+53
-25
lines changed

4 files changed

+53
-25
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)

modules/private/serv.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,16 @@ type ServCommandResults struct {
4646
}
4747

4848
// ServCommand preps for a serv call
49-
func ServCommand(ctx context.Context, keyID int64, ownerName, repoName string, mode perm.AccessMode, verbs ...string) (*ServCommandResults, ResponseExtra) {
49+
func ServCommand(ctx context.Context, keyID int64, ownerName, repoName string, mode perm.AccessMode, verb, lfsVerb string) (*ServCommandResults, ResponseExtra) {
5050
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/serv/command/%d/%s/%s?mode=%d",
5151
keyID,
5252
url.PathEscape(ownerName),
5353
url.PathEscape(repoName),
5454
mode,
5555
)
56-
for _, verb := range verbs {
57-
if verb != "" {
58-
reqURL += "&verb=" + url.QueryEscape(verb)
59-
}
56+
reqURL += "&verb=" + url.QueryEscape(verb)
57+
if lfsVerb != "" {
58+
reqURL += "&lfs_verb=" + url.QueryEscape(lfsVerb)
6059
}
6160
req := newInternalRequestAPI(ctx, reqURL, "GET")
6261
return requestJSONResp(req, &ServCommandResults{})

routers/private/serv.go

Lines changed: 6 additions & 2 deletions
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")
8485

8586
// Set the basic parts of the results to return
8687
results := private.ServCommandResults{
@@ -295,8 +296,11 @@ func ServCommand(ctx *context.PrivateContext) {
295296
return
296297
}
297298
} else {
298-
// Because of the special ref "refs/for" we will need to delay write permission check
299-
if git.DefaultFeatures().SupportProcReceive && unitType == unit.TypeCode {
299+
// Because of the special ref "refs/for" (AGit) we will need to delay write permission check,
300+
// AGit flow needs to write its own ref when the doer has "reader" permission (allowing to create PR).
301+
// The real permission check is done in HookPreReceive (routers/private/hook_pre_receive.go).
302+
// Here it should relax the permission check for "git push (git-receive-pack)", but not for others like LFS operations.
303+
if git.DefaultFeatures().SupportProcReceive && verb == "git-receive-pack" && unitType == unit.TypeCode {
300304
mode = perm.AccessModeRead
301305
}
302306

tests/integration/git_general_test.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"net/http"
1212
"net/url"
1313
"os"
14+
"os/exec"
1415
"path"
1516
"path/filepath"
1617
"strconv"
@@ -30,6 +31,7 @@ import (
3031
api "code.gitea.io/gitea/modules/structs"
3132
"code.gitea.io/gitea/tests"
3233

34+
"github.com/kballard/go-shellquote"
3335
"github.com/stretchr/testify/assert"
3436
"github.com/stretchr/testify/require"
3537
)
@@ -105,7 +107,11 @@ func testGitGeneral(t *testing.T, u *url.URL) {
105107

106108
// Setup key the user ssh key
107109
withKeyFile(t, keyname, func(keyFile string) {
108-
t.Run("CreateUserKey", doAPICreateUserKey(sshContext, "test-key", keyFile))
110+
var keyID int64
111+
t.Run("CreateUserKey", doAPICreateUserKey(sshContext, "test-key", keyFile, func(t *testing.T, key api.PublicKey) {
112+
keyID = key.ID
113+
}))
114+
assert.NotZero(t, keyID)
109115

110116
// Setup remote link
111117
// TODO: get url from api
@@ -132,10 +138,30 @@ func testGitGeneral(t *testing.T, u *url.URL) {
132138
})
133139

134140
t.Run("PushCreate", doPushCreate(sshContext, sshURL))
141+
t.Run("LFSUploadTest", doLFSUploadTest(sshContext, keyID))
135142
})
136143
})
137144
}
138145

146+
func doLFSUploadTest(_ APITestContext, keyID int64) func(*testing.T) {
147+
return func(t *testing.T) {
148+
sshCommand := os.Getenv("GIT_SSH_COMMAND") // it is set in withKeyFile and ensure correctly
149+
sshCmdParts, err := shellquote.Split(sshCommand) // and parse the ssh command to construct some mocked arguments
150+
require.NoError(t, err)
151+
152+
t.Run("User2AccessUser5Repo4", func(t *testing.T) {
153+
sshCmdParts = append(sshCmdParts,
154+
"-p", strconv.Itoa(setting.SSH.ListenPort), "git@"+setting.SSH.ListenHost,
155+
"git-lfs-authenticate", "user5/repo4.git", "upload", // user2's key upload lfs file to user5/repo4
156+
)
157+
cmd := exec.CommandContext(t.Context(), sshCmdParts[0], sshCmdParts[1:]...)
158+
out, err := cmd.Output()
159+
assert.ErrorContains(t, err, "exit status 1")
160+
assert.Contains(t, string(out), fmt.Sprintf("User: 2:user2 with Key: %d:test-key is not authorized to write to user5/repo4.", keyID))
161+
})
162+
}
163+
}
164+
139165
func ensureAnonymousClone(t *testing.T, u *url.URL) {
140166
dstLocalPath := t.TempDir()
141167
t.Run("CloneAnonymous", doGitClone(dstLocalPath, u))

0 commit comments

Comments
 (0)