Fix issue label deletion with Actions tokens (#37013)
Use shared repo permission resolution for Actions task users in issue label remove and clear paths, and add a regression test for deleting issue labels with a Gitea Actions token. This fixes issue label deletion when the request is authenticated with a Gitea Actions token. Fixes #37011 The bug was that the delete path re-resolved repository permissions using the normal user permission helper, which does not handle Actions task users. As a result, `DELETE /api/v1/repos/{owner}/{repo}/issues/{index}/labels/{id}` could return `500` for Actions tokens even though label listing and label addition worked. --------- Co-authored-by: Codex <codex@openai.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: Giteabot <teabot@gitea.io>
This commit is contained in:
@@ -24,6 +24,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/lfs"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/tests"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -284,6 +285,65 @@ func TestActionsCrossRepoAccess(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestActionsJobTokenPermissions(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
t.Run("WriteIssue", TestActionsJobTokenPermissionsWriteIssue)
|
||||
}
|
||||
|
||||
func TestActionsJobTokenPermissionsWriteIssue(t *testing.T) {
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||
task := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 53})
|
||||
require.Equal(t, repo.ID, task.RepoID)
|
||||
|
||||
require.NoError(t, db.Insert(t.Context(), &repo_model.RepoUnit{
|
||||
RepoID: repo.ID,
|
||||
Type: unit_model.TypeActions,
|
||||
Config: &repo_model.ActionsConfig{},
|
||||
}))
|
||||
|
||||
repoActionsUnit := repo.MustGetUnit(t.Context(), unit_model.TypeActions)
|
||||
repoActionsCfg := repoActionsUnit.ActionsConfig()
|
||||
repoActionsCfg.OverrideOwnerConfig = true
|
||||
repoActionsCfg.TokenPermissionMode = repo_model.ActionsTokenPermissionModePermissive
|
||||
repoActionsCfg.MaxTokenPermissions = nil
|
||||
require.NoError(t, repo_model.UpdateRepoUnitConfig(t.Context(), repoActionsUnit))
|
||||
|
||||
require.NoError(t, task.GenerateToken())
|
||||
task.Status = actions_model.StatusRunning
|
||||
require.NoError(t, actions_model.UpdateTask(t.Context(), task, "token_hash", "token_salt", "token_last_eight", "status"))
|
||||
|
||||
session := loginUser(t, user.Name)
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue, auth_model.AccessTokenScopeWriteRepository)
|
||||
|
||||
labelURL := fmt.Sprintf("/api/v1/repos/%s/%s/labels", user.Name, repo.Name)
|
||||
req := NewRequestWithJSON(t, "POST", labelURL, &structs.CreateLabelOption{
|
||||
Name: "task-label",
|
||||
Color: "0e8a16",
|
||||
}).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusCreated)
|
||||
label := DecodeJSON(t, resp, &structs.Label{})
|
||||
|
||||
issueURL := fmt.Sprintf("/api/v1/repos/%s/%s/issues", user.Name, repo.Name)
|
||||
req = NewRequestWithJSON(t, "POST", issueURL, &structs.CreateIssueOption{
|
||||
Title: "issue for actions token label deletion",
|
||||
}).AddTokenAuth(token)
|
||||
resp = MakeRequest(t, req, http.StatusCreated)
|
||||
issue := DecodeJSON(t, resp, &structs.Issue{})
|
||||
|
||||
taskToken := task.Token
|
||||
require.NotEmpty(t, taskToken)
|
||||
|
||||
issueLabelsURL := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels", user.Name, repo.Name, issue.Index)
|
||||
req = NewRequestWithJSON(t, "POST", issueLabelsURL, &structs.IssueLabelsOption{
|
||||
Labels: []any{label.ID},
|
||||
}).AddTokenAuth(taskToken)
|
||||
MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%d", issueLabelsURL, label.ID)).AddTokenAuth(taskToken)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
}
|
||||
|
||||
func createActionTask(t *testing.T, repoID int64, isFork bool) *actions_model.ActionTask {
|
||||
job := &actions_model.ActionRunJob{
|
||||
RepoID: repoID,
|
||||
|
||||
@@ -498,7 +498,7 @@ func TestAPIPullReviewStayDismissed(t *testing.T) {
|
||||
pullIssue.ID, user8.ID, 1, 1, 2, false)
|
||||
|
||||
// user8 dismiss review
|
||||
permUser8, err := access_model.GetUserRepoPermission(t.Context(), pullIssue.Repo, user8)
|
||||
permUser8, err := access_model.GetIndividualUserRepoPermission(t.Context(), pullIssue.Repo, user8)
|
||||
assert.NoError(t, err)
|
||||
_, err = issue_service.ReviewRequest(t.Context(), pullIssue, user8, &permUser8, user8, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
Reference in New Issue
Block a user