##// END OF EJS Templates
user-groups: moved the display of user group into a pyramid view
marcink -
r1980:f55ac84b default
parent child Browse files
Show More
@@ -0,0 +1,113 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import pytest
22
23 from rhodecode.model.db import UserGroup, User
24 from rhodecode.model.meta import Session
25
26 from rhodecode.tests import (
27 TestController, TEST_USER_REGULAR_LOGIN, assert_session_flash)
28 from rhodecode.tests.fixture import Fixture
29
30 fixture = Fixture()
31
32
33 def route_path(name, params=None, **kwargs):
34 import urllib
35 from rhodecode.apps._base import ADMIN_PREFIX
36
37 base_url = {
38 'user_groups': ADMIN_PREFIX + '/user_groups',
39 'user_groups_data': ADMIN_PREFIX + '/user_groups_data',
40 'user_group_members_data': ADMIN_PREFIX + '/user_groups/{user_group_id}/members',
41 }[name].format(**kwargs)
42
43 if params:
44 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
45 return base_url
46
47
48 class TestAdminUserGroupsView(TestController):
49
50 def test_show_users(self):
51 self.log_user()
52 self.app.get(route_path('user_groups'))
53
54 def test_show_user_groups_data(self, xhr_header):
55 self.log_user()
56 response = self.app.get(route_path(
57 'user_groups_data'), extra_environ=xhr_header)
58
59 all_user_groups = UserGroup.query().count()
60 assert response.json['recordsTotal'] == all_user_groups
61
62 def test_show_user_groups_data_filtered(self, xhr_header):
63 self.log_user()
64 response = self.app.get(route_path(
65 'user_groups_data', params={'search[value]': 'empty_search'}),
66 extra_environ=xhr_header)
67
68 all_user_groups = UserGroup.query().count()
69 assert response.json['recordsTotal'] == all_user_groups
70 assert response.json['recordsFiltered'] == 0
71
72 def test_usergroup_escape(self, user_util, xhr_header):
73 self.log_user()
74
75 xss_img = '<img src="/image1" onload="alert(\'Hello, World!\');">'
76 user = user_util.create_user()
77 user.name = xss_img
78 user.lastname = xss_img
79 Session().add(user)
80 Session().commit()
81
82 user_group = user_util.create_user_group()
83
84 user_group.users_group_name = xss_img
85 user_group.user_group_description = '<strong onload="alert();">DESC</strong>'
86
87 response = self.app.get(
88 route_path('user_groups_data'), extra_environ=xhr_header)
89
90 response.mustcontain(
91 '&lt;strong onload=&#34;alert();&#34;&gt;DESC&lt;/strong&gt;')
92 response.mustcontain(
93 '&lt;img src=&#34;/image1&#34; onload=&#34;'
94 'alert(&#39;Hello, World!&#39;);&#34;&gt;')
95
96 def test_edit_user_group_autocomplete_empty_members(self, xhr_header, user_util):
97 self.log_user()
98 ug = user_util.create_user_group()
99 response = self.app.get(
100 route_path('user_group_members_data', user_group_id=ug.users_group_id),
101 extra_environ=xhr_header)
102
103 assert response.json == {'members': []}
104
105 def test_edit_user_group_autocomplete_members(self, xhr_header, user_util):
106 self.log_user()
107 members = [u.user_id for u in User.get_all()]
108 ug = user_util.create_user_group(members=members)
109 response = self.app.get(
110 route_path('user_group_members_data', user_group_id=ug.users_group_id),
111 extra_environ=xhr_header)
112
113 assert len(response.json['members']) == len(members)
@@ -0,0 +1,195 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22 import datetime
23
24 from pyramid.httpexceptions import HTTPFound
25 from pyramid.view import view_config
26
27 from rhodecode.lib.helpers import Page
28 from rhodecode.model.scm import UserGroupList
29 from rhodecode_tools.lib.ext_json import json
30
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode.lib.auth import (
33 LoginRequired, HasPermissionAllDecorator, CSRFRequired, NotAnonymous,
34 HasUserGroupPermissionAnyDecorator)
35 from rhodecode.lib import helpers as h
36 from rhodecode.lib.utils import PartialRenderer
37 from rhodecode.lib.utils2 import safe_int, safe_unicode
38 from rhodecode.model.auth_token import AuthTokenModel
39 from rhodecode.model.user import UserModel
40 from rhodecode.model.user_group import UserGroupModel
41 from rhodecode.model.db import User, UserGroup, UserGroupMember, or_, count
42 from rhodecode.model.meta import Session
43
44 log = logging.getLogger(__name__)
45
46
47 class AdminUserGroupsView(BaseAppView, DataGridAppView):
48
49 def load_default_context(self):
50 c = self._get_local_tmpl_context()
51 self._register_global_c(c)
52 return c
53
54 # permission check in data loading of
55 # `user_groups_list_data` via UserGroupList
56 @NotAnonymous()
57 @view_config(
58 route_name='user_groups', request_method='GET',
59 renderer='rhodecode:templates/admin/user_groups/user_groups.mako')
60 def user_groups_list(self):
61 c = self.load_default_context()
62 return self._get_template_context(c)
63
64 # permission check inside
65 @NotAnonymous()
66 @view_config(
67 route_name='user_groups_data', request_method='GET',
68 renderer='json_ext', xhr=True)
69 def user_groups_list_data(self):
70 column_map = {
71 'active': 'users_group_active',
72 'description': 'user_group_description',
73 'members': 'members_total',
74 'owner': 'user_username',
75 'sync': 'group_data'
76 }
77 draw, start, limit = self._extract_chunk(self.request)
78 search_q, order_by, order_dir = self._extract_ordering(
79 self.request, column_map=column_map)
80
81 _render = PartialRenderer('data_table/_dt_elements.mako')
82
83 def user_group_name(user_group_id, user_group_name):
84 return _render("user_group_name", user_group_id, user_group_name)
85
86 def user_group_actions(user_group_id, user_group_name):
87 return _render("user_group_actions", user_group_id, user_group_name)
88
89 def user_profile(username):
90 return _render('user_profile', username)
91
92 user_groups_data_total_count = UserGroup.query().count()
93
94 member_count = count(UserGroupMember.user_id)
95 base_q = Session.query(
96 UserGroup.users_group_name,
97 UserGroup.user_group_description,
98 UserGroup.users_group_active,
99 UserGroup.users_group_id,
100 UserGroup.group_data,
101 User,
102 member_count.label('member_count')
103 ) \
104 .outerjoin(UserGroupMember) \
105 .join(User, User.user_id == UserGroup.user_id) \
106 .group_by(UserGroup, User)
107
108 if search_q:
109 like_expression = u'%{}%'.format(safe_unicode(search_q))
110 base_q = base_q.filter(or_(
111 UserGroup.users_group_name.ilike(like_expression),
112 ))
113
114 user_groups_data_total_filtered_count = base_q.count()
115
116 if order_by == 'members_total':
117 sort_col = member_count
118 elif order_by == 'user_username':
119 sort_col = User.username
120 else:
121 sort_col = getattr(UserGroup, order_by, None)
122
123 if isinstance(sort_col, count) or sort_col:
124 if order_dir == 'asc':
125 sort_col = sort_col.asc()
126 else:
127 sort_col = sort_col.desc()
128
129 base_q = base_q.order_by(sort_col)
130 base_q = base_q.offset(start).limit(limit)
131
132 # authenticated access to user groups
133 user_group_list = base_q.all()
134 auth_user_group_list = UserGroupList(
135 user_group_list, perm_set=['usergroup.admin'])
136
137 user_groups_data = []
138 for user_gr in auth_user_group_list:
139 user_groups_data.append({
140 "users_group_name": user_group_name(
141 user_gr.users_group_id, h.escape(user_gr.users_group_name)),
142 "name_raw": h.escape(user_gr.users_group_name),
143 "description": h.escape(user_gr.user_group_description),
144 "members": user_gr.member_count,
145 # NOTE(marcink): because of advanced query we
146 # need to load it like that
147 "sync": UserGroup._load_group_data(
148 user_gr.group_data).get('extern_type'),
149 "active": h.bool2icon(user_gr.users_group_active),
150 "owner": user_profile(user_gr.User.username),
151 "action": user_group_actions(
152 user_gr.users_group_id, user_gr.users_group_name)
153 })
154
155 data = ({
156 'draw': draw,
157 'data': user_groups_data,
158 'recordsTotal': user_groups_data_total_count,
159 'recordsFiltered': user_groups_data_total_filtered_count,
160 })
161
162 return data
163
164 @LoginRequired()
165 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
166 @view_config(
167 route_name='user_group_members_data', request_method='GET',
168 renderer='json_ext', xhr=True)
169 def user_group_members(self):
170 """
171 Return members of given user group
172 """
173 user_group_id = self.request.matchdict['user_group_id']
174 user_group = UserGroup.get_or_404(user_group_id)
175 group_members_obj = sorted((x.user for x in user_group.members),
176 key=lambda u: u.username.lower())
177
178 group_members = [
179 {
180 'id': user.user_id,
181 'first_name': user.first_name,
182 'last_name': user.last_name,
183 'username': user.username,
184 'icon_link': h.gravatar_url(user.email, 30),
185 'value_display': h.person(user.email),
186 'value': user.username,
187 'value_type': 'user',
188 'active': user.active,
189 }
190 for user in group_members_obj
191 ]
192
193 return {
194 'members': group_members
195 }
@@ -162,6 +162,19 b' def admin_routes(config):'
162 name='edit_user_audit_logs',
162 name='edit_user_audit_logs',
163 pattern='/users/{user_id:\d+}/edit/audit')
163 pattern='/users/{user_id:\d+}/edit/audit')
164
164
165 # user groups admin
166 config.add_route(
167 name='user_groups',
168 pattern='/user_groups')
169
170 config.add_route(
171 name='user_groups_data',
172 pattern='/user_groups_data')
173
174 config.add_route(
175 name='user_group_members_data',
176 pattern='/user_groups/{user_group_id:\d+}/members')
177
165
178
166 def includeme(config):
179 def includeme(config):
167 settings = config.get_settings()
180 settings = config.get_settings()
@@ -78,8 +78,13 b' class AdminUsersView(BaseAppView, DataGr'
78 route_name='users_data', request_method='GET',
78 route_name='users_data', request_method='GET',
79 renderer='json_ext', xhr=True)
79 renderer='json_ext', xhr=True)
80 def users_list_data(self):
80 def users_list_data(self):
81 column_map = {
82 'first_name': 'name',
83 'last_name': 'lastname',
84 }
81 draw, start, limit = self._extract_chunk(self.request)
85 draw, start, limit = self._extract_chunk(self.request)
82 search_q, order_by, order_dir = self._extract_ordering(self.request)
86 search_q, order_by, order_dir = self._extract_ordering(
87 self.request, column_map=column_map)
83
88
84 _render = self.request.get_partial_renderer(
89 _render = self.request.get_partial_renderer(
85 'data_table/_dt_elements.mako')
90 'data_table/_dt_elements.mako')
@@ -279,8 +279,6 b' def make_map(config):'
279 controller='admin/user_groups') as m:
279 controller='admin/user_groups') as m:
280 m.connect('users_groups', '/user_groups',
280 m.connect('users_groups', '/user_groups',
281 action='create', conditions={'method': ['POST']})
281 action='create', conditions={'method': ['POST']})
282 m.connect('users_groups', '/user_groups',
283 action='index', conditions={'method': ['GET']})
284 m.connect('new_users_group', '/user_groups/new',
282 m.connect('new_users_group', '/user_groups/new',
285 action='new', conditions={'method': ['GET']})
283 action='new', conditions={'method': ['GET']})
286 m.connect('update_users_group', '/user_groups/{user_group_id}',
284 m.connect('update_users_group', '/user_groups/{user_group_id}',
@@ -317,10 +315,6 b' def make_map(config):'
317 '/user_groups/{user_group_id}/edit/advanced/sync',
315 '/user_groups/{user_group_id}/edit/advanced/sync',
318 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
316 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
319
317
320 m.connect('edit_user_group_members',
321 '/user_groups/{user_group_id}/edit/members', jsroute=True,
322 action='user_group_members', conditions={'method': ['GET']})
323
324 # ADMIN DEFAULTS REST ROUTES
318 # ADMIN DEFAULTS REST ROUTES
325 with rmap.submapper(path_prefix=ADMIN_PREFIX,
319 with rmap.submapper(path_prefix=ADMIN_PREFIX,
326 controller='admin/defaults') as m:
320 controller='admin/defaults') as m:
@@ -103,42 +103,6 b' class UserGroupsController(BaseControlle'
103 return True
103 return True
104 return False
104 return False
105
105
106 # permission check inside
107 @NotAnonymous()
108 def index(self):
109 # TODO(marcink): remove bind to self.request after pyramid migration
110 self.request = c.pyramid_request
111 _render = self.request.get_partial_renderer(
112 'data_table/_dt_elements.mako')
113
114 def user_group_name(user_group_id, user_group_name):
115 return _render("user_group_name", user_group_id, user_group_name)
116
117 def user_group_actions(user_group_id, user_group_name):
118 return _render("user_group_actions", user_group_id, user_group_name)
119
120 # json generate
121 group_iter = UserGroupList(UserGroup.query().all(),
122 perm_set=['usergroup.admin'])
123
124 user_groups_data = []
125 for user_gr in group_iter:
126 user_groups_data.append({
127 "group_name": user_group_name(
128 user_gr.users_group_id, h.escape(user_gr.users_group_name)),
129 "group_name_raw": user_gr.users_group_name,
130 "desc": h.escape(user_gr.user_group_description),
131 "members": len(user_gr.members),
132 "sync": user_gr.group_data.get('extern_type'),
133 "active": h.bool2icon(user_gr.users_group_active),
134 "owner": h.escape(h.link_to_user(user_gr.user.username)),
135 "action": user_group_actions(
136 user_gr.users_group_id, user_gr.users_group_name)
137 })
138
139 c.data = json.dumps(user_groups_data)
140 return render('admin/user_groups/user_groups.mako')
141
142 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
106 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
143 @auth.CSRFRequired()
107 @auth.CSRFRequired()
144 def create(self):
108 def create(self):
@@ -482,33 +446,3 b' class UserGroupsController(BaseControlle'
482 return redirect(
446 return redirect(
483 url('edit_user_group_advanced', user_group_id=user_group_id))
447 url('edit_user_group_advanced', user_group_id=user_group_id))
484
448
485 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
486 @XHRRequired()
487 @jsonify
488 def user_group_members(self, user_group_id):
489 """
490 Return members of given user group
491 """
492 user_group_id = safe_int(user_group_id)
493 user_group = UserGroup.get_or_404(user_group_id)
494 group_members_obj = sorted((x.user for x in user_group.members),
495 key=lambda u: u.username.lower())
496
497 group_members = [
498 {
499 'id': user.user_id,
500 'first_name': user.first_name,
501 'last_name': user.last_name,
502 'username': user.username,
503 'icon_link': h.gravatar_url(user.email, 30),
504 'value_display': h.person(user.email),
505 'value': user.username,
506 'value_type': 'user',
507 'active': user.active,
508 }
509 for user in group_members_obj
510 ]
511
512 return {
513 'members': group_members
514 }
@@ -132,9 +132,9 b' def get_user_group_slug(request):'
132 if _group:
132 if _group:
133 _group = _group.users_group_name
133 _group = _group.users_group_name
134 except Exception:
134 except Exception:
135 log.debug(traceback.format_exc())
135 log.exception('Failed to get user group by id')
136 # catch all failures here
136 # catch all failures here
137 pass
137 return None
138
138
139 return _group
139 return _group
140
140
@@ -41,6 +41,7 b' from sqlalchemy.ext.hybrid import hybrid'
41 from sqlalchemy.orm import (
41 from sqlalchemy.orm import (
42 relationship, joinedload, class_mapper, validates, aliased)
42 relationship, joinedload, class_mapper, validates, aliased)
43 from sqlalchemy.sql.expression import true
43 from sqlalchemy.sql.expression import true
44 from sqlalchemy.sql.functions import coalesce, count # noqa
44 from beaker.cache import cache_region
45 from beaker.cache import cache_region
45 from zope.cachedescriptors.property import Lazy as LazyProperty
46 from zope.cachedescriptors.property import Lazy as LazyProperty
46
47
@@ -1205,7 +1206,17 b' class UserGroup(Base, BaseModel):'
1205 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
1206 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
1206 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
1207 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
1207
1208
1208 user = relationship('User')
1209 user = relationship('User', primaryjoin="User.user_id==UserGroup.user_id")
1210
1211 @classmethod
1212 def _load_group_data(cls, column):
1213 if not column:
1214 return {}
1215
1216 try:
1217 return json.loads(column) or {}
1218 except TypeError:
1219 return {}
1209
1220
1210 @hybrid_property
1221 @hybrid_property
1211 def description_safe(self):
1222 def description_safe(self):
@@ -1214,13 +1225,11 b' class UserGroup(Base, BaseModel):'
1214
1225
1215 @hybrid_property
1226 @hybrid_property
1216 def group_data(self):
1227 def group_data(self):
1217 if not self._group_data:
1228 return self._load_group_data(self._group_data)
1218 return {}
1229
1219
1230 @group_data.expression
1220 try:
1231 def group_data(self, **kwargs):
1221 return json.loads(self._group_data)
1232 return self._group_data
1222 except TypeError:
1223 return {}
1224
1233
1225 @group_data.setter
1234 @group_data.setter
1226 def group_data(self, val):
1235 def group_data(self, val):
@@ -14,7 +14,6 b' function registerRCRoutes() {'
14 // routes registration
14 // routes registration
15 pyroutes.register('new_repo', '/_admin/create_repository', []);
15 pyroutes.register('new_repo', '/_admin/create_repository', []);
16 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
16 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
17 pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']);
18 pyroutes.register('favicon', '/favicon.ico', []);
17 pyroutes.register('favicon', '/favicon.ico', []);
19 pyroutes.register('robots', '/robots.txt', []);
18 pyroutes.register('robots', '/robots.txt', []);
20 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
19 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
@@ -72,6 +71,9 b' function registerRCRoutes() {'
72 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
71 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
73 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
72 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
74 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
73 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
74 pyroutes.register('user_groups', '/_admin/user_groups', []);
75 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
76 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
75 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
77 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
76 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
78 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
77 pyroutes.register('channelstream_proxy', '/_channelstream', []);
79 pyroutes.register('channelstream_proxy', '/_channelstream', []);
@@ -166,7 +166,7 b''
166
166
167 if (suggestion.value_type == 'user_group') {
167 if (suggestion.value_type == 'user_group') {
168 $.getJSON(
168 $.getJSON(
169 pyroutes.url('edit_user_group_members',
169 pyroutes.url('user_group_members_data',
170 {'user_group_id': suggestion.id}),
170 {'user_group_id': suggestion.id}),
171 function(data) {
171 function(data) {
172 $.each(data.members, function(idx, user) {
172 $.each(data.members, function(idx, user) {
@@ -38,26 +38,31 b''
38 </div>
38 </div>
39 <script>
39 <script>
40 $(document).ready(function() {
40 $(document).ready(function() {
41 var getDatatableCount = function(){
42 var table = $('#user_group_list_table').dataTable();
43 var page = table.api().page.info();
44 var active = page.recordsDisplay;
45 var total = page.recordsTotal;
41
46
42 var get_datatable_count = function(){
47 var _text = _gettext("{0} out of {1} users").format(active, total);
43 var api = $('#user_group_list_table').dataTable().api();
48 $('#user_group_count').text(_text);
44 $('#user_group_count').text(api.page.info().recordsDisplay);
45 };
49 };
46
50
47 // user list
51 // user list
48 $('#user_group_list_table').DataTable({
52 $('#user_group_list_table').DataTable({
49 data: ${c.data|n},
53 processing: true,
54 serverSide: true,
55 ajax: "${h.route_path('user_groups_data')}",
50 dom: 'rtp',
56 dom: 'rtp',
51 pageLength: ${c.visual.admin_grid_items},
57 pageLength: ${c.visual.admin_grid_items},
52 order: [[ 0, "asc" ]],
58 order: [[ 0, "asc" ]],
53 columns: [
59 columns: [
54 { data: {"_": "group_name",
60 { data: {"_": "users_group_name",
55 "sort": "group_name_raw"}, title: "${_('Name')}", className: "td-componentname" },
61 "sort": "users_group_name"}, title: "${_('Name')}", className: "td-componentname" },
56 { data: {"_": "desc",
62 { data: {"_": "description",
57 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
63 "sort": "description"}, title: "${_('Description')}", className: "td-description" },
58 { data: {"_": "members",
64 { data: {"_": "members",
59 "sort": "members",
65 "sort": "members"}, title: "${_('Members')}", className: "td-number" },
60 "type": Number}, title: "${_('Members')}", className: "td-number" },
61 { data: {"_": "sync",
66 { data: {"_": "sync",
62 "sort": "sync"}, title: "${_('Sync')}", className: "td-sync" },
67 "sort": "sync"}, title: "${_('Sync')}", className: "td-sync" },
63 { data: {"_": "active",
68 { data: {"_": "active",
@@ -65,33 +70,36 b''
65 { data: {"_": "owner",
70 { data: {"_": "owner",
66 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
71 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
67 { data: {"_": "action",
72 { data: {"_": "action",
68 "sort": "action"}, title: "${_('Action')}", className: "td-action" }
73 "sort": "action"}, title: "${_('Action')}", className: "td-action", orderable: false}
69 ],
74 ],
70 language: {
75 language: {
71 paginate: DEFAULT_GRID_PAGINATION,
76 paginate: DEFAULT_GRID_PAGINATION,
77 sProcessing: _gettext('loading...'),
72 emptyTable: _gettext("No user groups available yet.")
78 emptyTable: _gettext("No user groups available yet.")
73 },
74 "initComplete": function( settings, json ) {
75 get_datatable_count();
76 }
79 }
77 });
80 });
78
81
79 // update the counter when doing search
82 $('#user_group_list_table').on('xhr.dt', function(e, settings, json, xhr){
80 $('#user_group_list_table').on( 'search.dt', function (e,settings) {
83 $('#user_group_list_table').css('opacity', 1);
81 get_datatable_count();
84 });
85
86 $('#user_group_list_table').on('preXhr.dt', function(e, settings, data){
87 $('#user_group_list_table').css('opacity', 0.3);
82 });
88 });
83
89
84 // filter, filter both grids
90 // refresh counters on draw
85 $('#q_filter').on( 'keyup', function () {
91 $('#user_group_list_table').on('draw.dt', function(){
86 var user_api = $('#user_group_list_table').dataTable().api();
92 getDatatableCount();
87 user_api
88 .columns(0)
89 .search(this.value)
90 .draw();
91 });
93 });
92
94
93 // refilter table if page load via back button
95 // filter
94 $("#q_filter").trigger('keyup');
96 $('#q_filter').on('keyup',
97 $.debounce(250, function() {
98 $('#user_group_list_table').DataTable().search(
99 $('#q_filter').val()
100 ).draw();
101 })
102 );
95
103
96 });
104 });
97
105
@@ -33,11 +33,6 b' fixture = Fixture()'
33
33
34 class TestAdminUsersGroupsController(TestController):
34 class TestAdminUsersGroupsController(TestController):
35
35
36 def test_index(self):
37 self.log_user()
38 response = self.app.get(url('users_groups'))
39 assert response.status_int == 200
40
41 def test_create(self):
36 def test_create(self):
42 self.log_user()
37 self.log_user()
43 users_group_name = TEST_USER_GROUP
38 users_group_name = TEST_USER_GROUP
@@ -190,46 +185,6 b' class TestAdminUsersGroupsController(Tes'
190 url('edit_users_group', user_group_id=ug.users_group_id))
185 url('edit_users_group', user_group_id=ug.users_group_id))
191 fixture.destroy_user_group(TEST_USER_GROUP)
186 fixture.destroy_user_group(TEST_USER_GROUP)
192
187
193 def test_edit_user_group_autocomplete_members(self, xhr_header):
194 self.log_user()
195 ug = fixture.create_user_group(TEST_USER_GROUP, skip_if_exists=True)
196 response = self.app.get(
197 url('edit_user_group_members', user_group_id=ug.users_group_id),
198 extra_environ=xhr_header)
199
200 assert response.body == '{"members": []}'
201 fixture.destroy_user_group(TEST_USER_GROUP)
202
203 def test_usergroup_escape(self, user_util):
204 user = user_util.create_user(
205 username='escape_user',
206 firstname='<img src="/image2" onload="alert(\'Hello, World!\');">',
207 lastname='<img src="/image2" onload="alert(\'Hello, World!\');">'
208 )
209
210 user_util.create_user_group(owner=user.username)
211
212 self.log_user()
213 users_group_name = 'samplegroup'
214 data = {
215 'users_group_name': users_group_name,
216 'user_group_description': (
217 '<strong onload="alert();">DESC</strong>'),
218 'active': True,
219 'csrf_token': self.csrf_token
220 }
221
222 self.app.post(url('users_groups'), data)
223 response = self.app.get(url('users_groups'))
224
225 response.mustcontain(
226 '&lt;strong onload=&#34;alert();&#34;&gt;'
227 'DESC&lt;/strong&gt;')
228 # TODO(marcink): fix this test after user-group grid rewrite
229 # response.mustcontain(
230 # '&lt;img src=&#34;/image2&#34; onload=&#34;'
231 # 'alert(&#39;Hello, World!&#39;);&#34;&gt;')
232
233 def test_update_members_from_user_ids(self, user_regular):
188 def test_update_members_from_user_ids(self, user_regular):
234 uid = user_regular.user_id
189 uid = user_regular.user_id
235 username = user_regular.username
190 username = user_regular.username
@@ -27,18 +27,30 b' from rhodecode.model.meta import Session'
27 from rhodecode.tests.fixture import Fixture
27 from rhodecode.tests.fixture import Fixture
28
28
29
29
30 def route_path(name, params=None, **kwargs):
31 import urllib
32 from rhodecode.apps._base import ADMIN_PREFIX
33
34 base_url = {
35 'home': '/',
36 'user_groups':
37 ADMIN_PREFIX + '/user_groups',
38 'user_groups_data':
39 ADMIN_PREFIX + '/user_groups_data',
40 }[name].format(**kwargs)
41
42 if params:
43 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
44 return base_url
45
46
30 fixture = Fixture()
47 fixture = Fixture()
31
48
32
49
33 def route_path(name, **kwargs):
50 class TestAdminDelegatedUser(TestController):
34 return {
35 'home': '/',
36 }[name].format(**kwargs)
37
51
38
52 def test_regular_user_cannot_see_admin_interfaces(
39 class TestAdminUsersGroupsController(TestController):
53 self, user_util, xhr_header):
40
41 def test_regular_user_cannot_see_admin_interfaces(self, user_util):
42 user = user_util.create_user(password='qweqwe')
54 user = user_util.create_user(password='qweqwe')
43 self.log_user(user.username, 'qweqwe')
55 self.log_user(user.username, 'qweqwe')
44
56
@@ -57,10 +69,12 b' class TestAdminUsersGroupsController(Tes'
57 response = self.app.get(url('repo_groups'), status=200)
69 response = self.app.get(url('repo_groups'), status=200)
58 response.mustcontain('data: []')
70 response.mustcontain('data: []')
59
71
60 response = self.app.get(url('users_groups'), status=200)
72 response = self.app.get(route_path('user_groups_data'),
61 response.mustcontain('data: []')
73 status=200, extra_environ=xhr_header)
74 assert response.json['data'] == []
62
75
63 def test_regular_user_can_see_admin_interfaces_if_owner(self, user_util):
76 def test_regular_user_can_see_admin_interfaces_if_owner(
77 self, user_util, xhr_header):
64 user = user_util.create_user(password='qweqwe')
78 user = user_util.create_user(password='qweqwe')
65 username = user.username
79 username = user.username
66
80
@@ -90,10 +104,12 b' class TestAdminUsersGroupsController(Tes'
90 response = self.app.get(url('repo_groups'), status=200)
104 response = self.app.get(url('repo_groups'), status=200)
91 response.mustcontain('"name_raw": "{}"'.format(repo_group_name))
105 response.mustcontain('"name_raw": "{}"'.format(repo_group_name))
92
106
93 response = self.app.get(url('users_groups'), status=200)
107 response = self.app.get(route_path('user_groups_data'),
94 response.mustcontain('"group_name_raw": "{}"'.format(user_group_name))
108 extra_environ=xhr_header, status=200)
109 response.mustcontain('"name_raw": "{}"'.format(user_group_name))
95
110
96 def test_regular_user_can_see_admin_interfaces_if_admin_perm(self, user_util):
111 def test_regular_user_can_see_admin_interfaces_if_admin_perm(
112 self, user_util, xhr_header):
97 user = user_util.create_user(password='qweqwe')
113 user = user_util.create_user(password='qweqwe')
98 username = user.username
114 username = user.username
99
115
@@ -130,5 +146,6 b' class TestAdminUsersGroupsController(Tes'
130 response = self.app.get(url('repo_groups'), status=200)
146 response = self.app.get(url('repo_groups'), status=200)
131 response.mustcontain('"name_raw": "{}"'.format(repo_group_name))
147 response.mustcontain('"name_raw": "{}"'.format(repo_group_name))
132
148
133 response = self.app.get(url('users_groups'), status=200)
149 response = self.app.get(route_path('user_groups_data'),
134 response.mustcontain('"group_name_raw": "{}"'.format(user_group_name))
150 extra_environ=xhr_header, status=200)
151 response.mustcontain('"name_raw": "{}"'.format(user_group_name))
General Comments 0
You need to be logged in to leave comments. Login now