##// END OF EJS Templates
admin-users: use new json_ext renderer
marcink -
r1665:353a4541 default
parent child Browse files
Show More
@@ -1,306 +1,306 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 from pyramid.httpexceptions import HTTPFound
24 from pyramid.httpexceptions import HTTPFound
25 from pyramid.view import view_config
25 from pyramid.view import view_config
26 from sqlalchemy.sql.functions import coalesce
26 from sqlalchemy.sql.functions import coalesce
27
27
28 from rhodecode.lib.helpers import Page
28 from rhodecode.lib.helpers import Page
29 from rhodecode_tools.lib.ext_json import json
29 from rhodecode_tools.lib.ext_json import json
30
30
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode.lib.auth import (
32 from rhodecode.lib.auth import (
33 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
33 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
34 from rhodecode.lib import helpers as h
34 from rhodecode.lib import helpers as h
35 from rhodecode.lib.utils import PartialRenderer
35 from rhodecode.lib.utils import PartialRenderer
36 from rhodecode.lib.utils2 import safe_int, safe_unicode
36 from rhodecode.lib.utils2 import safe_int, safe_unicode
37 from rhodecode.model.auth_token import AuthTokenModel
37 from rhodecode.model.auth_token import AuthTokenModel
38 from rhodecode.model.user import UserModel
38 from rhodecode.model.user import UserModel
39 from rhodecode.model.user_group import UserGroupModel
39 from rhodecode.model.user_group import UserGroupModel
40 from rhodecode.model.db import User, or_
40 from rhodecode.model.db import User, or_
41 from rhodecode.model.meta import Session
41 from rhodecode.model.meta import Session
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45
45
46 class AdminUsersView(BaseAppView, DataGridAppView):
46 class AdminUsersView(BaseAppView, DataGridAppView):
47 ALLOW_SCOPED_TOKENS = False
47 ALLOW_SCOPED_TOKENS = False
48 """
48 """
49 This view has alternative version inside EE, if modified please take a look
49 This view has alternative version inside EE, if modified please take a look
50 in there as well.
50 in there as well.
51 """
51 """
52
52
53 def load_default_context(self):
53 def load_default_context(self):
54 c = self._get_local_tmpl_context()
54 c = self._get_local_tmpl_context()
55 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
55 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
56 self._register_global_c(c)
56 self._register_global_c(c)
57 return c
57 return c
58
58
59 def _redirect_for_default_user(self, username):
59 def _redirect_for_default_user(self, username):
60 _ = self.request.translate
60 _ = self.request.translate
61 if username == User.DEFAULT_USER:
61 if username == User.DEFAULT_USER:
62 h.flash(_("You can't edit this user"), category='warning')
62 h.flash(_("You can't edit this user"), category='warning')
63 # TODO(marcink): redirect to 'users' admin panel once this
63 # TODO(marcink): redirect to 'users' admin panel once this
64 # is a pyramid view
64 # is a pyramid view
65 raise HTTPFound('/')
65 raise HTTPFound('/')
66
66
67 @HasPermissionAllDecorator('hg.admin')
67 @HasPermissionAllDecorator('hg.admin')
68 @view_config(
68 @view_config(
69 route_name='users', request_method='GET',
69 route_name='users', request_method='GET',
70 renderer='rhodecode:templates/admin/users/users.mako')
70 renderer='rhodecode:templates/admin/users/users.mako')
71 def users_list(self):
71 def users_list(self):
72 c = self.load_default_context()
72 c = self.load_default_context()
73 return self._get_template_context(c)
73 return self._get_template_context(c)
74
74
75 @HasPermissionAllDecorator('hg.admin')
75 @HasPermissionAllDecorator('hg.admin')
76 @view_config(
76 @view_config(
77 # renderer defined below
77 # renderer defined below
78 route_name='users_data', request_method='GET', renderer='json',
78 route_name='users_data', request_method='GET',
79 xhr=True)
79 renderer='json_ext', xhr=True)
80 def users_list_data(self):
80 def users_list_data(self):
81 draw, start, limit = self._extract_chunk(self.request)
81 draw, start, limit = self._extract_chunk(self.request)
82 search_q, order_by, order_dir = self._extract_ordering(self.request)
82 search_q, order_by, order_dir = self._extract_ordering(self.request)
83
83
84 _render = PartialRenderer('data_table/_dt_elements.mako')
84 _render = PartialRenderer('data_table/_dt_elements.mako')
85
85
86 def user_actions(user_id, username):
86 def user_actions(user_id, username):
87 return _render("user_actions", user_id, username)
87 return _render("user_actions", user_id, username)
88
88
89 users_data_total_count = User.query()\
89 users_data_total_count = User.query()\
90 .filter(User.username != User.DEFAULT_USER) \
90 .filter(User.username != User.DEFAULT_USER) \
91 .count()
91 .count()
92
92
93 # json generate
93 # json generate
94 base_q = User.query().filter(User.username != User.DEFAULT_USER)
94 base_q = User.query().filter(User.username != User.DEFAULT_USER)
95
95
96 if search_q:
96 if search_q:
97 like_expression = u'%{}%'.format(safe_unicode(search_q))
97 like_expression = u'%{}%'.format(safe_unicode(search_q))
98 base_q = base_q.filter(or_(
98 base_q = base_q.filter(or_(
99 User.username.ilike(like_expression),
99 User.username.ilike(like_expression),
100 User._email.ilike(like_expression),
100 User._email.ilike(like_expression),
101 User.name.ilike(like_expression),
101 User.name.ilike(like_expression),
102 User.lastname.ilike(like_expression),
102 User.lastname.ilike(like_expression),
103 ))
103 ))
104
104
105 users_data_total_filtered_count = base_q.count()
105 users_data_total_filtered_count = base_q.count()
106
106
107 sort_col = getattr(User, order_by, None)
107 sort_col = getattr(User, order_by, None)
108 if sort_col:
108 if sort_col:
109 if order_dir == 'asc':
109 if order_dir == 'asc':
110 # handle null values properly to order by NULL last
110 # handle null values properly to order by NULL last
111 if order_by in ['last_activity']:
111 if order_by in ['last_activity']:
112 sort_col = coalesce(sort_col, datetime.date.max)
112 sort_col = coalesce(sort_col, datetime.date.max)
113 sort_col = sort_col.asc()
113 sort_col = sort_col.asc()
114 else:
114 else:
115 # handle null values properly to order by NULL last
115 # handle null values properly to order by NULL last
116 if order_by in ['last_activity']:
116 if order_by in ['last_activity']:
117 sort_col = coalesce(sort_col, datetime.date.min)
117 sort_col = coalesce(sort_col, datetime.date.min)
118 sort_col = sort_col.desc()
118 sort_col = sort_col.desc()
119
119
120 base_q = base_q.order_by(sort_col)
120 base_q = base_q.order_by(sort_col)
121 base_q = base_q.offset(start).limit(limit)
121 base_q = base_q.offset(start).limit(limit)
122
122
123 users_list = base_q.all()
123 users_list = base_q.all()
124
124
125 users_data = []
125 users_data = []
126 for user in users_list:
126 for user in users_list:
127 users_data.append({
127 users_data.append({
128 "username": h.gravatar_with_user(user.username),
128 "username": h.gravatar_with_user(user.username),
129 "email": user.email,
129 "email": user.email,
130 "first_name": h.escape(user.name),
130 "first_name": h.escape(user.name),
131 "last_name": h.escape(user.lastname),
131 "last_name": h.escape(user.lastname),
132 "last_login": h.format_date(user.last_login),
132 "last_login": h.format_date(user.last_login),
133 "last_activity": h.format_date(user.last_activity),
133 "last_activity": h.format_date(user.last_activity),
134 "active": h.bool2icon(user.active),
134 "active": h.bool2icon(user.active),
135 "active_raw": user.active,
135 "active_raw": user.active,
136 "admin": h.bool2icon(user.admin),
136 "admin": h.bool2icon(user.admin),
137 "extern_type": user.extern_type,
137 "extern_type": user.extern_type,
138 "extern_name": user.extern_name,
138 "extern_name": user.extern_name,
139 "action": user_actions(user.user_id, user.username),
139 "action": user_actions(user.user_id, user.username),
140 })
140 })
141
141
142 data = ({
142 data = ({
143 'draw': draw,
143 'draw': draw,
144 'data': users_data,
144 'data': users_data,
145 'recordsTotal': users_data_total_count,
145 'recordsTotal': users_data_total_count,
146 'recordsFiltered': users_data_total_filtered_count,
146 'recordsFiltered': users_data_total_filtered_count,
147 })
147 })
148
148
149 return data
149 return data
150
150
151 @LoginRequired()
151 @LoginRequired()
152 @HasPermissionAllDecorator('hg.admin')
152 @HasPermissionAllDecorator('hg.admin')
153 @view_config(
153 @view_config(
154 route_name='edit_user_auth_tokens', request_method='GET',
154 route_name='edit_user_auth_tokens', request_method='GET',
155 renderer='rhodecode:templates/admin/users/user_edit.mako')
155 renderer='rhodecode:templates/admin/users/user_edit.mako')
156 def auth_tokens(self):
156 def auth_tokens(self):
157 _ = self.request.translate
157 _ = self.request.translate
158 c = self.load_default_context()
158 c = self.load_default_context()
159
159
160 user_id = self.request.matchdict.get('user_id')
160 user_id = self.request.matchdict.get('user_id')
161 c.user = User.get_or_404(user_id, pyramid_exc=True)
161 c.user = User.get_or_404(user_id, pyramid_exc=True)
162 self._redirect_for_default_user(c.user.username)
162 self._redirect_for_default_user(c.user.username)
163
163
164 c.active = 'auth_tokens'
164 c.active = 'auth_tokens'
165
165
166 c.lifetime_values = [
166 c.lifetime_values = [
167 (str(-1), _('forever')),
167 (str(-1), _('forever')),
168 (str(5), _('5 minutes')),
168 (str(5), _('5 minutes')),
169 (str(60), _('1 hour')),
169 (str(60), _('1 hour')),
170 (str(60 * 24), _('1 day')),
170 (str(60 * 24), _('1 day')),
171 (str(60 * 24 * 30), _('1 month')),
171 (str(60 * 24 * 30), _('1 month')),
172 ]
172 ]
173 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
173 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
174 c.role_values = [
174 c.role_values = [
175 (x, AuthTokenModel.cls._get_role_name(x))
175 (x, AuthTokenModel.cls._get_role_name(x))
176 for x in AuthTokenModel.cls.ROLES]
176 for x in AuthTokenModel.cls.ROLES]
177 c.role_options = [(c.role_values, _("Role"))]
177 c.role_options = [(c.role_values, _("Role"))]
178 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
178 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
179 c.user.user_id, show_expired=True)
179 c.user.user_id, show_expired=True)
180 return self._get_template_context(c)
180 return self._get_template_context(c)
181
181
182 def maybe_attach_token_scope(self, token):
182 def maybe_attach_token_scope(self, token):
183 # implemented in EE edition
183 # implemented in EE edition
184 pass
184 pass
185
185
186 @LoginRequired()
186 @LoginRequired()
187 @HasPermissionAllDecorator('hg.admin')
187 @HasPermissionAllDecorator('hg.admin')
188 @CSRFRequired()
188 @CSRFRequired()
189 @view_config(
189 @view_config(
190 route_name='edit_user_auth_tokens_add', request_method='POST')
190 route_name='edit_user_auth_tokens_add', request_method='POST')
191 def auth_tokens_add(self):
191 def auth_tokens_add(self):
192 _ = self.request.translate
192 _ = self.request.translate
193 c = self.load_default_context()
193 c = self.load_default_context()
194
194
195 user_id = self.request.matchdict.get('user_id')
195 user_id = self.request.matchdict.get('user_id')
196 c.user = User.get_or_404(user_id, pyramid_exc=True)
196 c.user = User.get_or_404(user_id, pyramid_exc=True)
197 self._redirect_for_default_user(c.user.username)
197 self._redirect_for_default_user(c.user.username)
198
198
199 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
199 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
200 description = self.request.POST.get('description')
200 description = self.request.POST.get('description')
201 role = self.request.POST.get('role')
201 role = self.request.POST.get('role')
202
202
203 token = AuthTokenModel().create(
203 token = AuthTokenModel().create(
204 c.user.user_id, description, lifetime, role)
204 c.user.user_id, description, lifetime, role)
205 self.maybe_attach_token_scope(token)
205 self.maybe_attach_token_scope(token)
206 Session().commit()
206 Session().commit()
207
207
208 h.flash(_("Auth token successfully created"), category='success')
208 h.flash(_("Auth token successfully created"), category='success')
209 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
209 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
210
210
211 @LoginRequired()
211 @LoginRequired()
212 @HasPermissionAllDecorator('hg.admin')
212 @HasPermissionAllDecorator('hg.admin')
213 @CSRFRequired()
213 @CSRFRequired()
214 @view_config(
214 @view_config(
215 route_name='edit_user_auth_tokens_delete', request_method='POST')
215 route_name='edit_user_auth_tokens_delete', request_method='POST')
216 def auth_tokens_delete(self):
216 def auth_tokens_delete(self):
217 _ = self.request.translate
217 _ = self.request.translate
218 c = self.load_default_context()
218 c = self.load_default_context()
219
219
220 user_id = self.request.matchdict.get('user_id')
220 user_id = self.request.matchdict.get('user_id')
221 c.user = User.get_or_404(user_id, pyramid_exc=True)
221 c.user = User.get_or_404(user_id, pyramid_exc=True)
222 self._redirect_for_default_user(c.user.username)
222 self._redirect_for_default_user(c.user.username)
223
223
224 del_auth_token = self.request.POST.get('del_auth_token')
224 del_auth_token = self.request.POST.get('del_auth_token')
225
225
226 if del_auth_token:
226 if del_auth_token:
227 AuthTokenModel().delete(del_auth_token, c.user.user_id)
227 AuthTokenModel().delete(del_auth_token, c.user.user_id)
228 Session().commit()
228 Session().commit()
229 h.flash(_("Auth token successfully deleted"), category='success')
229 h.flash(_("Auth token successfully deleted"), category='success')
230
230
231 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
231 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
232
232
233 @LoginRequired()
233 @LoginRequired()
234 @HasPermissionAllDecorator('hg.admin')
234 @HasPermissionAllDecorator('hg.admin')
235 @view_config(
235 @view_config(
236 route_name='edit_user_groups_management', request_method='GET',
236 route_name='edit_user_groups_management', request_method='GET',
237 renderer='rhodecode:templates/admin/users/user_edit.mako')
237 renderer='rhodecode:templates/admin/users/user_edit.mako')
238 def groups_management(self):
238 def groups_management(self):
239 c = self.load_default_context()
239 c = self.load_default_context()
240
240
241 user_id = self.request.matchdict.get('user_id')
241 user_id = self.request.matchdict.get('user_id')
242 c.user = User.get_or_404(user_id, pyramid_exc=True)
242 c.user = User.get_or_404(user_id, pyramid_exc=True)
243 c.data = c.user.group_member
243 c.data = c.user.group_member
244 self._redirect_for_default_user(c.user.username)
244 self._redirect_for_default_user(c.user.username)
245 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group) for group in c.user.group_member]
245 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group) for group in c.user.group_member]
246 c.groups = json.dumps(groups)
246 c.groups = json.dumps(groups)
247 c.active = 'groups'
247 c.active = 'groups'
248
248
249 return self._get_template_context(c)
249 return self._get_template_context(c)
250
250
251 @LoginRequired()
251 @LoginRequired()
252 @HasPermissionAllDecorator('hg.admin')
252 @HasPermissionAllDecorator('hg.admin')
253 @view_config(
253 @view_config(
254 route_name='edit_user_groups_management_updates', request_method='POST')
254 route_name='edit_user_groups_management_updates', request_method='POST')
255 def groups_management_updates(self):
255 def groups_management_updates(self):
256 _ = self.request.translate
256 _ = self.request.translate
257 c = self.load_default_context()
257 c = self.load_default_context()
258
258
259 user_id = self.request.matchdict.get('user_id')
259 user_id = self.request.matchdict.get('user_id')
260 c.user = User.get_or_404(user_id, pyramid_exc=True)
260 c.user = User.get_or_404(user_id, pyramid_exc=True)
261 self._redirect_for_default_user(c.user.username)
261 self._redirect_for_default_user(c.user.username)
262
262
263 users_groups = set(self.request.POST.getall('users_group_id'))
263 users_groups = set(self.request.POST.getall('users_group_id'))
264 users_groups_model = []
264 users_groups_model = []
265
265
266 for ugid in users_groups:
266 for ugid in users_groups:
267 users_groups_model.append(UserGroupModel().get_group(safe_int(ugid)))
267 users_groups_model.append(UserGroupModel().get_group(safe_int(ugid)))
268 user_group_model = UserGroupModel()
268 user_group_model = UserGroupModel()
269 user_group_model.change_groups(c.user, users_groups_model)
269 user_group_model.change_groups(c.user, users_groups_model)
270
270
271 Session().commit()
271 Session().commit()
272 c.active = 'user_groups_management'
272 c.active = 'user_groups_management'
273 h.flash(_("Groups successfully changed"), category='success')
273 h.flash(_("Groups successfully changed"), category='success')
274
274
275 return HTTPFound(h.route_path(
275 return HTTPFound(h.route_path(
276 'edit_user_groups_management', user_id=user_id))
276 'edit_user_groups_management', user_id=user_id))
277
277
278 @LoginRequired()
278 @LoginRequired()
279 @HasPermissionAllDecorator('hg.admin')
279 @HasPermissionAllDecorator('hg.admin')
280 @view_config(
280 @view_config(
281 route_name='edit_user_audit_logs', request_method='GET',
281 route_name='edit_user_audit_logs', request_method='GET',
282 renderer='rhodecode:templates/admin/users/user_edit.mako')
282 renderer='rhodecode:templates/admin/users/user_edit.mako')
283 def user_audit_logs(self):
283 def user_audit_logs(self):
284 _ = self.request.translate
284 _ = self.request.translate
285 c = self.load_default_context()
285 c = self.load_default_context()
286
286
287 user_id = self.request.matchdict.get('user_id')
287 user_id = self.request.matchdict.get('user_id')
288 c.user = User.get_or_404(user_id, pyramid_exc=True)
288 c.user = User.get_or_404(user_id, pyramid_exc=True)
289 self._redirect_for_default_user(c.user.username)
289 self._redirect_for_default_user(c.user.username)
290 c.active = 'audit'
290 c.active = 'audit'
291
291
292 p = safe_int(self.request.GET.get('page', 1), 1)
292 p = safe_int(self.request.GET.get('page', 1), 1)
293
293
294 filter_term = self.request.GET.get('filter')
294 filter_term = self.request.GET.get('filter')
295 c.user_log = UserModel().get_user_log(c.user, filter_term)
295 c.user_log = UserModel().get_user_log(c.user, filter_term)
296
296
297 def url_generator(**kw):
297 def url_generator(**kw):
298 if filter_term:
298 if filter_term:
299 kw['filter'] = filter_term
299 kw['filter'] = filter_term
300 return self.request.current_route_path(_query=kw)
300 return self.request.current_route_path(_query=kw)
301
301
302 c.user_log = Page(c.user_log, page=p, items_per_page=10,
302 c.user_log = Page(c.user_log, page=p, items_per_page=10,
303 url=url_generator)
303 url=url_generator)
304 c.filter_term = filter_term
304 c.filter_term = filter_term
305 return self._get_template_context(c)
305 return self._get_template_context(c)
306
306
General Comments 0
You need to be logged in to leave comments. Login now