##// END OF EJS Templates
audit-logs: implemented pull request and comment events.
marcink -
r1807:83e09901 default
parent child Browse files
Show More
@@ -47,13 +47,12 b' class TestClosePullRequest(object):'
47 47 'closed': True,
48 48 }
49 49 assert_ok(id_, expected, response.body)
50 action = 'user_closed_pull_request:%d' % pull_request_id
51 50 journal = UserLog.query()\
52 .filter(UserLog.user_id == author)\
51 .filter(UserLog.user_id == author) \
52 .order_by('user_log_id') \
53 53 .filter(UserLog.repository_id == repo)\
54 .filter(UserLog.action == action)\
55 54 .all()
56 assert len(journal) == 1
55 assert journal[-1].action == 'repo.pull_request.close'
57 56
58 57 @pytest.mark.backends("git", "hg")
59 58 def test_api_close_pull_request_already_closed_error(self, pr_util):
@@ -62,13 +62,12 b' class TestCommentPullRequest(object):'
62 62 }
63 63 assert_ok(id_, expected, response.body)
64 64
65 action = 'user_commented_pull_request:%d' % pull_request_id
66 65 journal = UserLog.query()\
67 66 .filter(UserLog.user_id == author)\
68 .filter(UserLog.repository_id == repo)\
69 .filter(UserLog.action == action)\
67 .filter(UserLog.repository_id == repo) \
68 .order_by('user_log_id') \
70 69 .all()
71 assert len(journal) == 2
70 assert journal[-1].action == 'repo.pull_request.comment.create'
72 71
73 72 @pytest.mark.backends("git", "hg")
74 73 def test_api_comment_pull_request_change_status(
@@ -33,7 +33,7 b' pytestmark = pytest.mark.backends("git",'
33 33 @pytest.mark.usefixtures("testuser_api", "app")
34 34 class TestGetPullRequest(object):
35 35
36 def test_api_get_pull_request(self, pr_util, http_host_stub, http_host_only_stub):
36 def test_api_get_pull_request(self, pr_util, http_host_only_stub):
37 37 from rhodecode.model.pull_request import PullRequestModel
38 38 pull_request = pr_util.create_pull_request(mergeable=True)
39 39 id_, params = build_data(
@@ -52,7 +52,7 b' class TestGetPullRequest(object):'
52 52 pull_request_id=pull_request.pull_request_id, qualified=True))
53 53
54 54 pr_url = safe_unicode(
55 url_obj.with_netloc(http_host_stub))
55 url_obj.with_netloc(http_host_only_stub))
56 56 source_url = safe_unicode(
57 57 pull_request.source_repo.clone_url().with_netloc(http_host_only_stub))
58 58 target_url = safe_unicode(
@@ -95,13 +95,13 b' class TestMergePullRequest(object):'
95 95
96 96 assert_ok(id_, expected, response.body)
97 97
98 action = 'user_merged_pull_request:%d' % (pull_request_id, )
99 98 journal = UserLog.query()\
100 99 .filter(UserLog.user_id == author)\
101 .filter(UserLog.repository_id == repo)\
102 .filter(UserLog.action == action)\
100 .filter(UserLog.repository_id == repo) \
101 .order_by('user_log_id') \
103 102 .all()
104 assert len(journal) == 1
103 assert journal[-2].action == 'repo.pull_request.merge'
104 assert journal[-1].action == 'repo.pull_request.close'
105 105
106 106 id_, params = build_data(
107 107 self.apikey, 'merge_pull_request',
@@ -33,7 +33,7 b' class TestUpdatePullRequest(object):'
33 33
34 34 @pytest.mark.backends("git", "hg")
35 35 def test_api_update_pull_request_title_or_description(
36 self, pr_util, silence_action_logger, no_notifications):
36 self, pr_util, no_notifications):
37 37 pull_request = pr_util.create_pull_request()
38 38
39 39 id_, params = build_data(
@@ -61,7 +61,7 b' class TestUpdatePullRequest(object):'
61 61
62 62 @pytest.mark.backends("git", "hg")
63 63 def test_api_try_update_closed_pull_request(
64 self, pr_util, silence_action_logger, no_notifications):
64 self, pr_util, no_notifications):
65 65 pull_request = pr_util.create_pull_request()
66 66 PullRequestModel().close_pull_request(
67 67 pull_request, TEST_USER_ADMIN_LOGIN)
@@ -78,8 +78,7 b' class TestUpdatePullRequest(object):'
78 78 assert_error(id_, expected, response.body)
79 79
80 80 @pytest.mark.backends("git", "hg")
81 def test_api_update_update_commits(
82 self, pr_util, silence_action_logger, no_notifications):
81 def test_api_update_update_commits(self, pr_util, no_notifications):
83 82 commits = [
84 83 {'message': 'a'},
85 84 {'message': 'b', 'added': [FileNode('file_b', 'test_content\n')]},
@@ -119,7 +118,7 b' class TestUpdatePullRequest(object):'
119 118
120 119 @pytest.mark.backends("git", "hg")
121 120 def test_api_update_change_reviewers(
122 self, user_util, pr_util, silence_action_logger, no_notifications):
121 self, user_util, pr_util, no_notifications):
123 122 a = user_util.create_user()
124 123 b = user_util.create_user()
125 124 c = user_util.create_user()
@@ -669,7 +669,7 b' def update_pull_request('
669 669 if title or description:
670 670 PullRequestModel().edit(
671 671 pull_request, title or pull_request.title,
672 description or pull_request.description)
672 description or pull_request.description, apiuser)
673 673 Session().commit()
674 674
675 675 commit_changes = {"added": [], "common": [], "removed": []}
@@ -683,7 +683,7 b' def update_pull_request('
683 683 reviewers_changes = {"added": [], "removed": []}
684 684 if reviewers:
685 685 added_reviewers, removed_reviewers = \
686 PullRequestModel().update_reviewers(pull_request, reviewers)
686 PullRequestModel().update_reviewers(pull_request, reviewers, apiuser)
687 687
688 688 reviewers_changes['added'] = sorted(
689 689 [get_user_or_error(n).username for n in added_reviewers])
@@ -628,8 +628,8 b' class UsersController(BaseController):'
628 628
629 629 ip_id = request.POST.get('del_ip_id')
630 630 user_model = UserModel()
631 user_data = c.user.get_api_data()
631 632 ip = UserIpMap.query().get(ip_id).ip_addr
632 user_data = c.user.get_api_data()
633 633 user_model.delete_extra_ip(user_id, ip_id)
634 634 audit_logger.store_web(
635 635 'user.edit.ip.delete',
@@ -440,7 +440,7 b' class ChangesetController(BaseRepoContro'
440 440 owner = (comment.author.user_id == c.rhodecode_user.user_id)
441 441 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(c.repo_name)
442 442 if h.HasPermissionAny('hg.admin')() or is_repo_admin or owner:
443 CommentsModel().delete(comment=comment)
443 CommentsModel().delete(comment=comment, user=c.rhodecode_user)
444 444 Session().commit()
445 445 return True
446 446 else:
@@ -38,7 +38,7 b' from rhodecode.lib import diffs, helpers'
38 38 from rhodecode.lib import audit_logger
39 39 from rhodecode.lib.codeblocks import (
40 40 filenode_as_lines_tokens, filenode_as_annotated_lines_tokens)
41 from rhodecode.lib.utils import jsonify, action_logger
41 from rhodecode.lib.utils import jsonify
42 42 from rhodecode.lib.utils2 import (
43 43 convert_line_endings, detect_mode, safe_str, str2bool)
44 44 from rhodecode.lib.auth import (
@@ -317,7 +317,7 b' class PullrequestsController(BaseRepoCon'
317 317 try:
318 318 PullRequestModel().edit(
319 319 pull_request, request.POST.get('title'),
320 request.POST.get('description'))
320 request.POST.get('description'), c.rhodecode_user)
321 321 except ValueError:
322 322 msg = _(u'Cannot update closed pull requests.')
323 323 h.flash(msg, category='error')
@@ -456,7 +456,8 b' class PullrequestsController(BaseRepoCon'
456 456 h.flash(e, category='error')
457 457 return
458 458
459 PullRequestModel().update_reviewers(pull_request_id, reviewers)
459 PullRequestModel().update_reviewers(
460 pull_request_id, reviewers, c.rhodecode_user)
460 461 h.flash(_('Pull request reviewers updated.'), category='success')
461 462 Session().commit()
462 463
@@ -476,7 +477,7 b' class PullrequestsController(BaseRepoCon'
476 477
477 478 # only owner can delete it !
478 479 if allowed_to_delete:
479 PullRequestModel().delete(pull_request)
480 PullRequestModel().delete(pull_request, c.rhodecode_user)
480 481 Session().commit()
481 482 h.flash(_('Successfully deleted pull request'),
482 483 category='success')
@@ -997,7 +998,7 b' class PullrequestsController(BaseRepoCon'
997 998 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(c.repo_name)
998 999 if h.HasPermissionAny('hg.admin')() or is_repo_admin or is_owner:
999 1000 old_calculated_status = co.pull_request.calculated_review_status()
1000 CommentsModel().delete(comment=co)
1001 CommentsModel().delete(comment=co, user=c.rhodecode_user)
1001 1002 Session().commit()
1002 1003 calculated_status = co.pull_request.calculated_review_status()
1003 1004 if old_calculated_status != calculated_status:
@@ -28,7 +28,7 b' from rhodecode.model.db import User, Use'
28 28 log = logging.getLogger(__name__)
29 29
30 30 # action as key, and expected action_data as value
31 ACTIONS = {
31 ACTIONS_V1 = {
32 32 'user.login.success': {'user_agent': ''},
33 33 'user.login.failure': {'user_agent': ''},
34 34 'user.logout': {'user_agent': ''},
@@ -64,11 +64,28 b' ACTIONS = {'
64 64 'repo.commit.strip': {},
65 65 'repo.archive.download': {},
66 66
67 'repo.pull_request.create': '',
68 'repo.pull_request.edit': '',
69 'repo.pull_request.delete': '',
70 'repo.pull_request.close': '',
71 'repo.pull_request.merge': '',
72 'repo.pull_request.vote': '',
73 'repo.pull_request.comment.create': '',
74 'repo.pull_request.comment.delete': '',
75
76 'repo.pull_request.reviewer.add': '',
77 'repo.pull_request.reviewer.delete': '',
78
79 'repo.commit.comment.create': '',
80 'repo.commit.comment.delete': '',
81 'repo.commit.vote': '',
82
67 83 'repo_group.create': {'data': {}},
68 84 'repo_group.edit': {'old_data': {}},
69 85 'repo_group.edit.permissions': {},
70 86 'repo_group.delete': {'old_data': {}},
71 87 }
88 ACTIONS = ACTIONS_V1
72 89
73 90 SOURCE_WEB = 'source_web'
74 91 SOURCE_API = 'source_api'
@@ -139,68 +139,6 b' def get_user_group_slug(request):'
139 139 return _group
140 140
141 141
142 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
143 """
144 Action logger for various actions made by users
145
146 :param user: user that made this action, can be a unique username string or
147 object containing user_id attribute
148 :param action: action to log, should be on of predefined unique actions for
149 easy translations
150 :param repo: string name of repository or object containing repo_id,
151 that action was made on
152 :param ipaddr: optional ip address from what the action was made
153 :param sa: optional sqlalchemy session
154
155 """
156
157 if not sa:
158 sa = meta.Session()
159 # if we don't get explicit IP address try to get one from registered user
160 # in tmpl context var
161 if not ipaddr:
162 ipaddr = getattr(get_current_rhodecode_user(), 'ip_addr', '')
163
164 try:
165 if getattr(user, 'user_id', None):
166 user_obj = User.get(user.user_id)
167 elif isinstance(user, basestring):
168 user_obj = User.get_by_username(user)
169 else:
170 raise Exception('You have to provide a user object or a username')
171
172 if getattr(repo, 'repo_id', None):
173 repo_obj = Repository.get(repo.repo_id)
174 repo_name = repo_obj.repo_name
175 elif isinstance(repo, basestring):
176 repo_name = repo.lstrip('/')
177 repo_obj = Repository.get_by_repo_name(repo_name)
178 else:
179 repo_obj = None
180 repo_name = ''
181
182 user_log = UserLog()
183 user_log.user_id = user_obj.user_id
184 user_log.username = user_obj.username
185 action = safe_unicode(action)
186 user_log.action = action[:1200000]
187
188 user_log.repository = repo_obj
189 user_log.repository_name = repo_name
190
191 user_log.action_date = datetime.datetime.now()
192 user_log.user_ip = ipaddr
193 sa.add(user_log)
194
195 log.info('Logging action:`%s` on repo:`%s` by user:%s ip:%s',
196 action, safe_unicode(repo), user_obj, ipaddr)
197 if commit:
198 sa.commit()
199 except Exception:
200 log.error(traceback.format_exc())
201 raise
202
203
204 142 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
205 143 """
206 144 Scans given path for repos and return (name,(type,path)) tuple
@@ -34,8 +34,8 b' from sqlalchemy.sql.expression import nu'
34 34 from sqlalchemy.sql.functions import coalesce
35 35
36 36 from rhodecode.lib import helpers as h, diffs
37 from rhodecode.lib import audit_logger
37 38 from rhodecode.lib.channelstream import channelstream_request
38 from rhodecode.lib.utils import action_logger
39 39 from rhodecode.lib.utils2 import extract_mentioned_users, safe_str
40 40 from rhodecode.model import BaseModel
41 41 from rhodecode.model.db import (
@@ -163,6 +163,13 b' class CommentsModel(BaseModel):'
163 163
164 164 return todos
165 165
166 def _log_audit_action(self, action, action_data, user, comment):
167 audit_logger.store(
168 action=action,
169 action_data=action_data,
170 user=user,
171 repo=comment.repo)
172
166 173 def create(self, text, repo, user, commit_id=None, pull_request=None,
167 174 f_path=None, line_no=None, status_change=None,
168 175 status_change_type=None, comment_type=None,
@@ -337,13 +344,15 b' class CommentsModel(BaseModel):'
337 344 email_kwargs=kwargs,
338 345 )
339 346
340 action = (
341 'user_commented_pull_request:{}'.format(
342 comment.pull_request.pull_request_id)
343 if comment.pull_request
344 else 'user_commented_revision:{}'.format(comment.revision)
345 )
346 action_logger(user, action, comment.repo)
347 Session().flush()
348 if comment.pull_request:
349 action = 'repo.pull_request.comment.create'
350 else:
351 action = 'repo.commit.comment.create'
352
353 comment_data = comment.get_api_data()
354 self._log_audit_action(
355 action, {'data': comment_data}, user, comment)
347 356
348 357 registry = get_current_registry()
349 358 rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {})
@@ -385,15 +394,22 b' class CommentsModel(BaseModel):'
385 394
386 395 return comment
387 396
388 def delete(self, comment):
397 def delete(self, comment, user):
389 398 """
390 399 Deletes given comment
391
392 :param comment_id:
393 400 """
394 401 comment = self.__get_commit_comment(comment)
402 old_data = comment.get_api_data()
395 403 Session().delete(comment)
396 404
405 if comment.pull_request:
406 action = 'repo.pull_request.comment.delete'
407 else:
408 action = 'repo.commit.comment.delete'
409
410 self._log_audit_action(
411 action, {'old_data': old_data}, user, comment)
412
397 413 return comment
398 414
399 415 def get_all_comments(self, repo_id, revision=None, pull_request=None):
@@ -3122,6 +3122,25 b' class ChangesetComment(Base, BaseModel):'
3122 3122 else:
3123 3123 return '<DB:Comment at %#x>' % id(self)
3124 3124
3125 def get_api_data(self):
3126 comment = self
3127 data = {
3128 'comment_id': comment.comment_id,
3129 'comment_type': comment.comment_type,
3130 'comment_text': comment.text,
3131 'comment_status': comment.status_change,
3132 'comment_f_path': comment.f_path,
3133 'comment_lineno': comment.line_no,
3134 'comment_author': comment.author,
3135 'comment_created_on': comment.created_on
3136 }
3137 return data
3138
3139 def __json__(self):
3140 data = dict()
3141 data.update(self.get_api_data())
3142 return data
3143
3125 3144
3126 3145 class ChangesetStatus(Base, BaseModel):
3127 3146 __tablename__ = 'changeset_statuses'
@@ -3173,6 +3192,19 b' class ChangesetStatus(Base, BaseModel):'
3173 3192 def status_lbl(self):
3174 3193 return ChangesetStatus.get_status_lbl(self.status)
3175 3194
3195 def get_api_data(self):
3196 status = self
3197 data = {
3198 'status_id': status.changeset_status_id,
3199 'status': status.status,
3200 }
3201 return data
3202
3203 def __json__(self):
3204 data = dict()
3205 data.update(self.get_api_data())
3206 return data
3207
3176 3208
3177 3209 class _PullRequestBase(BaseModel):
3178 3210 """
@@ -3304,15 +3336,19 b' class _PullRequestBase(BaseModel):'
3304 3336 else:
3305 3337 return None
3306 3338
3307 def get_api_data(self):
3308 from pylons import url
3339 def get_api_data(self, with_merge_state=True):
3309 3340 from rhodecode.model.pull_request import PullRequestModel
3341
3310 3342 pull_request = self
3311 merge_status = PullRequestModel().merge_status(pull_request)
3312
3313 pull_request_url = url(
3314 'pullrequest_show', repo_name=self.target_repo.repo_name,
3315 pull_request_id=self.pull_request_id, qualified=True)
3343 if with_merge_state:
3344 merge_status = PullRequestModel().merge_status(pull_request)
3345 merge_state = {
3346 'status': merge_status[0],
3347 'message': safe_unicode(merge_status[1]),
3348 }
3349 else:
3350 merge_state = {'status': 'not_available',
3351 'message': 'not_available'}
3316 3352
3317 3353 merge_data = {
3318 3354 'clone_url': PullRequestModel().get_shadow_clone_url(pull_request),
@@ -3323,7 +3359,7 b' class _PullRequestBase(BaseModel):'
3323 3359
3324 3360 data = {
3325 3361 'pull_request_id': pull_request.pull_request_id,
3326 'url': pull_request_url,
3362 'url': PullRequestModel().get_url(pull_request),
3327 3363 'title': pull_request.title,
3328 3364 'description': pull_request.description,
3329 3365 'status': pull_request.status,
@@ -3331,10 +3367,7 b' class _PullRequestBase(BaseModel):'
3331 3367 'updated_on': pull_request.updated_on,
3332 3368 'commit_ids': pull_request.revisions,
3333 3369 'review_status': pull_request.calculated_review_status(),
3334 'mergeable': {
3335 'status': merge_status[0],
3336 'message': unicode(merge_status[1]),
3337 },
3370 'mergeable': merge_state,
3338 3371 'source': {
3339 3372 'clone_url': pull_request.source_repo.clone_url(),
3340 3373 'repository': pull_request.source_repo.repo_name,
@@ -3389,7 +3422,8 b' class PullRequest(Base, _PullRequestBase'
3389 3422
3390 3423 reviewers = relationship('PullRequestReviewers',
3391 3424 cascade="all, delete, delete-orphan")
3392 statuses = relationship('ChangesetStatus')
3425 statuses = relationship('ChangesetStatus',
3426 cascade="all, delete, delete-orphan")
3393 3427 comments = relationship('ChangesetComment',
3394 3428 cascade="all, delete, delete-orphan")
3395 3429 versions = relationship('PullRequestVersion',
@@ -36,11 +36,11 b' from sqlalchemy import or_'
36 36
37 37 from rhodecode import events
38 38 from rhodecode.lib import helpers as h, hooks_utils, diffs
39 from rhodecode.lib import audit_logger
39 40 from rhodecode.lib.compat import OrderedDict
40 41 from rhodecode.lib.hooks_daemon import prepare_callback_daemon
41 42 from rhodecode.lib.markup_renderer import (
42 43 DEFAULT_COMMENTS_RENDERER, RstTemplateRenderer)
43 from rhodecode.lib.utils import action_logger
44 44 from rhodecode.lib.utils2 import safe_unicode, safe_str, md5_safe
45 45 from rhodecode.lib.vcs.backends.base import (
46 46 Reference, MergeResponse, MergeFailureReason, UpdateFailureReason)
@@ -470,6 +470,11 b' class PullRequestModel(BaseModel):'
470 470 self._trigger_pull_request_hook(
471 471 pull_request, created_by_user, 'create')
472 472
473 creation_data = pull_request.get_api_data(with_merge_state=False)
474 self._log_audit_action(
475 'repo.pull_request.create', {'data': creation_data},
476 created_by_user, pull_request)
477
473 478 return pull_request
474 479
475 480 def _trigger_pull_request_hook(self, pull_request, user, action):
@@ -520,7 +525,12 b' class PullRequestModel(BaseModel):'
520 525 log.debug(
521 526 "Merge was successful, updating the pull request comments.")
522 527 self._comment_and_close_pr(pull_request, user, merge_state)
523 self._log_action('user_merged_pull_request', user, pull_request)
528
529 self._log_audit_action(
530 'repo.pull_request.merge',
531 {'merge_state': merge_state.__dict__},
532 user, pull_request)
533
524 534 else:
525 535 log.warn("Merge failed, not updating the pull request.")
526 536 return merge_state
@@ -899,8 +909,9 b' class PullRequestModel(BaseModel):'
899 909 renderer = RstTemplateRenderer()
900 910 return renderer.render('pull_request_update.mako', **params)
901 911
902 def edit(self, pull_request, title, description):
912 def edit(self, pull_request, title, description, user):
903 913 pull_request = self.__get_pull_request(pull_request)
914 old_data = pull_request.get_api_data(with_merge_state=False)
904 915 if pull_request.is_closed():
905 916 raise ValueError('This pull request is closed')
906 917 if title:
@@ -908,8 +919,11 b' class PullRequestModel(BaseModel):'
908 919 pull_request.description = description
909 920 pull_request.updated_on = datetime.datetime.now()
910 921 Session().add(pull_request)
922 self._log_audit_action(
923 'repo.pull_request.edit', {'old_data': old_data},
924 user, pull_request)
911 925
912 def update_reviewers(self, pull_request, reviewer_data):
926 def update_reviewers(self, pull_request, reviewer_data, user):
913 927 """
914 928 Update the reviewers in the pull request
915 929
@@ -946,8 +960,11 b' class PullRequestModel(BaseModel):'
946 960 reviewer.pull_request = pull_request
947 961 reviewer.reasons = reviewers[uid]['reasons']
948 962 # NOTE(marcink): mandatory shouldn't be changed now
949 #reviewer.mandatory = reviewers[uid]['reasons']
963 # reviewer.mandatory = reviewers[uid]['reasons']
950 964 Session().add(reviewer)
965 self._log_audit_action(
966 'repo.pull_request.reviewer.add', {'data': reviewer.get_dict()},
967 user, pull_request)
951 968
952 969 for uid in ids_to_remove:
953 970 changed = True
@@ -958,7 +975,11 b' class PullRequestModel(BaseModel):'
958 975 # use .all() in case we accidentally added the same person twice
959 976 # this CAN happen due to the lack of DB checks
960 977 for obj in reviewers:
978 old_data = obj.get_dict()
961 979 Session().delete(obj)
980 self._log_audit_action(
981 'repo.pull_request.reviewer.delete',
982 {'old_data': old_data}, user, pull_request)
962 983
963 984 if changed:
964 985 pull_request.updated_on = datetime.datetime.now()
@@ -1054,9 +1075,13 b' class PullRequestModel(BaseModel):'
1054 1075 email_kwargs=kwargs,
1055 1076 )
1056 1077
1057 def delete(self, pull_request):
1078 def delete(self, pull_request, user):
1058 1079 pull_request = self.__get_pull_request(pull_request)
1080 old_data = pull_request.get_api_data(with_merge_state=False)
1059 1081 self._cleanup_merge_workspace(pull_request)
1082 self._log_audit_action(
1083 'repo.pull_request.delete', {'old_data': old_data},
1084 user, pull_request)
1060 1085 Session().delete(pull_request)
1061 1086
1062 1087 def close_pull_request(self, pull_request, user):
@@ -1067,7 +1092,8 b' class PullRequestModel(BaseModel):'
1067 1092 Session().add(pull_request)
1068 1093 self._trigger_pull_request_hook(
1069 1094 pull_request, pull_request.author, 'close')
1070 self._log_action('user_closed_pull_request', user, pull_request)
1095 self._log_audit_action(
1096 'repo.pull_request.close', {}, user, pull_request)
1071 1097
1072 1098 def close_pull_request_with_comment(
1073 1099 self, pull_request, user, repo, message=None):
@@ -1402,12 +1428,12 b' class PullRequestModel(BaseModel):'
1402 1428 settings = settings_model.get_general_settings()
1403 1429 return settings.get('rhodecode_hg_use_rebase_for_merging', False)
1404 1430
1405 def _log_action(self, action, user, pull_request):
1406 action_logger(
1407 user,
1408 '{action}:{pr_id}'.format(
1409 action=action, pr_id=pull_request.pull_request_id),
1410 pull_request.target_repo)
1431 def _log_audit_action(self, action, action_data, user, pull_request):
1432 audit_logger.store(
1433 action=action,
1434 action_data=action_data,
1435 user=user,
1436 repo=pull_request.target_repo)
1411 1437
1412 1438 def get_reviewer_functions(self):
1413 1439 """
@@ -199,6 +199,9 b' class TestAdminPermissionsController(Tes'
199 199 url('edit_user_ips', user_id=default_user_id),
200 200 params={'_method': 'delete', 'del_ip_id': del_ip_id,
201 201 'csrf_token': self.csrf_token})
202
203 assert_session_flash(response, 'Removed ip address from user whitelist')
204
202 205 clear_all_caches()
203 206 response = self.app.get(url('admin_permissions_ips'))
204 207 response.mustcontain('All IP addresses are allowed')
@@ -27,7 +27,7 b' from rhodecode.lib.vcs.nodes import File'
27 27 from rhodecode.lib import helpers as h
28 28 from rhodecode.model.changeset_status import ChangesetStatusModel
29 29 from rhodecode.model.db import (
30 PullRequest, ChangesetStatus, UserLog, Notification)
30 PullRequest, ChangesetStatus, UserLog, Notification, ChangesetComment)
31 31 from rhodecode.model.meta import Session
32 32 from rhodecode.model.pull_request import PullRequestModel
33 33 from rhodecode.model.user import UserModel
@@ -256,29 +256,32 b' class TestPullrequestsController(object)'
256 256 'csrf_token': csrf_token},
257 257 extra_environ=xhr_header,)
258 258
259 action = 'user_closed_pull_request:%d' % pull_request_id
260 259 journal = UserLog.query()\
261 260 .filter(UserLog.user_id == author)\
262 .filter(UserLog.repository_id == repo)\
263 .filter(UserLog.action == action)\
261 .filter(UserLog.repository_id == repo) \
262 .order_by('user_log_id') \
264 263 .all()
265 assert len(journal) == 1
264 assert journal[-1].action == 'repo.pull_request.close'
266 265
267 266 pull_request = PullRequest.get(pull_request_id)
268 267 assert pull_request.is_closed()
269 268
270 # check only the latest status, not the review status
271 269 status = ChangesetStatusModel().get_status(
272 270 pull_request.source_repo, pull_request=pull_request)
273 271 assert status == ChangesetStatus.STATUS_APPROVED
274 assert pull_request.comments[-1].text == 'Closing a PR'
272 comments = ChangesetComment().query() \
273 .filter(ChangesetComment.pull_request == pull_request) \
274 .order_by(ChangesetComment.comment_id.asc())\
275 .all()
276 assert comments[-1].text == 'Closing a PR'
275 277
276 278 def test_comment_force_close_pull_request_rejected(
277 279 self, pr_util, csrf_token, xhr_header):
278 280 pull_request = pr_util.create_pull_request()
279 281 pull_request_id = pull_request.pull_request_id
280 282 PullRequestModel().update_reviewers(
281 pull_request_id, [(1, ['reason'], False), (2, ['reason2'], False)])
283 pull_request_id, [(1, ['reason'], False), (2, ['reason2'], False)],
284 pull_request.author)
282 285 author = pull_request.user_id
283 286 repo = pull_request.target_repo.repo_id
284 287
@@ -294,12 +297,11 b' class TestPullrequestsController(object)'
294 297
295 298 pull_request = PullRequest.get(pull_request_id)
296 299
297 action = 'user_closed_pull_request:%d' % pull_request_id
298 journal = UserLog.query().filter(
299 UserLog.user_id == author,
300 UserLog.repository_id == repo,
301 UserLog.action == action).all()
302 assert len(journal) == 1
300 journal = UserLog.query()\
301 .filter(UserLog.user_id == author, UserLog.repository_id == repo) \
302 .order_by('user_log_id') \
303 .all()
304 assert journal[-1].action == 'repo.pull_request.close'
303 305
304 306 # check only the latest status, not the review status
305 307 status = ChangesetStatusModel().get_status(
@@ -449,7 +451,8 b' class TestPullrequestsController(object)'
449 451
450 452 # Change reviewers and check that a notification was made
451 453 PullRequestModel().update_reviewers(
452 pull_request.pull_request_id, [(1, [], False)])
454 pull_request.pull_request_id, [(1, [], False)],
455 pull_request.author)
453 456 assert len(notifications.all()) == 2
454 457
455 458 def test_create_pull_request_stores_ancestor_commit_id(self, backend,
@@ -541,25 +544,20 b' class TestPullrequestsController(object)'
541 544 pull_request, ChangesetStatus.STATUS_APPROVED)
542 545
543 546 # Check the relevant log entries were added
544 user_logs = UserLog.query() \
545 .filter(UserLog.version == UserLog.VERSION_1) \
546 .order_by('-user_log_id').limit(3)
547 user_logs = UserLog.query().order_by('-user_log_id').limit(3)
547 548 actions = [log.action for log in user_logs]
548 549 pr_commit_ids = PullRequestModel()._get_commit_ids(pull_request)
549 550 expected_actions = [
550 u'user_closed_pull_request:%d' % pull_request_id,
551 u'user_merged_pull_request:%d' % pull_request_id,
552 # The action below reflect that the post push actions were executed
553 u'user_commented_pull_request:%d' % pull_request_id,
551 u'repo.pull_request.close',
552 u'repo.pull_request.merge',
553 u'repo.pull_request.comment.create'
554 554 ]
555 555 assert actions == expected_actions
556 556
557 user_logs = UserLog.query() \
558 .filter(UserLog.version == UserLog.VERSION_2) \
559 .order_by('-user_log_id').limit(1)
560 actions = [log.action for log in user_logs]
561 assert actions == ['user.push']
562 assert user_logs[0].action_data['commit_ids'] == pr_commit_ids
557 user_logs = UserLog.query().order_by('-user_log_id').limit(4)
558 actions = [log for log in user_logs]
559 assert actions[-1].action == 'user.push'
560 assert actions[-1].action_data['commit_ids'] == pr_commit_ids
563 561
564 562 # Check post_push rcextension was really executed
565 563 push_calls = rhodecode.EXTENSIONS.calls['post_push']
@@ -50,7 +50,9 b' class TestPullRequestModel(object):'
50 50 A pull request combined with multiples patches.
51 51 """
52 52 BackendClass = get_backend(backend.alias)
53 self.merge_patcher = mock.patch.object(BackendClass, 'merge')
53 self.merge_patcher = mock.patch.object(
54 BackendClass, 'merge', return_value=MergeResponse(
55 False, False, None, MergeFailureReason.UNKNOWN))
54 56 self.workspace_remove_patcher = mock.patch.object(
55 57 BackendClass, 'cleanup_merge_workspace')
56 58
@@ -117,7 +119,8 b' class TestPullRequestModel(object):'
117 119
118 120 def test_get_awaiting_my_review(self, pull_request):
119 121 PullRequestModel().update_reviewers(
120 pull_request, [(pull_request.author, ['author'], False)])
122 pull_request, [(pull_request.author, ['author'], False)],
123 pull_request.author)
121 124 prs = PullRequestModel().get_awaiting_my_review(
122 125 pull_request.target_repo, user_id=pull_request.author.user_id)
123 126 assert isinstance(prs, list)
@@ -125,13 +128,14 b' class TestPullRequestModel(object):'
125 128
126 129 def test_count_awaiting_my_review(self, pull_request):
127 130 PullRequestModel().update_reviewers(
128 pull_request, [(pull_request.author, ['author'], False)])
131 pull_request, [(pull_request.author, ['author'], False)],
132 pull_request.author)
129 133 pr_count = PullRequestModel().count_awaiting_my_review(
130 134 pull_request.target_repo, user_id=pull_request.author.user_id)
131 135 assert pr_count == 1
132 136
133 137 def test_delete_calls_cleanup_merge(self, pull_request):
134 PullRequestModel().delete(pull_request)
138 PullRequestModel().delete(pull_request, pull_request.author)
135 139
136 140 self.workspace_remove_mock.assert_called_once_with(
137 141 self.workspace_id)
@@ -892,7 +892,7 b' class RepoServer(object):'
892 892
893 893
894 894 @pytest.fixture
895 def pr_util(backend, request):
895 def pr_util(backend, request, config_stub):
896 896 """
897 897 Utility for tests of models and for functional tests around pull requests.
898 898
@@ -1085,7 +1085,7 b' class PRTestUtility(object):'
1085 1085 # request will already be deleted.
1086 1086 pull_request = PullRequest().get(self.pull_request_id)
1087 1087 if pull_request:
1088 PullRequestModel().delete(pull_request)
1088 PullRequestModel().delete(pull_request, pull_request.author)
1089 1089 Session().commit()
1090 1090
1091 1091 if self.notification_patcher:
@@ -1648,14 +1648,6 b' def no_notifications(request):'
1648 1648 request.addfinalizer(notification_patcher.stop)
1649 1649
1650 1650
1651 @pytest.fixture
1652 def silence_action_logger(request):
1653 notification_patcher = mock.patch(
1654 'rhodecode.lib.utils.action_logger')
1655 notification_patcher.start()
1656 request.addfinalizer(notification_patcher.stop)
1657
1658
1659 1651 @pytest.fixture(scope='session')
1660 1652 def repeat(request):
1661 1653 """
General Comments 0
You need to be logged in to leave comments. Login now