comments: added immutable parameter to forbid editing/deleting certain comments

This commit is contained in:
Marcin Kuzminski 2020-05-05 11:36:32 +02:00
parent 0cc11b1d08
commit ef2b6c8a1c
8 changed files with 96 additions and 9 deletions

View file

@ -48,7 +48,7 @@ PYRAMID_SETTINGS = {}
EXTENSIONS = {}
__version__ = ('.'.join((str(each) for each in VERSION[:3])))
__dbversion__ = 105 # defines current db version for migrations
__dbversion__ = 106 # defines current db version for migrations
__platform__ = platform.system()
__license__ = 'AGPLv3, and Commercial License'
__author__ = 'RhodeCode GmbH'

View file

@ -220,7 +220,7 @@ def db():
'pr_comment_url': 'http://comment-url',
'pr_comment_reply_url': 'http://comment-url#reply',
'comment_file': 'rhodecode/model/db.py',
'comment_file': 'rhodecode/model/get_flow_commits',
'comment_line': 'o1210',
'comment_type': 'todo',
'comment_body': '''

View file

@ -22,8 +22,7 @@ import pytest
from rhodecode.tests import TestController
from rhodecode.model.db import (
ChangesetComment, Notification, UserNotification)
from rhodecode.model.db import ChangesetComment, Notification
from rhodecode.model.meta import Session
from rhodecode.lib import helpers as h
@ -269,7 +268,36 @@ class TestRepoCommitCommentsView(TestController):
repo_name=backend.repo_name, commit_id=commit_id))
assert_comment_links(response, 0, 0)
@pytest.mark.parametrize('renderer, input, output', [
def test_delete_forbidden_for_immutable_comments(self, backend):
self.log_user()
commit_id = backend.repo.get_commit('300').raw_id
text = u'CommentOnCommit'
params = {'text': text, 'csrf_token': self.csrf_token}
self.app.post(
route_path(
'repo_commit_comment_create',
repo_name=backend.repo_name, commit_id=commit_id),
params=params)
comments = ChangesetComment.query().all()
assert len(comments) == 1
comment_id = comments[0].comment_id
comment = ChangesetComment.get(comment_id)
comment.immutable_state = ChangesetComment.OP_IMMUTABLE
Session().add(comment)
Session().commit()
self.app.post(
route_path('repo_commit_comment_delete',
repo_name=backend.repo_name,
commit_id=commit_id,
comment_id=comment_id),
params={'csrf_token': self.csrf_token},
status=403)
@pytest.mark.parametrize('renderer, text_input, output', [
('rst', 'plain text', '<p>plain text</p>'),
('rst', 'header\n======', '<h1 class="title">header</h1>'),
('rst', '*italics*', '<em>italics</em>'),
@ -280,11 +308,11 @@ class TestRepoCommitCommentsView(TestController):
('markdown', '**bold**', '<strong>bold</strong>'),
], ids=['rst-plain', 'rst-header', 'rst-italics', 'rst-bold', 'md-plain',
'md-header', 'md-italics', 'md-bold', ])
def test_preview(self, renderer, input, output, backend, xhr_header):
def test_preview(self, renderer, text_input, output, backend, xhr_header):
self.log_user()
params = {
'renderer': renderer,
'text': input,
'text': text_input,
'csrf_token': self.csrf_token
}
commit_id = '0' * 16 # fake this for tests

View file

@ -22,7 +22,7 @@
import logging
import collections
from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound
from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound, HTTPForbidden
from pyramid.view import view_config
from pyramid.renderers import render
from pyramid.response import Response
@ -538,6 +538,10 @@ class RepoCommitsView(RepoAppView):
# comment already deleted in another call probably
return True
if comment.immutable:
# don't allow deleting comments that are immutable
raise HTTPForbidden()
is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name)
super_admin = h.HasPermissionAny('hg.admin')()
comment_owner = (comment.author.user_id == self._rhodecode_db_user.user_id)

View file

@ -1473,6 +1473,10 @@ class RepoPullRequestsView(RepoAppView, DataGridAppView):
self.request.matchdict['comment_id'])
comment_id = comment.comment_id
if comment.immutable:
# don't allow deleting comments that are immutable
raise HTTPForbidden()
if pull_request.is_closed():
log.debug('comment: forbidden because pull request is closed')
raise HTTPForbidden()

View file

@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
import logging
from sqlalchemy import *
from alembic.migration import MigrationContext
from alembic.operations import Operations
from sqlalchemy import BigInteger
from rhodecode.lib.dbmigrate.versions import _reset_base
from rhodecode.model import init_model_encryption
log = logging.getLogger(__name__)
def upgrade(migrate_engine):
"""
Upgrade operations go here.
Don't create your own engine; bind migrate_engine to your metadata
"""
_reset_base(migrate_engine)
from rhodecode.lib.dbmigrate.schema import db_4_18_0_1 as db
init_model_encryption(db)
context = MigrationContext.configure(migrate_engine.connect())
op = Operations(context)
comments = db.ChangesetComment.__table__
with op.batch_alter_table(comments.name) as batch_op:
new_column = Column('immutable_state', Unicode(128), nullable=True)
batch_op.add_column(new_column)
def downgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
def fixups(models, _SESSION):
pass

View file

@ -3711,6 +3711,9 @@ class ChangesetComment(Base, BaseModel):
COMMENT_TYPE_TODO = u'todo'
COMMENT_TYPES = [COMMENT_TYPE_NOTE, COMMENT_TYPE_TODO]
OP_IMMUTABLE = u'immutable'
OP_CHANGEABLE = u'changeable'
comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
revision = Column('revision', String(40), nullable=True)
@ -3725,6 +3728,7 @@ class ChangesetComment(Base, BaseModel):
modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
renderer = Column('renderer', Unicode(64), nullable=True)
display_state = Column('display_state', Unicode(128), nullable=True)
immutable_state = Column('immutable_state', Unicode(128), nullable=True, default=OP_CHANGEABLE)
comment_type = Column('comment_type', Unicode(128), nullable=True, default=COMMENT_TYPE_NOTE)
resolved_comment_id = Column('resolved_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'), nullable=True)
@ -3767,6 +3771,10 @@ class ChangesetComment(Base, BaseModel):
def outdated(self):
return self.display_state == self.COMMENT_OUTDATED
@property
def immutable(self):
return self.immutable_state == self.OP_IMMUTABLE
def outdated_at_version(self, version):
"""
Checks if comment is outdated for given pull request version

View file

@ -135,7 +135,7 @@
## only super-admin, repo admin OR comment owner can delete, also hide delete if currently viewed comment is outdated
%if not outdated_at_ver and (not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed())):
## permissions to delete
%if c.is_super_admin or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id:
%if comment.immutable is False and (c.is_super_admin or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id):
## TODO: dan: add edit comment here
<a onclick="return Rhodecode.comments.deleteComment(this);" class="delete-comment"> ${_('Delete')}</a>
%else: