mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-08 14:34:49 +09:00
## Summary
This PR adds support for updating pull mirror authentication via the
repository edit API and UI.
It introduces new mirror authentication fields in _EditRepoOption_,
updates the API logic to safely handle partial credential updates, and
fixes the web settings flow so that the existing remote username is
preserved when only the password is changed.
### What changed
- added _auth_username_, _auth_password_, and _auth_token_ to
EditRepoOption
- updated the repository edit API to apply mirror auth changes via
_updateMirror_
- preserved existing username/password when only part of the auth
payload is provided
- used oauth2 as the default username when _auth_token_ is provided
- kept stored mirror URLs sanitized in DB and API responses
- updated Swagger schema for the new API fields
- added API integration tests for password-only and token-only updates
- added a web settings test to ensure username preservation on partial
updates
## Why
Some use cases require automated synchronization of pull mirrors, for
example in CI/CD pipelines or integrations with external systems.
At the same time, many organizations enforce security policies that
require periodic token rotation (e.g., monthly).
Currently, mirror credentials can only be updated via the UI, which
makes automation difficult.
## This change enables:
- automated token rotation
- avoiding manual updates via the UI
- easier integration with secret management systems
## Testing
- added integration coverage for mirror auth updates via _PATCH
/api/v1/repos/{owner}/{repo}_
- added web settings tests for password-only updates preserving the
existing username
## Result
Ability to automate auth update
<img width="2400" height="1245" alt="1"
src="https://github.com/user-attachments/assets/67fd5cca-9cb3-4536-b0e2-4d09b8ebff0f"
/>
<img width="962" height="932" alt="image"
src="https://github.com/user-attachments/assets/5d548f5d-aadf-4807-ba52-9c29df93a4cc"
/>
Generative AI was used to help with making this PR.
##
491 lines
22 KiB
Go
491 lines
22 KiB
Go
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package integration
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"testing"
|
|
|
|
auth_model "code.gitea.io/gitea/models/auth"
|
|
repo_model "code.gitea.io/gitea/models/repo"
|
|
unit_model "code.gitea.io/gitea/models/unit"
|
|
"code.gitea.io/gitea/models/unittest"
|
|
user_model "code.gitea.io/gitea/models/user"
|
|
"code.gitea.io/gitea/modules/gitrepo"
|
|
api "code.gitea.io/gitea/modules/structs"
|
|
mirror_service "code.gitea.io/gitea/services/mirror"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// getRepoEditOptionFromRepo gets the options for an existing repo exactly as is
|
|
func getRepoEditOptionFromRepo(repo *repo_model.Repository) *api.EditRepoOption {
|
|
ctx := context.TODO()
|
|
name := repo.Name
|
|
description := repo.Description
|
|
website := repo.Website
|
|
private := repo.IsPrivate
|
|
hasIssues := false
|
|
var internalTracker *api.InternalTracker
|
|
var externalTracker *api.ExternalTracker
|
|
if unit, err := repo.GetUnit(ctx, unit_model.TypeIssues); err == nil {
|
|
config := unit.IssuesConfig()
|
|
hasIssues = true
|
|
internalTracker = &api.InternalTracker{
|
|
EnableTimeTracker: config.EnableTimetracker,
|
|
AllowOnlyContributorsToTrackTime: config.AllowOnlyContributorsToTrackTime,
|
|
EnableIssueDependencies: config.EnableDependencies,
|
|
}
|
|
} else if unit, err := repo.GetUnit(ctx, unit_model.TypeExternalTracker); err == nil {
|
|
config := unit.ExternalTrackerConfig()
|
|
hasIssues = true
|
|
externalTracker = &api.ExternalTracker{
|
|
ExternalTrackerURL: config.ExternalTrackerURL,
|
|
ExternalTrackerFormat: config.ExternalTrackerFormat,
|
|
ExternalTrackerStyle: config.ExternalTrackerStyle,
|
|
ExternalTrackerRegexpPattern: config.ExternalTrackerRegexpPattern,
|
|
}
|
|
}
|
|
hasWiki := false
|
|
var externalWiki *api.ExternalWiki
|
|
if _, err := repo.GetUnit(ctx, unit_model.TypeWiki); err == nil {
|
|
hasWiki = true
|
|
} else if unit, err := repo.GetUnit(ctx, unit_model.TypeExternalWiki); err == nil {
|
|
hasWiki = true
|
|
externalWiki = &api.ExternalWiki{
|
|
ExternalWikiURL: unit.ExternalWikiConfig().ExternalWikiURL,
|
|
}
|
|
}
|
|
defaultBranch := repo.DefaultBranch
|
|
hasPullRequests := false
|
|
ignoreWhitespaceConflicts := false
|
|
allowMerge := false
|
|
allowRebase := false
|
|
allowRebaseMerge := false
|
|
allowSquash := false
|
|
allowFastForwardOnly := false
|
|
if unit, err := repo.GetUnit(ctx, unit_model.TypePullRequests); err == nil {
|
|
config := unit.PullRequestsConfig()
|
|
hasPullRequests = true
|
|
ignoreWhitespaceConflicts = config.IgnoreWhitespaceConflicts
|
|
allowMerge = config.AllowMerge
|
|
allowRebase = config.AllowRebase
|
|
allowRebaseMerge = config.AllowRebaseMerge
|
|
allowSquash = config.AllowSquash
|
|
allowFastForwardOnly = config.AllowFastForwardOnly
|
|
}
|
|
archived := repo.IsArchived
|
|
hasProjects := false
|
|
var projectsMode *string
|
|
if unit, err := repo.GetUnit(ctx, unit_model.TypeProjects); err == nil && unit != nil {
|
|
hasProjects = true
|
|
pm := string(unit.ProjectsConfig().ProjectsMode)
|
|
projectsMode = &pm
|
|
}
|
|
hasCode := repo.UnitEnabled(ctx, unit_model.TypeCode)
|
|
hasPackages := repo.UnitEnabled(ctx, unit_model.TypePackages)
|
|
hasReleases := repo.UnitEnabled(ctx, unit_model.TypeReleases)
|
|
hasActions := false
|
|
if unit, err := repo.GetUnit(ctx, unit_model.TypeActions); err == nil && unit != nil {
|
|
hasActions = true
|
|
// TODO: expose action config of repo to api
|
|
// actionsConfig = &api.RepoActionsConfig{
|
|
// DisabledWorkflows: unit.ActionsConfig().DisabledWorkflows,
|
|
// }
|
|
}
|
|
return &api.EditRepoOption{
|
|
Name: &name,
|
|
Description: &description,
|
|
Website: &website,
|
|
Private: &private,
|
|
HasIssues: &hasIssues,
|
|
HasProjects: &hasProjects,
|
|
ProjectsMode: projectsMode,
|
|
HasCode: &hasCode,
|
|
HasPackages: &hasPackages,
|
|
HasReleases: &hasReleases,
|
|
HasActions: &hasActions,
|
|
ExternalTracker: externalTracker,
|
|
InternalTracker: internalTracker,
|
|
HasWiki: &hasWiki,
|
|
ExternalWiki: externalWiki,
|
|
DefaultBranch: &defaultBranch,
|
|
HasPullRequests: &hasPullRequests,
|
|
IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts,
|
|
AllowMerge: &allowMerge,
|
|
AllowRebase: &allowRebase,
|
|
AllowRebaseMerge: &allowRebaseMerge,
|
|
AllowSquash: &allowSquash,
|
|
AllowFastForwardOnly: &allowFastForwardOnly,
|
|
Archived: &archived,
|
|
}
|
|
}
|
|
|
|
// getNewRepoEditOption Gets the options to change everything about an existing repo by adding to strings or changing
|
|
// the boolean
|
|
func getNewRepoEditOption(opts *api.EditRepoOption) *api.EditRepoOption {
|
|
// Gives a new property to everything
|
|
name := *opts.Name + "renamed"
|
|
description := "new description"
|
|
website := "http://wwww.newwebsite.com"
|
|
private := !*opts.Private
|
|
hasIssues := !*opts.HasIssues
|
|
hasWiki := !*opts.HasWiki
|
|
hasProjects := !*opts.HasProjects
|
|
hasCode := !*opts.HasCode
|
|
hasPackages := !*opts.HasPackages
|
|
hasReleases := !*opts.HasReleases
|
|
hasActions := !*opts.HasActions
|
|
defaultBranch := "master"
|
|
hasPullRequests := !*opts.HasPullRequests
|
|
ignoreWhitespaceConflicts := !*opts.IgnoreWhitespaceConflicts
|
|
allowMerge := !*opts.AllowMerge
|
|
allowRebase := !*opts.AllowRebase
|
|
allowRebaseMerge := !*opts.AllowRebaseMerge
|
|
allowSquash := !*opts.AllowSquash
|
|
archived := !*opts.Archived
|
|
|
|
return &api.EditRepoOption{
|
|
Name: &name,
|
|
Description: &description,
|
|
Website: &website,
|
|
Private: &private,
|
|
DefaultBranch: &defaultBranch,
|
|
HasIssues: &hasIssues,
|
|
HasWiki: &hasWiki,
|
|
HasProjects: &hasProjects,
|
|
HasCode: &hasCode,
|
|
HasPackages: &hasPackages,
|
|
HasReleases: &hasReleases,
|
|
HasActions: &hasActions,
|
|
HasPullRequests: &hasPullRequests,
|
|
IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts,
|
|
AllowMerge: &allowMerge,
|
|
AllowRebase: &allowRebase,
|
|
AllowRebaseMerge: &allowRebaseMerge,
|
|
AllowSquash: &allowSquash,
|
|
Archived: &archived,
|
|
}
|
|
}
|
|
|
|
func TestAPIRepoEdit(t *testing.T) {
|
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
|
bFalse, bTrue := false, true
|
|
|
|
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // owner of the repo1 & repo16
|
|
org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) // owner of the repo3, is an org
|
|
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) // owner of neither repos
|
|
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) // public repo
|
|
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) // public repo
|
|
repo15 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 15}) // empty repo
|
|
repo16 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16}) // private repo
|
|
|
|
// Get user2's token
|
|
session := loginUser(t, user2.Name)
|
|
token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
|
|
// Get user4's token
|
|
session = loginUser(t, user4.Name)
|
|
token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
|
|
|
|
// Test editing a repo1 which user2 owns, changing name and many properties
|
|
origRepoEditOption := getRepoEditOptionFromRepo(repo1)
|
|
assert.True(t, *origRepoEditOption.HasCode)
|
|
assert.True(t, *origRepoEditOption.HasPackages)
|
|
assert.True(t, *origRepoEditOption.HasProjects)
|
|
assert.True(t, *origRepoEditOption.HasReleases)
|
|
assert.True(t, *origRepoEditOption.HasActions)
|
|
repoEditOption := getNewRepoEditOption(origRepoEditOption)
|
|
req := NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo1.Name), &repoEditOption).
|
|
AddTokenAuth(token2)
|
|
resp := MakeRequest(t, req, http.StatusOK)
|
|
repo := DecodeJSON(t, resp, &api.Repository{})
|
|
assert.NotNil(t, repo)
|
|
// check response
|
|
assert.Equal(t, *repoEditOption.Name, repo.Name)
|
|
assert.Equal(t, *repoEditOption.Description, repo.Description)
|
|
assert.Equal(t, *repoEditOption.Website, repo.Website)
|
|
assert.Equal(t, *repoEditOption.Archived, repo.Archived)
|
|
// check repo1 from database
|
|
repo1edited := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
|
repo1editedOption := getRepoEditOptionFromRepo(repo1edited)
|
|
assert.Equal(t, *repoEditOption.Name, *repo1editedOption.Name)
|
|
assert.Equal(t, *repoEditOption.Description, *repo1editedOption.Description)
|
|
assert.Equal(t, *repoEditOption.Website, *repo1editedOption.Website)
|
|
assert.Equal(t, *repoEditOption.Archived, *repo1editedOption.Archived)
|
|
assert.Equal(t, *repoEditOption.Private, *repo1editedOption.Private)
|
|
assert.Equal(t, *repoEditOption.HasWiki, *repo1editedOption.HasWiki)
|
|
assert.Equal(t, *repoEditOption.HasCode, *repo1editedOption.HasCode)
|
|
assert.Equal(t, *repoEditOption.HasPackages, *repo1editedOption.HasPackages)
|
|
assert.Equal(t, *repoEditOption.HasProjects, *repo1editedOption.HasProjects)
|
|
assert.Equal(t, *repoEditOption.HasReleases, *repo1editedOption.HasReleases)
|
|
assert.Equal(t, *repoEditOption.HasActions, *repo1editedOption.HasActions)
|
|
|
|
// Test editing repo1 to use internal issue and wiki (default)
|
|
*repoEditOption.HasIssues = true
|
|
repoEditOption.ExternalTracker = nil
|
|
repoEditOption.InternalTracker = &api.InternalTracker{
|
|
EnableTimeTracker: false,
|
|
AllowOnlyContributorsToTrackTime: false,
|
|
EnableIssueDependencies: false,
|
|
}
|
|
*repoEditOption.HasWiki = true
|
|
repoEditOption.ExternalWiki = nil
|
|
url := fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, *repoEditOption.Name)
|
|
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption).
|
|
AddTokenAuth(token2)
|
|
resp = MakeRequest(t, req, http.StatusOK)
|
|
repo = DecodeJSON(t, resp, &api.Repository{})
|
|
assert.NotNil(t, repo)
|
|
// check repo1 was written to database
|
|
repo1edited = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
|
repo1editedOption = getRepoEditOptionFromRepo(repo1edited)
|
|
assert.True(t, *repo1editedOption.HasIssues)
|
|
assert.Nil(t, repo1editedOption.ExternalTracker)
|
|
assert.Equal(t, *repo1editedOption.InternalTracker, *repoEditOption.InternalTracker)
|
|
assert.True(t, *repo1editedOption.HasWiki)
|
|
assert.Nil(t, repo1editedOption.ExternalWiki)
|
|
|
|
// Test editing repo1 to use external issue and wiki
|
|
repoEditOption.ExternalTracker = &api.ExternalTracker{
|
|
ExternalTrackerURL: "http://www.somewebsite.com",
|
|
ExternalTrackerFormat: "http://www.somewebsite.com/{user}/{repo}?issue={index}",
|
|
ExternalTrackerStyle: "alphanumeric",
|
|
}
|
|
repoEditOption.ExternalWiki = &api.ExternalWiki{
|
|
ExternalWikiURL: "http://www.somewebsite.com",
|
|
}
|
|
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption).
|
|
AddTokenAuth(token2)
|
|
resp = MakeRequest(t, req, http.StatusOK)
|
|
repo = DecodeJSON(t, resp, &api.Repository{})
|
|
assert.NotNil(t, repo)
|
|
// check repo1 was written to database
|
|
repo1edited = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
|
repo1editedOption = getRepoEditOptionFromRepo(repo1edited)
|
|
assert.True(t, *repo1editedOption.HasIssues)
|
|
assert.Equal(t, *repo1editedOption.ExternalTracker, *repoEditOption.ExternalTracker)
|
|
assert.True(t, *repo1editedOption.HasWiki)
|
|
assert.Equal(t, *repo1editedOption.ExternalWiki, *repoEditOption.ExternalWiki)
|
|
assert.False(t, *repo1editedOption.HasCode)
|
|
assert.False(t, *repo1editedOption.HasPackages)
|
|
assert.False(t, *repo1editedOption.HasProjects)
|
|
assert.False(t, *repo1editedOption.HasReleases)
|
|
assert.False(t, *repo1editedOption.HasActions)
|
|
|
|
repoEditOption.ExternalTracker.ExternalTrackerStyle = "regexp"
|
|
repoEditOption.ExternalTracker.ExternalTrackerRegexpPattern = `(\d+)`
|
|
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption).
|
|
AddTokenAuth(token2)
|
|
resp = MakeRequest(t, req, http.StatusOK)
|
|
repo = DecodeJSON(t, resp, &api.Repository{})
|
|
assert.NotNil(t, repo)
|
|
repo1edited = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
|
repo1editedOption = getRepoEditOptionFromRepo(repo1edited)
|
|
assert.True(t, *repo1editedOption.HasIssues)
|
|
assert.Equal(t, *repo1editedOption.ExternalTracker, *repoEditOption.ExternalTracker)
|
|
|
|
// Do some tests with invalid URL for external tracker and wiki
|
|
repoEditOption.ExternalTracker.ExternalTrackerURL = "htp://www.somewebsite.com"
|
|
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption).
|
|
AddTokenAuth(token2)
|
|
MakeRequest(t, req, http.StatusUnprocessableEntity)
|
|
repoEditOption.ExternalTracker.ExternalTrackerURL = "http://www.somewebsite.com"
|
|
repoEditOption.ExternalTracker.ExternalTrackerFormat = "http://www.somewebsite.com/{user/{repo}?issue={index}"
|
|
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption).
|
|
AddTokenAuth(token2)
|
|
MakeRequest(t, req, http.StatusUnprocessableEntity)
|
|
repoEditOption.ExternalTracker.ExternalTrackerFormat = "http://www.somewebsite.com/{user}/{repo}?issue={index}"
|
|
repoEditOption.ExternalWiki.ExternalWikiURL = "htp://www.somewebsite.com"
|
|
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption).
|
|
AddTokenAuth(token2)
|
|
MakeRequest(t, req, http.StatusUnprocessableEntity)
|
|
|
|
// Test small repo change through API with issue and wiki option not set; They shall not be touched.
|
|
*repoEditOption.Description = "small change"
|
|
repoEditOption.HasIssues = nil
|
|
repoEditOption.ExternalTracker = nil
|
|
repoEditOption.HasWiki = nil
|
|
repoEditOption.ExternalWiki = nil
|
|
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption).
|
|
AddTokenAuth(token2)
|
|
resp = MakeRequest(t, req, http.StatusOK)
|
|
repo = DecodeJSON(t, resp, &api.Repository{})
|
|
assert.NotNil(t, repo)
|
|
// check repo1 was written to database
|
|
repo1edited = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
|
repo1editedOption = getRepoEditOptionFromRepo(repo1edited)
|
|
assert.Equal(t, *repo1editedOption.Description, *repoEditOption.Description)
|
|
assert.True(t, *repo1editedOption.HasIssues)
|
|
assert.NotNil(t, *repo1editedOption.ExternalTracker)
|
|
assert.True(t, *repo1editedOption.HasWiki)
|
|
assert.NotNil(t, *repo1editedOption.ExternalWiki)
|
|
assert.False(t, *repo1editedOption.HasCode)
|
|
assert.False(t, *repo1editedOption.HasPackages)
|
|
assert.False(t, *repo1editedOption.HasProjects)
|
|
assert.False(t, *repo1editedOption.HasReleases)
|
|
assert.False(t, *repo1editedOption.HasActions)
|
|
|
|
// reset repo in db
|
|
req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, *repoEditOption.Name), &origRepoEditOption).
|
|
AddTokenAuth(token2)
|
|
_ = MakeRequest(t, req, http.StatusOK)
|
|
|
|
// Test editing a non-existing repo
|
|
name := "repodoesnotexist"
|
|
req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, name), &api.EditRepoOption{Name: &name}).
|
|
AddTokenAuth(token2)
|
|
_ = MakeRequest(t, req, http.StatusNotFound)
|
|
|
|
// Test editing repo16 by user4 who does not have write access
|
|
origRepoEditOption = getRepoEditOptionFromRepo(repo16)
|
|
repoEditOption = getNewRepoEditOption(origRepoEditOption)
|
|
req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo16.Name), &repoEditOption).
|
|
AddTokenAuth(token4)
|
|
MakeRequest(t, req, http.StatusNotFound)
|
|
|
|
// Tests a repo with no token given so will fail
|
|
origRepoEditOption = getRepoEditOptionFromRepo(repo16)
|
|
repoEditOption = getNewRepoEditOption(origRepoEditOption)
|
|
req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo16.Name), &repoEditOption)
|
|
_ = MakeRequest(t, req, http.StatusNotFound)
|
|
|
|
// Test using access token for a private repo that the user of the token owns
|
|
origRepoEditOption = getRepoEditOptionFromRepo(repo16)
|
|
repoEditOption = getNewRepoEditOption(origRepoEditOption)
|
|
req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo16.Name), &repoEditOption).
|
|
AddTokenAuth(token2)
|
|
_ = MakeRequest(t, req, http.StatusOK)
|
|
// reset repo in db
|
|
req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, *repoEditOption.Name), &origRepoEditOption).
|
|
AddTokenAuth(token2)
|
|
_ = MakeRequest(t, req, http.StatusOK)
|
|
|
|
// Test making a repo public that is private
|
|
repo16 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16})
|
|
assert.True(t, repo16.IsPrivate)
|
|
repoEditOption = &api.EditRepoOption{
|
|
Private: &bFalse,
|
|
}
|
|
url = fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo16.Name)
|
|
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption).
|
|
AddTokenAuth(token2)
|
|
_ = MakeRequest(t, req, http.StatusOK)
|
|
repo16 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16})
|
|
assert.False(t, repo16.IsPrivate)
|
|
// Make it private again
|
|
repoEditOption.Private = &bTrue
|
|
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption).
|
|
AddTokenAuth(token2)
|
|
_ = MakeRequest(t, req, http.StatusOK)
|
|
|
|
// Test to change empty repo
|
|
assert.False(t, repo15.IsArchived)
|
|
url = fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo15.Name)
|
|
req = NewRequestWithJSON(t, "PATCH", url, &api.EditRepoOption{
|
|
Archived: &bTrue,
|
|
}).AddTokenAuth(token2)
|
|
_ = MakeRequest(t, req, http.StatusOK)
|
|
repo15 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 15})
|
|
assert.True(t, repo15.IsArchived)
|
|
req = NewRequestWithJSON(t, "PATCH", url, &api.EditRepoOption{
|
|
Archived: &bFalse,
|
|
}).AddTokenAuth(token2)
|
|
_ = MakeRequest(t, req, http.StatusOK)
|
|
|
|
// Test using org repo "org3/repo3" where user2 is a collaborator
|
|
origRepoEditOption = getRepoEditOptionFromRepo(repo3)
|
|
repoEditOption = getNewRepoEditOption(origRepoEditOption)
|
|
req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", org3.Name, repo3.Name), &repoEditOption).
|
|
AddTokenAuth(token2)
|
|
MakeRequest(t, req, http.StatusOK)
|
|
// reset repo in db
|
|
req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", org3.Name, *repoEditOption.Name), &origRepoEditOption).
|
|
AddTokenAuth(token2)
|
|
_ = MakeRequest(t, req, http.StatusOK)
|
|
|
|
// Test using org repo "org3/repo3" with no user token
|
|
origRepoEditOption = getRepoEditOptionFromRepo(repo3)
|
|
repoEditOption = getNewRepoEditOption(origRepoEditOption)
|
|
req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", org3.Name, repo3.Name), &repoEditOption)
|
|
MakeRequest(t, req, http.StatusNotFound)
|
|
|
|
// Test using repo "user2/repo1" where user4 is a NOT collaborator
|
|
origRepoEditOption = getRepoEditOptionFromRepo(repo1)
|
|
repoEditOption = getNewRepoEditOption(origRepoEditOption)
|
|
req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo1.Name), &repoEditOption).
|
|
AddTokenAuth(token4)
|
|
MakeRequest(t, req, http.StatusForbidden)
|
|
|
|
// Test updating pull request settings without setting has_pull_requests
|
|
repo1 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
|
url = fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo1.Name)
|
|
req = NewRequestWithJSON(t, "PATCH", url, &api.EditRepoOption{
|
|
DefaultDeleteBranchAfterMerge: &bTrue,
|
|
}).AddTokenAuth(token2)
|
|
resp = MakeRequest(t, req, http.StatusOK)
|
|
repo = DecodeJSON(t, resp, &api.Repository{})
|
|
assert.True(t, repo.DefaultDeleteBranchAfterMerge)
|
|
// reset
|
|
req = NewRequestWithJSON(t, "PATCH", url, &api.EditRepoOption{
|
|
DefaultDeleteBranchAfterMerge: &bFalse,
|
|
}).AddTokenAuth(token2)
|
|
_ = MakeRequest(t, req, http.StatusOK)
|
|
|
|
// Test updating mirror password without changing the existing username
|
|
ctx := t.Context()
|
|
mirrorRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 5})
|
|
mirror := unittest.AssertExistsAndLoadBean(t, &repo_model.Mirror{RepoID: 5})
|
|
newPassword := "updated-password"
|
|
|
|
require.NoError(t, mirror_service.UpdateAddress(ctx, mirror, "https://existing-user:existing-password@example.com/user2/repo1.git"))
|
|
|
|
req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", mirrorRepo.OwnerName, mirrorRepo.Name), &api.EditRepoOption{
|
|
MirrorPassword: &newPassword,
|
|
}).AddTokenAuth(token2)
|
|
MakeRequest(t, req, http.StatusOK)
|
|
|
|
updatedMirror := unittest.AssertExistsAndLoadBean(t, &repo_model.Mirror{RepoID: mirrorRepo.ID})
|
|
assert.Equal(t, "https://example.com/user2/repo1.git", updatedMirror.RemoteAddress)
|
|
|
|
updatedRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: mirrorRepo.ID})
|
|
assert.Equal(t, "https://example.com/user2/repo1.git", updatedRepo.OriginalURL)
|
|
|
|
remoteURL, err := gitrepo.GitRemoteGetURL(ctx, updatedRepo, updatedMirror.GetRemoteName())
|
|
require.NoError(t, err)
|
|
require.NotNil(t, remoteURL.User)
|
|
assert.Equal(t, "existing-user", remoteURL.User.Username())
|
|
password, ok := remoteURL.User.Password()
|
|
require.True(t, ok)
|
|
assert.Equal(t, newPassword, password)
|
|
|
|
// Test updating mirror token without guessing a username
|
|
token := "mirror-token-value"
|
|
|
|
require.NoError(t, mirror_service.UpdateAddress(ctx, mirror, "https://example.com/user2/repo1.git"))
|
|
|
|
req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", mirrorRepo.OwnerName, mirrorRepo.Name), &api.EditRepoOption{
|
|
MirrorToken: &token,
|
|
}).AddTokenAuth(token2)
|
|
MakeRequest(t, req, http.StatusOK)
|
|
|
|
updatedMirror = unittest.AssertExistsAndLoadBean(t, &repo_model.Mirror{RepoID: mirrorRepo.ID})
|
|
assert.Equal(t, "https://example.com/user2/repo1.git", updatedMirror.RemoteAddress)
|
|
|
|
updatedRepo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: mirrorRepo.ID})
|
|
assert.Equal(t, "https://example.com/user2/repo1.git", updatedRepo.OriginalURL)
|
|
|
|
remoteURL, err = gitrepo.GitRemoteGetURL(ctx, updatedRepo, updatedMirror.GetRemoteName())
|
|
require.NoError(t, err)
|
|
require.NotNil(t, remoteURL.User)
|
|
assert.Empty(t, remoteURL.User.Username())
|
|
password, ok = remoteURL.User.Password()
|
|
require.True(t, ok)
|
|
assert.Equal(t, token, password)
|
|
})
|
|
}
|