From d8490031b2bd50a57277abf30f98e1d383f2418c Mon Sep 17 00:00:00 2001 From: Adam Majer Date: Sat, 7 Feb 2026 21:52:30 +0100 Subject: [PATCH] lfs: locks and test fixes --- services/lfs/server.go | 13 ++++++++++--- tests/integration/git_general_test.go | 13 +++++++++---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/services/lfs/server.go b/services/lfs/server.go index d7e0c0b97b..6443a9b2ae 100644 --- a/services/lfs/server.go +++ b/services/lfs/server.go @@ -463,12 +463,19 @@ func getAuthenticatedRepository(ctx *context.Context, rc *requestContext, requir return nil } - if !authenticate(ctx, repository, rc.Authorization, false, requireWrite) { + // Because of special ref "refs/for" (AGit), we need to delay/relax write permission check + // for LFS object uploads. LFS locks still require full write access. + lfsWriteMode := requireWrite + if lfsWriteMode && git.DefaultFeatures().SupportProcReceive { + lfsWriteMode = false + } + + if !authenticate(ctx, repository, rc.Authorization, false, lfsWriteMode) { requireAuth(ctx) return nil } - if requireWrite { + if lfsWriteMode { context.CheckRepoScopedToken(ctx, repository, auth_model.Write) } else { context.CheckRepoScopedToken(ctx, repository, auth_model.Read) @@ -538,7 +545,7 @@ func writeStatusMessage(ctx *context.Context, status int, message string) { // to proceed. This server assumes an HTTP Basic auth format. func authenticate(ctx *context.Context, repository *repo_model.Repository, authorization string, requireSigned, requireWrite bool) bool { accessMode := perm_model.AccessModeRead - if requireWrite && !git.DefaultFeatures().SupportProcReceive { + if requireWrite { accessMode = perm_model.AccessModeWrite } diff --git a/tests/integration/git_general_test.go b/tests/integration/git_general_test.go index f789ae3747..d1d091eee7 100644 --- a/tests/integration/git_general_test.go +++ b/tests/integration/git_general_test.go @@ -166,13 +166,18 @@ func doSSHLFSAccessTest(_ APITestContext, keyID int64) func(*testing.T) { t.Run("User2AccessOther", func(t *testing.T) { sshCmdUser2Other := append(slices.Clone(sshCmdParts), "-p", strconv.Itoa(setting.SSH.ListenPort), "git@"+setting.SSH.ListenHost, - "git-lfs-authenticate", "user5/repo4.git", "upload", // inaccessible to other's (user5/repo4) + "git-lfs-authenticate", "user5/repo4.git", "upload", // accessible to other's (user5/repo4) if AGit is supported ) cmd := exec.CommandContext(t.Context(), sshCmdUser2Other[0], sshCmdUser2Other[1:]...) _, err := cmd.Output() - var errExit *exec.ExitError - require.ErrorAs(t, err, &errExit) // inaccessible, error - assert.Contains(t, string(errExit.Stderr), fmt.Sprintf("User: 2:user2 with Key: %d:test-key is not authorized to write to user5/repo4.", keyID)) + + if git.DefaultFeatures().SupportProcReceive { + assert.NoError(t, err) // relaxed for AGit + } else { + var errExit *exec.ExitError + require.ErrorAs(t, err, &errExit) // inaccessible, error + assert.Contains(t, string(errExit.Stderr), fmt.Sprintf("User: 2:user2 with Key: %d:test-key is not authorized to write to user5/repo4.", keyID)) + } }) } }