##// END OF EJS Templates
admin users and admin users group: add number of inactive users/users_group, resolves #5454
Bartłomiej Wołyńczyk -
r2727:15796c8e default
parent child Browse files
Show More
@@ -1,246 +1,257 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 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
22
23 import formencode
23 import formencode
24 import formencode.htmlfill
24 import formencode.htmlfill
25
25
26 from pyramid.httpexceptions import HTTPFound
26 from pyramid.httpexceptions import HTTPFound
27 from pyramid.view import view_config
27 from pyramid.view import view_config
28 from pyramid.response import Response
28 from pyramid.response import Response
29 from pyramid.renderers import render
29 from pyramid.renderers import render
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, NotAnonymous, CSRFRequired, HasPermissionAnyDecorator)
33 LoginRequired, NotAnonymous, CSRFRequired, HasPermissionAnyDecorator)
34 from rhodecode.lib import helpers as h, audit_logger
34 from rhodecode.lib import helpers as h, audit_logger
35 from rhodecode.lib.utils2 import safe_unicode
35 from rhodecode.lib.utils2 import safe_unicode
36
36
37 from rhodecode.model.forms import UserGroupForm
37 from rhodecode.model.forms import UserGroupForm
38 from rhodecode.model.permission import PermissionModel
38 from rhodecode.model.permission import PermissionModel
39 from rhodecode.model.scm import UserGroupList
39 from rhodecode.model.scm import UserGroupList
40 from rhodecode.model.db import (
40 from rhodecode.model.db import (
41 or_, count, User, UserGroup, UserGroupMember)
41 or_, count, User, UserGroup, UserGroupMember)
42 from rhodecode.model.meta import Session
42 from rhodecode.model.meta import Session
43 from rhodecode.model.user_group import UserGroupModel
43 from rhodecode.model.user_group import UserGroupModel
44 from rhodecode.model.db import true
44
45
45 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
46
47
47
48
48 class AdminUserGroupsView(BaseAppView, DataGridAppView):
49 class AdminUserGroupsView(BaseAppView, DataGridAppView):
49
50
50 def load_default_context(self):
51 def load_default_context(self):
51 c = self._get_local_tmpl_context()
52 c = self._get_local_tmpl_context()
52
53
53 PermissionModel().set_global_permission_choices(
54 PermissionModel().set_global_permission_choices(
54 c, gettext_translator=self.request.translate)
55 c, gettext_translator=self.request.translate)
55
56
56 return c
57 return c
57
58
58 # permission check in data loading of
59 # permission check in data loading of
59 # `user_groups_list_data` via UserGroupList
60 # `user_groups_list_data` via UserGroupList
60 @LoginRequired()
61 @LoginRequired()
61 @NotAnonymous()
62 @NotAnonymous()
62 @view_config(
63 @view_config(
63 route_name='user_groups', request_method='GET',
64 route_name='user_groups', request_method='GET',
64 renderer='rhodecode:templates/admin/user_groups/user_groups.mako')
65 renderer='rhodecode:templates/admin/user_groups/user_groups.mako')
65 def user_groups_list(self):
66 def user_groups_list(self):
66 c = self.load_default_context()
67 c = self.load_default_context()
67 return self._get_template_context(c)
68 return self._get_template_context(c)
68
69
69 # permission check inside
70 # permission check inside
70 @LoginRequired()
71 @LoginRequired()
71 @NotAnonymous()
72 @NotAnonymous()
72 @view_config(
73 @view_config(
73 route_name='user_groups_data', request_method='GET',
74 route_name='user_groups_data', request_method='GET',
74 renderer='json_ext', xhr=True)
75 renderer='json_ext', xhr=True)
75 def user_groups_list_data(self):
76 def user_groups_list_data(self):
76 self.load_default_context()
77 self.load_default_context()
77 column_map = {
78 column_map = {
78 'active': 'users_group_active',
79 'active': 'users_group_active',
79 'description': 'user_group_description',
80 'description': 'user_group_description',
80 'members': 'members_total',
81 'members': 'members_total',
81 'owner': 'user_username',
82 'owner': 'user_username',
82 'sync': 'group_data'
83 'sync': 'group_data'
83 }
84 }
84 draw, start, limit = self._extract_chunk(self.request)
85 draw, start, limit = self._extract_chunk(self.request)
85 search_q, order_by, order_dir = self._extract_ordering(
86 search_q, order_by, order_dir = self._extract_ordering(
86 self.request, column_map=column_map)
87 self.request, column_map=column_map)
87
88
88 _render = self.request.get_partial_renderer(
89 _render = self.request.get_partial_renderer(
89 'rhodecode:templates/data_table/_dt_elements.mako')
90 'rhodecode:templates/data_table/_dt_elements.mako')
90
91
91 def user_group_name(user_group_name):
92 def user_group_name(user_group_name):
92 return _render("user_group_name", user_group_name)
93 return _render("user_group_name", user_group_name)
93
94
94 def user_group_actions(user_group_id, user_group_name):
95 def user_group_actions(user_group_id, user_group_name):
95 return _render("user_group_actions", user_group_id, user_group_name)
96 return _render("user_group_actions", user_group_id, user_group_name)
96
97
97 def user_profile(username):
98 def user_profile(username):
98 return _render('user_profile', username)
99 return _render('user_profile', username)
99
100
100 auth_user_group_list = UserGroupList(
101 auth_user_group_list = UserGroupList(
101 UserGroup.query().all(), perm_set=['usergroup.admin'])
102 UserGroup.query().all(), perm_set=['usergroup.admin'])
102
103
103 allowed_ids = [-1]
104 allowed_ids = [-1]
104 for user_group in auth_user_group_list:
105 for user_group in auth_user_group_list:
105 allowed_ids.append(user_group.users_group_id)
106 allowed_ids.append(user_group.users_group_id)
106
107
107 user_groups_data_total_count = UserGroup.query()\
108 user_groups_data_total_count = UserGroup.query()\
108 .filter(UserGroup.users_group_id.in_(allowed_ids))\
109 .filter(UserGroup.users_group_id.in_(allowed_ids))\
109 .count()
110 .count()
110
111
112 user_groups_data_total_inactive_count = UserGroup.query()\
113 .filter(UserGroup.users_group_id.in_(allowed_ids))\
114 .filter(UserGroup.users_group_active != true()).count()
115
111 member_count = count(UserGroupMember.user_id)
116 member_count = count(UserGroupMember.user_id)
112 base_q = Session.query(
117 base_q = Session.query(
113 UserGroup.users_group_name,
118 UserGroup.users_group_name,
114 UserGroup.user_group_description,
119 UserGroup.user_group_description,
115 UserGroup.users_group_active,
120 UserGroup.users_group_active,
116 UserGroup.users_group_id,
121 UserGroup.users_group_id,
117 UserGroup.group_data,
122 UserGroup.group_data,
118 User,
123 User,
119 member_count.label('member_count')
124 member_count.label('member_count')
120 ) \
125 ) \
121 .filter(UserGroup.users_group_id.in_(allowed_ids)) \
126 .filter(UserGroup.users_group_id.in_(allowed_ids)) \
122 .outerjoin(UserGroupMember) \
127 .outerjoin(UserGroupMember) \
123 .join(User, User.user_id == UserGroup.user_id) \
128 .join(User, User.user_id == UserGroup.user_id) \
124 .group_by(UserGroup, User)
129 .group_by(UserGroup, User)
125
130
131 base_q_inactive = base_q.filter(UserGroup.users_group_active != true())
132
126 if search_q:
133 if search_q:
127 like_expression = u'%{}%'.format(safe_unicode(search_q))
134 like_expression = u'%{}%'.format(safe_unicode(search_q))
128 base_q = base_q.filter(or_(
135 base_q = base_q.filter(or_(
129 UserGroup.users_group_name.ilike(like_expression),
136 UserGroup.users_group_name.ilike(like_expression),
130 ))
137 ))
138 base_q_inactive = base_q.filter(UserGroup.users_group_active != true())
131
139
132 user_groups_data_total_filtered_count = base_q.count()
140 user_groups_data_total_filtered_count = base_q.count()
141 user_groups_data_total_filtered_inactive_count = base_q_inactive.count()
133
142
134 if order_by == 'members_total':
143 if order_by == 'members_total':
135 sort_col = member_count
144 sort_col = member_count
136 elif order_by == 'user_username':
145 elif order_by == 'user_username':
137 sort_col = User.username
146 sort_col = User.username
138 else:
147 else:
139 sort_col = getattr(UserGroup, order_by, None)
148 sort_col = getattr(UserGroup, order_by, None)
140
149
141 if isinstance(sort_col, count) or sort_col:
150 if isinstance(sort_col, count) or sort_col:
142 if order_dir == 'asc':
151 if order_dir == 'asc':
143 sort_col = sort_col.asc()
152 sort_col = sort_col.asc()
144 else:
153 else:
145 sort_col = sort_col.desc()
154 sort_col = sort_col.desc()
146
155
147 base_q = base_q.order_by(sort_col)
156 base_q = base_q.order_by(sort_col)
148 base_q = base_q.offset(start).limit(limit)
157 base_q = base_q.offset(start).limit(limit)
149
158
150 # authenticated access to user groups
159 # authenticated access to user groups
151 auth_user_group_list = base_q.all()
160 auth_user_group_list = base_q.all()
152
161
153 user_groups_data = []
162 user_groups_data = []
154 for user_gr in auth_user_group_list:
163 for user_gr in auth_user_group_list:
155 user_groups_data.append({
164 user_groups_data.append({
156 "users_group_name": user_group_name(user_gr.users_group_name),
165 "users_group_name": user_group_name(user_gr.users_group_name),
157 "name_raw": h.escape(user_gr.users_group_name),
166 "name_raw": h.escape(user_gr.users_group_name),
158 "description": h.escape(user_gr.user_group_description),
167 "description": h.escape(user_gr.user_group_description),
159 "members": user_gr.member_count,
168 "members": user_gr.member_count,
160 # NOTE(marcink): because of advanced query we
169 # NOTE(marcink): because of advanced query we
161 # need to load it like that
170 # need to load it like that
162 "sync": UserGroup._load_sync(
171 "sync": UserGroup._load_sync(
163 UserGroup._load_group_data(user_gr.group_data)),
172 UserGroup._load_group_data(user_gr.group_data)),
164 "active": h.bool2icon(user_gr.users_group_active),
173 "active": h.bool2icon(user_gr.users_group_active),
165 "owner": user_profile(user_gr.User.username),
174 "owner": user_profile(user_gr.User.username),
166 "action": user_group_actions(
175 "action": user_group_actions(
167 user_gr.users_group_id, user_gr.users_group_name)
176 user_gr.users_group_id, user_gr.users_group_name)
168 })
177 })
169
178
170 data = ({
179 data = ({
171 'draw': draw,
180 'draw': draw,
172 'data': user_groups_data,
181 'data': user_groups_data,
173 'recordsTotal': user_groups_data_total_count,
182 'recordsTotal': user_groups_data_total_count,
183 'recordsTotalInactive': user_groups_data_total_inactive_count,
174 'recordsFiltered': user_groups_data_total_filtered_count,
184 'recordsFiltered': user_groups_data_total_filtered_count,
185 'recordsFilteredInactive': user_groups_data_total_filtered_inactive_count,
175 })
186 })
176
187
177 return data
188 return data
178
189
179 @LoginRequired()
190 @LoginRequired()
180 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
191 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
181 @view_config(
192 @view_config(
182 route_name='user_groups_new', request_method='GET',
193 route_name='user_groups_new', request_method='GET',
183 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
194 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
184 def user_groups_new(self):
195 def user_groups_new(self):
185 c = self.load_default_context()
196 c = self.load_default_context()
186 return self._get_template_context(c)
197 return self._get_template_context(c)
187
198
188 @LoginRequired()
199 @LoginRequired()
189 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
200 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
190 @CSRFRequired()
201 @CSRFRequired()
191 @view_config(
202 @view_config(
192 route_name='user_groups_create', request_method='POST',
203 route_name='user_groups_create', request_method='POST',
193 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
204 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
194 def user_groups_create(self):
205 def user_groups_create(self):
195 _ = self.request.translate
206 _ = self.request.translate
196 c = self.load_default_context()
207 c = self.load_default_context()
197 users_group_form = UserGroupForm(self.request.translate)()
208 users_group_form = UserGroupForm(self.request.translate)()
198
209
199 user_group_name = self.request.POST.get('users_group_name')
210 user_group_name = self.request.POST.get('users_group_name')
200 try:
211 try:
201 form_result = users_group_form.to_python(dict(self.request.POST))
212 form_result = users_group_form.to_python(dict(self.request.POST))
202 user_group = UserGroupModel().create(
213 user_group = UserGroupModel().create(
203 name=form_result['users_group_name'],
214 name=form_result['users_group_name'],
204 description=form_result['user_group_description'],
215 description=form_result['user_group_description'],
205 owner=self._rhodecode_user.user_id,
216 owner=self._rhodecode_user.user_id,
206 active=form_result['users_group_active'])
217 active=form_result['users_group_active'])
207 Session().flush()
218 Session().flush()
208 creation_data = user_group.get_api_data()
219 creation_data = user_group.get_api_data()
209 user_group_name = form_result['users_group_name']
220 user_group_name = form_result['users_group_name']
210
221
211 audit_logger.store_web(
222 audit_logger.store_web(
212 'user_group.create', action_data={'data': creation_data},
223 'user_group.create', action_data={'data': creation_data},
213 user=self._rhodecode_user)
224 user=self._rhodecode_user)
214
225
215 user_group_link = h.link_to(
226 user_group_link = h.link_to(
216 h.escape(user_group_name),
227 h.escape(user_group_name),
217 h.route_path(
228 h.route_path(
218 'edit_user_group', user_group_id=user_group.users_group_id))
229 'edit_user_group', user_group_id=user_group.users_group_id))
219 h.flash(h.literal(_('Created user group %(user_group_link)s')
230 h.flash(h.literal(_('Created user group %(user_group_link)s')
220 % {'user_group_link': user_group_link}),
231 % {'user_group_link': user_group_link}),
221 category='success')
232 category='success')
222 Session().commit()
233 Session().commit()
223 user_group_id = user_group.users_group_id
234 user_group_id = user_group.users_group_id
224 except formencode.Invalid as errors:
235 except formencode.Invalid as errors:
225
236
226 data = render(
237 data = render(
227 'rhodecode:templates/admin/user_groups/user_group_add.mako',
238 'rhodecode:templates/admin/user_groups/user_group_add.mako',
228 self._get_template_context(c), self.request)
239 self._get_template_context(c), self.request)
229 html = formencode.htmlfill.render(
240 html = formencode.htmlfill.render(
230 data,
241 data,
231 defaults=errors.value,
242 defaults=errors.value,
232 errors=errors.error_dict or {},
243 errors=errors.error_dict or {},
233 prefix_error=False,
244 prefix_error=False,
234 encoding="UTF-8",
245 encoding="UTF-8",
235 force_defaults=False
246 force_defaults=False
236 )
247 )
237 return Response(html)
248 return Response(html)
238
249
239 except Exception:
250 except Exception:
240 log.exception("Exception creating user group")
251 log.exception("Exception creating user group")
241 h.flash(_('Error occurred during creation of user group %s') \
252 h.flash(_('Error occurred during creation of user group %s') \
242 % user_group_name, category='error')
253 % user_group_name, category='error')
243 raise HTTPFound(h.route_path('user_groups_new'))
254 raise HTTPFound(h.route_path('user_groups_new'))
244
255
245 raise HTTPFound(
256 raise HTTPFound(
246 h.route_path('edit_user_group', user_group_id=user_group_id))
257 h.route_path('edit_user_group', user_group_id=user_group_id))
@@ -1,1191 +1,1200 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 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 import formencode.htmlfill
24 import formencode.htmlfill
25
25
26 from pyramid.httpexceptions import HTTPFound
26 from pyramid.httpexceptions import HTTPFound
27 from pyramid.view import view_config
27 from pyramid.view import view_config
28 from pyramid.renderers import render
28 from pyramid.renderers import render
29 from pyramid.response import Response
29 from pyramid.response import Response
30
30
31 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
32 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
32 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
33 from rhodecode.authentication.plugins import auth_rhodecode
33 from rhodecode.authentication.plugins import auth_rhodecode
34 from rhodecode.events import trigger
34 from rhodecode.events import trigger
35 from rhodecode.model.db import true
35
36
36 from rhodecode.lib import audit_logger
37 from rhodecode.lib import audit_logger
37 from rhodecode.lib.exceptions import (
38 from rhodecode.lib.exceptions import (
38 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
39 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
39 UserOwnsUserGroupsException, DefaultUserException)
40 UserOwnsUserGroupsException, DefaultUserException)
40 from rhodecode.lib.ext_json import json
41 from rhodecode.lib.ext_json import json
41 from rhodecode.lib.auth import (
42 from rhodecode.lib.auth import (
42 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
43 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
43 from rhodecode.lib import helpers as h
44 from rhodecode.lib import helpers as h
44 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
45 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
45 from rhodecode.model.auth_token import AuthTokenModel
46 from rhodecode.model.auth_token import AuthTokenModel
46 from rhodecode.model.forms import (
47 from rhodecode.model.forms import (
47 UserForm, UserIndividualPermissionsForm, UserPermissionsForm,
48 UserForm, UserIndividualPermissionsForm, UserPermissionsForm,
48 UserExtraEmailForm, UserExtraIpForm)
49 UserExtraEmailForm, UserExtraIpForm)
49 from rhodecode.model.permission import PermissionModel
50 from rhodecode.model.permission import PermissionModel
50 from rhodecode.model.repo_group import RepoGroupModel
51 from rhodecode.model.repo_group import RepoGroupModel
51 from rhodecode.model.ssh_key import SshKeyModel
52 from rhodecode.model.ssh_key import SshKeyModel
52 from rhodecode.model.user import UserModel
53 from rhodecode.model.user import UserModel
53 from rhodecode.model.user_group import UserGroupModel
54 from rhodecode.model.user_group import UserGroupModel
54 from rhodecode.model.db import (
55 from rhodecode.model.db import (
55 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
56 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
56 UserApiKeys, UserSshKeys, RepoGroup)
57 UserApiKeys, UserSshKeys, RepoGroup)
57 from rhodecode.model.meta import Session
58 from rhodecode.model.meta import Session
58
59
59 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
60
61
61
62
62 class AdminUsersView(BaseAppView, DataGridAppView):
63 class AdminUsersView(BaseAppView, DataGridAppView):
63
64
64 def load_default_context(self):
65 def load_default_context(self):
65 c = self._get_local_tmpl_context()
66 c = self._get_local_tmpl_context()
66 return c
67 return c
67
68
68 @LoginRequired()
69 @LoginRequired()
69 @HasPermissionAllDecorator('hg.admin')
70 @HasPermissionAllDecorator('hg.admin')
70 @view_config(
71 @view_config(
71 route_name='users', request_method='GET',
72 route_name='users', request_method='GET',
72 renderer='rhodecode:templates/admin/users/users.mako')
73 renderer='rhodecode:templates/admin/users/users.mako')
73 def users_list(self):
74 def users_list(self):
74 c = self.load_default_context()
75 c = self.load_default_context()
75 return self._get_template_context(c)
76 return self._get_template_context(c)
76
77
77 @LoginRequired()
78 @LoginRequired()
78 @HasPermissionAllDecorator('hg.admin')
79 @HasPermissionAllDecorator('hg.admin')
79 @view_config(
80 @view_config(
80 # renderer defined below
81 # renderer defined below
81 route_name='users_data', request_method='GET',
82 route_name='users_data', request_method='GET',
82 renderer='json_ext', xhr=True)
83 renderer='json_ext', xhr=True)
83 def users_list_data(self):
84 def users_list_data(self):
84 self.load_default_context()
85 self.load_default_context()
85 column_map = {
86 column_map = {
86 'first_name': 'name',
87 'first_name': 'name',
87 'last_name': 'lastname',
88 'last_name': 'lastname',
88 }
89 }
89 draw, start, limit = self._extract_chunk(self.request)
90 draw, start, limit = self._extract_chunk(self.request)
90 search_q, order_by, order_dir = self._extract_ordering(
91 search_q, order_by, order_dir = self._extract_ordering(
91 self.request, column_map=column_map)
92 self.request, column_map=column_map)
92
93 _render = self.request.get_partial_renderer(
93 _render = self.request.get_partial_renderer(
94 'rhodecode:templates/data_table/_dt_elements.mako')
94 'rhodecode:templates/data_table/_dt_elements.mako')
95
95
96 def user_actions(user_id, username):
96 def user_actions(user_id, username):
97 return _render("user_actions", user_id, username)
97 return _render("user_actions", user_id, username)
98
98
99 users_data_total_count = User.query()\
99 users_data_total_count = User.query()\
100 .filter(User.username != User.DEFAULT_USER) \
100 .filter(User.username != User.DEFAULT_USER) \
101 .count()
101 .count()
102
102
103 users_data_total_inactive_count = User.query()\
104 .filter(User.username != User.DEFAULT_USER) \
105 .filter(User.active != true())\
106 .count()
107
103 # json generate
108 # json generate
104 base_q = User.query().filter(User.username != User.DEFAULT_USER)
109 base_q = User.query().filter(User.username != User.DEFAULT_USER)
110 base_inactive_q = base_q.filter(User.active != true())
105
111
106 if search_q:
112 if search_q:
107 like_expression = u'%{}%'.format(safe_unicode(search_q))
113 like_expression = u'%{}%'.format(safe_unicode(search_q))
108 base_q = base_q.filter(or_(
114 base_q = base_q.filter(or_(
109 User.username.ilike(like_expression),
115 User.username.ilike(like_expression),
110 User._email.ilike(like_expression),
116 User._email.ilike(like_expression),
111 User.name.ilike(like_expression),
117 User.name.ilike(like_expression),
112 User.lastname.ilike(like_expression),
118 User.lastname.ilike(like_expression),
113 ))
119 ))
120 base_inactive_q = base_q.filter(User.active != true())
114
121
115 users_data_total_filtered_count = base_q.count()
122 users_data_total_filtered_count = base_q.count()
123 users_data_total_filtered_inactive_count = base_inactive_q.count()
116
124
117 sort_col = getattr(User, order_by, None)
125 sort_col = getattr(User, order_by, None)
118 if sort_col:
126 if sort_col:
119 if order_dir == 'asc':
127 if order_dir == 'asc':
120 # handle null values properly to order by NULL last
128 # handle null values properly to order by NULL last
121 if order_by in ['last_activity']:
129 if order_by in ['last_activity']:
122 sort_col = coalesce(sort_col, datetime.date.max)
130 sort_col = coalesce(sort_col, datetime.date.max)
123 sort_col = sort_col.asc()
131 sort_col = sort_col.asc()
124 else:
132 else:
125 # handle null values properly to order by NULL last
133 # handle null values properly to order by NULL last
126 if order_by in ['last_activity']:
134 if order_by in ['last_activity']:
127 sort_col = coalesce(sort_col, datetime.date.min)
135 sort_col = coalesce(sort_col, datetime.date.min)
128 sort_col = sort_col.desc()
136 sort_col = sort_col.desc()
129
137
130 base_q = base_q.order_by(sort_col)
138 base_q = base_q.order_by(sort_col)
131 base_q = base_q.offset(start).limit(limit)
139 base_q = base_q.offset(start).limit(limit)
132
140
133 users_list = base_q.all()
141 users_list = base_q.all()
134
142
135 users_data = []
143 users_data = []
136 for user in users_list:
144 for user in users_list:
137 users_data.append({
145 users_data.append({
138 "username": h.gravatar_with_user(self.request, user.username),
146 "username": h.gravatar_with_user(self.request, user.username),
139 "email": user.email,
147 "email": user.email,
140 "first_name": user.first_name,
148 "first_name": user.first_name,
141 "last_name": user.last_name,
149 "last_name": user.last_name,
142 "last_login": h.format_date(user.last_login),
150 "last_login": h.format_date(user.last_login),
143 "last_activity": h.format_date(user.last_activity),
151 "last_activity": h.format_date(user.last_activity),
144 "active": h.bool2icon(user.active),
152 "active": h.bool2icon(user.active),
145 "active_raw": user.active,
153 "active_raw": user.active,
146 "admin": h.bool2icon(user.admin),
154 "admin": h.bool2icon(user.admin),
147 "extern_type": user.extern_type,
155 "extern_type": user.extern_type,
148 "extern_name": user.extern_name,
156 "extern_name": user.extern_name,
149 "action": user_actions(user.user_id, user.username),
157 "action": user_actions(user.user_id, user.username),
150 })
158 })
151
152 data = ({
159 data = ({
153 'draw': draw,
160 'draw': draw,
154 'data': users_data,
161 'data': users_data,
155 'recordsTotal': users_data_total_count,
162 'recordsTotal': users_data_total_count,
156 'recordsFiltered': users_data_total_filtered_count,
163 'recordsFiltered': users_data_total_filtered_count,
164 'recordsTotalInactive': users_data_total_inactive_count,
165 'recordsFilteredInactive': users_data_total_filtered_inactive_count
157 })
166 })
158
167
159 return data
168 return data
160
169
161 def _set_personal_repo_group_template_vars(self, c_obj):
170 def _set_personal_repo_group_template_vars(self, c_obj):
162 DummyUser = AttributeDict({
171 DummyUser = AttributeDict({
163 'username': '${username}',
172 'username': '${username}',
164 'user_id': '${user_id}',
173 'user_id': '${user_id}',
165 })
174 })
166 c_obj.default_create_repo_group = RepoGroupModel() \
175 c_obj.default_create_repo_group = RepoGroupModel() \
167 .get_default_create_personal_repo_group()
176 .get_default_create_personal_repo_group()
168 c_obj.personal_repo_group_name = RepoGroupModel() \
177 c_obj.personal_repo_group_name = RepoGroupModel() \
169 .get_personal_group_name(DummyUser)
178 .get_personal_group_name(DummyUser)
170
179
171 @LoginRequired()
180 @LoginRequired()
172 @HasPermissionAllDecorator('hg.admin')
181 @HasPermissionAllDecorator('hg.admin')
173 @view_config(
182 @view_config(
174 route_name='users_new', request_method='GET',
183 route_name='users_new', request_method='GET',
175 renderer='rhodecode:templates/admin/users/user_add.mako')
184 renderer='rhodecode:templates/admin/users/user_add.mako')
176 def users_new(self):
185 def users_new(self):
177 _ = self.request.translate
186 _ = self.request.translate
178 c = self.load_default_context()
187 c = self.load_default_context()
179 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
188 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
180 self._set_personal_repo_group_template_vars(c)
189 self._set_personal_repo_group_template_vars(c)
181 return self._get_template_context(c)
190 return self._get_template_context(c)
182
191
183 @LoginRequired()
192 @LoginRequired()
184 @HasPermissionAllDecorator('hg.admin')
193 @HasPermissionAllDecorator('hg.admin')
185 @CSRFRequired()
194 @CSRFRequired()
186 @view_config(
195 @view_config(
187 route_name='users_create', request_method='POST',
196 route_name='users_create', request_method='POST',
188 renderer='rhodecode:templates/admin/users/user_add.mako')
197 renderer='rhodecode:templates/admin/users/user_add.mako')
189 def users_create(self):
198 def users_create(self):
190 _ = self.request.translate
199 _ = self.request.translate
191 c = self.load_default_context()
200 c = self.load_default_context()
192 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
201 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
193 user_model = UserModel()
202 user_model = UserModel()
194 user_form = UserForm(self.request.translate)()
203 user_form = UserForm(self.request.translate)()
195 try:
204 try:
196 form_result = user_form.to_python(dict(self.request.POST))
205 form_result = user_form.to_python(dict(self.request.POST))
197 user = user_model.create(form_result)
206 user = user_model.create(form_result)
198 Session().flush()
207 Session().flush()
199 creation_data = user.get_api_data()
208 creation_data = user.get_api_data()
200 username = form_result['username']
209 username = form_result['username']
201
210
202 audit_logger.store_web(
211 audit_logger.store_web(
203 'user.create', action_data={'data': creation_data},
212 'user.create', action_data={'data': creation_data},
204 user=c.rhodecode_user)
213 user=c.rhodecode_user)
205
214
206 user_link = h.link_to(
215 user_link = h.link_to(
207 h.escape(username),
216 h.escape(username),
208 h.route_path('user_edit', user_id=user.user_id))
217 h.route_path('user_edit', user_id=user.user_id))
209 h.flash(h.literal(_('Created user %(user_link)s')
218 h.flash(h.literal(_('Created user %(user_link)s')
210 % {'user_link': user_link}), category='success')
219 % {'user_link': user_link}), category='success')
211 Session().commit()
220 Session().commit()
212 except formencode.Invalid as errors:
221 except formencode.Invalid as errors:
213 self._set_personal_repo_group_template_vars(c)
222 self._set_personal_repo_group_template_vars(c)
214 data = render(
223 data = render(
215 'rhodecode:templates/admin/users/user_add.mako',
224 'rhodecode:templates/admin/users/user_add.mako',
216 self._get_template_context(c), self.request)
225 self._get_template_context(c), self.request)
217 html = formencode.htmlfill.render(
226 html = formencode.htmlfill.render(
218 data,
227 data,
219 defaults=errors.value,
228 defaults=errors.value,
220 errors=errors.error_dict or {},
229 errors=errors.error_dict or {},
221 prefix_error=False,
230 prefix_error=False,
222 encoding="UTF-8",
231 encoding="UTF-8",
223 force_defaults=False
232 force_defaults=False
224 )
233 )
225 return Response(html)
234 return Response(html)
226 except UserCreationError as e:
235 except UserCreationError as e:
227 h.flash(e, 'error')
236 h.flash(e, 'error')
228 except Exception:
237 except Exception:
229 log.exception("Exception creation of user")
238 log.exception("Exception creation of user")
230 h.flash(_('Error occurred during creation of user %s')
239 h.flash(_('Error occurred during creation of user %s')
231 % self.request.POST.get('username'), category='error')
240 % self.request.POST.get('username'), category='error')
232 raise HTTPFound(h.route_path('users'))
241 raise HTTPFound(h.route_path('users'))
233
242
234
243
235 class UsersView(UserAppView):
244 class UsersView(UserAppView):
236 ALLOW_SCOPED_TOKENS = False
245 ALLOW_SCOPED_TOKENS = False
237 """
246 """
238 This view has alternative version inside EE, if modified please take a look
247 This view has alternative version inside EE, if modified please take a look
239 in there as well.
248 in there as well.
240 """
249 """
241
250
242 def load_default_context(self):
251 def load_default_context(self):
243 c = self._get_local_tmpl_context()
252 c = self._get_local_tmpl_context()
244 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
253 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
245 c.allowed_languages = [
254 c.allowed_languages = [
246 ('en', 'English (en)'),
255 ('en', 'English (en)'),
247 ('de', 'German (de)'),
256 ('de', 'German (de)'),
248 ('fr', 'French (fr)'),
257 ('fr', 'French (fr)'),
249 ('it', 'Italian (it)'),
258 ('it', 'Italian (it)'),
250 ('ja', 'Japanese (ja)'),
259 ('ja', 'Japanese (ja)'),
251 ('pl', 'Polish (pl)'),
260 ('pl', 'Polish (pl)'),
252 ('pt', 'Portuguese (pt)'),
261 ('pt', 'Portuguese (pt)'),
253 ('ru', 'Russian (ru)'),
262 ('ru', 'Russian (ru)'),
254 ('zh', 'Chinese (zh)'),
263 ('zh', 'Chinese (zh)'),
255 ]
264 ]
256 req = self.request
265 req = self.request
257
266
258 c.available_permissions = req.registry.settings['available_permissions']
267 c.available_permissions = req.registry.settings['available_permissions']
259 PermissionModel().set_global_permission_choices(
268 PermissionModel().set_global_permission_choices(
260 c, gettext_translator=req.translate)
269 c, gettext_translator=req.translate)
261
270
262 return c
271 return c
263
272
264 @LoginRequired()
273 @LoginRequired()
265 @HasPermissionAllDecorator('hg.admin')
274 @HasPermissionAllDecorator('hg.admin')
266 @CSRFRequired()
275 @CSRFRequired()
267 @view_config(
276 @view_config(
268 route_name='user_update', request_method='POST',
277 route_name='user_update', request_method='POST',
269 renderer='rhodecode:templates/admin/users/user_edit.mako')
278 renderer='rhodecode:templates/admin/users/user_edit.mako')
270 def user_update(self):
279 def user_update(self):
271 _ = self.request.translate
280 _ = self.request.translate
272 c = self.load_default_context()
281 c = self.load_default_context()
273
282
274 user_id = self.db_user_id
283 user_id = self.db_user_id
275 c.user = self.db_user
284 c.user = self.db_user
276
285
277 c.active = 'profile'
286 c.active = 'profile'
278 c.extern_type = c.user.extern_type
287 c.extern_type = c.user.extern_type
279 c.extern_name = c.user.extern_name
288 c.extern_name = c.user.extern_name
280 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
289 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
281 available_languages = [x[0] for x in c.allowed_languages]
290 available_languages = [x[0] for x in c.allowed_languages]
282 _form = UserForm(self.request.translate, edit=True,
291 _form = UserForm(self.request.translate, edit=True,
283 available_languages=available_languages,
292 available_languages=available_languages,
284 old_data={'user_id': user_id,
293 old_data={'user_id': user_id,
285 'email': c.user.email})()
294 'email': c.user.email})()
286 form_result = {}
295 form_result = {}
287 old_values = c.user.get_api_data()
296 old_values = c.user.get_api_data()
288 try:
297 try:
289 form_result = _form.to_python(dict(self.request.POST))
298 form_result = _form.to_python(dict(self.request.POST))
290 skip_attrs = ['extern_type', 'extern_name']
299 skip_attrs = ['extern_type', 'extern_name']
291 # TODO: plugin should define if username can be updated
300 # TODO: plugin should define if username can be updated
292 if c.extern_type != "rhodecode":
301 if c.extern_type != "rhodecode":
293 # forbid updating username for external accounts
302 # forbid updating username for external accounts
294 skip_attrs.append('username')
303 skip_attrs.append('username')
295
304
296 UserModel().update_user(
305 UserModel().update_user(
297 user_id, skip_attrs=skip_attrs, **form_result)
306 user_id, skip_attrs=skip_attrs, **form_result)
298
307
299 audit_logger.store_web(
308 audit_logger.store_web(
300 'user.edit', action_data={'old_data': old_values},
309 'user.edit', action_data={'old_data': old_values},
301 user=c.rhodecode_user)
310 user=c.rhodecode_user)
302
311
303 Session().commit()
312 Session().commit()
304 h.flash(_('User updated successfully'), category='success')
313 h.flash(_('User updated successfully'), category='success')
305 except formencode.Invalid as errors:
314 except formencode.Invalid as errors:
306 data = render(
315 data = render(
307 'rhodecode:templates/admin/users/user_edit.mako',
316 'rhodecode:templates/admin/users/user_edit.mako',
308 self._get_template_context(c), self.request)
317 self._get_template_context(c), self.request)
309 html = formencode.htmlfill.render(
318 html = formencode.htmlfill.render(
310 data,
319 data,
311 defaults=errors.value,
320 defaults=errors.value,
312 errors=errors.error_dict or {},
321 errors=errors.error_dict or {},
313 prefix_error=False,
322 prefix_error=False,
314 encoding="UTF-8",
323 encoding="UTF-8",
315 force_defaults=False
324 force_defaults=False
316 )
325 )
317 return Response(html)
326 return Response(html)
318 except UserCreationError as e:
327 except UserCreationError as e:
319 h.flash(e, 'error')
328 h.flash(e, 'error')
320 except Exception:
329 except Exception:
321 log.exception("Exception updating user")
330 log.exception("Exception updating user")
322 h.flash(_('Error occurred during update of user %s')
331 h.flash(_('Error occurred during update of user %s')
323 % form_result.get('username'), category='error')
332 % form_result.get('username'), category='error')
324 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
333 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
325
334
326 @LoginRequired()
335 @LoginRequired()
327 @HasPermissionAllDecorator('hg.admin')
336 @HasPermissionAllDecorator('hg.admin')
328 @CSRFRequired()
337 @CSRFRequired()
329 @view_config(
338 @view_config(
330 route_name='user_delete', request_method='POST',
339 route_name='user_delete', request_method='POST',
331 renderer='rhodecode:templates/admin/users/user_edit.mako')
340 renderer='rhodecode:templates/admin/users/user_edit.mako')
332 def user_delete(self):
341 def user_delete(self):
333 _ = self.request.translate
342 _ = self.request.translate
334 c = self.load_default_context()
343 c = self.load_default_context()
335 c.user = self.db_user
344 c.user = self.db_user
336
345
337 _repos = c.user.repositories
346 _repos = c.user.repositories
338 _repo_groups = c.user.repository_groups
347 _repo_groups = c.user.repository_groups
339 _user_groups = c.user.user_groups
348 _user_groups = c.user.user_groups
340
349
341 handle_repos = None
350 handle_repos = None
342 handle_repo_groups = None
351 handle_repo_groups = None
343 handle_user_groups = None
352 handle_user_groups = None
344 # dummy call for flash of handle
353 # dummy call for flash of handle
345 set_handle_flash_repos = lambda: None
354 set_handle_flash_repos = lambda: None
346 set_handle_flash_repo_groups = lambda: None
355 set_handle_flash_repo_groups = lambda: None
347 set_handle_flash_user_groups = lambda: None
356 set_handle_flash_user_groups = lambda: None
348
357
349 if _repos and self.request.POST.get('user_repos'):
358 if _repos and self.request.POST.get('user_repos'):
350 do = self.request.POST['user_repos']
359 do = self.request.POST['user_repos']
351 if do == 'detach':
360 if do == 'detach':
352 handle_repos = 'detach'
361 handle_repos = 'detach'
353 set_handle_flash_repos = lambda: h.flash(
362 set_handle_flash_repos = lambda: h.flash(
354 _('Detached %s repositories') % len(_repos),
363 _('Detached %s repositories') % len(_repos),
355 category='success')
364 category='success')
356 elif do == 'delete':
365 elif do == 'delete':
357 handle_repos = 'delete'
366 handle_repos = 'delete'
358 set_handle_flash_repos = lambda: h.flash(
367 set_handle_flash_repos = lambda: h.flash(
359 _('Deleted %s repositories') % len(_repos),
368 _('Deleted %s repositories') % len(_repos),
360 category='success')
369 category='success')
361
370
362 if _repo_groups and self.request.POST.get('user_repo_groups'):
371 if _repo_groups and self.request.POST.get('user_repo_groups'):
363 do = self.request.POST['user_repo_groups']
372 do = self.request.POST['user_repo_groups']
364 if do == 'detach':
373 if do == 'detach':
365 handle_repo_groups = 'detach'
374 handle_repo_groups = 'detach'
366 set_handle_flash_repo_groups = lambda: h.flash(
375 set_handle_flash_repo_groups = lambda: h.flash(
367 _('Detached %s repository groups') % len(_repo_groups),
376 _('Detached %s repository groups') % len(_repo_groups),
368 category='success')
377 category='success')
369 elif do == 'delete':
378 elif do == 'delete':
370 handle_repo_groups = 'delete'
379 handle_repo_groups = 'delete'
371 set_handle_flash_repo_groups = lambda: h.flash(
380 set_handle_flash_repo_groups = lambda: h.flash(
372 _('Deleted %s repository groups') % len(_repo_groups),
381 _('Deleted %s repository groups') % len(_repo_groups),
373 category='success')
382 category='success')
374
383
375 if _user_groups and self.request.POST.get('user_user_groups'):
384 if _user_groups and self.request.POST.get('user_user_groups'):
376 do = self.request.POST['user_user_groups']
385 do = self.request.POST['user_user_groups']
377 if do == 'detach':
386 if do == 'detach':
378 handle_user_groups = 'detach'
387 handle_user_groups = 'detach'
379 set_handle_flash_user_groups = lambda: h.flash(
388 set_handle_flash_user_groups = lambda: h.flash(
380 _('Detached %s user groups') % len(_user_groups),
389 _('Detached %s user groups') % len(_user_groups),
381 category='success')
390 category='success')
382 elif do == 'delete':
391 elif do == 'delete':
383 handle_user_groups = 'delete'
392 handle_user_groups = 'delete'
384 set_handle_flash_user_groups = lambda: h.flash(
393 set_handle_flash_user_groups = lambda: h.flash(
385 _('Deleted %s user groups') % len(_user_groups),
394 _('Deleted %s user groups') % len(_user_groups),
386 category='success')
395 category='success')
387
396
388 old_values = c.user.get_api_data()
397 old_values = c.user.get_api_data()
389 try:
398 try:
390 UserModel().delete(c.user, handle_repos=handle_repos,
399 UserModel().delete(c.user, handle_repos=handle_repos,
391 handle_repo_groups=handle_repo_groups,
400 handle_repo_groups=handle_repo_groups,
392 handle_user_groups=handle_user_groups)
401 handle_user_groups=handle_user_groups)
393
402
394 audit_logger.store_web(
403 audit_logger.store_web(
395 'user.delete', action_data={'old_data': old_values},
404 'user.delete', action_data={'old_data': old_values},
396 user=c.rhodecode_user)
405 user=c.rhodecode_user)
397
406
398 Session().commit()
407 Session().commit()
399 set_handle_flash_repos()
408 set_handle_flash_repos()
400 set_handle_flash_repo_groups()
409 set_handle_flash_repo_groups()
401 set_handle_flash_user_groups()
410 set_handle_flash_user_groups()
402 h.flash(_('Successfully deleted user'), category='success')
411 h.flash(_('Successfully deleted user'), category='success')
403 except (UserOwnsReposException, UserOwnsRepoGroupsException,
412 except (UserOwnsReposException, UserOwnsRepoGroupsException,
404 UserOwnsUserGroupsException, DefaultUserException) as e:
413 UserOwnsUserGroupsException, DefaultUserException) as e:
405 h.flash(e, category='warning')
414 h.flash(e, category='warning')
406 except Exception:
415 except Exception:
407 log.exception("Exception during deletion of user")
416 log.exception("Exception during deletion of user")
408 h.flash(_('An error occurred during deletion of user'),
417 h.flash(_('An error occurred during deletion of user'),
409 category='error')
418 category='error')
410 raise HTTPFound(h.route_path('users'))
419 raise HTTPFound(h.route_path('users'))
411
420
412 @LoginRequired()
421 @LoginRequired()
413 @HasPermissionAllDecorator('hg.admin')
422 @HasPermissionAllDecorator('hg.admin')
414 @view_config(
423 @view_config(
415 route_name='user_edit', request_method='GET',
424 route_name='user_edit', request_method='GET',
416 renderer='rhodecode:templates/admin/users/user_edit.mako')
425 renderer='rhodecode:templates/admin/users/user_edit.mako')
417 def user_edit(self):
426 def user_edit(self):
418 _ = self.request.translate
427 _ = self.request.translate
419 c = self.load_default_context()
428 c = self.load_default_context()
420 c.user = self.db_user
429 c.user = self.db_user
421
430
422 c.active = 'profile'
431 c.active = 'profile'
423 c.extern_type = c.user.extern_type
432 c.extern_type = c.user.extern_type
424 c.extern_name = c.user.extern_name
433 c.extern_name = c.user.extern_name
425 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
434 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
426
435
427 defaults = c.user.get_dict()
436 defaults = c.user.get_dict()
428 defaults.update({'language': c.user.user_data.get('language')})
437 defaults.update({'language': c.user.user_data.get('language')})
429
438
430 data = render(
439 data = render(
431 'rhodecode:templates/admin/users/user_edit.mako',
440 'rhodecode:templates/admin/users/user_edit.mako',
432 self._get_template_context(c), self.request)
441 self._get_template_context(c), self.request)
433 html = formencode.htmlfill.render(
442 html = formencode.htmlfill.render(
434 data,
443 data,
435 defaults=defaults,
444 defaults=defaults,
436 encoding="UTF-8",
445 encoding="UTF-8",
437 force_defaults=False
446 force_defaults=False
438 )
447 )
439 return Response(html)
448 return Response(html)
440
449
441 @LoginRequired()
450 @LoginRequired()
442 @HasPermissionAllDecorator('hg.admin')
451 @HasPermissionAllDecorator('hg.admin')
443 @view_config(
452 @view_config(
444 route_name='user_edit_advanced', request_method='GET',
453 route_name='user_edit_advanced', request_method='GET',
445 renderer='rhodecode:templates/admin/users/user_edit.mako')
454 renderer='rhodecode:templates/admin/users/user_edit.mako')
446 def user_edit_advanced(self):
455 def user_edit_advanced(self):
447 _ = self.request.translate
456 _ = self.request.translate
448 c = self.load_default_context()
457 c = self.load_default_context()
449
458
450 user_id = self.db_user_id
459 user_id = self.db_user_id
451 c.user = self.db_user
460 c.user = self.db_user
452
461
453 c.active = 'advanced'
462 c.active = 'advanced'
454 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
463 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
455 c.personal_repo_group_name = RepoGroupModel()\
464 c.personal_repo_group_name = RepoGroupModel()\
456 .get_personal_group_name(c.user)
465 .get_personal_group_name(c.user)
457
466
458 c.user_to_review_rules = sorted(
467 c.user_to_review_rules = sorted(
459 (x.user for x in c.user.user_review_rules),
468 (x.user for x in c.user.user_review_rules),
460 key=lambda u: u.username.lower())
469 key=lambda u: u.username.lower())
461
470
462 c.first_admin = User.get_first_super_admin()
471 c.first_admin = User.get_first_super_admin()
463 defaults = c.user.get_dict()
472 defaults = c.user.get_dict()
464
473
465 # Interim workaround if the user participated on any pull requests as a
474 # Interim workaround if the user participated on any pull requests as a
466 # reviewer.
475 # reviewer.
467 has_review = len(c.user.reviewer_pull_requests)
476 has_review = len(c.user.reviewer_pull_requests)
468 c.can_delete_user = not has_review
477 c.can_delete_user = not has_review
469 c.can_delete_user_message = ''
478 c.can_delete_user_message = ''
470 inactive_link = h.link_to(
479 inactive_link = h.link_to(
471 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
480 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
472 if has_review == 1:
481 if has_review == 1:
473 c.can_delete_user_message = h.literal(_(
482 c.can_delete_user_message = h.literal(_(
474 'The user participates as reviewer in {} pull request and '
483 'The user participates as reviewer in {} pull request and '
475 'cannot be deleted. \nYou can set the user to '
484 'cannot be deleted. \nYou can set the user to '
476 '"{}" instead of deleting it.').format(
485 '"{}" instead of deleting it.').format(
477 has_review, inactive_link))
486 has_review, inactive_link))
478 elif has_review:
487 elif has_review:
479 c.can_delete_user_message = h.literal(_(
488 c.can_delete_user_message = h.literal(_(
480 'The user participates as reviewer in {} pull requests and '
489 'The user participates as reviewer in {} pull requests and '
481 'cannot be deleted. \nYou can set the user to '
490 'cannot be deleted. \nYou can set the user to '
482 '"{}" instead of deleting it.').format(
491 '"{}" instead of deleting it.').format(
483 has_review, inactive_link))
492 has_review, inactive_link))
484
493
485 data = render(
494 data = render(
486 'rhodecode:templates/admin/users/user_edit.mako',
495 'rhodecode:templates/admin/users/user_edit.mako',
487 self._get_template_context(c), self.request)
496 self._get_template_context(c), self.request)
488 html = formencode.htmlfill.render(
497 html = formencode.htmlfill.render(
489 data,
498 data,
490 defaults=defaults,
499 defaults=defaults,
491 encoding="UTF-8",
500 encoding="UTF-8",
492 force_defaults=False
501 force_defaults=False
493 )
502 )
494 return Response(html)
503 return Response(html)
495
504
496 @LoginRequired()
505 @LoginRequired()
497 @HasPermissionAllDecorator('hg.admin')
506 @HasPermissionAllDecorator('hg.admin')
498 @view_config(
507 @view_config(
499 route_name='user_edit_global_perms', request_method='GET',
508 route_name='user_edit_global_perms', request_method='GET',
500 renderer='rhodecode:templates/admin/users/user_edit.mako')
509 renderer='rhodecode:templates/admin/users/user_edit.mako')
501 def user_edit_global_perms(self):
510 def user_edit_global_perms(self):
502 _ = self.request.translate
511 _ = self.request.translate
503 c = self.load_default_context()
512 c = self.load_default_context()
504 c.user = self.db_user
513 c.user = self.db_user
505
514
506 c.active = 'global_perms'
515 c.active = 'global_perms'
507
516
508 c.default_user = User.get_default_user()
517 c.default_user = User.get_default_user()
509 defaults = c.user.get_dict()
518 defaults = c.user.get_dict()
510 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
519 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
511 defaults.update(c.default_user.get_default_perms())
520 defaults.update(c.default_user.get_default_perms())
512 defaults.update(c.user.get_default_perms())
521 defaults.update(c.user.get_default_perms())
513
522
514 data = render(
523 data = render(
515 'rhodecode:templates/admin/users/user_edit.mako',
524 'rhodecode:templates/admin/users/user_edit.mako',
516 self._get_template_context(c), self.request)
525 self._get_template_context(c), self.request)
517 html = formencode.htmlfill.render(
526 html = formencode.htmlfill.render(
518 data,
527 data,
519 defaults=defaults,
528 defaults=defaults,
520 encoding="UTF-8",
529 encoding="UTF-8",
521 force_defaults=False
530 force_defaults=False
522 )
531 )
523 return Response(html)
532 return Response(html)
524
533
525 @LoginRequired()
534 @LoginRequired()
526 @HasPermissionAllDecorator('hg.admin')
535 @HasPermissionAllDecorator('hg.admin')
527 @CSRFRequired()
536 @CSRFRequired()
528 @view_config(
537 @view_config(
529 route_name='user_edit_global_perms_update', request_method='POST',
538 route_name='user_edit_global_perms_update', request_method='POST',
530 renderer='rhodecode:templates/admin/users/user_edit.mako')
539 renderer='rhodecode:templates/admin/users/user_edit.mako')
531 def user_edit_global_perms_update(self):
540 def user_edit_global_perms_update(self):
532 _ = self.request.translate
541 _ = self.request.translate
533 c = self.load_default_context()
542 c = self.load_default_context()
534
543
535 user_id = self.db_user_id
544 user_id = self.db_user_id
536 c.user = self.db_user
545 c.user = self.db_user
537
546
538 c.active = 'global_perms'
547 c.active = 'global_perms'
539 try:
548 try:
540 # first stage that verifies the checkbox
549 # first stage that verifies the checkbox
541 _form = UserIndividualPermissionsForm(self.request.translate)
550 _form = UserIndividualPermissionsForm(self.request.translate)
542 form_result = _form.to_python(dict(self.request.POST))
551 form_result = _form.to_python(dict(self.request.POST))
543 inherit_perms = form_result['inherit_default_permissions']
552 inherit_perms = form_result['inherit_default_permissions']
544 c.user.inherit_default_permissions = inherit_perms
553 c.user.inherit_default_permissions = inherit_perms
545 Session().add(c.user)
554 Session().add(c.user)
546
555
547 if not inherit_perms:
556 if not inherit_perms:
548 # only update the individual ones if we un check the flag
557 # only update the individual ones if we un check the flag
549 _form = UserPermissionsForm(
558 _form = UserPermissionsForm(
550 self.request.translate,
559 self.request.translate,
551 [x[0] for x in c.repo_create_choices],
560 [x[0] for x in c.repo_create_choices],
552 [x[0] for x in c.repo_create_on_write_choices],
561 [x[0] for x in c.repo_create_on_write_choices],
553 [x[0] for x in c.repo_group_create_choices],
562 [x[0] for x in c.repo_group_create_choices],
554 [x[0] for x in c.user_group_create_choices],
563 [x[0] for x in c.user_group_create_choices],
555 [x[0] for x in c.fork_choices],
564 [x[0] for x in c.fork_choices],
556 [x[0] for x in c.inherit_default_permission_choices])()
565 [x[0] for x in c.inherit_default_permission_choices])()
557
566
558 form_result = _form.to_python(dict(self.request.POST))
567 form_result = _form.to_python(dict(self.request.POST))
559 form_result.update({'perm_user_id': c.user.user_id})
568 form_result.update({'perm_user_id': c.user.user_id})
560
569
561 PermissionModel().update_user_permissions(form_result)
570 PermissionModel().update_user_permissions(form_result)
562
571
563 # TODO(marcink): implement global permissions
572 # TODO(marcink): implement global permissions
564 # audit_log.store_web('user.edit.permissions')
573 # audit_log.store_web('user.edit.permissions')
565
574
566 Session().commit()
575 Session().commit()
567 h.flash(_('User global permissions updated successfully'),
576 h.flash(_('User global permissions updated successfully'),
568 category='success')
577 category='success')
569
578
570 except formencode.Invalid as errors:
579 except formencode.Invalid as errors:
571 data = render(
580 data = render(
572 'rhodecode:templates/admin/users/user_edit.mako',
581 'rhodecode:templates/admin/users/user_edit.mako',
573 self._get_template_context(c), self.request)
582 self._get_template_context(c), self.request)
574 html = formencode.htmlfill.render(
583 html = formencode.htmlfill.render(
575 data,
584 data,
576 defaults=errors.value,
585 defaults=errors.value,
577 errors=errors.error_dict or {},
586 errors=errors.error_dict or {},
578 prefix_error=False,
587 prefix_error=False,
579 encoding="UTF-8",
588 encoding="UTF-8",
580 force_defaults=False
589 force_defaults=False
581 )
590 )
582 return Response(html)
591 return Response(html)
583 except Exception:
592 except Exception:
584 log.exception("Exception during permissions saving")
593 log.exception("Exception during permissions saving")
585 h.flash(_('An error occurred during permissions saving'),
594 h.flash(_('An error occurred during permissions saving'),
586 category='error')
595 category='error')
587 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
596 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
588
597
589 @LoginRequired()
598 @LoginRequired()
590 @HasPermissionAllDecorator('hg.admin')
599 @HasPermissionAllDecorator('hg.admin')
591 @CSRFRequired()
600 @CSRFRequired()
592 @view_config(
601 @view_config(
593 route_name='user_force_password_reset', request_method='POST',
602 route_name='user_force_password_reset', request_method='POST',
594 renderer='rhodecode:templates/admin/users/user_edit.mako')
603 renderer='rhodecode:templates/admin/users/user_edit.mako')
595 def user_force_password_reset(self):
604 def user_force_password_reset(self):
596 """
605 """
597 toggle reset password flag for this user
606 toggle reset password flag for this user
598 """
607 """
599 _ = self.request.translate
608 _ = self.request.translate
600 c = self.load_default_context()
609 c = self.load_default_context()
601
610
602 user_id = self.db_user_id
611 user_id = self.db_user_id
603 c.user = self.db_user
612 c.user = self.db_user
604
613
605 try:
614 try:
606 old_value = c.user.user_data.get('force_password_change')
615 old_value = c.user.user_data.get('force_password_change')
607 c.user.update_userdata(force_password_change=not old_value)
616 c.user.update_userdata(force_password_change=not old_value)
608
617
609 if old_value:
618 if old_value:
610 msg = _('Force password change disabled for user')
619 msg = _('Force password change disabled for user')
611 audit_logger.store_web(
620 audit_logger.store_web(
612 'user.edit.password_reset.disabled',
621 'user.edit.password_reset.disabled',
613 user=c.rhodecode_user)
622 user=c.rhodecode_user)
614 else:
623 else:
615 msg = _('Force password change enabled for user')
624 msg = _('Force password change enabled for user')
616 audit_logger.store_web(
625 audit_logger.store_web(
617 'user.edit.password_reset.enabled',
626 'user.edit.password_reset.enabled',
618 user=c.rhodecode_user)
627 user=c.rhodecode_user)
619
628
620 Session().commit()
629 Session().commit()
621 h.flash(msg, category='success')
630 h.flash(msg, category='success')
622 except Exception:
631 except Exception:
623 log.exception("Exception during password reset for user")
632 log.exception("Exception during password reset for user")
624 h.flash(_('An error occurred during password reset for user'),
633 h.flash(_('An error occurred during password reset for user'),
625 category='error')
634 category='error')
626
635
627 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
636 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
628
637
629 @LoginRequired()
638 @LoginRequired()
630 @HasPermissionAllDecorator('hg.admin')
639 @HasPermissionAllDecorator('hg.admin')
631 @CSRFRequired()
640 @CSRFRequired()
632 @view_config(
641 @view_config(
633 route_name='user_create_personal_repo_group', request_method='POST',
642 route_name='user_create_personal_repo_group', request_method='POST',
634 renderer='rhodecode:templates/admin/users/user_edit.mako')
643 renderer='rhodecode:templates/admin/users/user_edit.mako')
635 def user_create_personal_repo_group(self):
644 def user_create_personal_repo_group(self):
636 """
645 """
637 Create personal repository group for this user
646 Create personal repository group for this user
638 """
647 """
639 from rhodecode.model.repo_group import RepoGroupModel
648 from rhodecode.model.repo_group import RepoGroupModel
640
649
641 _ = self.request.translate
650 _ = self.request.translate
642 c = self.load_default_context()
651 c = self.load_default_context()
643
652
644 user_id = self.db_user_id
653 user_id = self.db_user_id
645 c.user = self.db_user
654 c.user = self.db_user
646
655
647 personal_repo_group = RepoGroup.get_user_personal_repo_group(
656 personal_repo_group = RepoGroup.get_user_personal_repo_group(
648 c.user.user_id)
657 c.user.user_id)
649 if personal_repo_group:
658 if personal_repo_group:
650 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
659 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
651
660
652 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
661 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
653 c.user)
662 c.user)
654 named_personal_group = RepoGroup.get_by_group_name(
663 named_personal_group = RepoGroup.get_by_group_name(
655 personal_repo_group_name)
664 personal_repo_group_name)
656 try:
665 try:
657
666
658 if named_personal_group and named_personal_group.user_id == c.user.user_id:
667 if named_personal_group and named_personal_group.user_id == c.user.user_id:
659 # migrate the same named group, and mark it as personal
668 # migrate the same named group, and mark it as personal
660 named_personal_group.personal = True
669 named_personal_group.personal = True
661 Session().add(named_personal_group)
670 Session().add(named_personal_group)
662 Session().commit()
671 Session().commit()
663 msg = _('Linked repository group `%s` as personal' % (
672 msg = _('Linked repository group `%s` as personal' % (
664 personal_repo_group_name,))
673 personal_repo_group_name,))
665 h.flash(msg, category='success')
674 h.flash(msg, category='success')
666 elif not named_personal_group:
675 elif not named_personal_group:
667 RepoGroupModel().create_personal_repo_group(c.user)
676 RepoGroupModel().create_personal_repo_group(c.user)
668
677
669 msg = _('Created repository group `%s`' % (
678 msg = _('Created repository group `%s`' % (
670 personal_repo_group_name,))
679 personal_repo_group_name,))
671 h.flash(msg, category='success')
680 h.flash(msg, category='success')
672 else:
681 else:
673 msg = _('Repository group `%s` is already taken' % (
682 msg = _('Repository group `%s` is already taken' % (
674 personal_repo_group_name,))
683 personal_repo_group_name,))
675 h.flash(msg, category='warning')
684 h.flash(msg, category='warning')
676 except Exception:
685 except Exception:
677 log.exception("Exception during repository group creation")
686 log.exception("Exception during repository group creation")
678 msg = _(
687 msg = _(
679 'An error occurred during repository group creation for user')
688 'An error occurred during repository group creation for user')
680 h.flash(msg, category='error')
689 h.flash(msg, category='error')
681 Session().rollback()
690 Session().rollback()
682
691
683 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
692 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
684
693
685 @LoginRequired()
694 @LoginRequired()
686 @HasPermissionAllDecorator('hg.admin')
695 @HasPermissionAllDecorator('hg.admin')
687 @view_config(
696 @view_config(
688 route_name='edit_user_auth_tokens', request_method='GET',
697 route_name='edit_user_auth_tokens', request_method='GET',
689 renderer='rhodecode:templates/admin/users/user_edit.mako')
698 renderer='rhodecode:templates/admin/users/user_edit.mako')
690 def auth_tokens(self):
699 def auth_tokens(self):
691 _ = self.request.translate
700 _ = self.request.translate
692 c = self.load_default_context()
701 c = self.load_default_context()
693 c.user = self.db_user
702 c.user = self.db_user
694
703
695 c.active = 'auth_tokens'
704 c.active = 'auth_tokens'
696
705
697 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
706 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
698 c.role_values = [
707 c.role_values = [
699 (x, AuthTokenModel.cls._get_role_name(x))
708 (x, AuthTokenModel.cls._get_role_name(x))
700 for x in AuthTokenModel.cls.ROLES]
709 for x in AuthTokenModel.cls.ROLES]
701 c.role_options = [(c.role_values, _("Role"))]
710 c.role_options = [(c.role_values, _("Role"))]
702 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
711 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
703 c.user.user_id, show_expired=True)
712 c.user.user_id, show_expired=True)
704 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
713 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
705 return self._get_template_context(c)
714 return self._get_template_context(c)
706
715
707 def maybe_attach_token_scope(self, token):
716 def maybe_attach_token_scope(self, token):
708 # implemented in EE edition
717 # implemented in EE edition
709 pass
718 pass
710
719
711 @LoginRequired()
720 @LoginRequired()
712 @HasPermissionAllDecorator('hg.admin')
721 @HasPermissionAllDecorator('hg.admin')
713 @CSRFRequired()
722 @CSRFRequired()
714 @view_config(
723 @view_config(
715 route_name='edit_user_auth_tokens_add', request_method='POST')
724 route_name='edit_user_auth_tokens_add', request_method='POST')
716 def auth_tokens_add(self):
725 def auth_tokens_add(self):
717 _ = self.request.translate
726 _ = self.request.translate
718 c = self.load_default_context()
727 c = self.load_default_context()
719
728
720 user_id = self.db_user_id
729 user_id = self.db_user_id
721 c.user = self.db_user
730 c.user = self.db_user
722
731
723 user_data = c.user.get_api_data()
732 user_data = c.user.get_api_data()
724 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
733 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
725 description = self.request.POST.get('description')
734 description = self.request.POST.get('description')
726 role = self.request.POST.get('role')
735 role = self.request.POST.get('role')
727
736
728 token = AuthTokenModel().create(
737 token = AuthTokenModel().create(
729 c.user.user_id, description, lifetime, role)
738 c.user.user_id, description, lifetime, role)
730 token_data = token.get_api_data()
739 token_data = token.get_api_data()
731
740
732 self.maybe_attach_token_scope(token)
741 self.maybe_attach_token_scope(token)
733 audit_logger.store_web(
742 audit_logger.store_web(
734 'user.edit.token.add', action_data={
743 'user.edit.token.add', action_data={
735 'data': {'token': token_data, 'user': user_data}},
744 'data': {'token': token_data, 'user': user_data}},
736 user=self._rhodecode_user, )
745 user=self._rhodecode_user, )
737 Session().commit()
746 Session().commit()
738
747
739 h.flash(_("Auth token successfully created"), category='success')
748 h.flash(_("Auth token successfully created"), category='success')
740 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
749 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
741
750
742 @LoginRequired()
751 @LoginRequired()
743 @HasPermissionAllDecorator('hg.admin')
752 @HasPermissionAllDecorator('hg.admin')
744 @CSRFRequired()
753 @CSRFRequired()
745 @view_config(
754 @view_config(
746 route_name='edit_user_auth_tokens_delete', request_method='POST')
755 route_name='edit_user_auth_tokens_delete', request_method='POST')
747 def auth_tokens_delete(self):
756 def auth_tokens_delete(self):
748 _ = self.request.translate
757 _ = self.request.translate
749 c = self.load_default_context()
758 c = self.load_default_context()
750
759
751 user_id = self.db_user_id
760 user_id = self.db_user_id
752 c.user = self.db_user
761 c.user = self.db_user
753
762
754 user_data = c.user.get_api_data()
763 user_data = c.user.get_api_data()
755
764
756 del_auth_token = self.request.POST.get('del_auth_token')
765 del_auth_token = self.request.POST.get('del_auth_token')
757
766
758 if del_auth_token:
767 if del_auth_token:
759 token = UserApiKeys.get_or_404(del_auth_token)
768 token = UserApiKeys.get_or_404(del_auth_token)
760 token_data = token.get_api_data()
769 token_data = token.get_api_data()
761
770
762 AuthTokenModel().delete(del_auth_token, c.user.user_id)
771 AuthTokenModel().delete(del_auth_token, c.user.user_id)
763 audit_logger.store_web(
772 audit_logger.store_web(
764 'user.edit.token.delete', action_data={
773 'user.edit.token.delete', action_data={
765 'data': {'token': token_data, 'user': user_data}},
774 'data': {'token': token_data, 'user': user_data}},
766 user=self._rhodecode_user,)
775 user=self._rhodecode_user,)
767 Session().commit()
776 Session().commit()
768 h.flash(_("Auth token successfully deleted"), category='success')
777 h.flash(_("Auth token successfully deleted"), category='success')
769
778
770 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
779 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
771
780
772 @LoginRequired()
781 @LoginRequired()
773 @HasPermissionAllDecorator('hg.admin')
782 @HasPermissionAllDecorator('hg.admin')
774 @view_config(
783 @view_config(
775 route_name='edit_user_ssh_keys', request_method='GET',
784 route_name='edit_user_ssh_keys', request_method='GET',
776 renderer='rhodecode:templates/admin/users/user_edit.mako')
785 renderer='rhodecode:templates/admin/users/user_edit.mako')
777 def ssh_keys(self):
786 def ssh_keys(self):
778 _ = self.request.translate
787 _ = self.request.translate
779 c = self.load_default_context()
788 c = self.load_default_context()
780 c.user = self.db_user
789 c.user = self.db_user
781
790
782 c.active = 'ssh_keys'
791 c.active = 'ssh_keys'
783 c.default_key = self.request.GET.get('default_key')
792 c.default_key = self.request.GET.get('default_key')
784 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
793 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
785 return self._get_template_context(c)
794 return self._get_template_context(c)
786
795
787 @LoginRequired()
796 @LoginRequired()
788 @HasPermissionAllDecorator('hg.admin')
797 @HasPermissionAllDecorator('hg.admin')
789 @view_config(
798 @view_config(
790 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
799 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
791 renderer='rhodecode:templates/admin/users/user_edit.mako')
800 renderer='rhodecode:templates/admin/users/user_edit.mako')
792 def ssh_keys_generate_keypair(self):
801 def ssh_keys_generate_keypair(self):
793 _ = self.request.translate
802 _ = self.request.translate
794 c = self.load_default_context()
803 c = self.load_default_context()
795
804
796 c.user = self.db_user
805 c.user = self.db_user
797
806
798 c.active = 'ssh_keys_generate'
807 c.active = 'ssh_keys_generate'
799 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
808 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
800 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
809 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
801
810
802 return self._get_template_context(c)
811 return self._get_template_context(c)
803
812
804 @LoginRequired()
813 @LoginRequired()
805 @HasPermissionAllDecorator('hg.admin')
814 @HasPermissionAllDecorator('hg.admin')
806 @CSRFRequired()
815 @CSRFRequired()
807 @view_config(
816 @view_config(
808 route_name='edit_user_ssh_keys_add', request_method='POST')
817 route_name='edit_user_ssh_keys_add', request_method='POST')
809 def ssh_keys_add(self):
818 def ssh_keys_add(self):
810 _ = self.request.translate
819 _ = self.request.translate
811 c = self.load_default_context()
820 c = self.load_default_context()
812
821
813 user_id = self.db_user_id
822 user_id = self.db_user_id
814 c.user = self.db_user
823 c.user = self.db_user
815
824
816 user_data = c.user.get_api_data()
825 user_data = c.user.get_api_data()
817 key_data = self.request.POST.get('key_data')
826 key_data = self.request.POST.get('key_data')
818 description = self.request.POST.get('description')
827 description = self.request.POST.get('description')
819
828
820 fingerprint = 'unknown'
829 fingerprint = 'unknown'
821 try:
830 try:
822 if not key_data:
831 if not key_data:
823 raise ValueError('Please add a valid public key')
832 raise ValueError('Please add a valid public key')
824
833
825 key = SshKeyModel().parse_key(key_data.strip())
834 key = SshKeyModel().parse_key(key_data.strip())
826 fingerprint = key.hash_md5()
835 fingerprint = key.hash_md5()
827
836
828 ssh_key = SshKeyModel().create(
837 ssh_key = SshKeyModel().create(
829 c.user.user_id, fingerprint, key_data, description)
838 c.user.user_id, fingerprint, key_data, description)
830 ssh_key_data = ssh_key.get_api_data()
839 ssh_key_data = ssh_key.get_api_data()
831
840
832 audit_logger.store_web(
841 audit_logger.store_web(
833 'user.edit.ssh_key.add', action_data={
842 'user.edit.ssh_key.add', action_data={
834 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
843 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
835 user=self._rhodecode_user, )
844 user=self._rhodecode_user, )
836 Session().commit()
845 Session().commit()
837
846
838 # Trigger an event on change of keys.
847 # Trigger an event on change of keys.
839 trigger(SshKeyFileChangeEvent(), self.request.registry)
848 trigger(SshKeyFileChangeEvent(), self.request.registry)
840
849
841 h.flash(_("Ssh Key successfully created"), category='success')
850 h.flash(_("Ssh Key successfully created"), category='success')
842
851
843 except IntegrityError:
852 except IntegrityError:
844 log.exception("Exception during ssh key saving")
853 log.exception("Exception during ssh key saving")
845 err = 'Such key with fingerprint `{}` already exists, ' \
854 err = 'Such key with fingerprint `{}` already exists, ' \
846 'please use a different one'.format(fingerprint)
855 'please use a different one'.format(fingerprint)
847 h.flash(_('An error occurred during ssh key saving: {}').format(err),
856 h.flash(_('An error occurred during ssh key saving: {}').format(err),
848 category='error')
857 category='error')
849 except Exception as e:
858 except Exception as e:
850 log.exception("Exception during ssh key saving")
859 log.exception("Exception during ssh key saving")
851 h.flash(_('An error occurred during ssh key saving: {}').format(e),
860 h.flash(_('An error occurred during ssh key saving: {}').format(e),
852 category='error')
861 category='error')
853
862
854 return HTTPFound(
863 return HTTPFound(
855 h.route_path('edit_user_ssh_keys', user_id=user_id))
864 h.route_path('edit_user_ssh_keys', user_id=user_id))
856
865
857 @LoginRequired()
866 @LoginRequired()
858 @HasPermissionAllDecorator('hg.admin')
867 @HasPermissionAllDecorator('hg.admin')
859 @CSRFRequired()
868 @CSRFRequired()
860 @view_config(
869 @view_config(
861 route_name='edit_user_ssh_keys_delete', request_method='POST')
870 route_name='edit_user_ssh_keys_delete', request_method='POST')
862 def ssh_keys_delete(self):
871 def ssh_keys_delete(self):
863 _ = self.request.translate
872 _ = self.request.translate
864 c = self.load_default_context()
873 c = self.load_default_context()
865
874
866 user_id = self.db_user_id
875 user_id = self.db_user_id
867 c.user = self.db_user
876 c.user = self.db_user
868
877
869 user_data = c.user.get_api_data()
878 user_data = c.user.get_api_data()
870
879
871 del_ssh_key = self.request.POST.get('del_ssh_key')
880 del_ssh_key = self.request.POST.get('del_ssh_key')
872
881
873 if del_ssh_key:
882 if del_ssh_key:
874 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
883 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
875 ssh_key_data = ssh_key.get_api_data()
884 ssh_key_data = ssh_key.get_api_data()
876
885
877 SshKeyModel().delete(del_ssh_key, c.user.user_id)
886 SshKeyModel().delete(del_ssh_key, c.user.user_id)
878 audit_logger.store_web(
887 audit_logger.store_web(
879 'user.edit.ssh_key.delete', action_data={
888 'user.edit.ssh_key.delete', action_data={
880 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
889 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
881 user=self._rhodecode_user,)
890 user=self._rhodecode_user,)
882 Session().commit()
891 Session().commit()
883 # Trigger an event on change of keys.
892 # Trigger an event on change of keys.
884 trigger(SshKeyFileChangeEvent(), self.request.registry)
893 trigger(SshKeyFileChangeEvent(), self.request.registry)
885 h.flash(_("Ssh key successfully deleted"), category='success')
894 h.flash(_("Ssh key successfully deleted"), category='success')
886
895
887 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
896 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
888
897
889 @LoginRequired()
898 @LoginRequired()
890 @HasPermissionAllDecorator('hg.admin')
899 @HasPermissionAllDecorator('hg.admin')
891 @view_config(
900 @view_config(
892 route_name='edit_user_emails', request_method='GET',
901 route_name='edit_user_emails', request_method='GET',
893 renderer='rhodecode:templates/admin/users/user_edit.mako')
902 renderer='rhodecode:templates/admin/users/user_edit.mako')
894 def emails(self):
903 def emails(self):
895 _ = self.request.translate
904 _ = self.request.translate
896 c = self.load_default_context()
905 c = self.load_default_context()
897 c.user = self.db_user
906 c.user = self.db_user
898
907
899 c.active = 'emails'
908 c.active = 'emails'
900 c.user_email_map = UserEmailMap.query() \
909 c.user_email_map = UserEmailMap.query() \
901 .filter(UserEmailMap.user == c.user).all()
910 .filter(UserEmailMap.user == c.user).all()
902
911
903 return self._get_template_context(c)
912 return self._get_template_context(c)
904
913
905 @LoginRequired()
914 @LoginRequired()
906 @HasPermissionAllDecorator('hg.admin')
915 @HasPermissionAllDecorator('hg.admin')
907 @CSRFRequired()
916 @CSRFRequired()
908 @view_config(
917 @view_config(
909 route_name='edit_user_emails_add', request_method='POST')
918 route_name='edit_user_emails_add', request_method='POST')
910 def emails_add(self):
919 def emails_add(self):
911 _ = self.request.translate
920 _ = self.request.translate
912 c = self.load_default_context()
921 c = self.load_default_context()
913
922
914 user_id = self.db_user_id
923 user_id = self.db_user_id
915 c.user = self.db_user
924 c.user = self.db_user
916
925
917 email = self.request.POST.get('new_email')
926 email = self.request.POST.get('new_email')
918 user_data = c.user.get_api_data()
927 user_data = c.user.get_api_data()
919 try:
928 try:
920
929
921 form = UserExtraEmailForm(self.request.translate)()
930 form = UserExtraEmailForm(self.request.translate)()
922 data = form.to_python({'email': email})
931 data = form.to_python({'email': email})
923 email = data['email']
932 email = data['email']
924
933
925 UserModel().add_extra_email(c.user.user_id, email)
934 UserModel().add_extra_email(c.user.user_id, email)
926 audit_logger.store_web(
935 audit_logger.store_web(
927 'user.edit.email.add',
936 'user.edit.email.add',
928 action_data={'email': email, 'user': user_data},
937 action_data={'email': email, 'user': user_data},
929 user=self._rhodecode_user)
938 user=self._rhodecode_user)
930 Session().commit()
939 Session().commit()
931 h.flash(_("Added new email address `%s` for user account") % email,
940 h.flash(_("Added new email address `%s` for user account") % email,
932 category='success')
941 category='success')
933 except formencode.Invalid as error:
942 except formencode.Invalid as error:
934 h.flash(h.escape(error.error_dict['email']), category='error')
943 h.flash(h.escape(error.error_dict['email']), category='error')
935 except IntegrityError:
944 except IntegrityError:
936 log.warning("Email %s already exists", email)
945 log.warning("Email %s already exists", email)
937 h.flash(_('Email `{}` is already registered for another user.').format(email),
946 h.flash(_('Email `{}` is already registered for another user.').format(email),
938 category='error')
947 category='error')
939 except Exception:
948 except Exception:
940 log.exception("Exception during email saving")
949 log.exception("Exception during email saving")
941 h.flash(_('An error occurred during email saving'),
950 h.flash(_('An error occurred during email saving'),
942 category='error')
951 category='error')
943 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
952 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
944
953
945 @LoginRequired()
954 @LoginRequired()
946 @HasPermissionAllDecorator('hg.admin')
955 @HasPermissionAllDecorator('hg.admin')
947 @CSRFRequired()
956 @CSRFRequired()
948 @view_config(
957 @view_config(
949 route_name='edit_user_emails_delete', request_method='POST')
958 route_name='edit_user_emails_delete', request_method='POST')
950 def emails_delete(self):
959 def emails_delete(self):
951 _ = self.request.translate
960 _ = self.request.translate
952 c = self.load_default_context()
961 c = self.load_default_context()
953
962
954 user_id = self.db_user_id
963 user_id = self.db_user_id
955 c.user = self.db_user
964 c.user = self.db_user
956
965
957 email_id = self.request.POST.get('del_email_id')
966 email_id = self.request.POST.get('del_email_id')
958 user_model = UserModel()
967 user_model = UserModel()
959
968
960 email = UserEmailMap.query().get(email_id).email
969 email = UserEmailMap.query().get(email_id).email
961 user_data = c.user.get_api_data()
970 user_data = c.user.get_api_data()
962 user_model.delete_extra_email(c.user.user_id, email_id)
971 user_model.delete_extra_email(c.user.user_id, email_id)
963 audit_logger.store_web(
972 audit_logger.store_web(
964 'user.edit.email.delete',
973 'user.edit.email.delete',
965 action_data={'email': email, 'user': user_data},
974 action_data={'email': email, 'user': user_data},
966 user=self._rhodecode_user)
975 user=self._rhodecode_user)
967 Session().commit()
976 Session().commit()
968 h.flash(_("Removed email address from user account"),
977 h.flash(_("Removed email address from user account"),
969 category='success')
978 category='success')
970 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
979 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
971
980
972 @LoginRequired()
981 @LoginRequired()
973 @HasPermissionAllDecorator('hg.admin')
982 @HasPermissionAllDecorator('hg.admin')
974 @view_config(
983 @view_config(
975 route_name='edit_user_ips', request_method='GET',
984 route_name='edit_user_ips', request_method='GET',
976 renderer='rhodecode:templates/admin/users/user_edit.mako')
985 renderer='rhodecode:templates/admin/users/user_edit.mako')
977 def ips(self):
986 def ips(self):
978 _ = self.request.translate
987 _ = self.request.translate
979 c = self.load_default_context()
988 c = self.load_default_context()
980 c.user = self.db_user
989 c.user = self.db_user
981
990
982 c.active = 'ips'
991 c.active = 'ips'
983 c.user_ip_map = UserIpMap.query() \
992 c.user_ip_map = UserIpMap.query() \
984 .filter(UserIpMap.user == c.user).all()
993 .filter(UserIpMap.user == c.user).all()
985
994
986 c.inherit_default_ips = c.user.inherit_default_permissions
995 c.inherit_default_ips = c.user.inherit_default_permissions
987 c.default_user_ip_map = UserIpMap.query() \
996 c.default_user_ip_map = UserIpMap.query() \
988 .filter(UserIpMap.user == User.get_default_user()).all()
997 .filter(UserIpMap.user == User.get_default_user()).all()
989
998
990 return self._get_template_context(c)
999 return self._get_template_context(c)
991
1000
992 @LoginRequired()
1001 @LoginRequired()
993 @HasPermissionAllDecorator('hg.admin')
1002 @HasPermissionAllDecorator('hg.admin')
994 @CSRFRequired()
1003 @CSRFRequired()
995 @view_config(
1004 @view_config(
996 route_name='edit_user_ips_add', request_method='POST')
1005 route_name='edit_user_ips_add', request_method='POST')
997 # NOTE(marcink): this view is allowed for default users, as we can
1006 # NOTE(marcink): this view is allowed for default users, as we can
998 # edit their IP white list
1007 # edit their IP white list
999 def ips_add(self):
1008 def ips_add(self):
1000 _ = self.request.translate
1009 _ = self.request.translate
1001 c = self.load_default_context()
1010 c = self.load_default_context()
1002
1011
1003 user_id = self.db_user_id
1012 user_id = self.db_user_id
1004 c.user = self.db_user
1013 c.user = self.db_user
1005
1014
1006 user_model = UserModel()
1015 user_model = UserModel()
1007 desc = self.request.POST.get('description')
1016 desc = self.request.POST.get('description')
1008 try:
1017 try:
1009 ip_list = user_model.parse_ip_range(
1018 ip_list = user_model.parse_ip_range(
1010 self.request.POST.get('new_ip'))
1019 self.request.POST.get('new_ip'))
1011 except Exception as e:
1020 except Exception as e:
1012 ip_list = []
1021 ip_list = []
1013 log.exception("Exception during ip saving")
1022 log.exception("Exception during ip saving")
1014 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1023 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1015 category='error')
1024 category='error')
1016 added = []
1025 added = []
1017 user_data = c.user.get_api_data()
1026 user_data = c.user.get_api_data()
1018 for ip in ip_list:
1027 for ip in ip_list:
1019 try:
1028 try:
1020 form = UserExtraIpForm(self.request.translate)()
1029 form = UserExtraIpForm(self.request.translate)()
1021 data = form.to_python({'ip': ip})
1030 data = form.to_python({'ip': ip})
1022 ip = data['ip']
1031 ip = data['ip']
1023
1032
1024 user_model.add_extra_ip(c.user.user_id, ip, desc)
1033 user_model.add_extra_ip(c.user.user_id, ip, desc)
1025 audit_logger.store_web(
1034 audit_logger.store_web(
1026 'user.edit.ip.add',
1035 'user.edit.ip.add',
1027 action_data={'ip': ip, 'user': user_data},
1036 action_data={'ip': ip, 'user': user_data},
1028 user=self._rhodecode_user)
1037 user=self._rhodecode_user)
1029 Session().commit()
1038 Session().commit()
1030 added.append(ip)
1039 added.append(ip)
1031 except formencode.Invalid as error:
1040 except formencode.Invalid as error:
1032 msg = error.error_dict['ip']
1041 msg = error.error_dict['ip']
1033 h.flash(msg, category='error')
1042 h.flash(msg, category='error')
1034 except Exception:
1043 except Exception:
1035 log.exception("Exception during ip saving")
1044 log.exception("Exception during ip saving")
1036 h.flash(_('An error occurred during ip saving'),
1045 h.flash(_('An error occurred during ip saving'),
1037 category='error')
1046 category='error')
1038 if added:
1047 if added:
1039 h.flash(
1048 h.flash(
1040 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1049 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1041 category='success')
1050 category='success')
1042 if 'default_user' in self.request.POST:
1051 if 'default_user' in self.request.POST:
1043 # case for editing global IP list we do it for 'DEFAULT' user
1052 # case for editing global IP list we do it for 'DEFAULT' user
1044 raise HTTPFound(h.route_path('admin_permissions_ips'))
1053 raise HTTPFound(h.route_path('admin_permissions_ips'))
1045 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1054 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1046
1055
1047 @LoginRequired()
1056 @LoginRequired()
1048 @HasPermissionAllDecorator('hg.admin')
1057 @HasPermissionAllDecorator('hg.admin')
1049 @CSRFRequired()
1058 @CSRFRequired()
1050 @view_config(
1059 @view_config(
1051 route_name='edit_user_ips_delete', request_method='POST')
1060 route_name='edit_user_ips_delete', request_method='POST')
1052 # NOTE(marcink): this view is allowed for default users, as we can
1061 # NOTE(marcink): this view is allowed for default users, as we can
1053 # edit their IP white list
1062 # edit their IP white list
1054 def ips_delete(self):
1063 def ips_delete(self):
1055 _ = self.request.translate
1064 _ = self.request.translate
1056 c = self.load_default_context()
1065 c = self.load_default_context()
1057
1066
1058 user_id = self.db_user_id
1067 user_id = self.db_user_id
1059 c.user = self.db_user
1068 c.user = self.db_user
1060
1069
1061 ip_id = self.request.POST.get('del_ip_id')
1070 ip_id = self.request.POST.get('del_ip_id')
1062 user_model = UserModel()
1071 user_model = UserModel()
1063 user_data = c.user.get_api_data()
1072 user_data = c.user.get_api_data()
1064 ip = UserIpMap.query().get(ip_id).ip_addr
1073 ip = UserIpMap.query().get(ip_id).ip_addr
1065 user_model.delete_extra_ip(c.user.user_id, ip_id)
1074 user_model.delete_extra_ip(c.user.user_id, ip_id)
1066 audit_logger.store_web(
1075 audit_logger.store_web(
1067 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1076 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1068 user=self._rhodecode_user)
1077 user=self._rhodecode_user)
1069 Session().commit()
1078 Session().commit()
1070 h.flash(_("Removed ip address from user whitelist"), category='success')
1079 h.flash(_("Removed ip address from user whitelist"), category='success')
1071
1080
1072 if 'default_user' in self.request.POST:
1081 if 'default_user' in self.request.POST:
1073 # case for editing global IP list we do it for 'DEFAULT' user
1082 # case for editing global IP list we do it for 'DEFAULT' user
1074 raise HTTPFound(h.route_path('admin_permissions_ips'))
1083 raise HTTPFound(h.route_path('admin_permissions_ips'))
1075 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1084 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1076
1085
1077 @LoginRequired()
1086 @LoginRequired()
1078 @HasPermissionAllDecorator('hg.admin')
1087 @HasPermissionAllDecorator('hg.admin')
1079 @view_config(
1088 @view_config(
1080 route_name='edit_user_groups_management', request_method='GET',
1089 route_name='edit_user_groups_management', request_method='GET',
1081 renderer='rhodecode:templates/admin/users/user_edit.mako')
1090 renderer='rhodecode:templates/admin/users/user_edit.mako')
1082 def groups_management(self):
1091 def groups_management(self):
1083 c = self.load_default_context()
1092 c = self.load_default_context()
1084 c.user = self.db_user
1093 c.user = self.db_user
1085 c.data = c.user.group_member
1094 c.data = c.user.group_member
1086
1095
1087 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1096 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1088 for group in c.user.group_member]
1097 for group in c.user.group_member]
1089 c.groups = json.dumps(groups)
1098 c.groups = json.dumps(groups)
1090 c.active = 'groups'
1099 c.active = 'groups'
1091
1100
1092 return self._get_template_context(c)
1101 return self._get_template_context(c)
1093
1102
1094 @LoginRequired()
1103 @LoginRequired()
1095 @HasPermissionAllDecorator('hg.admin')
1104 @HasPermissionAllDecorator('hg.admin')
1096 @CSRFRequired()
1105 @CSRFRequired()
1097 @view_config(
1106 @view_config(
1098 route_name='edit_user_groups_management_updates', request_method='POST')
1107 route_name='edit_user_groups_management_updates', request_method='POST')
1099 def groups_management_updates(self):
1108 def groups_management_updates(self):
1100 _ = self.request.translate
1109 _ = self.request.translate
1101 c = self.load_default_context()
1110 c = self.load_default_context()
1102
1111
1103 user_id = self.db_user_id
1112 user_id = self.db_user_id
1104 c.user = self.db_user
1113 c.user = self.db_user
1105
1114
1106 user_groups = set(self.request.POST.getall('users_group_id'))
1115 user_groups = set(self.request.POST.getall('users_group_id'))
1107 user_groups_objects = []
1116 user_groups_objects = []
1108
1117
1109 for ugid in user_groups:
1118 for ugid in user_groups:
1110 user_groups_objects.append(
1119 user_groups_objects.append(
1111 UserGroupModel().get_group(safe_int(ugid)))
1120 UserGroupModel().get_group(safe_int(ugid)))
1112 user_group_model = UserGroupModel()
1121 user_group_model = UserGroupModel()
1113 added_to_groups, removed_from_groups = \
1122 added_to_groups, removed_from_groups = \
1114 user_group_model.change_groups(c.user, user_groups_objects)
1123 user_group_model.change_groups(c.user, user_groups_objects)
1115
1124
1116 user_data = c.user.get_api_data()
1125 user_data = c.user.get_api_data()
1117 for user_group_id in added_to_groups:
1126 for user_group_id in added_to_groups:
1118 user_group = UserGroup.get(user_group_id)
1127 user_group = UserGroup.get(user_group_id)
1119 old_values = user_group.get_api_data()
1128 old_values = user_group.get_api_data()
1120 audit_logger.store_web(
1129 audit_logger.store_web(
1121 'user_group.edit.member.add',
1130 'user_group.edit.member.add',
1122 action_data={'user': user_data, 'old_data': old_values},
1131 action_data={'user': user_data, 'old_data': old_values},
1123 user=self._rhodecode_user)
1132 user=self._rhodecode_user)
1124
1133
1125 for user_group_id in removed_from_groups:
1134 for user_group_id in removed_from_groups:
1126 user_group = UserGroup.get(user_group_id)
1135 user_group = UserGroup.get(user_group_id)
1127 old_values = user_group.get_api_data()
1136 old_values = user_group.get_api_data()
1128 audit_logger.store_web(
1137 audit_logger.store_web(
1129 'user_group.edit.member.delete',
1138 'user_group.edit.member.delete',
1130 action_data={'user': user_data, 'old_data': old_values},
1139 action_data={'user': user_data, 'old_data': old_values},
1131 user=self._rhodecode_user)
1140 user=self._rhodecode_user)
1132
1141
1133 Session().commit()
1142 Session().commit()
1134 c.active = 'user_groups_management'
1143 c.active = 'user_groups_management'
1135 h.flash(_("Groups successfully changed"), category='success')
1144 h.flash(_("Groups successfully changed"), category='success')
1136
1145
1137 return HTTPFound(h.route_path(
1146 return HTTPFound(h.route_path(
1138 'edit_user_groups_management', user_id=user_id))
1147 'edit_user_groups_management', user_id=user_id))
1139
1148
1140 @LoginRequired()
1149 @LoginRequired()
1141 @HasPermissionAllDecorator('hg.admin')
1150 @HasPermissionAllDecorator('hg.admin')
1142 @view_config(
1151 @view_config(
1143 route_name='edit_user_audit_logs', request_method='GET',
1152 route_name='edit_user_audit_logs', request_method='GET',
1144 renderer='rhodecode:templates/admin/users/user_edit.mako')
1153 renderer='rhodecode:templates/admin/users/user_edit.mako')
1145 def user_audit_logs(self):
1154 def user_audit_logs(self):
1146 _ = self.request.translate
1155 _ = self.request.translate
1147 c = self.load_default_context()
1156 c = self.load_default_context()
1148 c.user = self.db_user
1157 c.user = self.db_user
1149
1158
1150 c.active = 'audit'
1159 c.active = 'audit'
1151
1160
1152 p = safe_int(self.request.GET.get('page', 1), 1)
1161 p = safe_int(self.request.GET.get('page', 1), 1)
1153
1162
1154 filter_term = self.request.GET.get('filter')
1163 filter_term = self.request.GET.get('filter')
1155 user_log = UserModel().get_user_log(c.user, filter_term)
1164 user_log = UserModel().get_user_log(c.user, filter_term)
1156
1165
1157 def url_generator(**kw):
1166 def url_generator(**kw):
1158 if filter_term:
1167 if filter_term:
1159 kw['filter'] = filter_term
1168 kw['filter'] = filter_term
1160 return self.request.current_route_path(_query=kw)
1169 return self.request.current_route_path(_query=kw)
1161
1170
1162 c.audit_logs = h.Page(
1171 c.audit_logs = h.Page(
1163 user_log, page=p, items_per_page=10, url=url_generator)
1172 user_log, page=p, items_per_page=10, url=url_generator)
1164 c.filter_term = filter_term
1173 c.filter_term = filter_term
1165 return self._get_template_context(c)
1174 return self._get_template_context(c)
1166
1175
1167 @LoginRequired()
1176 @LoginRequired()
1168 @HasPermissionAllDecorator('hg.admin')
1177 @HasPermissionAllDecorator('hg.admin')
1169 @view_config(
1178 @view_config(
1170 route_name='edit_user_perms_summary', request_method='GET',
1179 route_name='edit_user_perms_summary', request_method='GET',
1171 renderer='rhodecode:templates/admin/users/user_edit.mako')
1180 renderer='rhodecode:templates/admin/users/user_edit.mako')
1172 def user_perms_summary(self):
1181 def user_perms_summary(self):
1173 _ = self.request.translate
1182 _ = self.request.translate
1174 c = self.load_default_context()
1183 c = self.load_default_context()
1175 c.user = self.db_user
1184 c.user = self.db_user
1176
1185
1177 c.active = 'perms_summary'
1186 c.active = 'perms_summary'
1178 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1187 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1179
1188
1180 return self._get_template_context(c)
1189 return self._get_template_context(c)
1181
1190
1182 @LoginRequired()
1191 @LoginRequired()
1183 @HasPermissionAllDecorator('hg.admin')
1192 @HasPermissionAllDecorator('hg.admin')
1184 @view_config(
1193 @view_config(
1185 route_name='edit_user_perms_summary_json', request_method='GET',
1194 route_name='edit_user_perms_summary_json', request_method='GET',
1186 renderer='json_ext')
1195 renderer='json_ext')
1187 def user_perms_summary_json(self):
1196 def user_perms_summary_json(self):
1188 self.load_default_context()
1197 self.load_default_context()
1189 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1198 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1190
1199
1191 return perm_user.permissions
1200 return perm_user.permissions
@@ -1,108 +1,115 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('User groups administration')}
5 ${_('User groups administration')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
13 ${h.link_to(_('Admin'),h.route_path('admin_home'))} &raquo; <span id="user_group_count">0</span> ${_('user groups')}
13 ${h.link_to(_('Admin'),h.route_path('admin_home'))} &raquo; <span id="user_group_count">0</span>
14 </%def>
14 </%def>
15
15
16 <%def name="menu_bar_nav()">
16 <%def name="menu_bar_nav()">
17 ${self.menu_items(active='admin')}
17 ${self.menu_items(active='admin')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box">
21 <div class="box">
22
22
23 <div class="title">
23 <div class="title">
24 ${self.breadcrumbs()}
24 ${self.breadcrumbs()}
25 <ul class="links">
25 <ul class="links">
26 %if h.HasPermissionAny('hg.admin', 'hg.usergroup.create.true')():
26 %if h.HasPermissionAny('hg.admin', 'hg.usergroup.create.true')():
27 <li>
27 <li>
28 <a href="${h.route_path('user_groups_new')}" class="btn btn-small btn-success">${_(u'Add User Group')}</a>
28 <a href="${h.route_path('user_groups_new')}" class="btn btn-small btn-success">${_(u'Add User Group')}</a>
29 </li>
29 </li>
30 %endif
30 %endif
31 </ul>
31 </ul>
32 </div>
32 </div>
33
33
34 <div id="repos_list_wrap">
34 <div id="repos_list_wrap">
35 <table id="user_group_list_table" class="display"></table>
35 <table id="user_group_list_table" class="display"></table>
36 </div>
36 </div>
37
37
38 </div>
38 </div>
39 <script>
39 <script>
40 $(document).ready(function() {
40 $(document).ready(function() {
41 var getDatatableCount = function(){
41 var $userGroupsListTable = $('#user_group_list_table');
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;
46
47 var _text = _gettext("{0} out of {1} users").format(active, total);
48 $('#user_group_count').text(_text);
49 };
50
42
51 // user list
43 // user list
52 $('#user_group_list_table').DataTable({
44 $userGroupsListTable.DataTable({
53 processing: true,
45 processing: true,
54 serverSide: true,
46 serverSide: true,
55 ajax: "${h.route_path('user_groups_data')}",
47 ajax: {
48 "url": "${h.route_path('user_groups_data')}",
49 "dataSrc": function (json) {
50 var filteredCount = json.recordsFiltered;
51 var filteredInactiveCount = json.recordsFilteredInactive;
52 var totalInactive = json.recordsTotalInactive;
53 var total = json.recordsTotal;
54
55 var _text = _gettext(
56 "{0} ({1} inactive) of {2} user groups ({3} inactive)").format(
57 filteredCount, filteredInactiveCount, total, totalInactive);
58
59 if (total === filteredCount) {
60 _text = _gettext(
61 "{0} user groups ({1} inactive)").format(total, totalInactive);
62 }
63 $('#user_group_count').text(_text);
64 return json.data;
65 },
66 },
67
56 dom: 'rtp',
68 dom: 'rtp',
57 pageLength: ${c.visual.admin_grid_items},
69 pageLength: ${c.visual.admin_grid_items},
58 order: [[ 0, "asc" ]],
70 order: [[ 0, "asc" ]],
59 columns: [
71 columns: [
60 { data: {"_": "users_group_name",
72 { data: {"_": "users_group_name",
61 "sort": "users_group_name"}, title: "${_('Name')}", className: "td-componentname" },
73 "sort": "users_group_name"}, title: "${_('Name')}", className: "td-componentname" },
62 { data: {"_": "description",
74 { data: {"_": "description",
63 "sort": "description"}, title: "${_('Description')}", className: "td-description" },
75 "sort": "description"}, title: "${_('Description')}", className: "td-description" },
64 { data: {"_": "members",
76 { data: {"_": "members",
65 "sort": "members"}, title: "${_('Members')}", className: "td-number" },
77 "sort": "members"}, title: "${_('Members')}", className: "td-number" },
66 { data: {"_": "sync",
78 { data: {"_": "sync",
67 "sort": "sync"}, title: "${_('Sync')}", className: "td-sync" },
79 "sort": "sync"}, title: "${_('Sync')}", className: "td-sync" },
68 { data: {"_": "active",
80 { data: {"_": "active",
69 "sort": "active"}, title: "${_('Active')}", className: "td-active" },
81 "sort": "active"}, title: "${_('Active')}", className: "td-active" },
70 { data: {"_": "owner",
82 { data: {"_": "owner",
71 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
83 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
72 { data: {"_": "action",
84 { data: {"_": "action",
73 "sort": "action"}, title: "${_('Action')}", className: "td-action", orderable: false}
85 "sort": "action"}, title: "${_('Action')}", className: "td-action", orderable: false}
74 ],
86 ],
75 language: {
87 language: {
76 paginate: DEFAULT_GRID_PAGINATION,
88 paginate: DEFAULT_GRID_PAGINATION,
77 sProcessing: _gettext('loading...'),
89 sProcessing: _gettext('loading...'),
78 emptyTable: _gettext("No user groups available yet.")
90 emptyTable: _gettext("No user groups available yet.")
79 }
91 }
80 });
92 });
81
93
82 $('#user_group_list_table').on('xhr.dt', function(e, settings, json, xhr){
94 $userGroupsListTable.on('xhr.dt', function(e, settings, json, xhr){
83 $('#user_group_list_table').css('opacity', 1);
95 $userGroupsListTable.css('opacity', 1);
84 });
96 });
85
97
86 $('#user_group_list_table').on('preXhr.dt', function(e, settings, data){
98 $userGroupsListTable.on('preXhr.dt', function(e, settings, data){
87 $('#user_group_list_table').css('opacity', 0.3);
99 $userGroupsListTable.css('opacity', 0.3);
88 });
89
90 // refresh counters on draw
91 $('#user_group_list_table').on('draw.dt', function(){
92 getDatatableCount();
93 });
100 });
94
101
95 // filter
102 // filter
96 $('#q_filter').on('keyup',
103 $('#q_filter').on('keyup',
97 $.debounce(250, function() {
104 $.debounce(250, function() {
98 $('#user_group_list_table').DataTable().search(
105 $('#user_group_list_table').DataTable().search(
99 $('#q_filter').val()
106 $('#q_filter').val()
100 ).draw();
107 ).draw();
101 })
108 })
102 );
109 );
103
110
104 });
111 });
105
112
106 </script>
113 </script>
107
114
108 </%def>
115 </%def>
@@ -1,120 +1,123 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Users administration')}
5 ${_('Users administration')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
13 ${h.link_to(_('Admin'),h.route_path('admin_home'))} &raquo; <span id="user_count">0</span>
13 ${h.link_to(_('Admin'),h.route_path('admin_home'))} &raquo; <span id="user_count">0</span>
14 </%def>
14 </%def>
15
15
16 <%def name="menu_bar_nav()">
16 <%def name="menu_bar_nav()">
17 ${self.menu_items(active='admin')}
17 ${self.menu_items(active='admin')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21
21
22 <div class="box">
22 <div class="box">
23
23
24 <div class="title">
24 <div class="title">
25 ${self.breadcrumbs()}
25 ${self.breadcrumbs()}
26 <ul class="links">
26 <ul class="links">
27 <li>
27 <li>
28 <a href="${h.route_path('users_new')}" class="btn btn-small btn-success">${_(u'Add User')}</a>
28 <a href="${h.route_path('users_new')}" class="btn btn-small btn-success">${_(u'Add User')}</a>
29 </li>
29 </li>
30 </ul>
30 </ul>
31 </div>
31 </div>
32
32
33 <div id="repos_list_wrap">
33 <div id="repos_list_wrap">
34 <table id="user_list_table" class="display"></table>
34 <table id="user_list_table" class="display"></table>
35 </div>
35 </div>
36 </div>
36 </div>
37
37
38 <script type="text/javascript">
38 <script type="text/javascript">
39
39
40 $(document).ready(function() {
40 $(document).ready(function() {
41 var $userListTable = $('#user_list_table');
41 var $userListTable = $('#user_list_table');
42
43 var getDatatableCount = function(){
44 var table = $userListTable.dataTable();
45 var page = table.api().page.info();
46 var active = page.recordsDisplay;
47 var total = page.recordsTotal;
48
49 var _text = _gettext("{0} out of {1} users").format(active, total);
50 $('#user_count').text(_text);
51 };
52
53 // user list
42 // user list
54 $userListTable.DataTable({
43 $userListTable.DataTable({
55 processing: true,
44 processing: true,
56 serverSide: true,
45 serverSide: true,
57 ajax: "${h.route_path('users_data')}",
46 ajax: {
47 "url": "${h.route_path('users_data')}",
48 "dataSrc": function ( json ) {
49 var filteredCount = json.recordsFiltered;
50 var filteredInactiveCount = json.recordsFilteredInactive;
51 var totalInactive = json.recordsTotalInactive;
52 var total = json.recordsTotal;
53
54 var _text = _gettext(
55 "{0} ({1} inactive) of {2} users ({3} inactive)").format(
56 filteredCount, filteredInactiveCount, total, totalInactive);
57
58 if(total === filteredCount){
59 _text = _gettext(
60 "{0} users ({1} inactive)").format(total, totalInactive);
61 }
62 $('#user_count').text(_text);
63 return json.data;
64 }
65 },
58 dom: 'rtp',
66 dom: 'rtp',
59 pageLength: ${c.visual.admin_grid_items},
67 pageLength: ${c.visual.admin_grid_items},
60 order: [[ 0, "asc" ]],
68 order: [[ 0, "asc" ]],
61 columns: [
69 columns: [
62 { data: {"_": "username",
70 { data: {"_": "username",
63 "sort": "username"}, title: "${_('Username')}", className: "td-user" },
71 "sort": "username"}, title: "${_('Username')}", className: "td-user" },
64 { data: {"_": "email",
72 { data: {"_": "email",
65 "sort": "email"}, title: "${_('Email')}", className: "td-email" },
73 "sort": "email"}, title: "${_('Email')}", className: "td-email" },
66 { data: {"_": "first_name",
74 { data: {"_": "first_name",
67 "sort": "first_name"}, title: "${_('First Name')}", className: "td-user" },
75 "sort": "first_name"}, title: "${_('First Name')}", className: "td-user" },
68 { data: {"_": "last_name",
76 { data: {"_": "last_name",
69 "sort": "last_name"}, title: "${_('Last Name')}", className: "td-user" },
77 "sort": "last_name"}, title: "${_('Last Name')}", className: "td-user" },
70 { data: {"_": "last_activity",
78 { data: {"_": "last_activity",
71 "sort": "last_activity",
79 "sort": "last_activity",
72 "type": Number}, title: "${_('Last activity')}", className: "td-time" },
80 "type": Number}, title: "${_('Last activity')}", className: "td-time" },
73 { data: {"_": "active",
81 { data: {"_": "active",
74 "sort": "active"}, title: "${_('Active')}", className: "td-active" },
82 "sort": "active"}, title: "${_('Active')}", className: "td-active" },
75 { data: {"_": "admin",
83 { data: {"_": "admin",
76 "sort": "admin"}, title: "${_('Admin')}", className: "td-admin" },
84 "sort": "admin"}, title: "${_('Admin')}", className: "td-admin" },
77 { data: {"_": "extern_type",
85 { data: {"_": "extern_type",
78 "sort": "extern_type"}, title: "${_('Auth type')}", className: "td-type" },
86 "sort": "extern_type"}, title: "${_('Auth type')}", className: "td-type" },
79 { data: {"_": "action",
87 { data: {"_": "action",
80 "sort": "action"}, title: "${_('Action')}", className: "td-action", orderable: false }
88 "sort": "action"}, title: "${_('Action')}", className: "td-action", orderable: false }
81 ],
89 ],
82 language: {
90 language: {
83 paginate: DEFAULT_GRID_PAGINATION,
91 paginate: DEFAULT_GRID_PAGINATION,
84 sProcessing: _gettext('loading...'),
92 sProcessing: _gettext('loading...'),
85 emptyTable: _gettext("No users available yet.")
93 emptyTable: _gettext("No users available yet.")
86 },
94 },
87
95
88 "createdRow": function ( row, data, index ) {
96 "createdRow": function ( row, data, index ) {
89 if (!data['active_raw']){
97 if (!data['active_raw']){
90 $(row).addClass('closed')
98 $(row).addClass('closed')
91 }
99 }
92 }
100 }
93 });
101 });
94
102
95 $userListTable.on('xhr.dt', function(e, settings, json, xhr){
103 $userListTable.on('xhr.dt', function(e, settings, json, xhr){
96 $userListTable.css('opacity', 1);
104 $userListTable.css('opacity', 1);
97 });
105 });
98
106
99 $userListTable.on('preXhr.dt', function(e, settings, data){
107 $userListTable.on('preXhr.dt', function(e, settings, data){
100 $userListTable.css('opacity', 0.3);
108 $userListTable.css('opacity', 0.3);
101 });
109 });
102
110
103 // refresh counters on draw
104 $userListTable.on('draw.dt', function(){
105 getDatatableCount();
106 });
107
108 // filter
111 // filter
109 $('#q_filter').on('keyup',
112 $('#q_filter').on('keyup',
110 $.debounce(250, function() {
113 $.debounce(250, function() {
111 $userListTable.DataTable().search(
114 $userListTable.DataTable().search(
112 $('#q_filter').val()
115 $('#q_filter').val()
113 ).draw();
116 ).draw();
114 })
117 })
115 );
118 );
116
119
117 });
120 });
118 </script>
121 </script>
119
122
120 </%def>
123 </%def>
General Comments 0
You need to be logged in to leave comments. Login now