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