mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-25 23:05:39 +03:00
[GITEA] Fix panic in canSoftDeleteContentHistory
- It's possible that `canSoftDeleteContentHistory` is called without `ctx.Doer` being set, such as an anonymous user requesting the `/content-history/detail` endpoint. - Add a simple condition to always set to `canSoftDelete` to false if an anonymous user is requesting this, this avoids a panic in the code that assumes `ctx.Doer` is set. - Added integration testing.
This commit is contained in:
parent
06d419d3ff
commit
0b5db0dcc6
3 changed files with 71 additions and 0 deletions
|
@ -94,6 +94,8 @@ func canSoftDeleteContentHistory(ctx *context.Context, issue *issues_model.Issue
|
||||||
// CanWrite means the doer can manage the issue/PR list
|
// CanWrite means the doer can manage the issue/PR list
|
||||||
if ctx.Repo.IsOwner() || ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
|
if ctx.Repo.IsOwner() || ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
|
||||||
canSoftDelete = true
|
canSoftDelete = true
|
||||||
|
} else if ctx.Doer == nil {
|
||||||
|
canSoftDelete = false
|
||||||
} else {
|
} else {
|
||||||
// for read-only users, they could still post issues or comments,
|
// for read-only users, they could still post issues or comments,
|
||||||
// they should be able to delete the history related to their own issue/comment, a case is:
|
// they should be able to delete the history related to their own issue/comment, a case is:
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
-
|
||||||
|
id: 1
|
||||||
|
issue_id: 1
|
||||||
|
comment_id: 3
|
||||||
|
edited_unix: 1687612839
|
||||||
|
content_text: Original Text
|
||||||
|
is_first_created: true
|
||||||
|
is_deleted: false
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 2
|
||||||
|
issue_id: 1
|
||||||
|
comment_id: 3
|
||||||
|
edited_unix: 1687612840
|
||||||
|
content_text: "meh..." # This has to be consistent with comment.yml
|
||||||
|
is_first_created: false
|
||||||
|
is_deleted: false
|
|
@ -718,3 +718,55 @@ func TestIssueReferenceURL(t *testing.T) {
|
||||||
ref, _ = htmlDoc.Find(`.timeline-item.comment:not(.first) .reference-issue`).Attr("data-reference")
|
ref, _ = htmlDoc.Find(`.timeline-item.comment:not(.first) .reference-issue`).Attr("data-reference")
|
||||||
assert.EqualValues(t, "/user2/repo1/issues/1#issuecomment-2", ref)
|
assert.EqualValues(t, "/user2/repo1/issues/1#issuecomment-2", ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetContentHistory(t *testing.T) {
|
||||||
|
defer tests.AddFixtures("tests/integration/fixtures/TestGetContentHistory/")()
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
|
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
|
||||||
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID})
|
||||||
|
issueURL := fmt.Sprintf("%s/issues/%d", repo.FullName(), issue.Index)
|
||||||
|
contentHistory := unittest.AssertExistsAndLoadBean(t, &issues_model.ContentHistory{ID: 2, IssueID: issue.ID})
|
||||||
|
contentHistoryURL := fmt.Sprintf("%s/issues/%d/content-history/detail?comment_id=%d&history_id=%d", repo.FullName(), issue.Index, contentHistory.CommentID, contentHistory.ID)
|
||||||
|
|
||||||
|
type contentHistoryResp struct {
|
||||||
|
CanSoftDelete bool `json:"canSoftDelete"`
|
||||||
|
HistoryID int `json:"historyId"`
|
||||||
|
PrevHistoryID int `json:"prevHistoryId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
testCase := func(t *testing.T, session *TestSession, canDelete bool) {
|
||||||
|
t.Helper()
|
||||||
|
contentHistoryURL := contentHistoryURL + "&_csrf=" + GetCSRF(t, session, issueURL)
|
||||||
|
|
||||||
|
req := NewRequest(t, "GET", contentHistoryURL)
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
var respJSON contentHistoryResp
|
||||||
|
DecodeJSON(t, resp, &respJSON)
|
||||||
|
|
||||||
|
assert.EqualValues(t, canDelete, respJSON.CanSoftDelete)
|
||||||
|
assert.EqualValues(t, contentHistory.ID, respJSON.HistoryID)
|
||||||
|
assert.EqualValues(t, contentHistory.ID-1, respJSON.PrevHistoryID)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("Anonymous", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
testCase(t, emptyTestSession(t), false)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Another user", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
testCase(t, loginUser(t, "user8"), false)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Repo owner", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
testCase(t, loginUser(t, "user2"), true)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Poster", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
testCase(t, loginUser(t, "user5"), true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue