##// END OF EJS Templates
audit-logs: added web strip command to audit logs
marcink -
r1699:5efb37e3 default
parent child Browse files
Show More
@@ -1,109 +1,117 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2017-2017 RhodeCode GmbH
3 # Copyright (C) 2017-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 from pyramid.view import view_config
22 from pyramid.view import view_config
23
23
24 from rhodecode.apps._base import RepoAppView
24 from rhodecode.apps._base import RepoAppView
25 from rhodecode.lib import audit_logger
25 from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator,
26 from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator,
26 NotAnonymous)
27 NotAnonymous)
27 from rhodecode.lib.ext_json import json
28 from rhodecode.lib.ext_json import json
28
29
29 log = logging.getLogger(__name__)
30 log = logging.getLogger(__name__)
30
31
31
32
32 class StripView(RepoAppView):
33 class StripView(RepoAppView):
33 def load_default_context(self):
34 def load_default_context(self):
34 c = self._get_local_tmpl_context()
35 c = self._get_local_tmpl_context()
35
36
36 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
37 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
37 c.repo_info = self.db_repo
38 c.repo_info = self.db_repo
38
39
39 self._register_global_c(c)
40 self._register_global_c(c)
40 return c
41 return c
41
42
42 @LoginRequired()
43 @LoginRequired()
43 @NotAnonymous()
44 @NotAnonymous()
44 @HasRepoPermissionAnyDecorator('repository.admin')
45 @HasRepoPermissionAnyDecorator('repository.admin')
45 @view_config(
46 @view_config(
46 route_name='strip', request_method='GET',
47 route_name='strip', request_method='GET',
47 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
48 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
48 def strip(self):
49 def strip(self):
49 c = self.load_default_context()
50 c = self.load_default_context()
50 c.active = 'strip'
51 c.active = 'strip'
51 c.strip_limit = 10
52 c.strip_limit = 10
52
53
53 return self._get_template_context(c)
54 return self._get_template_context(c)
54
55
55 @LoginRequired()
56 @LoginRequired()
56 @NotAnonymous()
57 @NotAnonymous()
57 @HasRepoPermissionAnyDecorator('repository.admin')
58 @HasRepoPermissionAnyDecorator('repository.admin')
58 @view_config(
59 @view_config(
59 route_name='strip_check', request_method='POST',
60 route_name='strip_check', request_method='POST',
60 renderer='json', xhr=True)
61 renderer='json', xhr=True)
61 def strip_check(self):
62 def strip_check(self):
62 from rhodecode.lib.vcs.backends.base import EmptyCommit
63 from rhodecode.lib.vcs.backends.base import EmptyCommit
63 data = {}
64 data = {}
64 rp = self.request.POST
65 rp = self.request.POST
65 for i in range(1, 11):
66 for i in range(1, 11):
66 chset = 'changeset_id-%d' % (i,)
67 chset = 'changeset_id-%d' % (i,)
67 check = rp.get(chset)
68 check = rp.get(chset)
68 if check:
69 if check:
69 data[i] = self.db_repo.get_changeset(rp[chset])
70 data[i] = self.db_repo.get_changeset(rp[chset])
70 if isinstance(data[i], EmptyCommit):
71 if isinstance(data[i], EmptyCommit):
71 data[i] = {'rev': None, 'commit': rp[chset]}
72 data[i] = {'rev': None, 'commit': rp[chset]}
72 else:
73 else:
73 data[i] = {'rev': data[i].raw_id, 'branch': data[i].branch,
74 data[i] = {'rev': data[i].raw_id, 'branch': data[i].branch,
74 'author': data[i].author,
75 'author': data[i].author,
75 'comment': data[i].message}
76 'comment': data[i].message}
76 else:
77 else:
77 break
78 break
78 return data
79 return data
79
80
80 @LoginRequired()
81 @LoginRequired()
81 @NotAnonymous()
82 @NotAnonymous()
82 @HasRepoPermissionAnyDecorator('repository.admin')
83 @HasRepoPermissionAnyDecorator('repository.admin')
83 @view_config(
84 @view_config(
84 route_name='strip_execute', request_method='POST',
85 route_name='strip_execute', request_method='POST',
85 renderer='json', xhr=True)
86 renderer='json', xhr=True)
86 def strip_execute(self):
87 def strip_execute(self):
87 from rhodecode.model.scm import ScmModel
88 from rhodecode.model.scm import ScmModel
88
89
89 c = self.load_default_context()
90 c = self.load_default_context()
90 user = self._rhodecode_user
91 user = self._rhodecode_user
91 rp = self.request.POST
92 rp = self.request.POST
92 data = {}
93 data = {}
93 for idx in rp:
94 for idx in rp:
94 commit = json.loads(rp[idx])
95 commit = json.loads(rp[idx])
95 # If someone put two times the same branch
96 # If someone put two times the same branch
96 if commit['branch'] in data.keys():
97 if commit['branch'] in data.keys():
97 continue
98 continue
98 try:
99 try:
99 ScmModel().strip(
100 ScmModel().strip(
100 repo=c.repo_info,
101 repo=c.repo_info,
101 commit_id=commit['rev'], branch=commit['branch'])
102 commit_id=commit['rev'], branch=commit['branch'])
102 log.info('Stripped commit %s from repo `%s` by %s' % (
103 log.info('Stripped commit %s from repo `%s` by %s' % (
103 commit['rev'], c.repo_info.repo_name, user))
104 commit['rev'], c.repo_info.repo_name, user))
104 data[commit['rev']] = True
105 data[commit['rev']] = True
106
107 audit_logger.store(
108 action='repo.commit.strip',
109 action_data={'commit_id': commit['rev']},
110 repo=self.db_repo,
111 user=self._rhodecode_user, commit=True)
112
105 except Exception as e:
113 except Exception as e:
106 data[commit['rev']] = False
114 data[commit['rev']] = False
107 log.debug('Stripped commit %s from repo `%s` failed by %s, exeption %s' % (
115 log.debug('Stripped commit %s from repo `%s` failed by %s, exeption %s' % (
108 commit['rev'], self.db_repo_name, user, e.message))
116 commit['rev'], self.db_repo_name, user, e.message))
109 return data
117 return data
@@ -1,143 +1,144 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2017-2017 RhodeCode GmbH
3 # Copyright (C) 2017-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import datetime
22 import datetime
23
23
24 from rhodecode.model import meta
24 from rhodecode.model import meta
25 from rhodecode.model.db import User, UserLog
25 from rhodecode.model.db import User, UserLog
26
26
27
27
28 log = logging.getLogger(__name__)
28 log = logging.getLogger(__name__)
29
29
30
30
31 ACTIONS = {
31 ACTIONS = {
32 'user.login.success': {},
32 'user.login.success': {},
33 'user.login.failure': {},
33 'user.login.failure': {},
34 'user.logout': {},
34 'user.logout': {},
35 'user.password.reset_request': {},
35 'user.password.reset_request': {},
36
36
37 'repo.add': {},
37 'repo.add': {},
38 'repo.edit': {},
38 'repo.edit': {},
39 'repo.commit.strip': {}
39 }
40 }
40
41
41
42
42 class UserWrap(object):
43 class UserWrap(object):
43 """
44 """
44 Fake object used to imitate AuthUser
45 Fake object used to imitate AuthUser
45 """
46 """
46
47
47 def __init__(self, user_id=None, username=None, ip_addr=None):
48 def __init__(self, user_id=None, username=None, ip_addr=None):
48 self.user_id = user_id
49 self.user_id = user_id
49 self.username = username
50 self.username = username
50 self.ip_addr = ip_addr
51 self.ip_addr = ip_addr
51
52
52
53
53 def _store_log(action_name, action_data, user_id, username, user_data,
54 def _store_log(action_name, action_data, user_id, username, user_data,
54 ip_address, repository_id, repository_name):
55 ip_address, repository_id, repository_name):
55 user_log = UserLog()
56 user_log = UserLog()
56 user_log.version = UserLog.VERSION_2
57 user_log.version = UserLog.VERSION_2
57
58
58 user_log.action = action_name
59 user_log.action = action_name
59 user_log.action_data = action_data
60 user_log.action_data = action_data
60
61
61 user_log.user_ip = ip_address
62 user_log.user_ip = ip_address
62
63
63 user_log.user_id = user_id
64 user_log.user_id = user_id
64 user_log.username = username
65 user_log.username = username
65 user_log.user_data = user_data
66 user_log.user_data = user_data
66
67
67 user_log.repository_id = repository_id
68 user_log.repository_id = repository_id
68 user_log.repository_name = repository_name
69 user_log.repository_name = repository_name
69
70
70 user_log.action_date = datetime.datetime.now()
71 user_log.action_date = datetime.datetime.now()
71
72
72 log.info('AUDIT: Logging action: `%s` by user:id:%s[%s] ip:%s',
73 log.info('AUDIT: Logging action: `%s` by user:id:%s[%s] ip:%s',
73 action_name, user_id, username, ip_address)
74 action_name, user_id, username, ip_address)
74
75
75 return user_log
76 return user_log
76
77
77
78
78 def store(
79 def store(
79 action, user, action_data=None, user_data=None, ip_addr=None,
80 action, user, action_data=None, user_data=None, ip_addr=None,
80 repo=None, sa_session=None, commit=False):
81 repo=None, sa_session=None, commit=False):
81 """
82 """
82 Audit logger for various actions made by users, typically this results in a call such::
83 Audit logger for various actions made by users, typically this results in a call such::
83
84
84 from rhodecode.lib import audit_logger
85 from rhodecode.lib import audit_logger
85
86
86 audit_logger.store(action='repo.edit', user=self._rhodecode_user)
87 audit_logger.store(action='repo.edit', user=self._rhodecode_user)
87 audit_logger.store(action='repo.delete', user=audit_logger.UserWrap(username='itried-to-login', ip_addr='8.8.8.8'))
88 audit_logger.store(action='repo.delete', user=audit_logger.UserWrap(username='itried-to-login', ip_addr='8.8.8.8'))
88
89
89 # without an user ?
90 # without an user ?
90 audit_user = audit_logger.UserWrap(
91 audit_user = audit_logger.UserWrap(
91 username=self.request.params.get('username'),
92 username=self.request.params.get('username'),
92 ip_addr=self.request.remote_addr)
93 ip_addr=self.request.remote_addr)
93 audit_logger.store(action='user.login.failure', user=audit_user)
94 audit_logger.store(action='user.login.failure', user=audit_user)
94 """
95 """
95 from rhodecode.lib.utils2 import safe_unicode
96 from rhodecode.lib.utils2 import safe_unicode
96 from rhodecode.lib.auth import AuthUser
97 from rhodecode.lib.auth import AuthUser
97
98
98 if action not in ACTIONS:
99 if action not in ACTIONS:
99 raise ValueError('Action `{}` not in valid actions'.format(action))
100 raise ValueError('Action `{}` not in valid actions'.format(action))
100
101
101 if not sa_session:
102 if not sa_session:
102 sa_session = meta.Session()
103 sa_session = meta.Session()
103
104
104 try:
105 try:
105 username = getattr(user, 'username', None)
106 username = getattr(user, 'username', None)
106 if not username:
107 if not username:
107 pass
108 pass
108
109
109 user_id = getattr(user, 'user_id', None)
110 user_id = getattr(user, 'user_id', None)
110 if not user_id:
111 if not user_id:
111 # maybe we have username ? Try to figure user_id from username
112 # maybe we have username ? Try to figure user_id from username
112 if username:
113 if username:
113 user_id = getattr(
114 user_id = getattr(
114 User.get_by_username(username), 'user_id', None)
115 User.get_by_username(username), 'user_id', None)
115
116
116 ip_addr = ip_addr or getattr(user, 'ip_addr', None)
117 ip_addr = ip_addr or getattr(user, 'ip_addr', None)
117 if not ip_addr:
118 if not ip_addr:
118 pass
119 pass
119
120
120 if not user_data:
121 if not user_data:
121 # try to get this from the auth user
122 # try to get this from the auth user
122 if isinstance(user, AuthUser):
123 if isinstance(user, AuthUser):
123 user_data = {}
124 user_data = {}
124
125
125 repository_id = getattr(repo, 'repo_id', None)
126 repository_id = getattr(repo, 'repo_id', None)
126 repository_name = getattr(repo, 'repo_name', None)
127 repository_name = getattr(repo, 'repo_name', None)
127
128
128 user_log = _store_log(
129 user_log = _store_log(
129 action_name=safe_unicode(action),
130 action_name=safe_unicode(action),
130 action_data=action_data or {},
131 action_data=action_data or {},
131 user_id=user_id,
132 user_id=user_id,
132 username=username,
133 username=username,
133 user_data=user_data or {},
134 user_data=user_data or {},
134 ip_address=safe_unicode(ip_addr),
135 ip_address=safe_unicode(ip_addr),
135 repository_id=repository_id,
136 repository_id=repository_id,
136 repository_name=repository_name
137 repository_name=repository_name
137 )
138 )
138 sa_session.add(user_log)
139 sa_session.add(user_log)
139 if commit:
140 if commit:
140 sa_session.commit()
141 sa_session.commit()
141
142
142 except Exception:
143 except Exception:
143 log.exception('AUDIT: failed to store audit log')
144 log.exception('AUDIT: failed to store audit log')
General Comments 0
You need to be logged in to leave comments. Login now