mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Only allow admins to rename default/protected branches (#33276)
Currently, anyone with write permissions to a repo are able to rename default or protected branches. This change follows [GitHub's](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-branches-in-your-repository/renaming-a-branch) design by only allowing repo/site admins to change these branches. However, it also follows are current design for protected branches and only allows admins to modify branch names == branch protection rule names. Glob-based rules cannot be renamed by anyone (as was already the case, but we now catch `ErrBranchIsProtected` which we previously did not catch, throwing a 500).
This commit is contained in:
		| @@ -190,28 +190,61 @@ func testAPICreateBranch(t testing.TB, session *TestSession, user, repo, oldBran | ||||
| func TestAPIUpdateBranch(t *testing.T) { | ||||
| 	onGiteaRun(t, func(t *testing.T, _ *url.URL) { | ||||
| 		t.Run("UpdateBranchWithEmptyRepo", func(t *testing.T) { | ||||
| 			testAPIUpdateBranch(t, "user10", "repo6", "master", "test", http.StatusNotFound) | ||||
| 			testAPIUpdateBranch(t, "user10", "user10", "repo6", "master", "test", http.StatusNotFound) | ||||
| 		}) | ||||
| 		t.Run("UpdateBranchWithSameBranchNames", func(t *testing.T) { | ||||
| 			resp := testAPIUpdateBranch(t, "user2", "repo1", "master", "master", http.StatusUnprocessableEntity) | ||||
| 			resp := testAPIUpdateBranch(t, "user2", "user2", "repo1", "master", "master", http.StatusUnprocessableEntity) | ||||
| 			assert.Contains(t, resp.Body.String(), "Cannot rename a branch using the same name or rename to a branch that already exists.") | ||||
| 		}) | ||||
| 		t.Run("UpdateBranchThatAlreadyExists", func(t *testing.T) { | ||||
| 			resp := testAPIUpdateBranch(t, "user2", "repo1", "master", "branch2", http.StatusUnprocessableEntity) | ||||
| 			resp := testAPIUpdateBranch(t, "user2", "user2", "repo1", "master", "branch2", http.StatusUnprocessableEntity) | ||||
| 			assert.Contains(t, resp.Body.String(), "Cannot rename a branch using the same name or rename to a branch that already exists.") | ||||
| 		}) | ||||
| 		t.Run("UpdateBranchWithNonExistentBranch", func(t *testing.T) { | ||||
| 			resp := testAPIUpdateBranch(t, "user2", "repo1", "i-dont-exist", "new-branch-name", http.StatusNotFound) | ||||
| 			resp := testAPIUpdateBranch(t, "user2", "user2", "repo1", "i-dont-exist", "new-branch-name", http.StatusNotFound) | ||||
| 			assert.Contains(t, resp.Body.String(), "Branch doesn't exist.") | ||||
| 		}) | ||||
| 		t.Run("RenameBranchNormalScenario", func(t *testing.T) { | ||||
| 			testAPIUpdateBranch(t, "user2", "repo1", "branch2", "new-branch-name", http.StatusNoContent) | ||||
| 		t.Run("UpdateBranchWithNonAdminDoer", func(t *testing.T) { | ||||
| 			// don't allow default branch renaming | ||||
| 			resp := testAPIUpdateBranch(t, "user40", "user2", "repo1", "master", "new-branch-name", http.StatusForbidden) | ||||
| 			assert.Contains(t, resp.Body.String(), "User must be a repo or site admin to rename default or protected branches.") | ||||
|  | ||||
| 			// don't allow protected branch renaming | ||||
| 			token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteRepository) | ||||
| 			req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/branches", &api.CreateBranchRepoOption{ | ||||
| 				BranchName: "protected-branch", | ||||
| 			}).AddTokenAuth(token) | ||||
| 			MakeRequest(t, req, http.StatusCreated) | ||||
| 			testAPICreateBranchProtection(t, "protected-branch", 1, http.StatusCreated) | ||||
| 			resp = testAPIUpdateBranch(t, "user40", "user2", "repo1", "protected-branch", "new-branch-name", http.StatusForbidden) | ||||
| 			assert.Contains(t, resp.Body.String(), "User must be a repo or site admin to rename default or protected branches.") | ||||
| 		}) | ||||
| 		t.Run("UpdateBranchWithGlobedBasedProtectionRulesAndAdminAccess", func(t *testing.T) { | ||||
| 			// don't allow branch that falls under glob-based protection rules to be renamed | ||||
| 			token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteRepository) | ||||
| 			req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/branch_protections", &api.BranchProtection{ | ||||
| 				RuleName:   "protected/**", | ||||
| 				EnablePush: true, | ||||
| 			}).AddTokenAuth(token) | ||||
| 			MakeRequest(t, req, http.StatusCreated) | ||||
|  | ||||
| 			from := "protected/1" | ||||
| 			req = NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/branches", &api.CreateBranchRepoOption{ | ||||
| 				BranchName: from, | ||||
| 			}).AddTokenAuth(token) | ||||
| 			MakeRequest(t, req, http.StatusCreated) | ||||
|  | ||||
| 			resp := testAPIUpdateBranch(t, "user2", "user2", "repo1", from, "new-branch-name", http.StatusForbidden) | ||||
| 			assert.Contains(t, resp.Body.String(), "Branch is protected by glob-based protection rules.") | ||||
| 		}) | ||||
| 		t.Run("UpdateBranchNormalScenario", func(t *testing.T) { | ||||
| 			testAPIUpdateBranch(t, "user2", "user2", "repo1", "branch2", "new-branch-name", http.StatusNoContent) | ||||
| 		}) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func testAPIUpdateBranch(t *testing.T, ownerName, repoName, from, to string, expectedHTTPStatus int) *httptest.ResponseRecorder { | ||||
| 	token := getUserToken(t, ownerName, auth_model.AccessTokenScopeWriteRepository) | ||||
| func testAPIUpdateBranch(t *testing.T, doerName, ownerName, repoName, from, to string, expectedHTTPStatus int) *httptest.ResponseRecorder { | ||||
| 	token := getUserToken(t, doerName, auth_model.AccessTokenScopeWriteRepository) | ||||
| 	req := NewRequestWithJSON(t, "PATCH", "api/v1/repos/"+ownerName+"/"+repoName+"/branches/"+from, &api.UpdateBranchRepoOption{ | ||||
| 		Name: to, | ||||
| 	}).AddTokenAuth(token) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user