From 9dd47d932f094b970a55b180719e55365d31f548 Mon Sep 17 00:00:00 2001 From: Litchi Pi Date: Sat, 1 Mar 2025 12:39:52 +0000 Subject: [PATCH] fix(web): forbid blocked users from reopening issues (#7010) Closes https://codeberg.org/forgejo/forgejo/issues/6841. Signed-off-by: Litchi Pi Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7010 Reviewed-by: Gusted Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Litchi Pi Co-committed-by: Litchi Pi --- models/issues/issue_update.go | 4 ++++ options/locale/locale_en-US.ini | 4 +++- routers/web/repo/issue.go | 22 ++++++++++++++++++++-- services/user/block_test.go | 21 +++++++++++++++++++++ templates/repo/issue/view_content.tmpl | 10 +++++++++- tests/integration/block_test.go | 12 ++++++++++++ 6 files changed, 69 insertions(+), 4 deletions(-) diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go index 775ebfadad..ad59b767ab 100644 --- a/models/issues/issue_update.go +++ b/models/issues/issue_update.go @@ -64,6 +64,10 @@ func changeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, } func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isMergePull bool) (*Comment, error) { + if user_model.IsBlockedMultiple(ctx, []int64{issue.Repo.OwnerID, issue.PosterID}, doer.ID) { + return nil, user_model.ErrBlockedByUser + } + // Check for open dependencies if issue.IsClosed && issue.Repo.IsDependenciesEnabled(ctx) { // only check if dependencies are enabled and we're about to close an issue, otherwise reopening an issue would fail when there are unsatisfied dependencies diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index dfae2b7b6f..8d27c9f3f8 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1878,7 +1878,8 @@ issues.content_history.delete_from_history_confirm = Delete from history? issues.content_history.options = Options issues.reference_link = Reference: %s issues.blocked_by_user = You cannot create a issue on this repository because you are blocked by the repository owner. -issues.comment.blocked_by_user = You cannot create a comment on this issue because you are blocked by the repository owner or the poster of the issue. +issues.comment.blocked_by_user = You cannot comment on this issue because you are blocked by the repository owner or the poster of the issue. +issues.reopen.blocked_by_user = You cannot reopen this issue because you are blocked by the repository owner or the poster of this issue. issues.summary_card_alt = Summary card of an issue titled "%s" in repository %s compare.compare_base = base @@ -1962,6 +1963,7 @@ pulls.waiting_count_1 = %d waiting review pulls.waiting_count_n = %d waiting reviews pulls.wrong_commit_id = commit id must be a commit id on the target branch pulls.blocked_by_user = You cannot create a pull request on this repository because you are blocked by the repository owner. +pulls.comment.blocked_by_user = You cannot comment on this pull request because you are blocked by the repository owner or the poster of the pull request. pulls.no_merge_desc = This pull request cannot be merged because all repository merge options are disabled. pulls.no_merge_helper = Enable merge options in the repository settings or merge the pull request manually. diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index b4c673ffd7..a86d043497 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -1260,7 +1260,11 @@ func NewIssuePost(ctx *context.Context) { if err := issue_service.NewIssue(ctx, repo, issue, labelIDs, attachments, assigneeIDs); err != nil { if errors.Is(err, user_model.ErrBlockedByUser) { - ctx.JSONError(ctx.Tr("repo.issues.blocked_by_user")) + if issue.IsPull { + ctx.JSONError(ctx.Tr("repo.pulls.blocked_by_user")) + } else { + ctx.JSONError(ctx.Tr("repo.issues.blocked_by_user")) + } return } else if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) { ctx.Error(http.StatusBadRequest, "UserDoesNotHaveAccessToRepo", err.Error()) @@ -2081,6 +2085,7 @@ func ViewIssue(ctx *context.Context) { ctx.Data["OpenGraphDescription"] = issue.Content ctx.Data["OpenGraphImageURL"] = issue.SummaryCardURL() ctx.Data["OpenGraphImageAltText"] = ctx.Tr("repo.issues.summary_card_alt", issue.Title, issue.Repo.FullName()) + ctx.Data["IsBlocked"] = ctx.Doer != nil && user_model.IsBlockedMultiple(ctx, []int64{issue.PosterID, issue.Repo.OwnerID}, ctx.Doer.ID) prepareHiddenCommentType(ctx) if ctx.Written() { @@ -3199,6 +3204,15 @@ func NewComment(ctx *context.Context) { } else { isClosed := form.Status == "close" if err := issue_service.ChangeStatus(ctx, issue, ctx.Doer, "", isClosed); err != nil { + if errors.Is(err, user_model.ErrBlockedByUser) { + if issue.IsPull { + ctx.JSONError(ctx.Tr("repo.pulls.blocked_by_user")) + } else { + ctx.JSONError(ctx.Tr("repo.issues.blocked_by_user")) + } + return + } + log.Error("ChangeStatus: %v", err) if issues_model.IsErrDependenciesLeft(err) { @@ -3240,7 +3254,11 @@ func NewComment(ctx *context.Context) { comment, err := issue_service.CreateIssueComment(ctx, ctx.Doer, ctx.Repo.Repository, issue, form.Content, attachments) if err != nil { if errors.Is(err, user_model.ErrBlockedByUser) { - ctx.JSONError(ctx.Tr("repo.issues.comment.blocked_by_user")) + if issue.IsPull { + ctx.JSONError(ctx.Tr("repo.pulls.comment.blocked_by_user")) + } else { + ctx.JSONError(ctx.Tr("repo.issues.comment.blocked_by_user")) + } } else { ctx.ServerError("CreateIssueComment", err) } diff --git a/services/user/block_test.go b/services/user/block_test.go index f9e95ed7f7..13959e56b4 100644 --- a/services/user/block_test.go +++ b/services/user/block_test.go @@ -8,6 +8,7 @@ import ( model "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" + issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -89,4 +90,24 @@ func TestBlockUser(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3, OwnerID: blockedUser.ID}) assert.Equal(t, repo_model.RepositoryReady, repo.Status) }) + + t.Run("Issues", func(t *testing.T) { + doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + blockedUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) + defer user_model.UnblockUser(db.DefaultContext, doer.ID, blockedUser.ID) + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2, OwnerID: doer.ID}) + issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4, RepoID: repo.ID}, "is_closed = true") + + _, err := issues_model.ChangeIssueStatus(db.DefaultContext, issue, blockedUser, false) + require.NoError(t, err) + + _, err = issues_model.ChangeIssueStatus(db.DefaultContext, issue, doer, true) + require.NoError(t, err) + + require.NoError(t, BlockUser(db.DefaultContext, doer.ID, blockedUser.ID)) + + _, err = issues_model.ChangeIssueStatus(db.DefaultContext, issue, blockedUser, false) + require.Error(t, err) + }) } diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl index 8eac2d78cf..8ede0f36e5 100644 --- a/templates/repo/issue/view_content.tmpl +++ b/templates/repo/issue/view_content.tmpl @@ -79,7 +79,7 @@ {{template "repo/issue/view_content/pull".}} {{end}} - {{if .IsSigned}} + {{if and .IsSigned (not .IsBlocked)}} {{if and (or .IsRepoAdmin .HasIssuesOrPullsWritePermission (not .Issue.IsLocked)) (not .Repository.IsArchived)}} {{end}} + {{else if .IsBlocked}} +
+ {{if .Issue.IsPull}} + {{ctx.Locale.Tr "repo.pulls.comment.blocked_by_user"}} + {{else}} + {{ctx.Locale.Tr "repo.issues.comment.blocked_by_user"}} + {{end}} +
{{else}} {{/* not .IsSigned */}} {{if .Repository.IsArchived}}
diff --git a/tests/integration/block_test.go b/tests/integration/block_test.go index 1fa201a0be..8eda4ffa54 100644 --- a/tests/integration/block_test.go +++ b/tests/integration/block_test.go @@ -253,6 +253,12 @@ func TestBlockActions(t *testing.T) { DecodeJSON(t, resp, &errorResp) assert.EqualValues(t, expectedMessage, errorResp.Error) + + req = NewRequest(t, "GET", issue10URL) + resp = session.MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + msg := htmlDoc.doc.Find("div .warning").Text() + assert.Contains(t, msg, "You cannot comment on this issue because you are blocked") }) t.Run("Blocked by issue poster", func(t *testing.T) { @@ -274,6 +280,12 @@ func TestBlockActions(t *testing.T) { DecodeJSON(t, resp, &errorResp) assert.EqualValues(t, expectedMessage, errorResp.Error) + + req = NewRequest(t, "GET", issue10URL) + resp = session.MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + msg := htmlDoc.doc.Find("div .warning").Text() + assert.Contains(t, msg, "You cannot comment on this issue because you are blocked") }) })