##// END OF EJS Templates
audit-logs: added audit logs on user groups admin page.
marcink -
r1805:48072a57 default
parent child Browse files
Show More
@@ -1,517 +1,510 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-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 """
22 22 User Groups crud controller for pylons
23 23 """
24 24
25 25 import logging
26 26 import formencode
27 27
28 28 import peppercorn
29 29 from formencode import htmlfill
30 30 from pylons import request, tmpl_context as c, url, config
31 31 from pylons.controllers.util import redirect
32 32 from pylons.i18n.translation import _
33 33
34 34 from sqlalchemy.orm import joinedload
35 35
36 36 from rhodecode.lib import auth
37 37 from rhodecode.lib import helpers as h
38 from rhodecode.lib import audit_logger
38 39 from rhodecode.lib.ext_json import json
39 40 from rhodecode.lib.exceptions import UserGroupAssignedException,\
40 41 RepoGroupAssignmentError
41 from rhodecode.lib.utils import jsonify, action_logger
42 from rhodecode.lib.utils import jsonify
42 43 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
43 44 from rhodecode.lib.auth import (
44 45 LoginRequired, NotAnonymous, HasUserGroupPermissionAnyDecorator,
45 46 HasPermissionAnyDecorator, XHRRequired)
46 47 from rhodecode.lib.base import BaseController, render
47 48 from rhodecode.model.permission import PermissionModel
48 49 from rhodecode.model.scm import UserGroupList
49 50 from rhodecode.model.user_group import UserGroupModel
50 51 from rhodecode.model.db import (
51 52 User, UserGroup, UserGroupRepoToPerm, UserGroupRepoGroupToPerm)
52 53 from rhodecode.model.forms import (
53 54 UserGroupForm, UserGroupPermsForm, UserIndividualPermissionsForm,
54 55 UserPermissionsForm)
55 56 from rhodecode.model.meta import Session
56 57
57 58
58 59 log = logging.getLogger(__name__)
59 60
60 61
61 62 class UserGroupsController(BaseController):
62 63 """REST Controller styled on the Atom Publishing Protocol"""
63 64
64 65 @LoginRequired()
65 66 def __before__(self):
66 67 super(UserGroupsController, self).__before__()
67 68 c.available_permissions = config['available_permissions']
68 69 PermissionModel().set_global_permission_choices(c, gettext_translator=_)
69 70
70 71 def __load_data(self, user_group_id):
71 72 c.group_members_obj = [x.user for x in c.user_group.members]
72 73 c.group_members_obj.sort(key=lambda u: u.username.lower())
73 74 c.group_members = [(x.user_id, x.username) for x in c.group_members_obj]
74 75
75 76 def __load_defaults(self, user_group_id):
76 77 """
77 78 Load defaults settings for edit, and update
78 79
79 80 :param user_group_id:
80 81 """
81 82 user_group = UserGroup.get_or_404(user_group_id)
82 83 data = user_group.get_dict()
83 84 # fill owner
84 85 if user_group.user:
85 86 data.update({'user': user_group.user.username})
86 87 else:
87 88 replacement_user = User.get_first_super_admin().username
88 89 data.update({'user': replacement_user})
89 90 return data
90 91
91 92 def _revoke_perms_on_yourself(self, form_result):
92 93 _updates = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
93 94 form_result['perm_updates'])
94 95 _additions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
95 96 form_result['perm_additions'])
96 97 _deletions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
97 98 form_result['perm_deletions'])
98 99 admin_perm = 'usergroup.admin'
99 100 if _updates and _updates[0][1] != admin_perm or \
100 101 _additions and _additions[0][1] != admin_perm or \
101 102 _deletions and _deletions[0][1] != admin_perm:
102 103 return True
103 104 return False
104 105
105 106 # permission check inside
106 107 @NotAnonymous()
107 108 def index(self):
108 """GET /users_groups: All items in the collection"""
109 # url('users_groups')
110 109
111 110 from rhodecode.lib.utils import PartialRenderer
112 111 _render = PartialRenderer('data_table/_dt_elements.mako')
113 112
114 113 def user_group_name(user_group_id, user_group_name):
115 114 return _render("user_group_name", user_group_id, user_group_name)
116 115
117 116 def user_group_actions(user_group_id, user_group_name):
118 117 return _render("user_group_actions", user_group_id, user_group_name)
119 118
120 119 # json generate
121 120 group_iter = UserGroupList(UserGroup.query().all(),
122 121 perm_set=['usergroup.admin'])
123 122
124 123 user_groups_data = []
125 124 for user_gr in group_iter:
126 125 user_groups_data.append({
127 126 "group_name": user_group_name(
128 127 user_gr.users_group_id, h.escape(user_gr.users_group_name)),
129 128 "group_name_raw": user_gr.users_group_name,
130 129 "desc": h.escape(user_gr.user_group_description),
131 130 "members": len(user_gr.members),
132 131 "sync": user_gr.group_data.get('extern_type'),
133 132 "active": h.bool2icon(user_gr.users_group_active),
134 133 "owner": h.escape(h.link_to_user(user_gr.user.username)),
135 134 "action": user_group_actions(
136 135 user_gr.users_group_id, user_gr.users_group_name)
137 136 })
138 137
139 138 c.data = json.dumps(user_groups_data)
140 139 return render('admin/user_groups/user_groups.mako')
141 140
142 141 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
143 142 @auth.CSRFRequired()
144 143 def create(self):
145 """POST /users_groups: Create a new item"""
146 # url('users_groups')
147 144
148 145 users_group_form = UserGroupForm()()
149 146 try:
150 147 form_result = users_group_form.to_python(dict(request.POST))
151 148 user_group = UserGroupModel().create(
152 149 name=form_result['users_group_name'],
153 150 description=form_result['user_group_description'],
154 151 owner=c.rhodecode_user.user_id,
155 152 active=form_result['users_group_active'])
156 153 Session().flush()
157
154 creation_data = user_group.get_api_data()
158 155 user_group_name = form_result['users_group_name']
159 action_logger(c.rhodecode_user,
160 'admin_created_users_group:%s' % user_group_name,
161 None, self.ip_addr, self.sa)
162 user_group_link = h.link_to(h.escape(user_group_name),
163 url('edit_users_group',
164 user_group_id=user_group.users_group_id))
156
157 audit_logger.store_web(
158 'user_group.create', action_data={'data': creation_data},
159 user=c.rhodecode_user)
160
161 user_group_link = h.link_to(
162 h.escape(user_group_name),
163 url('edit_users_group', user_group_id=user_group.users_group_id))
165 164 h.flash(h.literal(_('Created user group %(user_group_link)s')
166 165 % {'user_group_link': user_group_link}),
167 166 category='success')
168 167 Session().commit()
169 168 except formencode.Invalid as errors:
170 169 return htmlfill.render(
171 170 render('admin/user_groups/user_group_add.mako'),
172 171 defaults=errors.value,
173 172 errors=errors.error_dict or {},
174 173 prefix_error=False,
175 174 encoding="UTF-8",
176 175 force_defaults=False)
177 176 except Exception:
178 177 log.exception("Exception creating user group")
179 178 h.flash(_('Error occurred during creation of user group %s') \
180 179 % request.POST.get('users_group_name'), category='error')
181 180
182 181 return redirect(
183 182 url('edit_users_group', user_group_id=user_group.users_group_id))
184 183
185 184 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
186 185 def new(self):
187 186 """GET /user_groups/new: Form to create a new item"""
188 187 # url('new_users_group')
189 188 return render('admin/user_groups/user_group_add.mako')
190 189
191 190 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
192 191 @auth.CSRFRequired()
193 192 def update(self, user_group_id):
194 """PUT /user_groups/user_group_id: Update an existing item"""
195 # Forms posted to this method should contain a hidden field:
196 # <input type="hidden" name="_method" value="PUT" />
197 # Or using helpers:
198 # h.form(url('users_group', user_group_id=ID),
199 # method='put')
200 # url('users_group', user_group_id=ID)
201 193
202 194 user_group_id = safe_int(user_group_id)
203 195 c.user_group = UserGroup.get_or_404(user_group_id)
204 196 c.active = 'settings'
205 197 self.__load_data(user_group_id)
206 198
207 199 users_group_form = UserGroupForm(
208 200 edit=True, old_data=c.user_group.get_dict(), allow_disabled=True)()
209 201
202 old_values = c.user_group.get_api_data()
210 203 try:
211 204 form_result = users_group_form.to_python(request.POST)
212 205 pstruct = peppercorn.parse(request.POST.items())
213 206 form_result['users_group_members'] = pstruct['user_group_members']
214 207
215 208 UserGroupModel().update(c.user_group, form_result)
216 209 updated_user_group = form_result['users_group_name']
217 action_logger(c.rhodecode_user,
218 'admin_updated_users_group:%s' % updated_user_group,
219 None, self.ip_addr, self.sa)
210
211 audit_logger.store_web(
212 'user_group.edit', action_data={'old_data': old_values},
213 user=c.rhodecode_user)
214
220 215 h.flash(_('Updated user group %s') % updated_user_group,
221 216 category='success')
222 217 Session().commit()
223 218 except formencode.Invalid as errors:
224 219 defaults = errors.value
225 220 e = errors.error_dict or {}
226 221
227 222 return htmlfill.render(
228 223 render('admin/user_groups/user_group_edit.mako'),
229 224 defaults=defaults,
230 225 errors=e,
231 226 prefix_error=False,
232 227 encoding="UTF-8",
233 228 force_defaults=False)
234 229 except Exception:
235 230 log.exception("Exception during update of user group")
236 231 h.flash(_('Error occurred during update of user group %s')
237 232 % request.POST.get('users_group_name'), category='error')
238 233
239 234 return redirect(url('edit_users_group', user_group_id=user_group_id))
240 235
241 236 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
242 237 @auth.CSRFRequired()
243 238 def delete(self, user_group_id):
244 """DELETE /user_groups/user_group_id: Delete an existing item"""
245 # Forms posted to this method should contain a hidden field:
246 # <input type="hidden" name="_method" value="DELETE" />
247 # Or using helpers:
248 # h.form(url('users_group', user_group_id=ID),
249 # method='delete')
250 # url('users_group', user_group_id=ID)
251 239 user_group_id = safe_int(user_group_id)
252 240 c.user_group = UserGroup.get_or_404(user_group_id)
253 241 force = str2bool(request.POST.get('force'))
254 242
243 old_values = c.user_group.get_api_data()
255 244 try:
256 245 UserGroupModel().delete(c.user_group, force=force)
246 audit_logger.store_web(
247 'user.delete', action_data={'old_data': old_values},
248 user=c.rhodecode_user)
257 249 Session().commit()
258 250 h.flash(_('Successfully deleted user group'), category='success')
259 251 except UserGroupAssignedException as e:
260 252 h.flash(str(e), category='error')
261 253 except Exception:
262 254 log.exception("Exception during deletion of user group")
263 255 h.flash(_('An error occurred during deletion of user group'),
264 256 category='error')
265 257 return redirect(url('users_groups'))
266 258
267 259 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
268 260 def edit(self, user_group_id):
269 261 """GET /user_groups/user_group_id/edit: Form to edit an existing item"""
270 262 # url('edit_users_group', user_group_id=ID)
271 263
272 264 user_group_id = safe_int(user_group_id)
273 265 c.user_group = UserGroup.get_or_404(user_group_id)
274 266 c.active = 'settings'
275 267 self.__load_data(user_group_id)
276 268
277 269 defaults = self.__load_defaults(user_group_id)
278 270
279 271 return htmlfill.render(
280 272 render('admin/user_groups/user_group_edit.mako'),
281 273 defaults=defaults,
282 274 encoding="UTF-8",
283 275 force_defaults=False
284 276 )
285 277
286 278 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
287 279 def edit_perms(self, user_group_id):
288 280 user_group_id = safe_int(user_group_id)
289 281 c.user_group = UserGroup.get_or_404(user_group_id)
290 282 c.active = 'perms'
291 283
292 284 defaults = {}
293 285 # fill user group users
294 286 for p in c.user_group.user_user_group_to_perm:
295 287 defaults.update({'u_perm_%s' % p.user.user_id:
296 288 p.permission.permission_name})
297 289
298 290 for p in c.user_group.user_group_user_group_to_perm:
299 291 defaults.update({'g_perm_%s' % p.user_group.users_group_id:
300 292 p.permission.permission_name})
301 293
302 294 return htmlfill.render(
303 295 render('admin/user_groups/user_group_edit.mako'),
304 296 defaults=defaults,
305 297 encoding="UTF-8",
306 298 force_defaults=False
307 299 )
308 300
309 301 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
310 302 @auth.CSRFRequired()
311 303 def update_perms(self, user_group_id):
312 304 """
313 305 grant permission for given usergroup
314 306
315 307 :param user_group_id:
316 308 """
317 309 user_group_id = safe_int(user_group_id)
318 310 c.user_group = UserGroup.get_or_404(user_group_id)
319 311 form = UserGroupPermsForm()().to_python(request.POST)
320 312
321 313 if not c.rhodecode_user.is_admin:
322 314 if self._revoke_perms_on_yourself(form):
323 315 msg = _('Cannot change permission for yourself as admin')
324 316 h.flash(msg, category='warning')
325 317 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
326 318
327 319 try:
328 320 UserGroupModel().update_permissions(user_group_id,
329 321 form['perm_additions'], form['perm_updates'], form['perm_deletions'])
330 322 except RepoGroupAssignmentError:
331 323 h.flash(_('Target group cannot be the same'), category='error')
332 324 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
333 #TODO: implement this
334 #action_logger(c.rhodecode_user, 'admin_changed_repo_permissions',
335 # repo_name, self.ip_addr, self.sa)
325
326 # TODO(marcink): implement global permissions
327 # audit_log.store_web('user_group.edit.permissions')
336 328 Session().commit()
337 329 h.flash(_('User Group permissions updated'), category='success')
338 330 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
339 331
340 332 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
341 333 def edit_perms_summary(self, user_group_id):
342 334 user_group_id = safe_int(user_group_id)
343 335 c.user_group = UserGroup.get_or_404(user_group_id)
344 336 c.active = 'perms_summary'
345 337 permissions = {
346 338 'repositories': {},
347 339 'repositories_groups': {},
348 340 }
349 341 ugroup_repo_perms = UserGroupRepoToPerm.query()\
350 342 .options(joinedload(UserGroupRepoToPerm.permission))\
351 343 .options(joinedload(UserGroupRepoToPerm.repository))\
352 344 .filter(UserGroupRepoToPerm.users_group_id == user_group_id)\
353 345 .all()
354 346
355 347 for gr in ugroup_repo_perms:
356 348 permissions['repositories'][gr.repository.repo_name] \
357 349 = gr.permission.permission_name
358 350
359 351 ugroup_group_perms = UserGroupRepoGroupToPerm.query()\
360 352 .options(joinedload(UserGroupRepoGroupToPerm.permission))\
361 353 .options(joinedload(UserGroupRepoGroupToPerm.group))\
362 354 .filter(UserGroupRepoGroupToPerm.users_group_id == user_group_id)\
363 355 .all()
364 356
365 357 for gr in ugroup_group_perms:
366 358 permissions['repositories_groups'][gr.group.group_name] \
367 359 = gr.permission.permission_name
368 360 c.permissions = permissions
369 361 return render('admin/user_groups/user_group_edit.mako')
370 362
371 363 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
372 364 def edit_global_perms(self, user_group_id):
373 365 user_group_id = safe_int(user_group_id)
374 366 c.user_group = UserGroup.get_or_404(user_group_id)
375 367 c.active = 'global_perms'
376 368
377 369 c.default_user = User.get_default_user()
378 370 defaults = c.user_group.get_dict()
379 371 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
380 372 defaults.update(c.user_group.get_default_perms())
381 373
382 374 return htmlfill.render(
383 375 render('admin/user_groups/user_group_edit.mako'),
384 376 defaults=defaults,
385 377 encoding="UTF-8",
386 378 force_defaults=False
387 379 )
388 380
389 381 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
390 382 @auth.CSRFRequired()
391 383 def update_global_perms(self, user_group_id):
392 """PUT /users_perm/user_group_id: Update an existing item"""
393 # url('users_group_perm', user_group_id=ID, method='put')
394 384 user_group_id = safe_int(user_group_id)
395 385 user_group = UserGroup.get_or_404(user_group_id)
396 386 c.active = 'global_perms'
397 387
398 388 try:
399 389 # first stage that verifies the checkbox
400 390 _form = UserIndividualPermissionsForm()
401 391 form_result = _form.to_python(dict(request.POST))
402 392 inherit_perms = form_result['inherit_default_permissions']
403 393 user_group.inherit_default_permissions = inherit_perms
404 394 Session().add(user_group)
405 395
406 396 if not inherit_perms:
407 397 # only update the individual ones if we un check the flag
408 398 _form = UserPermissionsForm(
409 399 [x[0] for x in c.repo_create_choices],
410 400 [x[0] for x in c.repo_create_on_write_choices],
411 401 [x[0] for x in c.repo_group_create_choices],
412 402 [x[0] for x in c.user_group_create_choices],
413 403 [x[0] for x in c.fork_choices],
414 404 [x[0] for x in c.inherit_default_permission_choices])()
415 405
416 406 form_result = _form.to_python(dict(request.POST))
417 407 form_result.update({'perm_user_group_id': user_group.users_group_id})
418 408
419 409 PermissionModel().update_user_group_permissions(form_result)
420 410
421 411 Session().commit()
422 412 h.flash(_('User Group global permissions updated successfully'),
423 413 category='success')
424 414
425 415 except formencode.Invalid as errors:
426 416 defaults = errors.value
427 417 c.user_group = user_group
428 418 return htmlfill.render(
429 419 render('admin/user_groups/user_group_edit.mako'),
430 420 defaults=defaults,
431 421 errors=errors.error_dict or {},
432 422 prefix_error=False,
433 423 encoding="UTF-8",
434 424 force_defaults=False)
435 425 except Exception:
436 426 log.exception("Exception during permissions saving")
437 427 h.flash(_('An error occurred during permissions saving'),
438 428 category='error')
439 429
440 430 return redirect(url('edit_user_group_global_perms', user_group_id=user_group_id))
441 431
442 432 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
443 433 def edit_advanced(self, user_group_id):
444 434 user_group_id = safe_int(user_group_id)
445 435 c.user_group = UserGroup.get_or_404(user_group_id)
446 436 c.active = 'advanced'
447 437 c.group_members_obj = sorted(
448 438 (x.user for x in c.user_group.members),
449 439 key=lambda u: u.username.lower())
450 440
451 441 c.group_to_repos = sorted(
452 442 (x.repository for x in c.user_group.users_group_repo_to_perm),
453 443 key=lambda u: u.repo_name.lower())
454 444
455 445 c.group_to_repo_groups = sorted(
456 446 (x.group for x in c.user_group.users_group_repo_group_to_perm),
457 447 key=lambda u: u.group_name.lower())
458 448
459 449 return render('admin/user_groups/user_group_edit.mako')
460 450
461 451 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
462 452 def edit_advanced_set_synchronization(self, user_group_id):
463 453 user_group_id = safe_int(user_group_id)
464 454 user_group = UserGroup.get_or_404(user_group_id)
465 455
466 456 existing = user_group.group_data.get('extern_type')
467 457
468 458 if existing:
469 459 new_state = user_group.group_data
470 460 new_state['extern_type'] = None
471 461 else:
472 462 new_state = user_group.group_data
473 463 new_state['extern_type'] = 'manual'
474 464 new_state['extern_type_set_by'] = c.rhodecode_user.username
475 465
476 466 try:
477 467 user_group.group_data = new_state
478 468 Session().add(user_group)
479 469 Session().commit()
480 470
481 471 h.flash(_('User Group synchronization updated successfully'),
482 472 category='success')
483 473 except Exception:
484 474 log.exception("Exception during sync settings saving")
485 475 h.flash(_('An error occurred during synchronization update'),
486 476 category='error')
487 477
488 478 return redirect(
489 479 url('edit_user_group_advanced', user_group_id=user_group_id))
490 480
491 481 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
492 482 @XHRRequired()
493 483 @jsonify
494 484 def user_group_members(self, user_group_id):
485 """
486 Return members of given user group
487 """
495 488 user_group_id = safe_int(user_group_id)
496 489 user_group = UserGroup.get_or_404(user_group_id)
497 490 group_members_obj = sorted((x.user for x in user_group.members),
498 491 key=lambda u: u.username.lower())
499 492
500 493 group_members = [
501 494 {
502 495 'id': user.user_id,
503 496 'first_name': user.name,
504 497 'last_name': user.lastname,
505 498 'username': user.username,
506 499 'icon_link': h.gravatar_url(user.email, 30),
507 500 'value_display': h.person(user.email),
508 501 'value': user.username,
509 502 'value_type': 'user',
510 503 'active': user.active,
511 504 }
512 505 for user in group_members_obj
513 506 ]
514 507
515 508 return {
516 509 'members': group_members
517 510 }
@@ -1,233 +1,240 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.model import meta
25 25 from rhodecode.model.db import User, UserLog, Repository
26 26
27 27
28 28 log = logging.getLogger(__name__)
29 29
30 30 # action as key, and expected action_data as value
31 31 ACTIONS = {
32 32 'user.login.success': {'user_agent': ''},
33 33 'user.login.failure': {'user_agent': ''},
34 34 'user.logout': {'user_agent': ''},
35 35 'user.password.reset_request': {},
36 36 'user.push': {'user_agent': '', 'commit_ids': []},
37 37 'user.pull': {'user_agent': ''},
38 38
39 39 'user.create': {'data': {}},
40 40 'user.delete': {'old_data': {}},
41 41 'user.edit': {'old_data': {}},
42 42 'user.edit.permissions': {},
43 43 'user.edit.ip.add': {},
44 44 'user.edit.ip.delete': {},
45 45 'user.edit.token.add': {},
46 46 'user.edit.token.delete': {},
47 47 'user.edit.email.add': {},
48 48 'user.edit.email.delete': {},
49 49 'user.edit.password_reset.enabled': {},
50 50 'user.edit.password_reset.disabled': {},
51 51
52 'user_group.create': {'data': {}},
53 'user_group.delete': {'old_data': {}},
54 'user_group.edit': {'old_data': {}},
55 'user_group.edit.permissions': {},
56 'user_group.edit.member.add': {},
57 'user_group.edit.member.delete': {},
58
52 59 'repo.create': {'data': {}},
53 60 'repo.fork': {'data': {}},
54 61 'repo.edit': {'old_data': {}},
55 62 'repo.edit.permissions': {},
56 63 'repo.delete': {'old_data': {}},
57 64 'repo.commit.strip': {},
58 65 'repo.archive.download': {},
59 66
60 67 'repo_group.create': {'data': {}},
61 68 'repo_group.edit': {'old_data': {}},
62 69 'repo_group.edit.permissions': {},
63 70 'repo_group.delete': {'old_data': {}},
64 71 }
65 72
66 73 SOURCE_WEB = 'source_web'
67 74 SOURCE_API = 'source_api'
68 75
69 76
70 77 class UserWrap(object):
71 78 """
72 79 Fake object used to imitate AuthUser
73 80 """
74 81
75 82 def __init__(self, user_id=None, username=None, ip_addr=None):
76 83 self.user_id = user_id
77 84 self.username = username
78 85 self.ip_addr = ip_addr
79 86
80 87
81 88 class RepoWrap(object):
82 89 """
83 90 Fake object used to imitate RepoObject that audit logger requires
84 91 """
85 92
86 93 def __init__(self, repo_id=None, repo_name=None):
87 94 self.repo_id = repo_id
88 95 self.repo_name = repo_name
89 96
90 97
91 98 def _store_log(action_name, action_data, user_id, username, user_data,
92 99 ip_address, repository_id, repository_name):
93 100 user_log = UserLog()
94 101 user_log.version = UserLog.VERSION_2
95 102
96 103 user_log.action = action_name
97 104 user_log.action_data = action_data
98 105
99 106 user_log.user_ip = ip_address
100 107
101 108 user_log.user_id = user_id
102 109 user_log.username = username
103 110 user_log.user_data = user_data
104 111
105 112 user_log.repository_id = repository_id
106 113 user_log.repository_name = repository_name
107 114
108 115 user_log.action_date = datetime.datetime.now()
109 116
110 117 log.info('AUDIT: Logging action: `%s` by user:id:%s[%s] ip:%s',
111 118 action_name, user_id, username, ip_address)
112 119
113 120 return user_log
114 121
115 122
116 123 def store_web(*args, **kwargs):
117 124 if 'action_data' not in kwargs:
118 125 kwargs['action_data'] = {}
119 126 kwargs['action_data'].update({
120 127 'source': SOURCE_WEB
121 128 })
122 129 return store(*args, **kwargs)
123 130
124 131
125 132 def store_api(*args, **kwargs):
126 133 if 'action_data' not in kwargs:
127 134 kwargs['action_data'] = {}
128 135 kwargs['action_data'].update({
129 136 'source': SOURCE_API
130 137 })
131 138 return store(*args, **kwargs)
132 139
133 140
134 141 def store(action, user, action_data=None, user_data=None, ip_addr=None,
135 142 repo=None, sa_session=None, commit=False):
136 143 """
137 144 Audit logger for various actions made by users, typically this
138 145 results in a call such::
139 146
140 147 from rhodecode.lib import audit_logger
141 148
142 149 audit_logger.store(
143 150 action='repo.edit', user=self._rhodecode_user)
144 151 audit_logger.store(
145 152 action='repo.delete', action_data={'repo_data': repo_data},
146 153 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'))
147 154
148 155 # repo action
149 156 audit_logger.store(
150 157 action='repo.delete',
151 158 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'),
152 159 repo=audit_logger.RepoWrap(repo_name='some-repo'))
153 160
154 161 # repo action, when we know and have the repository object already
155 162 audit_logger.store(
156 163 action='repo.delete',
157 164 action_data={'source': audit_logger.SOURCE_WEB, },
158 165 user=self._rhodecode_user,
159 166 repo=repo_object)
160 167
161 168 # alternative wrapper to the above
162 169 audit_logger.store_web(
163 170 action='repo.delete',
164 171 action_data={},
165 172 user=self._rhodecode_user,
166 173 repo=repo_object)
167 174
168 175 # without an user ?
169 176 audit_logger.store(
170 177 action='user.login.failure',
171 178 user=audit_logger.UserWrap(
172 179 username=self.request.params.get('username'),
173 180 ip_addr=self.request.remote_addr))
174 181
175 182 """
176 183 from rhodecode.lib.utils2 import safe_unicode
177 184 from rhodecode.lib.auth import AuthUser
178 185
179 186 action_spec = ACTIONS.get(action, None)
180 187 if action_spec is None:
181 188 raise ValueError('Action `{}` is not supported'.format(action))
182 189
183 190 if not sa_session:
184 191 sa_session = meta.Session()
185 192
186 193 try:
187 194 username = getattr(user, 'username', None)
188 195 if not username:
189 196 pass
190 197
191 198 user_id = getattr(user, 'user_id', None)
192 199 if not user_id:
193 200 # maybe we have username ? Try to figure user_id from username
194 201 if username:
195 202 user_id = getattr(
196 203 User.get_by_username(username), 'user_id', None)
197 204
198 205 ip_addr = ip_addr or getattr(user, 'ip_addr', None)
199 206 if not ip_addr:
200 207 pass
201 208
202 209 if not user_data:
203 210 # try to get this from the auth user
204 211 if isinstance(user, AuthUser):
205 212 user_data = {
206 213 'username': user.username,
207 214 'email': user.email,
208 215 }
209 216
210 217 repository_name = getattr(repo, 'repo_name', None)
211 218 repository_id = getattr(repo, 'repo_id', None)
212 219 if not repository_id:
213 220 # maybe we have repo_name ? Try to figure repo_id from repo_name
214 221 if repository_name:
215 222 repository_id = getattr(
216 223 Repository.get_by_repo_name(repository_name), 'repo_id', None)
217 224
218 225 user_log = _store_log(
219 226 action_name=safe_unicode(action),
220 227 action_data=action_data or {},
221 228 user_id=user_id,
222 229 username=username,
223 230 user_data=user_data or {},
224 231 ip_address=safe_unicode(ip_addr),
225 232 repository_id=repository_id,
226 233 repository_name=repository_name
227 234 )
228 235 sa_session.add(user_log)
229 236 if commit:
230 237 sa_session.commit()
231 238
232 239 except Exception:
233 240 log.exception('AUDIT: failed to store audit log')
General Comments 0
You need to be logged in to leave comments. Login now