##// END OF EJS Templates
audit-logs: added user.register audit log entry.
marcink -
r2384:ef27e94a default
parent child Browse files
Show More
@@ -1,428 +1,435 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import time
22 22 import collections
23 23 import datetime
24 24 import formencode
25 25 import formencode.htmlfill
26 26 import logging
27 27 import urlparse
28 28
29 29 from pyramid.httpexceptions import HTTPFound
30 30 from pyramid.view import view_config
31 31 from recaptcha.client.captcha import submit
32 32
33 33 from rhodecode.apps._base import BaseAppView
34 34 from rhodecode.authentication.base import authenticate, HTTP_TYPE
35 35 from rhodecode.events import UserRegistered, trigger
36 36 from rhodecode.lib import helpers as h
37 37 from rhodecode.lib import audit_logger
38 38 from rhodecode.lib.auth import (
39 39 AuthUser, HasPermissionAnyDecorator, CSRFRequired)
40 40 from rhodecode.lib.base import get_ip_addr
41 41 from rhodecode.lib.exceptions import UserCreationError
42 42 from rhodecode.lib.utils2 import safe_str
43 43 from rhodecode.model.db import User, UserApiKeys
44 44 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
45 45 from rhodecode.model.meta import Session
46 46 from rhodecode.model.auth_token import AuthTokenModel
47 47 from rhodecode.model.settings import SettingsModel
48 48 from rhodecode.model.user import UserModel
49 49 from rhodecode.translation import _
50 50
51 51
52 52 log = logging.getLogger(__name__)
53 53
54 54 CaptchaData = collections.namedtuple(
55 55 'CaptchaData', 'active, private_key, public_key')
56 56
57 57
58 58 def _store_user_in_session(session, username, remember=False):
59 59 user = User.get_by_username(username, case_insensitive=True)
60 60 auth_user = AuthUser(user.user_id)
61 61 auth_user.set_authenticated()
62 62 cs = auth_user.get_cookie_store()
63 63 session['rhodecode_user'] = cs
64 64 user.update_lastlogin()
65 65 Session().commit()
66 66
67 67 # If they want to be remembered, update the cookie
68 68 if remember:
69 69 _year = (datetime.datetime.now() +
70 70 datetime.timedelta(seconds=60 * 60 * 24 * 365))
71 71 session._set_cookie_expires(_year)
72 72
73 73 session.save()
74 74
75 75 safe_cs = cs.copy()
76 76 safe_cs['password'] = '****'
77 77 log.info('user %s is now authenticated and stored in '
78 78 'session, session attrs %s', username, safe_cs)
79 79
80 80 # dumps session attrs back to cookie
81 81 session._update_cookie_out()
82 82 # we set new cookie
83 83 headers = None
84 84 if session.request['set_cookie']:
85 85 # send set-cookie headers back to response to update cookie
86 86 headers = [('Set-Cookie', session.request['cookie_out'])]
87 87 return headers
88 88
89 89
90 90 def get_came_from(request):
91 91 came_from = safe_str(request.GET.get('came_from', ''))
92 92 parsed = urlparse.urlparse(came_from)
93 93 allowed_schemes = ['http', 'https']
94 94 default_came_from = h.route_path('home')
95 95 if parsed.scheme and parsed.scheme not in allowed_schemes:
96 96 log.error('Suspicious URL scheme detected %s for url %s' %
97 97 (parsed.scheme, parsed))
98 98 came_from = default_came_from
99 99 elif parsed.netloc and request.host != parsed.netloc:
100 100 log.error('Suspicious NETLOC detected %s for url %s server url '
101 101 'is: %s' % (parsed.netloc, parsed, request.host))
102 102 came_from = default_came_from
103 103 elif any(bad_str in parsed.path for bad_str in ('\r', '\n')):
104 104 log.error('Header injection detected `%s` for url %s server url ' %
105 105 (parsed.path, parsed))
106 106 came_from = default_came_from
107 107
108 108 return came_from or default_came_from
109 109
110 110
111 111 class LoginView(BaseAppView):
112 112
113 113 def load_default_context(self):
114 114 c = self._get_local_tmpl_context()
115 115 c.came_from = get_came_from(self.request)
116 116
117 117 return c
118 118
119 119 def _get_captcha_data(self):
120 120 settings = SettingsModel().get_all_settings()
121 121 private_key = settings.get('rhodecode_captcha_private_key')
122 122 public_key = settings.get('rhodecode_captcha_public_key')
123 123 active = bool(private_key)
124 124 return CaptchaData(
125 125 active=active, private_key=private_key, public_key=public_key)
126 126
127 127 @view_config(
128 128 route_name='login', request_method='GET',
129 129 renderer='rhodecode:templates/login.mako')
130 130 def login(self):
131 131 c = self.load_default_context()
132 132 auth_user = self._rhodecode_user
133 133
134 134 # redirect if already logged in
135 135 if (auth_user.is_authenticated and
136 136 not auth_user.is_default and auth_user.ip_allowed):
137 137 raise HTTPFound(c.came_from)
138 138
139 139 # check if we use headers plugin, and try to login using it.
140 140 try:
141 141 log.debug('Running PRE-AUTH for headers based authentication')
142 142 auth_info = authenticate(
143 143 '', '', self.request.environ, HTTP_TYPE, skip_missing=True)
144 144 if auth_info:
145 145 headers = _store_user_in_session(
146 146 self.session, auth_info.get('username'))
147 147 raise HTTPFound(c.came_from, headers=headers)
148 148 except UserCreationError as e:
149 149 log.error(e)
150 150 h.flash(e, category='error')
151 151
152 152 return self._get_template_context(c)
153 153
154 154 @view_config(
155 155 route_name='login', request_method='POST',
156 156 renderer='rhodecode:templates/login.mako')
157 157 def login_post(self):
158 158 c = self.load_default_context()
159 159
160 160 login_form = LoginForm(self.request.translate)()
161 161
162 162 try:
163 163 self.session.invalidate()
164 164 form_result = login_form.to_python(self.request.POST)
165 165 # form checks for username/password, now we're authenticated
166 166 headers = _store_user_in_session(
167 167 self.session,
168 168 username=form_result['username'],
169 169 remember=form_result['remember'])
170 170 log.debug('Redirecting to "%s" after login.', c.came_from)
171 171
172 172 audit_user = audit_logger.UserWrap(
173 173 username=self.request.POST.get('username'),
174 174 ip_addr=self.request.remote_addr)
175 175 action_data = {'user_agent': self.request.user_agent}
176 176 audit_logger.store_web(
177 177 'user.login.success', action_data=action_data,
178 178 user=audit_user, commit=True)
179 179
180 180 raise HTTPFound(c.came_from, headers=headers)
181 181 except formencode.Invalid as errors:
182 182 defaults = errors.value
183 183 # remove password from filling in form again
184 184 defaults.pop('password', None)
185 185 render_ctx = {
186 186 'errors': errors.error_dict,
187 187 'defaults': defaults,
188 188 }
189 189
190 190 audit_user = audit_logger.UserWrap(
191 191 username=self.request.POST.get('username'),
192 192 ip_addr=self.request.remote_addr)
193 193 action_data = {'user_agent': self.request.user_agent}
194 194 audit_logger.store_web(
195 195 'user.login.failure', action_data=action_data,
196 196 user=audit_user, commit=True)
197 197 return self._get_template_context(c, **render_ctx)
198 198
199 199 except UserCreationError as e:
200 200 # headers auth or other auth functions that create users on
201 201 # the fly can throw this exception signaling that there's issue
202 202 # with user creation, explanation should be provided in
203 203 # Exception itself
204 204 h.flash(e, category='error')
205 205 return self._get_template_context(c)
206 206
207 207 @CSRFRequired()
208 208 @view_config(route_name='logout', request_method='POST')
209 209 def logout(self):
210 210 auth_user = self._rhodecode_user
211 211 log.info('Deleting session for user: `%s`', auth_user)
212 212
213 213 action_data = {'user_agent': self.request.user_agent}
214 214 audit_logger.store_web(
215 215 'user.logout', action_data=action_data,
216 216 user=auth_user, commit=True)
217 217 self.session.delete()
218 218 return HTTPFound(h.route_path('home'))
219 219
220 220 @HasPermissionAnyDecorator(
221 221 'hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
222 222 @view_config(
223 223 route_name='register', request_method='GET',
224 224 renderer='rhodecode:templates/register.mako',)
225 225 def register(self, defaults=None, errors=None):
226 226 c = self.load_default_context()
227 227 defaults = defaults or {}
228 228 errors = errors or {}
229 229
230 230 settings = SettingsModel().get_all_settings()
231 231 register_message = settings.get('rhodecode_register_message') or ''
232 232 captcha = self._get_captcha_data()
233 233 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
234 234 .AuthUser().permissions['global']
235 235
236 236 render_ctx = self._get_template_context(c)
237 237 render_ctx.update({
238 238 'defaults': defaults,
239 239 'errors': errors,
240 240 'auto_active': auto_active,
241 241 'captcha_active': captcha.active,
242 242 'captcha_public_key': captcha.public_key,
243 243 'register_message': register_message,
244 244 })
245 245 return render_ctx
246 246
247 247 @HasPermissionAnyDecorator(
248 248 'hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
249 249 @view_config(
250 250 route_name='register', request_method='POST',
251 251 renderer='rhodecode:templates/register.mako')
252 252 def register_post(self):
253 253 self.load_default_context()
254 254 captcha = self._get_captcha_data()
255 255 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
256 256 .AuthUser().permissions['global']
257 257
258 258 register_form = RegisterForm(self.request.translate)()
259 259 try:
260 260
261 261 form_result = register_form.to_python(self.request.POST)
262 262 form_result['active'] = auto_active
263 263
264 264 if captcha.active:
265 265 response = submit(
266 266 self.request.POST.get('recaptcha_challenge_field'),
267 267 self.request.POST.get('recaptcha_response_field'),
268 268 private_key=captcha.private_key,
269 269 remoteip=get_ip_addr(self.request.environ))
270 270 if not response.is_valid:
271 271 _value = form_result
272 272 _msg = _('Bad captcha')
273 273 error_dict = {'recaptcha_field': _msg}
274 274 raise formencode.Invalid(_msg, _value, None,
275 275 error_dict=error_dict)
276 276
277 277 new_user = UserModel().create_registration(form_result)
278
279 action_data = {'data': new_user.get_api_data(),
280 'user_agent': self.request.user_agent}
281 audit_logger.store_web(
282 'user.register', action_data=action_data,
283 user=new_user)
284
278 285 event = UserRegistered(user=new_user, session=self.session)
279 286 trigger(event)
280 287 h.flash(
281 288 _('You have successfully registered with RhodeCode'),
282 289 category='success')
283 290 Session().commit()
284 291
285 292 redirect_ro = self.request.route_path('login')
286 293 raise HTTPFound(redirect_ro)
287 294
288 295 except formencode.Invalid as errors:
289 296 errors.value.pop('password', None)
290 297 errors.value.pop('password_confirmation', None)
291 298 return self.register(
292 299 defaults=errors.value, errors=errors.error_dict)
293 300
294 301 except UserCreationError as e:
295 302 # container auth or other auth functions that create users on
296 303 # the fly can throw this exception signaling that there's issue
297 304 # with user creation, explanation should be provided in
298 305 # Exception itself
299 306 h.flash(e, category='error')
300 307 return self.register()
301 308
302 309 @view_config(
303 310 route_name='reset_password', request_method=('GET', 'POST'),
304 311 renderer='rhodecode:templates/password_reset.mako')
305 312 def password_reset(self):
306 313 c = self.load_default_context()
307 314 captcha = self._get_captcha_data()
308 315
309 316 template_context = {
310 317 'captcha_active': captcha.active,
311 318 'captcha_public_key': captcha.public_key,
312 319 'defaults': {},
313 320 'errors': {},
314 321 }
315 322
316 323 # always send implicit message to prevent from discovery of
317 324 # matching emails
318 325 msg = _('If such email exists, a password reset link was sent to it.')
319 326
320 327 if self.request.POST:
321 328 if h.HasPermissionAny('hg.password_reset.disabled')():
322 329 _email = self.request.POST.get('email', '')
323 330 log.error('Failed attempt to reset password for `%s`.', _email)
324 331 h.flash(_('Password reset has been disabled.'),
325 332 category='error')
326 333 return HTTPFound(self.request.route_path('reset_password'))
327 334
328 335 password_reset_form = PasswordResetForm(self.request.translate)()
329 336 try:
330 337 form_result = password_reset_form.to_python(
331 338 self.request.POST)
332 339 user_email = form_result['email']
333 340
334 341 if captcha.active:
335 342 response = submit(
336 343 self.request.POST.get('recaptcha_challenge_field'),
337 344 self.request.POST.get('recaptcha_response_field'),
338 345 private_key=captcha.private_key,
339 346 remoteip=get_ip_addr(self.request.environ))
340 347 if not response.is_valid:
341 348 _value = form_result
342 349 _msg = _('Bad captcha')
343 350 error_dict = {'recaptcha_field': _msg}
344 351 raise formencode.Invalid(
345 352 _msg, _value, None, error_dict=error_dict)
346 353
347 354 # Generate reset URL and send mail.
348 355 user = User.get_by_email(user_email)
349 356
350 357 # generate password reset token that expires in 10minutes
351 358 desc = 'Generated token for password reset from {}'.format(
352 359 datetime.datetime.now().isoformat())
353 360 reset_token = AuthTokenModel().create(
354 361 user, lifetime=10,
355 362 description=desc,
356 363 role=UserApiKeys.ROLE_PASSWORD_RESET)
357 364 Session().commit()
358 365
359 366 log.debug('Successfully created password recovery token')
360 367 password_reset_url = self.request.route_url(
361 368 'reset_password_confirmation',
362 369 _query={'key': reset_token.api_key})
363 370 UserModel().reset_password_link(
364 371 form_result, password_reset_url)
365 372 # Display success message and redirect.
366 373 h.flash(msg, category='success')
367 374
368 375 action_data = {'email': user_email,
369 376 'user_agent': self.request.user_agent}
370 377 audit_logger.store_web(
371 378 'user.password.reset_request', action_data=action_data,
372 379 user=self._rhodecode_user, commit=True)
373 380 return HTTPFound(self.request.route_path('reset_password'))
374 381
375 382 except formencode.Invalid as errors:
376 383 template_context.update({
377 384 'defaults': errors.value,
378 385 'errors': errors.error_dict,
379 386 })
380 387 if not self.request.POST.get('email'):
381 388 # case of empty email, we want to report that
382 389 return self._get_template_context(c, **template_context)
383 390
384 391 if 'recaptcha_field' in errors.error_dict:
385 392 # case of failed captcha
386 393 return self._get_template_context(c, **template_context)
387 394
388 395 log.debug('faking response on invalid password reset')
389 396 # make this take 2s, to prevent brute forcing.
390 397 time.sleep(2)
391 398 h.flash(msg, category='success')
392 399 return HTTPFound(self.request.route_path('reset_password'))
393 400
394 401 return self._get_template_context(c, **template_context)
395 402
396 403 @view_config(route_name='reset_password_confirmation',
397 404 request_method='GET')
398 405 def password_reset_confirmation(self):
399 406 self.load_default_context()
400 407 if self.request.GET and self.request.GET.get('key'):
401 408 # make this take 2s, to prevent brute forcing.
402 409 time.sleep(2)
403 410
404 411 token = AuthTokenModel().get_auth_token(
405 412 self.request.GET.get('key'))
406 413
407 414 # verify token is the correct role
408 415 if token is None or token.role != UserApiKeys.ROLE_PASSWORD_RESET:
409 416 log.debug('Got token with role:%s expected is %s',
410 417 getattr(token, 'role', 'EMPTY_TOKEN'),
411 418 UserApiKeys.ROLE_PASSWORD_RESET)
412 419 h.flash(
413 420 _('Given reset token is invalid'), category='error')
414 421 return HTTPFound(self.request.route_path('reset_password'))
415 422
416 423 try:
417 424 owner = token.user
418 425 data = {'email': owner.email, 'token': token.api_key}
419 426 UserModel().reset_password(data)
420 427 h.flash(
421 428 _('Your password reset was successful, '
422 429 'a new password has been sent to your email'),
423 430 category='success')
424 431 except Exception as e:
425 432 log.error(e)
426 433 return HTTPFound(self.request.route_path('reset_password'))
427 434
428 435 return HTTPFound(self.request.route_path('login'))
@@ -1,263 +1,264 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2017-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import datetime
23 23
24 24 from rhodecode.lib.jsonalchemy import JsonRaw
25 25 from rhodecode.model import meta
26 26 from rhodecode.model.db import User, UserLog, Repository
27 27
28 28
29 29 log = logging.getLogger(__name__)
30 30
31 31 # action as key, and expected action_data as value
32 32 ACTIONS_V1 = {
33 33 'user.login.success': {'user_agent': ''},
34 34 'user.login.failure': {'user_agent': ''},
35 35 'user.logout': {'user_agent': ''},
36 'user.register': {},
36 37 'user.password.reset_request': {},
37 38 'user.push': {'user_agent': '', 'commit_ids': []},
38 39 'user.pull': {'user_agent': ''},
39 40
40 41 'user.create': {'data': {}},
41 42 'user.delete': {'old_data': {}},
42 43 'user.edit': {'old_data': {}},
43 44 'user.edit.permissions': {},
44 45 'user.edit.ip.add': {'ip': {}, 'user': {}},
45 46 'user.edit.ip.delete': {'ip': {}, 'user': {}},
46 47 'user.edit.token.add': {'token': {}, 'user': {}},
47 48 'user.edit.token.delete': {'token': {}, 'user': {}},
48 49 'user.edit.email.add': {'email': ''},
49 50 'user.edit.email.delete': {'email': ''},
50 51 'user.edit.ssh_key.add': {'token': {}, 'user': {}},
51 52 'user.edit.ssh_key.delete': {'token': {}, 'user': {}},
52 53 'user.edit.password_reset.enabled': {},
53 54 'user.edit.password_reset.disabled': {},
54 55
55 56 'user_group.create': {'data': {}},
56 57 'user_group.delete': {'old_data': {}},
57 58 'user_group.edit': {'old_data': {}},
58 59 'user_group.edit.permissions': {},
59 60 'user_group.edit.member.add': {'user': {}},
60 61 'user_group.edit.member.delete': {'user': {}},
61 62
62 63 'repo.create': {'data': {}},
63 64 'repo.fork': {'data': {}},
64 65 'repo.edit': {'old_data': {}},
65 66 'repo.edit.permissions': {},
66 67 'repo.delete': {'old_data': {}},
67 68 'repo.commit.strip': {'commit_id': ''},
68 69 'repo.archive.download': {'user_agent': '', 'archive_name': '',
69 70 'archive_spec': '', 'archive_cached': ''},
70 71 'repo.pull_request.create': '',
71 72 'repo.pull_request.edit': '',
72 73 'repo.pull_request.delete': '',
73 74 'repo.pull_request.close': '',
74 75 'repo.pull_request.merge': '',
75 76 'repo.pull_request.vote': '',
76 77 'repo.pull_request.comment.create': '',
77 78 'repo.pull_request.comment.delete': '',
78 79
79 80 'repo.pull_request.reviewer.add': '',
80 81 'repo.pull_request.reviewer.delete': '',
81 82
82 83 'repo.commit.comment.create': {'data': {}},
83 84 'repo.commit.comment.delete': {'data': {}},
84 85 'repo.commit.vote': '',
85 86
86 87 'repo_group.create': {'data': {}},
87 88 'repo_group.edit': {'old_data': {}},
88 89 'repo_group.edit.permissions': {},
89 90 'repo_group.delete': {'old_data': {}},
90 91 }
91 92 ACTIONS = ACTIONS_V1
92 93
93 94 SOURCE_WEB = 'source_web'
94 95 SOURCE_API = 'source_api'
95 96
96 97
97 98 class UserWrap(object):
98 99 """
99 100 Fake object used to imitate AuthUser
100 101 """
101 102
102 103 def __init__(self, user_id=None, username=None, ip_addr=None):
103 104 self.user_id = user_id
104 105 self.username = username
105 106 self.ip_addr = ip_addr
106 107
107 108
108 109 class RepoWrap(object):
109 110 """
110 111 Fake object used to imitate RepoObject that audit logger requires
111 112 """
112 113
113 114 def __init__(self, repo_id=None, repo_name=None):
114 115 self.repo_id = repo_id
115 116 self.repo_name = repo_name
116 117
117 118
118 119 def _store_log(action_name, action_data, user_id, username, user_data,
119 120 ip_address, repository_id, repository_name):
120 121 user_log = UserLog()
121 122 user_log.version = UserLog.VERSION_2
122 123
123 124 user_log.action = action_name
124 125 user_log.action_data = action_data or JsonRaw(u'{}')
125 126
126 127 user_log.user_ip = ip_address
127 128
128 129 user_log.user_id = user_id
129 130 user_log.username = username
130 131 user_log.user_data = user_data or JsonRaw(u'{}')
131 132
132 133 user_log.repository_id = repository_id
133 134 user_log.repository_name = repository_name
134 135
135 136 user_log.action_date = datetime.datetime.now()
136 137
137 138 return user_log
138 139
139 140
140 141 def store_web(*args, **kwargs):
141 142 if 'action_data' not in kwargs:
142 143 kwargs['action_data'] = {}
143 144 kwargs['action_data'].update({
144 145 'source': SOURCE_WEB
145 146 })
146 147 return store(*args, **kwargs)
147 148
148 149
149 150 def store_api(*args, **kwargs):
150 151 if 'action_data' not in kwargs:
151 152 kwargs['action_data'] = {}
152 153 kwargs['action_data'].update({
153 154 'source': SOURCE_API
154 155 })
155 156 return store(*args, **kwargs)
156 157
157 158
158 159 def store(action, user, action_data=None, user_data=None, ip_addr=None,
159 160 repo=None, sa_session=None, commit=False):
160 161 """
161 162 Audit logger for various actions made by users, typically this
162 163 results in a call such::
163 164
164 165 from rhodecode.lib import audit_logger
165 166
166 167 audit_logger.store(
167 168 'repo.edit', user=self._rhodecode_user)
168 169 audit_logger.store(
169 170 'repo.delete', action_data={'data': repo_data},
170 171 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'))
171 172
172 173 # repo action
173 174 audit_logger.store(
174 175 'repo.delete',
175 176 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'),
176 177 repo=audit_logger.RepoWrap(repo_name='some-repo'))
177 178
178 179 # repo action, when we know and have the repository object already
179 180 audit_logger.store(
180 181 'repo.delete', action_data={'source': audit_logger.SOURCE_WEB, },
181 182 user=self._rhodecode_user,
182 183 repo=repo_object)
183 184
184 185 # alternative wrapper to the above
185 186 audit_logger.store_web(
186 187 'repo.delete', action_data={},
187 188 user=self._rhodecode_user,
188 189 repo=repo_object)
189 190
190 191 # without an user ?
191 192 audit_logger.store(
192 193 'user.login.failure',
193 194 user=audit_logger.UserWrap(
194 195 username=self.request.params.get('username'),
195 196 ip_addr=self.request.remote_addr))
196 197
197 198 """
198 199 from rhodecode.lib.utils2 import safe_unicode
199 200 from rhodecode.lib.auth import AuthUser
200 201
201 202 action_spec = ACTIONS.get(action, None)
202 203 if action_spec is None:
203 204 raise ValueError('Action `{}` is not supported'.format(action))
204 205
205 206 if not sa_session:
206 207 sa_session = meta.Session()
207 208
208 209 try:
209 210 username = getattr(user, 'username', None)
210 211 if not username:
211 212 pass
212 213
213 214 user_id = getattr(user, 'user_id', None)
214 215 if not user_id:
215 216 # maybe we have username ? Try to figure user_id from username
216 217 if username:
217 218 user_id = getattr(
218 219 User.get_by_username(username), 'user_id', None)
219 220
220 221 ip_addr = ip_addr or getattr(user, 'ip_addr', None)
221 222 if not ip_addr:
222 223 pass
223 224
224 225 if not user_data:
225 226 # try to get this from the auth user
226 227 if isinstance(user, AuthUser):
227 228 user_data = {
228 229 'username': user.username,
229 230 'email': user.email,
230 231 }
231 232
232 233 repository_name = getattr(repo, 'repo_name', None)
233 234 repository_id = getattr(repo, 'repo_id', None)
234 235 if not repository_id:
235 236 # maybe we have repo_name ? Try to figure repo_id from repo_name
236 237 if repository_name:
237 238 repository_id = getattr(
238 239 Repository.get_by_repo_name(repository_name), 'repo_id', None)
239 240
240 241 action_name = safe_unicode(action)
241 242 ip_address = safe_unicode(ip_addr)
242 243
243 244 user_log = _store_log(
244 245 action_name=action_name,
245 246 action_data=action_data or {},
246 247 user_id=user_id,
247 248 username=username,
248 249 user_data=user_data or {},
249 250 ip_address=ip_address,
250 251 repository_id=repository_id,
251 252 repository_name=repository_name
252 253 )
253 254
254 255 sa_session.add(user_log)
255 256 if commit:
256 257 sa_session.commit()
257 258
258 259 entry_id = user_log.entry_id or ''
259 260 log.info('AUDIT[%s]: Logging action: `%s` by user:id:%s[%s] ip:%s',
260 261 entry_id, action_name, user_id, username, ip_address)
261 262
262 263 except Exception:
263 264 log.exception('AUDIT: failed to store audit log')
General Comments 0
You need to be logged in to leave comments. Login now