[GITEA] Fix panic in canSoftDeleteContentHistory

- Backport of #2134
- 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.

(cherry picked from commit 0b5db0dcc6)
This commit is contained in:
Gusted 2024-01-13 11:55:33 +01:00
parent bd3e5109ba
commit ab1ccc55dc
No known key found for this signature in database
GPG key ID: FD821B732837125F
3 changed files with 71 additions and 0 deletions

View file

@ -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:

View file

@ -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

View file

@ -727,3 +727,55 @@ func TestAbsoluteReferenceURL(t *testing.T) {
assert.EqualValues(t, setting.AppURL+"user2/repo1/issues/1#issuecomment-2", referenceURL) assert.EqualValues(t, setting.AppURL+"user2/repo1/issues/1#issuecomment-2", referenceURL)
}) })
} }
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)
})
}