Supports wildcard protected branch (#20825)

This PR introduce glob match for protected branch name. The separator is
`/` and you can use `*` matching non-separator chars and use `**` across
separator.

It also supports input an exist or non-exist branch name as matching
condition and branch name condition has high priority than glob rule.

Should fix #2529 and #15705

screenshots

<img width="1160" alt="image"
src="https://user-images.githubusercontent.com/81045/205651179-ebb5492a-4ade-4bb4-a13c-965e8c927063.png">

Co-authored-by: zeripath <art27@cantab.net>
This commit is contained in:
Lunny Xiao 2023-01-16 16:00:22 +08:00 committed by GitHub
parent cc1f8cbe96
commit 2782c14396
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 1222 additions and 819 deletions

View file

@ -38,21 +38,21 @@ func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPSta
if resp.Code == http.StatusOK {
var branchProtection api.BranchProtection
DecodeJSON(t, resp, &branchProtection)
assert.EqualValues(t, branchName, branchProtection.BranchName)
assert.EqualValues(t, branchName, branchProtection.RuleName)
}
}
func testAPICreateBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) {
token := getUserToken(t, "user2")
req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/branch_protections?token="+token, &api.BranchProtection{
BranchName: branchName,
RuleName: branchName,
})
resp := MakeRequest(t, req, expectedHTTPStatus)
if resp.Code == http.StatusCreated {
var branchProtection api.BranchProtection
DecodeJSON(t, resp, &branchProtection)
assert.EqualValues(t, branchName, branchProtection.BranchName)
assert.EqualValues(t, branchName, branchProtection.RuleName)
}
}
@ -64,7 +64,7 @@ func testAPIEditBranchProtection(t *testing.T, branchName string, body *api.Bran
if resp.Code == http.StatusOK {
var branchProtection api.BranchProtection
DecodeJSON(t, resp, &branchProtection)
assert.EqualValues(t, branchName, branchProtection.BranchName)
assert.EqualValues(t, branchName, branchProtection.RuleName)
}
}
@ -169,8 +169,8 @@ func testAPICreateBranch(t testing.TB, session *TestSession, user, repo, oldBran
func TestAPIBranchProtection(t *testing.T) {
defer tests.PrepareTestEnv(t)()
// Branch protection only on branch that exist
testAPICreateBranchProtection(t, "master/doesnotexist", http.StatusNotFound)
// Branch protection on branch that not exist
testAPICreateBranchProtection(t, "master/doesnotexist", http.StatusCreated)
// Get branch protection on branch that exist but not branch protection
testAPIGetBranchProtection(t, "master", http.StatusNotFound)

View file

@ -10,6 +10,8 @@ import (
"path"
"testing"
"code.gitea.io/gitea/modules/json"
"github.com/stretchr/testify/assert"
)
@ -43,15 +45,16 @@ func TestCreateFileOnProtectedBranch(t *testing.T) {
csrf := GetCSRF(t, session, "/user2/repo1/settings/branches")
// Change master branch to protected
req := NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/master", map[string]string{
"_csrf": csrf,
"protected": "on",
req := NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/edit", map[string]string{
"_csrf": csrf,
"rule_name": "master",
"enable_push": "true",
})
session.MakeRequest(t, req, http.StatusSeeOther)
// Check if master branch has been locked successfully
flashCookie := session.GetCookie("macaron_flash")
assert.NotNil(t, flashCookie)
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Bbranch%2B%2527master%2527%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value)
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Brule%2B%2527master%2527%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value)
// Request editor page
req = NewRequest(t, "GET", "/user2/repo1/_new/master/")
@ -76,16 +79,22 @@ func TestCreateFileOnProtectedBranch(t *testing.T) {
// remove the protected branch
csrf = GetCSRF(t, session, "/user2/repo1/settings/branches")
// Change master branch to protected
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/master", map[string]string{
"_csrf": csrf,
"protected": "off",
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/1/delete", map[string]string{
"_csrf": csrf,
})
session.MakeRequest(t, req, http.StatusSeeOther)
resp = session.MakeRequest(t, req, http.StatusOK)
res := make(map[string]string)
assert.NoError(t, json.NewDecoder(resp.Body).Decode(&res))
assert.EqualValues(t, "/user2/repo1/settings/branches", res["redirect"])
// Check if master branch has been locked successfully
flashCookie = session.GetCookie("macaron_flash")
assert.NotNil(t, flashCookie)
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Bbranch%2B%2527master%2527%2Bhas%2Bbeen%2Bdisabled.", flashCookie.Value)
assert.EqualValues(t, "error%3DRemoving%2Bbranch%2Bprotection%2Brule%2B%25271%2527%2Bfailed.", flashCookie.Value)
})
}

View file

@ -414,9 +414,9 @@ func doProtectBranch(ctx APITestContext, branch, userToWhitelist, unprotectedFil
if userToWhitelist == "" {
// Change branch to protected
req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/branches/%s", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame), url.PathEscape(branch)), map[string]string{
req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/branches/edit", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)), map[string]string{
"_csrf": csrf,
"protected": "on",
"rule_name": branch,
"unprotected_file_patterns": unprotectedFilePatterns,
})
ctx.Session.MakeRequest(t, req, http.StatusSeeOther)
@ -424,9 +424,9 @@ func doProtectBranch(ctx APITestContext, branch, userToWhitelist, unprotectedFil
user, err := user_model.GetUserByName(db.DefaultContext, userToWhitelist)
assert.NoError(t, err)
// Change branch to protected
req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/branches/%s", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame), url.PathEscape(branch)), map[string]string{
req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/branches/edit", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)), map[string]string{
"_csrf": csrf,
"protected": "on",
"rule_name": branch,
"enable_push": "whitelist",
"enable_whitelist": "on",
"whitelist_users": strconv.FormatInt(user.ID, 10),
@ -437,7 +437,7 @@ func doProtectBranch(ctx APITestContext, branch, userToWhitelist, unprotectedFil
// Check if master branch has been locked successfully
flashCookie := ctx.Session.GetCookie("macaron_flash")
assert.NotNil(t, flashCookie)
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Bbranch%2B%2527"+url.QueryEscape(branch)+"%2527%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value)
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Brule%2B%2527"+url.QueryEscape(branch)+"%2527%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value)
}
}