Files
gitea-MIRROR/routers/web/repo/setting/settings_test.go
Basavaraj S m 4f69599981 fix(repo): do not reuse pull mirror credentials for a new host (#39991)
Changing a pull mirror address with an empty password field carried the
stored credentials over to the new address. The password is write-only,
so a repo admin who did not set up the mirror could send the owner's
token to a host they control, or pull another repository on the same
host that the token can read.

Stored credentials are now reused only when the address is unchanged,
like a changed username already drops the stored password. The API
cannot change the address and push mirrors never reuse credentials, so
both are unaffected.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-07 12:35:10 +00:00

200 lines
7.6 KiB
Go

// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
import (
"net/http"
"net/url"
"testing"
deploykey_model "gitea.dev/models/deploykey"
"gitea.dev/models/organization"
"gitea.dev/models/perm"
access_model "gitea.dev/models/perm/access"
repo_model "gitea.dev/models/repo"
"gitea.dev/models/unittest"
user_model "gitea.dev/models/user"
"gitea.dev/modules/git"
"gitea.dev/modules/setting"
"gitea.dev/modules/test"
"gitea.dev/modules/web"
"gitea.dev/services/context"
"gitea.dev/services/contexttest"
"gitea.dev/services/forms"
mirror_service "gitea.dev/services/mirror"
repo_service "gitea.dev/services/repository"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAddDeployKey(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
t.Run("ReadOnly", func(t *testing.T) {
const testKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICV0MGX/W9IvLA4FXpIuUcdDcbj5KX4syHgsTy7soVgf\n"
ctx, _ := contexttest.MockContext(t, "POST /user2/repo1/settings/keys")
contexttest.MockRequestPostForm(ctx.Req, url.Values{"title": {"read-only"}, "content": {testKey}})
contexttest.LoadRepo(t, ctx, 2)
DeployKeysPost(ctx)
assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
unittest.AssertExistsAndLoadBean(t, &deploykey_model.DeployKey{Name: "read-only", Mode: perm.AccessModeRead})
})
t.Run("ReadWrite", func(t *testing.T) {
const testKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEHjnNEfE88W1pvBLdV3otv28x760gdmPao3lVD5uAt9\n"
ctx, _ := contexttest.MockContext(t, "POST /user2/repo1/settings/keys")
contexttest.MockRequestPostForm(ctx.Req, url.Values{"title": {"read-write"}, "content": {testKey}, "is_writable": {"on"}})
contexttest.LoadRepo(t, ctx, 2)
DeployKeysPost(ctx)
assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
unittest.AssertExistsAndLoadBean(t, &deploykey_model.DeployKey{Name: "read-write", Mode: perm.AccessModeWrite})
})
}
func TestCollaborationPost(t *testing.T) {
for _, c := range []struct {
name, collaborator string
userID int64
}{
{"AddCollaboratorTwice", "user4", 4},
{"InactiveUser", "user9", 9},
{"NonExistentUser", "user34", 0},
} {
t.Run(c.name, func(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
ctx, _ := contexttest.MockContext(t, "user2/repo1/issues/labels")
contexttest.LoadUser(t, ctx, 2)
if c.userID != 0 {
contexttest.LoadUser(t, ctx, c.userID)
}
contexttest.LoadRepo(t, ctx, 1)
ctx.Req.Form.Set("collaborator", c.collaborator)
owner := &user_model.User{LowerName: "user2", Type: user_model.UserTypeIndividual}
ctx.Repo = &context.Repository{Owner: owner}
if c.userID == 4 {
ctx.Repo.Repository = &repo_model.Repository{ID: 2, Owner: owner}
}
CollaborationPost(ctx)
assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
if c.userID == 4 {
exists, err := repo_model.IsCollaborator(ctx, ctx.Repo.Repository.ID, 4)
assert.NoError(t, err)
assert.True(t, exists)
// Try adding the same collaborator again
CollaborationPost(ctx)
assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
}
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
})
}
}
func TestAddTeamPost(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 26})
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 43})
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 11})
repo.Owner = org
testAddTeamPost := func(t *testing.T, teamName string, repoAdminChangeTeamAccess bool) *context.Context {
ctx, _ := contexttest.MockContext(t, "org26/repo43")
ctx.Req.Form.Set("team", teamName)
org.RepoAdminChangeTeamAccess = repoAdminChangeTeamAccess
ctx.Repo = &context.Repository{
Permission: access_model.Permission{AccessMode: perm.AccessModeAdmin},
Owner: repo.Owner,
Repository: repo,
}
ctx.Doer = &user_model.User{ID: 1, IsAdmin: true}
AddTeamPost(ctx)
return ctx
}
t.Run("NonExisting", func(t *testing.T) {
ctx := testAddTeamPost(t, "team-not-exist", true)
assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
assert.Contains(t, ctx.Flash.ErrorMsg, "form.team_not_exist")
})
t.Run("NotAllowed", func(t *testing.T) {
ctx := testAddTeamPost(t, team.Name, false)
assert.False(t, repo_service.HasRepository(t.Context(), team, repo.ID))
assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
assert.Contains(t, ctx.Flash.ErrorMsg, "repo.settings.change_team_access_not_allowed")
})
t.Run("Allowed", func(t *testing.T) {
ctx := testAddTeamPost(t, team.Name, true)
assert.True(t, repo_service.HasRepository(t.Context(), team, repo.ID))
assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
assert.Empty(t, ctx.Flash.ErrorMsg)
t.Run("Twice", func(t *testing.T) {
ctx := testAddTeamPost(t, team.Name, true)
assert.True(t, repo_service.HasRepository(t.Context(), team, repo.ID))
assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
assert.Contains(t, ctx.Flash.ErrorMsg, "repo.settings.add_team_duplicate")
})
})
}
func TestDeleteTeam(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
ctx, _ := contexttest.MockContext(t, "org3/team1/repo3")
ctx.Req.Form.Set("id", "2")
org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2})
repo.Owner = org
org.RepoAdminChangeTeamAccess = true
ctx.Repo = &context.Repository{
Permission: access_model.Permission{AccessMode: perm.AccessModeAdmin},
Owner: repo.Owner,
Repository: repo,
}
ctx.Doer = &user_model.User{ID: 1, IsAdmin: true}
assert.True(t, repo_service.HasRepository(t.Context(), team, repo.ID))
DeleteTeam(ctx)
assert.False(t, repo_service.HasRepository(t.Context(), team, repo.ID))
}
func TestHandleSettingsPostMirrorReusesCredentialsOnlyForSameAddress(t *testing.T) {
defer test.MockVariableValue(&setting.Mirror.Enabled, true)()
unittest.PrepareTestEnv(t)
// Use the existing fixture mirror repo (org3/repo5) which has a git repo on disk.
mirrorRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 5})
mirror := unittest.AssertExistsAndLoadBean(t, &repo_model.Mirror{RepoID: 5})
require.NoError(t, mirror_service.UpdateAddress(t.Context(), mirror, "https://existing-user:existing-password@example.com/user2/repo1.git"))
postMirrorSettings := func(form *forms.RepoSettingForm) string {
ctx, _ := contexttest.MockContext(t, mirrorRepo.Link()+"/settings")
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadRepo(t, ctx, mirrorRepo.ID)
form.Interval = "8h"
web.SetForm(ctx, form)
handleSettingsPostMirror(ctx)
require.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
remoteURL, err := git.ParseRemoteAddressURL(t.Context(), mirrorRepo, mirror.GetRemoteName())
require.NoError(t, err)
return remoteURL.User.String()
}
assert.Equal(t, "existing-user:updated-password", postMirrorSettings(&forms.RepoSettingForm{
MirrorAddress: "https://example.com/user2/repo1.git",
MirrorPassword: "updated-password",
}))
assert.Equal(t, "https://example.com/user2/repo1.git", unittest.AssertExistsAndLoadBean(t, &repo_model.Mirror{RepoID: mirrorRepo.ID}).RemoteAddress)
assert.Equal(t, "https://example.com/user2/repo1.git", unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: mirrorRepo.ID}).OriginalURL)
assert.Equal(t, "existing-user:", postMirrorSettings(&forms.RepoSettingForm{
MirrorAddress: "https://example.com/user2/private.git",
MirrorUsername: "existing-user",
}))
}