Skip to content

Commit 8156057

Browse files
6543zeripath
authored andcommitted
Enhance Ghost comment mitigation Settings (go-gitea#14392)
* refactor models.DeleteComment and delete related reactions too * use deleteComment for UserDeleteWithCommentsMaxDays in DeleteUser * nits * Use time.Duration as other time settings have * docs * Resolve Fixme & fix potential deadlock * Disabled by Default * Update Config Value Description * switch args * Update models/issue_comment.go Co-authored-by: zeripath <[email protected]> Co-authored-by: zeripath <[email protected]>
1 parent 88925d5 commit 8156057

File tree

12 files changed

+54
-32
lines changed

12 files changed

+54
-32
lines changed

custom/conf/app.example.ini

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -671,9 +671,8 @@ AUTO_WATCH_NEW_REPOS = true
671671
; Default value for AutoWatchOnChanges
672672
; Make the user watch a repository When they commit for the first time
673673
AUTO_WATCH_ON_CHANGES = false
674-
; Default value for the minimum age a user has to exist before deletion to keep issue comments.
675-
; If a user deletes his account before that amount of days, his comments will be deleted as well.
676-
USER_DELETE_WITH_COMMENTS_MAX_DAYS = 0
674+
; Minimum amount of time a user must exist before comments are kept when the user is deleted.
675+
USER_DELETE_WITH_COMMENTS_MAX_TIME = 0
677676

678677
[webhook]
679678
; Hook task queue length, increase if webhook shooting starts hanging

docs/content/doc/advanced/config-cheat-sheet.en-us.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ relation to port exhaustion.
474474
- `ALLOW_ONLY_EXTERNAL_REGISTRATION`: **false** Set to true to force registration only using third-party services.
475475
- `NO_REPLY_ADDRESS`: **DOMAIN** Default value for the domain part of the user's email address in the git log if he has set KeepEmailPrivate to true.
476476
The user's email will be replaced with a concatenation of the user name in lower case, "@" and NO_REPLY_ADDRESS.
477-
- `USER_DELETE_WITH_COMMENTS_MAX_DAYS`: **0** If a user deletes his account before that amount of days, his comments will be deleted as well.
477+
- `USER_DELETE_WITH_COMMENTS_MAX_TIME`: **0** Minimum amount of time a user must exist before comments are kept when the user is deleted.
478478

479479
## SSH Minimum Key Sizes (`ssh.minimum_key_sizes`)
480480

models/issue_comment.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,37 +1059,41 @@ func UpdateComment(c *Comment, doer *User) error {
10591059
}
10601060

10611061
// DeleteComment deletes the comment
1062-
func DeleteComment(comment *Comment, doer *User) error {
1062+
func DeleteComment(comment *Comment) error {
10631063
sess := x.NewSession()
10641064
defer sess.Close()
10651065
if err := sess.Begin(); err != nil {
10661066
return err
10671067
}
10681068

1069-
if _, err := sess.Delete(&Comment{
1069+
if err := deleteComment(sess, comment); err != nil {
1070+
return err
1071+
}
1072+
1073+
return sess.Commit()
1074+
}
1075+
1076+
func deleteComment(e Engine, comment *Comment) error {
1077+
if _, err := e.Delete(&Comment{
10701078
ID: comment.ID,
10711079
}); err != nil {
10721080
return err
10731081
}
10741082

10751083
if comment.Type == CommentTypeComment {
1076-
if _, err := sess.Exec("UPDATE `issue` SET num_comments = num_comments - 1 WHERE id = ?", comment.IssueID); err != nil {
1084+
if _, err := e.Exec("UPDATE `issue` SET num_comments = num_comments - 1 WHERE id = ?", comment.IssueID); err != nil {
10771085
return err
10781086
}
10791087
}
1080-
if _, err := sess.Where("comment_id = ?", comment.ID).Cols("is_deleted").Update(&Action{IsDeleted: true}); err != nil {
1088+
if _, err := e.Where("comment_id = ?", comment.ID).Cols("is_deleted").Update(&Action{IsDeleted: true}); err != nil {
10811089
return err
10821090
}
10831091

1084-
if err := comment.neuterCrossReferences(sess); err != nil {
1092+
if err := comment.neuterCrossReferences(e); err != nil {
10851093
return err
10861094
}
10871095

1088-
if err := deleteReaction(sess, &ReactionOptions{Comment: comment}); err != nil {
1089-
return err
1090-
}
1091-
1092-
return sess.Commit()
1096+
return deleteReaction(e, &ReactionOptions{Comment: comment})
10931097
}
10941098

10951099
// CodeComments represents comments on code by using this structure: FILENAME -> LINE (+ == proposed; - == previous) -> COMMENTS

models/user.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,12 +1140,30 @@ func deleteUser(e Engine, u *User) error {
11401140
return fmt.Errorf("deleteBeans: %v", err)
11411141
}
11421142

1143-
if setting.Service.UserDeleteWithCommentsMaxDays != 0 &&
1144-
u.CreatedUnix.AsTime().Add(time.Duration(setting.Service.UserDeleteWithCommentsMaxDays)*24*time.Hour).After(time.Now()) {
1145-
if err = deleteBeans(e,
1146-
&Comment{PosterID: u.ID},
1147-
); err != nil {
1148-
return fmt.Errorf("deleteBeans: %v", err)
1143+
if setting.Service.UserDeleteWithCommentsMaxTime != 0 &&
1144+
u.CreatedUnix.AsTime().Add(setting.Service.UserDeleteWithCommentsMaxTime).After(time.Now()) {
1145+
1146+
// Delete Comments
1147+
const batchSize = 50
1148+
for start := 0; ; start += batchSize {
1149+
comments := make([]*Comment, 0, batchSize)
1150+
if err = e.Where("type=? AND poster_id=?", CommentTypeComment, u.ID).Limit(batchSize, start).Find(&comments); err != nil {
1151+
return err
1152+
}
1153+
if len(comments) == 0 {
1154+
break
1155+
}
1156+
1157+
for _, comment := range comments {
1158+
if err = deleteComment(e, comment); err != nil {
1159+
return err
1160+
}
1161+
}
1162+
}
1163+
1164+
// Delete Reactions
1165+
if err = deleteReaction(e, &ReactionOptions{Doer: u}); err != nil {
1166+
return err
11491167
}
11501168
}
11511169

modules/setting/service.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package setting
66

77
import (
88
"regexp"
9+
"time"
910

1011
"code.gitea.io/gitea/modules/structs"
1112
)
@@ -49,7 +50,7 @@ var Service struct {
4950
AutoWatchNewRepos bool
5051
AutoWatchOnChanges bool
5152
DefaultOrgMemberVisible bool
52-
UserDeleteWithCommentsMaxDays int
53+
UserDeleteWithCommentsMaxTime time.Duration
5354

5455
// OpenID settings
5556
EnableOpenIDSignIn bool
@@ -97,7 +98,7 @@ func newService() {
9798
Service.DefaultOrgVisibility = sec.Key("DEFAULT_ORG_VISIBILITY").In("public", structs.ExtractKeysFromMapString(structs.VisibilityModes))
9899
Service.DefaultOrgVisibilityMode = structs.VisibilityModes[Service.DefaultOrgVisibility]
99100
Service.DefaultOrgMemberVisible = sec.Key("DEFAULT_ORG_MEMBER_VISIBLE").MustBool()
100-
Service.UserDeleteWithCommentsMaxDays = sec.Key("USER_DELETE_WITH_COMMENTS_MAX_DAYS").MustInt(0)
101+
Service.UserDeleteWithCommentsMaxTime = sec.Key("USER_DELETE_WITH_COMMENTS_MAX_TIME").MustDuration(0)
101102

102103
sec = Cfg.Section("openid")
103104
Service.EnableOpenIDSignIn = sec.Key("ENABLE_OPENID_SIGNIN").MustBool(!InstallLock)

options/locale/locale_de-DE.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ repos_none=Du besitzt keine Repositories
626626

627627
delete_account=Konto löschen
628628
delete_prompt=Wenn du fortfährst, wird dein Account permanent gelöscht. Dies <strong>KANN NICHT</strong> rückgängig gemacht werden.
629-
delete_with_all_comments = Dein Account ist jünger als %d Tage. Um Geisterkommentare zu vermeiden, werden alle Issue/PR-Kommentare zusammen mit deinem Benutzeraccount gelöscht.
629+
delete_with_all_comments = Dein Account ist jünger als %s. Um Geisterkommentare zu vermeiden, werden alle Issue/PR-Kommentare zusammen mit deinem Benutzeraccount gelöscht.
630630
confirm_delete_account=Löschen bestätigen
631631
delete_account_title=Benutzerkonto löschen
632632
delete_account_desc=Bist du sicher, dass du diesen Account dauerhaft löschen möchtest?

options/locale/locale_en-US.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,7 @@ repos_none = You do not own any repositories
640640

641641
delete_account = Delete Your Account
642642
delete_prompt = This operation will permanently delete your user account. It <strong>CAN NOT</strong> be undone.
643-
delete_with_all_comments = Your account is younger than %d days. To avoid ghost comments, all issue/PR comments will be deleted with it.
643+
delete_with_all_comments = Your account is younger than %s. To avoid ghost comments, all issue/PR comments will be deleted with it.
644644
confirm_delete_account = Confirm Deletion
645645
delete_account_title = Delete User Account
646646
delete_account_desc = Are you sure you want to permanently delete this user account?

routers/api/v1/repo/issue_comment.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ func deleteIssueComment(ctx *context.APIContext) {
508508
return
509509
}
510510

511-
if err = comment_service.DeleteComment(comment, ctx.User); err != nil {
511+
if err = comment_service.DeleteComment(ctx.User, comment); err != nil {
512512
ctx.Error(http.StatusInternalServerError, "DeleteCommentByID", err)
513513
return
514514
}

routers/repo/issue.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2240,7 +2240,7 @@ func DeleteComment(ctx *context.Context) {
22402240
return
22412241
}
22422242

2243-
if err = comment_service.DeleteComment(comment, ctx.User); err != nil {
2243+
if err = comment_service.DeleteComment(ctx.User, comment); err != nil {
22442244
ctx.ServerError("DeleteCommentByID", err)
22452245
return
22462246
}

routers/user/setting/account.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,8 @@ func loadAccountData(ctx *context.Context) {
303303
ctx.Data["ActivationsPending"] = pendingActivation
304304
ctx.Data["CanAddEmails"] = !pendingActivation || !setting.Service.RegisterEmailConfirm
305305

306-
if setting.Service.UserDeleteWithCommentsMaxDays != 0 {
307-
ctx.Data["UserDeleteWithCommentsMaxDays"] = setting.Service.UserDeleteWithCommentsMaxDays
308-
ctx.Data["UserDeleteWithComments"] = ctx.User.CreatedUnix.AsTime().Add(time.Duration(setting.Service.UserDeleteWithCommentsMaxDays) * 24 * time.Hour).After(time.Now())
306+
if setting.Service.UserDeleteWithCommentsMaxTime != 0 {
307+
ctx.Data["UserDeleteWithCommentsMaxTime"] = setting.Service.UserDeleteWithCommentsMaxTime.String()
308+
ctx.Data["UserDeleteWithComments"] = ctx.User.CreatedUnix.AsTime().Add(setting.Service.UserDeleteWithCommentsMaxTime).After(time.Now())
309309
}
310310
}

services/comments/comments.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ func UpdateComment(c *models.Comment, doer *models.User, oldContent string) erro
4343
}
4444

4545
// DeleteComment deletes the comment
46-
func DeleteComment(comment *models.Comment, doer *models.User) error {
47-
if err := models.DeleteComment(comment, doer); err != nil {
46+
func DeleteComment(doer *models.User, comment *models.Comment) error {
47+
if err := models.DeleteComment(comment); err != nil {
4848
return err
4949
}
5050

templates/user/settings/account.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@
174174
<div class="ui red message">
175175
<p class="text left">{{svg "octicon-alert"}} {{.i18n.Tr "settings.delete_prompt" | Str2html}}</p>
176176
{{ if .UserDeleteWithComments }}
177-
<p class="text left" style="font-weight: bold;">{{.i18n.Tr "settings.delete_with_all_comments" .UserDeleteWithCommentsMaxDays | Str2html}}</p>
177+
<p class="text left" style="font-weight: bold;">{{.i18n.Tr "settings.delete_with_all_comments" .UserDeleteWithCommentsMaxTime | Str2html}}</p>
178178
{{ end }}
179179
</div>
180180
<form class="ui form ignore-dirty" id="delete-form" action="{{AppSubUrl}}/user/settings/account/delete" method="post">

0 commit comments

Comments
 (0)