##// END OF EJS Templates
audit-logs: updated action data attrbiutes.
marcink -
r1823:e27e4796 default
parent child Browse files
Show More
@@ -1,257 +1,257 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, Repository
25 from rhodecode.model.db import User, UserLog, Repository
26
26
27
27
28 log = logging.getLogger(__name__)
28 log = logging.getLogger(__name__)
29
29
30 # action as key, and expected action_data as value
30 # action as key, and expected action_data as value
31 ACTIONS_V1 = {
31 ACTIONS_V1 = {
32 'user.login.success': {'user_agent': ''},
32 'user.login.success': {'user_agent': ''},
33 'user.login.failure': {'user_agent': ''},
33 'user.login.failure': {'user_agent': ''},
34 'user.logout': {'user_agent': ''},
34 'user.logout': {'user_agent': ''},
35 'user.password.reset_request': {},
35 'user.password.reset_request': {},
36 'user.push': {'user_agent': '', 'commit_ids': []},
36 'user.push': {'user_agent': '', 'commit_ids': []},
37 'user.pull': {'user_agent': ''},
37 'user.pull': {'user_agent': ''},
38
38
39 'user.create': {'data': {}},
39 'user.create': {'data': {}},
40 'user.delete': {'old_data': {}},
40 'user.delete': {'old_data': {}},
41 'user.edit': {'old_data': {}},
41 'user.edit': {'old_data': {}},
42 'user.edit.permissions': {},
42 'user.edit.permissions': {},
43 'user.edit.ip.add': {},
43 'user.edit.ip.add': {'ip': {}, 'user': {}},
44 'user.edit.ip.delete': {},
44 'user.edit.ip.delete': {'ip': {}, 'user': {}},
45 'user.edit.token.add': {},
45 'user.edit.token.add': {'token': {}, 'user': {}},
46 'user.edit.token.delete': {},
46 'user.edit.token.delete': {'token': {}, 'user': {}},
47 'user.edit.email.add': {},
47 'user.edit.email.add': {'email': ''},
48 'user.edit.email.delete': {},
48 'user.edit.email.delete': {'email': ''},
49 'user.edit.password_reset.enabled': {},
49 'user.edit.password_reset.enabled': {},
50 'user.edit.password_reset.disabled': {},
50 'user.edit.password_reset.disabled': {},
51
51
52 'user_group.create': {'data': {}},
52 'user_group.create': {'data': {}},
53 'user_group.delete': {'old_data': {}},
53 'user_group.delete': {'old_data': {}},
54 'user_group.edit': {'old_data': {}},
54 'user_group.edit': {'old_data': {}},
55 'user_group.edit.permissions': {},
55 'user_group.edit.permissions': {},
56 'user_group.edit.member.add': {},
56 'user_group.edit.member.add': {},
57 'user_group.edit.member.delete': {},
57 'user_group.edit.member.delete': {},
58
58
59 'repo.create': {'data': {}},
59 'repo.create': {'data': {}},
60 'repo.fork': {'data': {}},
60 'repo.fork': {'data': {}},
61 'repo.edit': {'old_data': {}},
61 'repo.edit': {'old_data': {}},
62 'repo.edit.permissions': {},
62 'repo.edit.permissions': {},
63 'repo.delete': {'old_data': {}},
63 'repo.delete': {'old_data': {}},
64 'repo.commit.strip': {},
64 'repo.commit.strip': {'commit_id': ''},
65 'repo.archive.download': {},
65 'repo.archive.download': {'user_agent': '', 'archive_name': '',
66
66 'archive_spec': '', 'archive_cached': ''},
67 'repo.pull_request.create': '',
67 'repo.pull_request.create': '',
68 'repo.pull_request.edit': '',
68 'repo.pull_request.edit': '',
69 'repo.pull_request.delete': '',
69 'repo.pull_request.delete': '',
70 'repo.pull_request.close': '',
70 'repo.pull_request.close': '',
71 'repo.pull_request.merge': '',
71 'repo.pull_request.merge': '',
72 'repo.pull_request.vote': '',
72 'repo.pull_request.vote': '',
73 'repo.pull_request.comment.create': '',
73 'repo.pull_request.comment.create': '',
74 'repo.pull_request.comment.delete': '',
74 'repo.pull_request.comment.delete': '',
75
75
76 'repo.pull_request.reviewer.add': '',
76 'repo.pull_request.reviewer.add': '',
77 'repo.pull_request.reviewer.delete': '',
77 'repo.pull_request.reviewer.delete': '',
78
78
79 'repo.commit.comment.create': '',
79 'repo.commit.comment.create': '',
80 'repo.commit.comment.delete': '',
80 'repo.commit.comment.delete': '',
81 'repo.commit.vote': '',
81 'repo.commit.vote': '',
82
82
83 'repo_group.create': {'data': {}},
83 'repo_group.create': {'data': {}},
84 'repo_group.edit': {'old_data': {}},
84 'repo_group.edit': {'old_data': {}},
85 'repo_group.edit.permissions': {},
85 'repo_group.edit.permissions': {},
86 'repo_group.delete': {'old_data': {}},
86 'repo_group.delete': {'old_data': {}},
87 }
87 }
88 ACTIONS = ACTIONS_V1
88 ACTIONS = ACTIONS_V1
89
89
90 SOURCE_WEB = 'source_web'
90 SOURCE_WEB = 'source_web'
91 SOURCE_API = 'source_api'
91 SOURCE_API = 'source_api'
92
92
93
93
94 class UserWrap(object):
94 class UserWrap(object):
95 """
95 """
96 Fake object used to imitate AuthUser
96 Fake object used to imitate AuthUser
97 """
97 """
98
98
99 def __init__(self, user_id=None, username=None, ip_addr=None):
99 def __init__(self, user_id=None, username=None, ip_addr=None):
100 self.user_id = user_id
100 self.user_id = user_id
101 self.username = username
101 self.username = username
102 self.ip_addr = ip_addr
102 self.ip_addr = ip_addr
103
103
104
104
105 class RepoWrap(object):
105 class RepoWrap(object):
106 """
106 """
107 Fake object used to imitate RepoObject that audit logger requires
107 Fake object used to imitate RepoObject that audit logger requires
108 """
108 """
109
109
110 def __init__(self, repo_id=None, repo_name=None):
110 def __init__(self, repo_id=None, repo_name=None):
111 self.repo_id = repo_id
111 self.repo_id = repo_id
112 self.repo_name = repo_name
112 self.repo_name = repo_name
113
113
114
114
115 def _store_log(action_name, action_data, user_id, username, user_data,
115 def _store_log(action_name, action_data, user_id, username, user_data,
116 ip_address, repository_id, repository_name):
116 ip_address, repository_id, repository_name):
117 user_log = UserLog()
117 user_log = UserLog()
118 user_log.version = UserLog.VERSION_2
118 user_log.version = UserLog.VERSION_2
119
119
120 user_log.action = action_name
120 user_log.action = action_name
121 user_log.action_data = action_data
121 user_log.action_data = action_data
122
122
123 user_log.user_ip = ip_address
123 user_log.user_ip = ip_address
124
124
125 user_log.user_id = user_id
125 user_log.user_id = user_id
126 user_log.username = username
126 user_log.username = username
127 user_log.user_data = user_data
127 user_log.user_data = user_data
128
128
129 user_log.repository_id = repository_id
129 user_log.repository_id = repository_id
130 user_log.repository_name = repository_name
130 user_log.repository_name = repository_name
131
131
132 user_log.action_date = datetime.datetime.now()
132 user_log.action_date = datetime.datetime.now()
133
133
134 log.info('AUDIT: Logging action: `%s` by user:id:%s[%s] ip:%s',
134 log.info('AUDIT: Logging action: `%s` by user:id:%s[%s] ip:%s',
135 action_name, user_id, username, ip_address)
135 action_name, user_id, username, ip_address)
136
136
137 return user_log
137 return user_log
138
138
139
139
140 def store_web(*args, **kwargs):
140 def store_web(*args, **kwargs):
141 if 'action_data' not in kwargs:
141 if 'action_data' not in kwargs:
142 kwargs['action_data'] = {}
142 kwargs['action_data'] = {}
143 kwargs['action_data'].update({
143 kwargs['action_data'].update({
144 'source': SOURCE_WEB
144 'source': SOURCE_WEB
145 })
145 })
146 return store(*args, **kwargs)
146 return store(*args, **kwargs)
147
147
148
148
149 def store_api(*args, **kwargs):
149 def store_api(*args, **kwargs):
150 if 'action_data' not in kwargs:
150 if 'action_data' not in kwargs:
151 kwargs['action_data'] = {}
151 kwargs['action_data'] = {}
152 kwargs['action_data'].update({
152 kwargs['action_data'].update({
153 'source': SOURCE_API
153 'source': SOURCE_API
154 })
154 })
155 return store(*args, **kwargs)
155 return store(*args, **kwargs)
156
156
157
157
158 def store(action, user, action_data=None, user_data=None, ip_addr=None,
158 def store(action, user, action_data=None, user_data=None, ip_addr=None,
159 repo=None, sa_session=None, commit=False):
159 repo=None, sa_session=None, commit=False):
160 """
160 """
161 Audit logger for various actions made by users, typically this
161 Audit logger for various actions made by users, typically this
162 results in a call such::
162 results in a call such::
163
163
164 from rhodecode.lib import audit_logger
164 from rhodecode.lib import audit_logger
165
165
166 audit_logger.store(
166 audit_logger.store(
167 action='repo.edit', user=self._rhodecode_user)
167 action='repo.edit', user=self._rhodecode_user)
168 audit_logger.store(
168 audit_logger.store(
169 action='repo.delete', action_data={'data': repo_data},
169 action='repo.delete', action_data={'data': repo_data},
170 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'))
170 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'))
171
171
172 # repo action
172 # repo action
173 audit_logger.store(
173 audit_logger.store(
174 action='repo.delete',
174 action='repo.delete',
175 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'),
175 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'),
176 repo=audit_logger.RepoWrap(repo_name='some-repo'))
176 repo=audit_logger.RepoWrap(repo_name='some-repo'))
177
177
178 # repo action, when we know and have the repository object already
178 # repo action, when we know and have the repository object already
179 audit_logger.store(
179 audit_logger.store(
180 action='repo.delete',
180 action='repo.delete',
181 action_data={'source': audit_logger.SOURCE_WEB, },
181 action_data={'source': audit_logger.SOURCE_WEB, },
182 user=self._rhodecode_user,
182 user=self._rhodecode_user,
183 repo=repo_object)
183 repo=repo_object)
184
184
185 # alternative wrapper to the above
185 # alternative wrapper to the above
186 audit_logger.store_web(
186 audit_logger.store_web(
187 action='repo.delete',
187 action='repo.delete',
188 action_data={},
188 action_data={},
189 user=self._rhodecode_user,
189 user=self._rhodecode_user,
190 repo=repo_object)
190 repo=repo_object)
191
191
192 # without an user ?
192 # without an user ?
193 audit_logger.store(
193 audit_logger.store(
194 action='user.login.failure',
194 action='user.login.failure',
195 user=audit_logger.UserWrap(
195 user=audit_logger.UserWrap(
196 username=self.request.params.get('username'),
196 username=self.request.params.get('username'),
197 ip_addr=self.request.remote_addr))
197 ip_addr=self.request.remote_addr))
198
198
199 """
199 """
200 from rhodecode.lib.utils2 import safe_unicode
200 from rhodecode.lib.utils2 import safe_unicode
201 from rhodecode.lib.auth import AuthUser
201 from rhodecode.lib.auth import AuthUser
202
202
203 action_spec = ACTIONS.get(action, None)
203 action_spec = ACTIONS.get(action, None)
204 if action_spec is None:
204 if action_spec is None:
205 raise ValueError('Action `{}` is not supported'.format(action))
205 raise ValueError('Action `{}` is not supported'.format(action))
206
206
207 if not sa_session:
207 if not sa_session:
208 sa_session = meta.Session()
208 sa_session = meta.Session()
209
209
210 try:
210 try:
211 username = getattr(user, 'username', None)
211 username = getattr(user, 'username', None)
212 if not username:
212 if not username:
213 pass
213 pass
214
214
215 user_id = getattr(user, 'user_id', None)
215 user_id = getattr(user, 'user_id', None)
216 if not user_id:
216 if not user_id:
217 # maybe we have username ? Try to figure user_id from username
217 # maybe we have username ? Try to figure user_id from username
218 if username:
218 if username:
219 user_id = getattr(
219 user_id = getattr(
220 User.get_by_username(username), 'user_id', None)
220 User.get_by_username(username), 'user_id', None)
221
221
222 ip_addr = ip_addr or getattr(user, 'ip_addr', None)
222 ip_addr = ip_addr or getattr(user, 'ip_addr', None)
223 if not ip_addr:
223 if not ip_addr:
224 pass
224 pass
225
225
226 if not user_data:
226 if not user_data:
227 # try to get this from the auth user
227 # try to get this from the auth user
228 if isinstance(user, AuthUser):
228 if isinstance(user, AuthUser):
229 user_data = {
229 user_data = {
230 'username': user.username,
230 'username': user.username,
231 'email': user.email,
231 'email': user.email,
232 }
232 }
233
233
234 repository_name = getattr(repo, 'repo_name', None)
234 repository_name = getattr(repo, 'repo_name', None)
235 repository_id = getattr(repo, 'repo_id', None)
235 repository_id = getattr(repo, 'repo_id', None)
236 if not repository_id:
236 if not repository_id:
237 # maybe we have repo_name ? Try to figure repo_id from repo_name
237 # maybe we have repo_name ? Try to figure repo_id from repo_name
238 if repository_name:
238 if repository_name:
239 repository_id = getattr(
239 repository_id = getattr(
240 Repository.get_by_repo_name(repository_name), 'repo_id', None)
240 Repository.get_by_repo_name(repository_name), 'repo_id', None)
241
241
242 user_log = _store_log(
242 user_log = _store_log(
243 action_name=safe_unicode(action),
243 action_name=safe_unicode(action),
244 action_data=action_data or {},
244 action_data=action_data or {},
245 user_id=user_id,
245 user_id=user_id,
246 username=username,
246 username=username,
247 user_data=user_data or {},
247 user_data=user_data or {},
248 ip_address=safe_unicode(ip_addr),
248 ip_address=safe_unicode(ip_addr),
249 repository_id=repository_id,
249 repository_id=repository_id,
250 repository_name=repository_name
250 repository_name=repository_name
251 )
251 )
252 sa_session.add(user_log)
252 sa_session.add(user_log)
253 if commit:
253 if commit:
254 sa_session.commit()
254 sa_session.commit()
255
255
256 except Exception:
256 except Exception:
257 log.exception('AUDIT: failed to store audit log')
257 log.exception('AUDIT: failed to store audit log')
General Comments 0
You need to be logged in to leave comments. Login now