Admin edit (#997)

* allow admin edits without restrictions

* lint: err reassignment

* lint: suppress false positive

* add admin-edit to readme

* expose admin_edit to config controller
This commit is contained in:
Umputun
2021-05-10 11:36:02 -05:00
committed by GitHub
parent 6d4be02dab
commit 52fbac904e
7 changed files with 54 additions and 13 deletions
+1
View File
@@ -189,6 +189,7 @@ _this is the recommended way to run remark42_
| restricted-words | RESTRICTED_WORDS | | words banned in comments (can use `*`), _multi_ |
| restricted-names | RESTRICTED_NAMES | | names prohibited to use by the user, _multi_ |
| edit-time | EDIT_TIME | `5m` | edit window |
| admin-edit | ADMIN_EDIT | `false` | unlimited edit for admins |
| read-age | READONLY_AGE | | read-only age of comments, days |
| image-proxy.http2https | IMAGE_PROXY_HTTP2HTTPS | `false` | enable http->https proxy for images |
| image-proxy.cache-external | IMAGE_PROXY_CACHE_EXTERNAL | `false` | enable caching external images to current image storage |
+2
View File
@@ -68,6 +68,7 @@ type ServerCommand struct {
PositiveScore bool `long:"positive-score" env:"POSITIVE_SCORE" description:"enable positive score only"`
ReadOnlyAge int `long:"read-age" env:"READONLY_AGE" default:"0" description:"read-only age of comments, days"`
EditDuration time.Duration `long:"edit-time" env:"EDIT_TIME" default:"5m" description:"edit window"`
AdminEdit bool `long:"admin-edit" env:"ADMIN_EDIT" description:"unlimited edit for admins"`
Port int `long:"port" env:"REMARK_PORT" default:"8080" description:"port"`
WebRoot string `long:"web-root" env:"REMARK_WEB_ROOT" default:"./web" description:"web root directory"`
UpdateLimit float64 `long:"update-limit" env:"UPDATE_LIMIT" default:"0.5" description:"updates/sec limit"`
@@ -355,6 +356,7 @@ func (s *ServerCommand) newServerApp() (*serverApp, error) {
dataService := &service.DataStore{
Engine: storeEngine,
EditDuration: s.EditDuration,
AdminEdits: s.AdminEdit,
AdminStore: adminStore,
MaxCommentSize: s.MaxCommentSize,
MaxVotes: s.MaxVotes,
+2
View File
@@ -404,6 +404,7 @@ func (s *Rest) configCtrl(w http.ResponseWriter, r *http.Request) {
cnf := struct {
Version string `json:"version"`
EditDuration int `json:"edit_duration"`
AdminEdit bool `json:"admin_edit"`
MaxCommentSize int `json:"max_comment_size"`
Admins []string `json:"admins"`
AdminEmail string `json:"admin_email"`
@@ -421,6 +422,7 @@ func (s *Rest) configCtrl(w http.ResponseWriter, r *http.Request) {
}{
Version: s.Version,
EditDuration: int(s.DataService.EditDuration.Seconds()),
AdminEdit: s.DataService.AdminEdits,
MaxCommentSize: s.DataService.MaxCommentSize,
Admins: admins,
AdminEmail: emails,
+1
View File
@@ -166,6 +166,7 @@ func (s *private) updateCommentCtrl(w http.ResponseWriter, r *http.Request) {
Orig: edit.Text,
Summary: edit.Summary,
Delete: edit.Delete,
Admin: user.Admin,
}
res, err := s.dataService.EditComment(locator, id, editReq)
+1
View File
@@ -550,6 +550,7 @@ func TestRest_Config(t *testing.T) {
assert.Equal(t, 10.0, j["readonly_age"])
assert.Equal(t, 10000.0, j["max_image_size"])
assert.Equal(t, true, j["emoji_enabled"].(bool))
assert.Equal(t, false, j["admin_edit"].(bool))
}
func TestRest_Info(t *testing.T) {
+24 -13
View File
@@ -36,6 +36,7 @@ type DataStore struct {
TitleExtractor *TitleExtractor
RestrictedWordsMatcher *RestrictedWordsMatcher
ImageService *image.Service
AdminEdits bool // allow admin unlimited edits
// granular locks
scopedLocks struct {
@@ -433,22 +434,35 @@ type EditRequest struct {
Orig string
Summary string
Delete bool
Admin bool
}
// EditComment to edit text and update Edit info
func (s *DataStore) EditComment(locator store.Locator, commentID string, req EditRequest) (comment store.Comment, err error) {
comment, err = s.Engine.Get(engine.GetRequest{Locator: locator, CommentID: commentID})
if err != nil {
editAllowed := func(comment store.Comment) error {
if req.Admin && s.AdminEdits {
return nil
}
// edit allowed in editDuration window only
if s.EditDuration > 0 && time.Now().After(comment.Timestamp.Add(s.EditDuration)) {
return errors.Errorf("too late to edit %s", commentID)
}
// edit rejected on replayed threads
if s.HasReplies(comment) {
return errors.Errorf("parent comment with reply can't be edited, %s", commentID)
}
return nil
}
if comment, err = s.Engine.Get(engine.GetRequest{Locator: locator, CommentID: commentID}); err != nil {
return comment, err
}
// edit allowed in editDuration window only
if s.EditDuration > 0 && time.Now().After(comment.Timestamp.Add(s.EditDuration)) {
return comment, errors.Errorf("too late to edit %s", commentID)
}
if s.HasReplies(comment) {
return comment, errors.Errorf("parent comment with reply can't be edited, %s", commentID)
if err = editAllowed(comment); err != nil { //nolint gocritic
return comment, err
}
if req.Delete { // delete request
@@ -466,10 +480,7 @@ func (s *DataStore) EditComment(locator store.Locator, commentID string, req Edi
comment.Text = req.Text
comment.Orig = req.Orig
comment.Edit = &store.Edit{
Timestamp: time.Now(),
Summary: req.Summary,
}
comment.Edit = &store.Edit{Timestamp: time.Now(), Summary: req.Summary}
comment.Locator = locator
comment.Sanitize()
+23
View File
@@ -742,6 +742,29 @@ func TestService_EditCommentReplyFailed(t *testing.T) {
assert.EqualError(t, err, "parent comment with reply can't be edited, id-1")
}
func TestService_EditCommentAdmin(t *testing.T) {
eng, teardown := prepStoreEngine(t)
defer teardown()
b := DataStore{Engine: eng, EditDuration: 100 * time.Millisecond,
AdminStore: admin.NewStaticKeyStore("secret 123"), AdminEdits: true}
res, err := b.Last("radio-t", 0, time.Time{}, store.User{})
t.Logf("%+v", res[0])
assert.NoError(t, err)
require.Equal(t, 2, len(res))
assert.Nil(t, res[0].Edit)
time.Sleep(time.Second)
_, err = b.EditComment(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID,
EditRequest{Orig: "yyy", Text: "xxx", Summary: "my edit", Admin: true})
assert.NoError(t, err)
_, err = b.EditComment(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID,
EditRequest{Orig: "yyy", Text: "xxx", Summary: "my edit", Admin: false})
assert.Error(t, err)
}
func TestService_ValidateComment(t *testing.T) {
b := DataStore{MaxCommentSize: 2000, AdminStore: admin.NewStaticKeyStore("secret 123")}