##// END OF EJS Templates
audit-logs: consistent data between my-account and admin user logs.
marcink -
r1822:4bb2ace4 default
parent child Browse files
Show More
@@ -1,515 +1,510 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 logging
22 22 import datetime
23 23 import formencode
24 24
25 25 from pyramid.httpexceptions import HTTPFound
26 26 from pyramid.view import view_config
27 27 from sqlalchemy.sql.functions import coalesce
28 28
29 29 from rhodecode.apps._base import BaseAppView, DataGridAppView
30 30
31 31 from rhodecode.lib import audit_logger
32 32 from rhodecode.lib.ext_json import json
33 33 from rhodecode.lib.auth import (
34 34 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
35 35 from rhodecode.lib import helpers as h
36 36 from rhodecode.lib.utils import PartialRenderer
37 37 from rhodecode.lib.utils2 import safe_int, safe_unicode
38 38 from rhodecode.model.auth_token import AuthTokenModel
39 39 from rhodecode.model.user import UserModel
40 40 from rhodecode.model.user_group import UserGroupModel
41 41 from rhodecode.model.db import User, or_, UserIpMap, UserEmailMap, UserApiKeys
42 42 from rhodecode.model.meta import Session
43 43
44 44 log = logging.getLogger(__name__)
45 45
46 46
47 47 class AdminUsersView(BaseAppView, DataGridAppView):
48 48 ALLOW_SCOPED_TOKENS = False
49 49 """
50 50 This view has alternative version inside EE, if modified please take a look
51 51 in there as well.
52 52 """
53 53
54 54 def load_default_context(self):
55 55 c = self._get_local_tmpl_context()
56 56 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
57 57 self._register_global_c(c)
58 58 return c
59 59
60 60 def _redirect_for_default_user(self, username):
61 61 _ = self.request.translate
62 62 if username == User.DEFAULT_USER:
63 63 h.flash(_("You can't edit this user"), category='warning')
64 64 # TODO(marcink): redirect to 'users' admin panel once this
65 65 # is a pyramid view
66 66 raise HTTPFound('/')
67 67
68 68 @HasPermissionAllDecorator('hg.admin')
69 69 @view_config(
70 70 route_name='users', request_method='GET',
71 71 renderer='rhodecode:templates/admin/users/users.mako')
72 72 def users_list(self):
73 73 c = self.load_default_context()
74 74 return self._get_template_context(c)
75 75
76 76 @HasPermissionAllDecorator('hg.admin')
77 77 @view_config(
78 78 # renderer defined below
79 79 route_name='users_data', request_method='GET',
80 80 renderer='json_ext', xhr=True)
81 81 def users_list_data(self):
82 82 draw, start, limit = self._extract_chunk(self.request)
83 83 search_q, order_by, order_dir = self._extract_ordering(self.request)
84 84
85 85 _render = PartialRenderer('data_table/_dt_elements.mako')
86 86
87 87 def user_actions(user_id, username):
88 88 return _render("user_actions", user_id, username)
89 89
90 90 users_data_total_count = User.query()\
91 91 .filter(User.username != User.DEFAULT_USER) \
92 92 .count()
93 93
94 94 # json generate
95 95 base_q = User.query().filter(User.username != User.DEFAULT_USER)
96 96
97 97 if search_q:
98 98 like_expression = u'%{}%'.format(safe_unicode(search_q))
99 99 base_q = base_q.filter(or_(
100 100 User.username.ilike(like_expression),
101 101 User._email.ilike(like_expression),
102 102 User.name.ilike(like_expression),
103 103 User.lastname.ilike(like_expression),
104 104 ))
105 105
106 106 users_data_total_filtered_count = base_q.count()
107 107
108 108 sort_col = getattr(User, order_by, None)
109 109 if sort_col:
110 110 if order_dir == 'asc':
111 111 # handle null values properly to order by NULL last
112 112 if order_by in ['last_activity']:
113 113 sort_col = coalesce(sort_col, datetime.date.max)
114 114 sort_col = sort_col.asc()
115 115 else:
116 116 # handle null values properly to order by NULL last
117 117 if order_by in ['last_activity']:
118 118 sort_col = coalesce(sort_col, datetime.date.min)
119 119 sort_col = sort_col.desc()
120 120
121 121 base_q = base_q.order_by(sort_col)
122 122 base_q = base_q.offset(start).limit(limit)
123 123
124 124 users_list = base_q.all()
125 125
126 126 users_data = []
127 127 for user in users_list:
128 128 users_data.append({
129 129 "username": h.gravatar_with_user(user.username),
130 130 "email": user.email,
131 131 "first_name": user.first_name,
132 132 "last_name": user.last_name,
133 133 "last_login": h.format_date(user.last_login),
134 134 "last_activity": h.format_date(user.last_activity),
135 135 "active": h.bool2icon(user.active),
136 136 "active_raw": user.active,
137 137 "admin": h.bool2icon(user.admin),
138 138 "extern_type": user.extern_type,
139 139 "extern_name": user.extern_name,
140 140 "action": user_actions(user.user_id, user.username),
141 141 })
142 142
143 143 data = ({
144 144 'draw': draw,
145 145 'data': users_data,
146 146 'recordsTotal': users_data_total_count,
147 147 'recordsFiltered': users_data_total_filtered_count,
148 148 })
149 149
150 150 return data
151 151
152 152 @LoginRequired()
153 153 @HasPermissionAllDecorator('hg.admin')
154 154 @view_config(
155 155 route_name='edit_user_auth_tokens', request_method='GET',
156 156 renderer='rhodecode:templates/admin/users/user_edit.mako')
157 157 def auth_tokens(self):
158 158 _ = self.request.translate
159 159 c = self.load_default_context()
160 160
161 161 user_id = self.request.matchdict.get('user_id')
162 162 c.user = User.get_or_404(user_id, pyramid_exc=True)
163 163 self._redirect_for_default_user(c.user.username)
164 164
165 165 c.active = 'auth_tokens'
166 166
167 167 c.lifetime_values = [
168 168 (str(-1), _('forever')),
169 169 (str(5), _('5 minutes')),
170 170 (str(60), _('1 hour')),
171 171 (str(60 * 24), _('1 day')),
172 172 (str(60 * 24 * 30), _('1 month')),
173 173 ]
174 174 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
175 175 c.role_values = [
176 176 (x, AuthTokenModel.cls._get_role_name(x))
177 177 for x in AuthTokenModel.cls.ROLES]
178 178 c.role_options = [(c.role_values, _("Role"))]
179 179 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
180 180 c.user.user_id, show_expired=True)
181 181 return self._get_template_context(c)
182 182
183 183 def maybe_attach_token_scope(self, token):
184 184 # implemented in EE edition
185 185 pass
186 186
187 187 @LoginRequired()
188 188 @HasPermissionAllDecorator('hg.admin')
189 189 @CSRFRequired()
190 190 @view_config(
191 191 route_name='edit_user_auth_tokens_add', request_method='POST')
192 192 def auth_tokens_add(self):
193 193 _ = self.request.translate
194 194 c = self.load_default_context()
195 195
196 196 user_id = self.request.matchdict.get('user_id')
197 197 c.user = User.get_or_404(user_id, pyramid_exc=True)
198 198
199 199 self._redirect_for_default_user(c.user.username)
200 200
201 201 user_data = c.user.get_api_data()
202 202 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
203 203 description = self.request.POST.get('description')
204 204 role = self.request.POST.get('role')
205 205
206 206 token = AuthTokenModel().create(
207 207 c.user.user_id, description, lifetime, role)
208 208 token_data = token.get_api_data()
209 209
210 210 self.maybe_attach_token_scope(token)
211 audit_logger.store(
211 audit_logger.store_web(
212 212 action='user.edit.token.add',
213 213 action_data={'data': {'token': token_data, 'user': user_data}},
214 214 user=self._rhodecode_user, )
215 215 Session().commit()
216 216
217 217 h.flash(_("Auth token successfully created"), category='success')
218 218 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
219 219
220 220 @LoginRequired()
221 221 @HasPermissionAllDecorator('hg.admin')
222 222 @CSRFRequired()
223 223 @view_config(
224 224 route_name='edit_user_auth_tokens_delete', request_method='POST')
225 225 def auth_tokens_delete(self):
226 226 _ = self.request.translate
227 227 c = self.load_default_context()
228 228
229 229 user_id = self.request.matchdict.get('user_id')
230 230 c.user = User.get_or_404(user_id, pyramid_exc=True)
231 231 self._redirect_for_default_user(c.user.username)
232 232 user_data = c.user.get_api_data()
233 233
234 234 del_auth_token = self.request.POST.get('del_auth_token')
235 235
236 236 if del_auth_token:
237 237 token = UserApiKeys.get_or_404(del_auth_token, pyramid_exc=True)
238 238 token_data = token.get_api_data()
239 239
240 240 AuthTokenModel().delete(del_auth_token, c.user.user_id)
241 audit_logger.store(
241 audit_logger.store_web(
242 242 action='user.edit.token.delete',
243 243 action_data={'data': {'token': token_data, 'user': user_data}},
244 244 user=self._rhodecode_user,)
245 245 Session().commit()
246 246 h.flash(_("Auth token successfully deleted"), category='success')
247 247
248 248 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
249 249
250
251
252
253
254
255 250 @LoginRequired()
256 251 @HasPermissionAllDecorator('hg.admin')
257 252 @view_config(
258 253 route_name='edit_user_emails', request_method='GET',
259 254 renderer='rhodecode:templates/admin/users/user_edit.mako')
260 255 def emails(self):
261 256 _ = self.request.translate
262 257 c = self.load_default_context()
263 258
264 259 user_id = self.request.matchdict.get('user_id')
265 260 c.user = User.get_or_404(user_id, pyramid_exc=True)
266 261 self._redirect_for_default_user(c.user.username)
267 262
268 263 c.active = 'emails'
269 264 c.user_email_map = UserEmailMap.query() \
270 265 .filter(UserEmailMap.user == c.user).all()
271 266
272 267 return self._get_template_context(c)
273 268
274 269 @LoginRequired()
275 270 @HasPermissionAllDecorator('hg.admin')
276 271 @CSRFRequired()
277 272 @view_config(
278 273 route_name='edit_user_emails_add', request_method='POST')
279 274 def emails_add(self):
280 275 _ = self.request.translate
281 276 c = self.load_default_context()
282 277
283 278 user_id = self.request.matchdict.get('user_id')
284 279 c.user = User.get_or_404(user_id, pyramid_exc=True)
285 280 self._redirect_for_default_user(c.user.username)
286 281
287 282 email = self.request.POST.get('new_email')
288 283 user_data = c.user.get_api_data()
289 284 try:
290 285 UserModel().add_extra_email(c.user.user_id, email)
291 286 audit_logger.store_web(
292 287 'user.edit.email.add',
293 288 action_data={'email': email, 'user': user_data},
294 289 user=self._rhodecode_user)
295 290 Session().commit()
296 291 h.flash(_("Added new email address `%s` for user account") % email,
297 292 category='success')
298 293 except formencode.Invalid as error:
299 294 msg = error.error_dict['email']
300 295 h.flash(msg, category='error')
301 296 except Exception:
302 297 log.exception("Exception during email saving")
303 298 h.flash(_('An error occurred during email saving'),
304 299 category='error')
305 300 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
306 301
307 302 @LoginRequired()
308 303 @HasPermissionAllDecorator('hg.admin')
309 304 @CSRFRequired()
310 305 @view_config(
311 306 route_name='edit_user_emails_delete', request_method='POST')
312 307 def emails_delete(self):
313 308 _ = self.request.translate
314 309 c = self.load_default_context()
315 310
316 311 user_id = self.request.matchdict.get('user_id')
317 312 c.user = User.get_or_404(user_id, pyramid_exc=True)
318 313 self._redirect_for_default_user(c.user.username)
319 314
320 315 email_id = self.request.POST.get('del_email_id')
321 316 user_model = UserModel()
322 317
323 318 email = UserEmailMap.query().get(email_id).email
324 319 user_data = c.user.get_api_data()
325 320 user_model.delete_extra_email(c.user.user_id, email_id)
326 321 audit_logger.store_web(
327 322 'user.edit.email.delete',
328 323 action_data={'email': email, 'user': user_data},
329 324 user=self._rhodecode_user)
330 325 Session().commit()
331 326 h.flash(_("Removed email address from user account"),
332 327 category='success')
333 328 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
334 329
335 330 @LoginRequired()
336 331 @HasPermissionAllDecorator('hg.admin')
337 332 @view_config(
338 333 route_name='edit_user_ips', request_method='GET',
339 334 renderer='rhodecode:templates/admin/users/user_edit.mako')
340 335 def ips(self):
341 336 _ = self.request.translate
342 337 c = self.load_default_context()
343 338
344 339 user_id = self.request.matchdict.get('user_id')
345 340 c.user = User.get_or_404(user_id, pyramid_exc=True)
346 341 self._redirect_for_default_user(c.user.username)
347 342
348 343 c.active = 'ips'
349 344 c.user_ip_map = UserIpMap.query() \
350 345 .filter(UserIpMap.user == c.user).all()
351 346
352 347 c.inherit_default_ips = c.user.inherit_default_permissions
353 348 c.default_user_ip_map = UserIpMap.query() \
354 349 .filter(UserIpMap.user == User.get_default_user()).all()
355 350
356 351 return self._get_template_context(c)
357 352
358 353 @LoginRequired()
359 354 @HasPermissionAllDecorator('hg.admin')
360 355 @CSRFRequired()
361 356 @view_config(
362 357 route_name='edit_user_ips_add', request_method='POST')
363 358 def ips_add(self):
364 359 _ = self.request.translate
365 360 c = self.load_default_context()
366 361
367 362 user_id = self.request.matchdict.get('user_id')
368 363 c.user = User.get_or_404(user_id, pyramid_exc=True)
369 364 # NOTE(marcink): this view is allowed for default users, as we can
370 365 # edit their IP white list
371 366
372 367 user_model = UserModel()
373 368 desc = self.request.POST.get('description')
374 369 try:
375 370 ip_list = user_model.parse_ip_range(
376 371 self.request.POST.get('new_ip'))
377 372 except Exception as e:
378 373 ip_list = []
379 374 log.exception("Exception during ip saving")
380 375 h.flash(_('An error occurred during ip saving:%s' % (e,)),
381 376 category='error')
382 377 added = []
383 378 user_data = c.user.get_api_data()
384 379 for ip in ip_list:
385 380 try:
386 381 user_model.add_extra_ip(c.user.user_id, ip, desc)
387 382 audit_logger.store_web(
388 383 'user.edit.ip.add',
389 384 action_data={'ip': ip, 'user': user_data},
390 385 user=self._rhodecode_user)
391 386 Session().commit()
392 387 added.append(ip)
393 388 except formencode.Invalid as error:
394 389 msg = error.error_dict['ip']
395 390 h.flash(msg, category='error')
396 391 except Exception:
397 392 log.exception("Exception during ip saving")
398 393 h.flash(_('An error occurred during ip saving'),
399 394 category='error')
400 395 if added:
401 396 h.flash(
402 397 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
403 398 category='success')
404 399 if 'default_user' in self.request.POST:
405 400 # case for editing global IP list we do it for 'DEFAULT' user
406 401 raise HTTPFound(h.route_path('admin_permissions_ips'))
407 402 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
408 403
409 404 @LoginRequired()
410 405 @HasPermissionAllDecorator('hg.admin')
411 406 @CSRFRequired()
412 407 @view_config(
413 408 route_name='edit_user_ips_delete', request_method='POST')
414 409 def ips_delete(self):
415 410 _ = self.request.translate
416 411 c = self.load_default_context()
417 412
418 413 user_id = self.request.matchdict.get('user_id')
419 414 c.user = User.get_or_404(user_id, pyramid_exc=True)
420 415 # NOTE(marcink): this view is allowed for default users, as we can
421 416 # edit their IP white list
422 417
423 418 ip_id = self.request.POST.get('del_ip_id')
424 419 user_model = UserModel()
425 420 user_data = c.user.get_api_data()
426 421 ip = UserIpMap.query().get(ip_id).ip_addr
427 422 user_model.delete_extra_ip(c.user.user_id, ip_id)
428 423 audit_logger.store_web(
429 424 'user.edit.ip.delete',
430 425 action_data={'ip': ip, 'user': user_data},
431 426 user=self._rhodecode_user)
432 427 Session().commit()
433 428 h.flash(_("Removed ip address from user whitelist"), category='success')
434 429
435 430 if 'default_user' in self.request.POST:
436 431 # case for editing global IP list we do it for 'DEFAULT' user
437 432 raise HTTPFound(h.route_path('admin_permissions_ips'))
438 433 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
439 434
440 435 @LoginRequired()
441 436 @HasPermissionAllDecorator('hg.admin')
442 437 @view_config(
443 438 route_name='edit_user_groups_management', request_method='GET',
444 439 renderer='rhodecode:templates/admin/users/user_edit.mako')
445 440 def groups_management(self):
446 441 c = self.load_default_context()
447 442
448 443 user_id = self.request.matchdict.get('user_id')
449 444 c.user = User.get_or_404(user_id, pyramid_exc=True)
450 445 c.data = c.user.group_member
451 446 self._redirect_for_default_user(c.user.username)
452 447 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
453 448 for group in c.user.group_member]
454 449 c.groups = json.dumps(groups)
455 450 c.active = 'groups'
456 451
457 452 return self._get_template_context(c)
458 453
459 454 @LoginRequired()
460 455 @HasPermissionAllDecorator('hg.admin')
461 456 @CSRFRequired()
462 457 @view_config(
463 458 route_name='edit_user_groups_management_updates', request_method='POST')
464 459 def groups_management_updates(self):
465 460 _ = self.request.translate
466 461 c = self.load_default_context()
467 462
468 463 user_id = self.request.matchdict.get('user_id')
469 464 c.user = User.get_or_404(user_id, pyramid_exc=True)
470 465 self._redirect_for_default_user(c.user.username)
471 466
472 467 users_groups = set(self.request.POST.getall('users_group_id'))
473 468 users_groups_model = []
474 469
475 470 for ugid in users_groups:
476 471 users_groups_model.append(UserGroupModel().get_group(safe_int(ugid)))
477 472 user_group_model = UserGroupModel()
478 473 user_group_model.change_groups(c.user, users_groups_model)
479 474
480 475 Session().commit()
481 476 c.active = 'user_groups_management'
482 477 h.flash(_("Groups successfully changed"), category='success')
483 478
484 479 return HTTPFound(h.route_path(
485 480 'edit_user_groups_management', user_id=user_id))
486 481
487 482 @LoginRequired()
488 483 @HasPermissionAllDecorator('hg.admin')
489 484 @view_config(
490 485 route_name='edit_user_audit_logs', request_method='GET',
491 486 renderer='rhodecode:templates/admin/users/user_edit.mako')
492 487 def user_audit_logs(self):
493 488 _ = self.request.translate
494 489 c = self.load_default_context()
495 490
496 491 user_id = self.request.matchdict.get('user_id')
497 492 c.user = User.get_or_404(user_id, pyramid_exc=True)
498 493 self._redirect_for_default_user(c.user.username)
499 494 c.active = 'audit'
500 495
501 496 p = safe_int(self.request.GET.get('page', 1), 1)
502 497
503 498 filter_term = self.request.GET.get('filter')
504 499 user_log = UserModel().get_user_log(c.user, filter_term)
505 500
506 501 def url_generator(**kw):
507 502 if filter_term:
508 503 kw['filter'] = filter_term
509 504 return self.request.current_route_path(_query=kw)
510 505
511 506 c.audit_logs = h.Page(
512 507 user_log, page=p, items_per_page=10, url=url_generator)
513 508 c.filter_term = filter_term
514 509 return self._get_template_context(c)
515 510
@@ -1,400 +1,400 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 logging
22 22 import datetime
23 23
24 24 import formencode
25 25 from pyramid.httpexceptions import HTTPFound
26 26 from pyramid.view import view_config
27 27
28 28 from rhodecode.apps._base import BaseAppView
29 29 from rhodecode import forms
30 30 from rhodecode.lib import helpers as h
31 31 from rhodecode.lib import audit_logger
32 32 from rhodecode.lib.ext_json import json
33 33 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
34 34 from rhodecode.lib.channelstream import channelstream_request, \
35 35 ChannelstreamException
36 36 from rhodecode.lib.utils2 import safe_int, md5
37 37 from rhodecode.model.auth_token import AuthTokenModel
38 38 from rhodecode.model.db import (
39 39 Repository, UserEmailMap, UserApiKeys, UserFollowing, joinedload)
40 40 from rhodecode.model.meta import Session
41 41 from rhodecode.model.scm import RepoList
42 42 from rhodecode.model.user import UserModel
43 43 from rhodecode.model.repo import RepoModel
44 44 from rhodecode.model.validation_schema.schemas import user_schema
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48
49 49 class MyAccountView(BaseAppView):
50 50 ALLOW_SCOPED_TOKENS = False
51 51 """
52 52 This view has alternative version inside EE, if modified please take a look
53 53 in there as well.
54 54 """
55 55
56 56 def load_default_context(self):
57 57 c = self._get_local_tmpl_context()
58 58 c.user = c.auth_user.get_instance()
59 59 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
60 60 self._register_global_c(c)
61 61 return c
62 62
63 63 @LoginRequired()
64 64 @NotAnonymous()
65 65 @view_config(
66 66 route_name='my_account_profile', request_method='GET',
67 67 renderer='rhodecode:templates/admin/my_account/my_account.mako')
68 68 def my_account_profile(self):
69 69 c = self.load_default_context()
70 70 c.active = 'profile'
71 71 return self._get_template_context(c)
72 72
73 73 @LoginRequired()
74 74 @NotAnonymous()
75 75 @view_config(
76 76 route_name='my_account_password', request_method='GET',
77 77 renderer='rhodecode:templates/admin/my_account/my_account.mako')
78 78 def my_account_password(self):
79 79 c = self.load_default_context()
80 80 c.active = 'password'
81 81 c.extern_type = c.user.extern_type
82 82
83 83 schema = user_schema.ChangePasswordSchema().bind(
84 84 username=c.user.username)
85 85
86 86 form = forms.Form(
87 87 schema, buttons=(forms.buttons.save, forms.buttons.reset))
88 88
89 89 c.form = form
90 90 return self._get_template_context(c)
91 91
92 92 @LoginRequired()
93 93 @NotAnonymous()
94 94 @CSRFRequired()
95 95 @view_config(
96 96 route_name='my_account_password', request_method='POST',
97 97 renderer='rhodecode:templates/admin/my_account/my_account.mako')
98 98 def my_account_password_update(self):
99 99 _ = self.request.translate
100 100 c = self.load_default_context()
101 101 c.active = 'password'
102 102 c.extern_type = c.user.extern_type
103 103
104 104 schema = user_schema.ChangePasswordSchema().bind(
105 105 username=c.user.username)
106 106
107 107 form = forms.Form(
108 108 schema, buttons=(forms.buttons.save, forms.buttons.reset))
109 109
110 110 if c.extern_type != 'rhodecode':
111 111 raise HTTPFound(self.request.route_path('my_account_password'))
112 112
113 113 controls = self.request.POST.items()
114 114 try:
115 115 valid_data = form.validate(controls)
116 116 UserModel().update_user(c.user.user_id, **valid_data)
117 117 c.user.update_userdata(force_password_change=False)
118 118 Session().commit()
119 119 except forms.ValidationFailure as e:
120 120 c.form = e
121 121 return self._get_template_context(c)
122 122
123 123 except Exception:
124 124 log.exception("Exception updating password")
125 125 h.flash(_('Error occurred during update of user password'),
126 126 category='error')
127 127 else:
128 128 instance = c.auth_user.get_instance()
129 129 self.session.setdefault('rhodecode_user', {}).update(
130 130 {'password': md5(instance.password)})
131 131 self.session.save()
132 132 h.flash(_("Successfully updated password"), category='success')
133 133
134 134 raise HTTPFound(self.request.route_path('my_account_password'))
135 135
136 136 @LoginRequired()
137 137 @NotAnonymous()
138 138 @view_config(
139 139 route_name='my_account_auth_tokens', request_method='GET',
140 140 renderer='rhodecode:templates/admin/my_account/my_account.mako')
141 141 def my_account_auth_tokens(self):
142 142 _ = self.request.translate
143 143
144 144 c = self.load_default_context()
145 145 c.active = 'auth_tokens'
146 146
147 147 c.lifetime_values = [
148 148 (str(-1), _('forever')),
149 149 (str(5), _('5 minutes')),
150 150 (str(60), _('1 hour')),
151 151 (str(60 * 24), _('1 day')),
152 152 (str(60 * 24 * 30), _('1 month')),
153 153 ]
154 154 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
155 155 c.role_values = [
156 156 (x, AuthTokenModel.cls._get_role_name(x))
157 157 for x in AuthTokenModel.cls.ROLES]
158 158 c.role_options = [(c.role_values, _("Role"))]
159 159 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
160 160 c.user.user_id, show_expired=True)
161 161 return self._get_template_context(c)
162 162
163 163 def maybe_attach_token_scope(self, token):
164 164 # implemented in EE edition
165 165 pass
166 166
167 167 @LoginRequired()
168 168 @NotAnonymous()
169 169 @CSRFRequired()
170 170 @view_config(
171 171 route_name='my_account_auth_tokens_add', request_method='POST',)
172 172 def my_account_auth_tokens_add(self):
173 173 _ = self.request.translate
174 174 c = self.load_default_context()
175 175
176 176 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
177 177 description = self.request.POST.get('description')
178 178 role = self.request.POST.get('role')
179 179
180 180 token = AuthTokenModel().create(
181 181 c.user.user_id, description, lifetime, role)
182 182 token_data = token.get_api_data()
183 183
184 184 self.maybe_attach_token_scope(token)
185 audit_logger.store(
185 audit_logger.store_web(
186 186 action='user.edit.token.add',
187 action_data={'data': {'token': token_data}},
187 action_data={'data': {'token': token_data, 'user': 'self'}},
188 188 user=self._rhodecode_user, )
189 189 Session().commit()
190 190
191 191 h.flash(_("Auth token successfully created"), category='success')
192 192 return HTTPFound(h.route_path('my_account_auth_tokens'))
193 193
194 194 @LoginRequired()
195 195 @NotAnonymous()
196 196 @CSRFRequired()
197 197 @view_config(
198 198 route_name='my_account_auth_tokens_delete', request_method='POST')
199 199 def my_account_auth_tokens_delete(self):
200 200 _ = self.request.translate
201 201 c = self.load_default_context()
202 202
203 203 del_auth_token = self.request.POST.get('del_auth_token')
204 204
205 205 if del_auth_token:
206 206 token = UserApiKeys.get_or_404(del_auth_token, pyramid_exc=True)
207 207 token_data = token.get_api_data()
208 208
209 209 AuthTokenModel().delete(del_auth_token, c.user.user_id)
210 audit_logger.store(
210 audit_logger.store_web(
211 211 action='user.edit.token.delete',
212 action_data={'data': {'token': token_data}},
212 action_data={'data': {'token': token_data, 'user': 'self'}},
213 213 user=self._rhodecode_user,)
214 214 Session().commit()
215 215 h.flash(_("Auth token successfully deleted"), category='success')
216 216
217 217 return HTTPFound(h.route_path('my_account_auth_tokens'))
218 218
219 219 @LoginRequired()
220 220 @NotAnonymous()
221 221 @view_config(
222 222 route_name='my_account_emails', request_method='GET',
223 223 renderer='rhodecode:templates/admin/my_account/my_account.mako')
224 224 def my_account_emails(self):
225 225 _ = self.request.translate
226 226
227 227 c = self.load_default_context()
228 228 c.active = 'emails'
229 229
230 230 c.user_email_map = UserEmailMap.query()\
231 231 .filter(UserEmailMap.user == c.user).all()
232 232 return self._get_template_context(c)
233 233
234 234 @LoginRequired()
235 235 @NotAnonymous()
236 236 @CSRFRequired()
237 237 @view_config(
238 238 route_name='my_account_emails_add', request_method='POST')
239 239 def my_account_emails_add(self):
240 240 _ = self.request.translate
241 241 c = self.load_default_context()
242 242
243 243 email = self.request.POST.get('new_email')
244 244
245 245 try:
246 246 UserModel().add_extra_email(c.user.user_id, email)
247 audit_logger.store(
247 audit_logger.store_web(
248 248 action='user.edit.email.add',
249 action_data={'data': {'email': email}},
249 action_data={'data': {'email': email, 'user': 'self'}},
250 250 user=self._rhodecode_user,)
251 251
252 252 Session().commit()
253 253 h.flash(_("Added new email address `%s` for user account") % email,
254 254 category='success')
255 255 except formencode.Invalid as error:
256 256 msg = error.error_dict['email']
257 257 h.flash(msg, category='error')
258 258 except Exception:
259 259 log.exception("Exception in my_account_emails")
260 260 h.flash(_('An error occurred during email saving'),
261 261 category='error')
262 262 return HTTPFound(h.route_path('my_account_emails'))
263 263
264 264 @LoginRequired()
265 265 @NotAnonymous()
266 266 @CSRFRequired()
267 267 @view_config(
268 268 route_name='my_account_emails_delete', request_method='POST')
269 269 def my_account_emails_delete(self):
270 270 _ = self.request.translate
271 271 c = self.load_default_context()
272 272
273 273 del_email_id = self.request.POST.get('del_email_id')
274 274 if del_email_id:
275 275 email = UserEmailMap.get_or_404(del_email_id, pyramid_exc=True).email
276 276 UserModel().delete_extra_email(c.user.user_id, del_email_id)
277 audit_logger.store(
277 audit_logger.store_web(
278 278 action='user.edit.email.delete',
279 action_data={'data': {'email': email}},
279 action_data={'data': {'email': email, 'user': 'self'}},
280 280 user=self._rhodecode_user,)
281 281 Session().commit()
282 282 h.flash(_("Email successfully deleted"),
283 283 category='success')
284 284 return HTTPFound(h.route_path('my_account_emails'))
285 285
286 286 @LoginRequired()
287 287 @NotAnonymous()
288 288 @CSRFRequired()
289 289 @view_config(
290 290 route_name='my_account_notifications_test_channelstream',
291 291 request_method='POST', renderer='json_ext')
292 292 def my_account_notifications_test_channelstream(self):
293 293 message = 'Test message sent via Channelstream by user: {}, on {}'.format(
294 294 self._rhodecode_user.username, datetime.datetime.now())
295 295 payload = {
296 296 # 'channel': 'broadcast',
297 297 'type': 'message',
298 298 'timestamp': datetime.datetime.utcnow(),
299 299 'user': 'system',
300 300 'pm_users': [self._rhodecode_user.username],
301 301 'message': {
302 302 'message': message,
303 303 'level': 'info',
304 304 'topic': '/notifications'
305 305 }
306 306 }
307 307
308 308 registry = self.request.registry
309 309 rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {})
310 310 channelstream_config = rhodecode_plugins.get('channelstream', {})
311 311
312 312 try:
313 313 channelstream_request(channelstream_config, [payload], '/message')
314 314 except ChannelstreamException as e:
315 315 log.exception('Failed to send channelstream data')
316 316 return {"response": 'ERROR: {}'.format(e.__class__.__name__)}
317 317 return {"response": 'Channelstream data sent. '
318 318 'You should see a new live message now.'}
319 319
320 320 def _load_my_repos_data(self, watched=False):
321 321 if watched:
322 322 admin = False
323 323 follows_repos = Session().query(UserFollowing)\
324 324 .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\
325 325 .options(joinedload(UserFollowing.follows_repository))\
326 326 .all()
327 327 repo_list = [x.follows_repository for x in follows_repos]
328 328 else:
329 329 admin = True
330 330 repo_list = Repository.get_all_repos(
331 331 user_id=self._rhodecode_user.user_id)
332 332 repo_list = RepoList(repo_list, perm_set=[
333 333 'repository.read', 'repository.write', 'repository.admin'])
334 334
335 335 repos_data = RepoModel().get_repos_as_dict(
336 336 repo_list=repo_list, admin=admin)
337 337 # json used to render the grid
338 338 return json.dumps(repos_data)
339 339
340 340 @LoginRequired()
341 341 @NotAnonymous()
342 342 @view_config(
343 343 route_name='my_account_repos', request_method='GET',
344 344 renderer='rhodecode:templates/admin/my_account/my_account.mako')
345 345 def my_account_repos(self):
346 346 c = self.load_default_context()
347 347 c.active = 'repos'
348 348
349 349 # json used to render the grid
350 350 c.data = self._load_my_repos_data()
351 351 return self._get_template_context(c)
352 352
353 353 @LoginRequired()
354 354 @NotAnonymous()
355 355 @view_config(
356 356 route_name='my_account_watched', request_method='GET',
357 357 renderer='rhodecode:templates/admin/my_account/my_account.mako')
358 358 def my_account_watched(self):
359 359 c = self.load_default_context()
360 360 c.active = 'watched'
361 361
362 362 # json used to render the grid
363 363 c.data = self._load_my_repos_data(watched=True)
364 364 return self._get_template_context(c)
365 365
366 366 @LoginRequired()
367 367 @NotAnonymous()
368 368 @view_config(
369 369 route_name='my_account_perms', request_method='GET',
370 370 renderer='rhodecode:templates/admin/my_account/my_account.mako')
371 371 def my_account_perms(self):
372 372 c = self.load_default_context()
373 373 c.active = 'perms'
374 374
375 375 c.perm_user = c.auth_user
376 376 return self._get_template_context(c)
377 377
378 378 @LoginRequired()
379 379 @NotAnonymous()
380 380 @view_config(
381 381 route_name='my_account_notifications', request_method='GET',
382 382 renderer='rhodecode:templates/admin/my_account/my_account.mako')
383 383 def my_notifications(self):
384 384 c = self.load_default_context()
385 385 c.active = 'notifications'
386 386
387 387 return self._get_template_context(c)
388 388
389 389 @LoginRequired()
390 390 @NotAnonymous()
391 391 @CSRFRequired()
392 392 @view_config(
393 393 route_name='my_account_notifications_toggle_visibility',
394 394 request_method='POST', renderer='json_ext')
395 395 def my_notifications_toggle_visibility(self):
396 396 user = self._rhodecode_db_user
397 397 new_status = not user.user_data.get('notification_status', True)
398 398 user.update_userdata(notification_status=new_status)
399 399 Session().commit()
400 400 return user.user_data['notification_status'] No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now