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