##// END OF EJS Templates
users/user_groups: ported permission summary pages into pyramid....
marcink -
r1998:2561e110 default
parent child Browse files
Show More
@@ -1,206 +1,222 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 from rhodecode.apps.admin.navigation import NavigationRegistry
23 23 from rhodecode.config.routing import ADMIN_PREFIX
24 24 from rhodecode.lib.utils2 import str2bool
25 25
26 26
27 27 def admin_routes(config):
28 28 """
29 29 Admin prefixed routes
30 30 """
31 31
32 32 config.add_route(
33 33 name='admin_audit_logs',
34 34 pattern='/audit_logs')
35 35
36 36 config.add_route(
37 37 name='pull_requests_global_0', # backward compat
38 38 pattern='/pull_requests/{pull_request_id:\d+}')
39 39 config.add_route(
40 40 name='pull_requests_global_1', # backward compat
41 41 pattern='/pull-requests/{pull_request_id:\d+}')
42 42 config.add_route(
43 43 name='pull_requests_global',
44 44 pattern='/pull-request/{pull_request_id:\d+}')
45 45
46 46 config.add_route(
47 47 name='admin_settings_open_source',
48 48 pattern='/settings/open_source')
49 49 config.add_route(
50 50 name='admin_settings_vcs_svn_generate_cfg',
51 51 pattern='/settings/vcs/svn_generate_cfg')
52 52
53 53 config.add_route(
54 54 name='admin_settings_system',
55 55 pattern='/settings/system')
56 56 config.add_route(
57 57 name='admin_settings_system_update',
58 58 pattern='/settings/system/updates')
59 59
60 60 config.add_route(
61 61 name='admin_settings_sessions',
62 62 pattern='/settings/sessions')
63 63 config.add_route(
64 64 name='admin_settings_sessions_cleanup',
65 65 pattern='/settings/sessions/cleanup')
66 66
67 67 config.add_route(
68 68 name='admin_settings_process_management',
69 69 pattern='/settings/process_management')
70 70 config.add_route(
71 71 name='admin_settings_process_management_signal',
72 72 pattern='/settings/process_management/signal')
73 73
74 74 # global permissions
75 75
76 76 config.add_route(
77 77 name='admin_permissions_application',
78 78 pattern='/permissions/application')
79 79 config.add_route(
80 80 name='admin_permissions_application_update',
81 81 pattern='/permissions/application/update')
82 82
83 83 config.add_route(
84 84 name='admin_permissions_global',
85 85 pattern='/permissions/global')
86 86 config.add_route(
87 87 name='admin_permissions_global_update',
88 88 pattern='/permissions/global/update')
89 89
90 90 config.add_route(
91 91 name='admin_permissions_object',
92 92 pattern='/permissions/object')
93 93 config.add_route(
94 94 name='admin_permissions_object_update',
95 95 pattern='/permissions/object/update')
96 96
97 97 config.add_route(
98 98 name='admin_permissions_ips',
99 99 pattern='/permissions/ips')
100 100
101 101 config.add_route(
102 102 name='admin_permissions_overview',
103 103 pattern='/permissions/overview')
104 104
105 105 config.add_route(
106 106 name='admin_permissions_auth_token_access',
107 107 pattern='/permissions/auth_token_access')
108 108
109 109 # users admin
110 110 config.add_route(
111 111 name='users',
112 112 pattern='/users')
113 113
114 114 config.add_route(
115 115 name='users_data',
116 116 pattern='/users_data')
117 117
118 118 # user auth tokens
119 119 config.add_route(
120 120 name='edit_user_auth_tokens',
121 121 pattern='/users/{user_id:\d+}/edit/auth_tokens')
122 122 config.add_route(
123 123 name='edit_user_auth_tokens_add',
124 124 pattern='/users/{user_id:\d+}/edit/auth_tokens/new')
125 125 config.add_route(
126 126 name='edit_user_auth_tokens_delete',
127 127 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete')
128 128
129 129 # user ssh keys
130 130 config.add_route(
131 131 name='edit_user_ssh_keys',
132 132 pattern='/users/{user_id:\d+}/edit/ssh_keys')
133 133 config.add_route(
134 134 name='edit_user_ssh_keys_generate_keypair',
135 135 pattern='/users/{user_id:\d+}/edit/ssh_keys/generate')
136 136 config.add_route(
137 137 name='edit_user_ssh_keys_add',
138 138 pattern='/users/{user_id:\d+}/edit/ssh_keys/new')
139 139 config.add_route(
140 140 name='edit_user_ssh_keys_delete',
141 141 pattern='/users/{user_id:\d+}/edit/ssh_keys/delete')
142 142
143 143 # user emails
144 144 config.add_route(
145 145 name='edit_user_emails',
146 146 pattern='/users/{user_id:\d+}/edit/emails')
147 147 config.add_route(
148 148 name='edit_user_emails_add',
149 149 pattern='/users/{user_id:\d+}/edit/emails/new')
150 150 config.add_route(
151 151 name='edit_user_emails_delete',
152 152 pattern='/users/{user_id:\d+}/edit/emails/delete')
153 153
154 154 # user IPs
155 155 config.add_route(
156 156 name='edit_user_ips',
157 157 pattern='/users/{user_id:\d+}/edit/ips')
158 158 config.add_route(
159 159 name='edit_user_ips_add',
160 160 pattern='/users/{user_id:\d+}/edit/ips/new')
161 161 config.add_route(
162 162 name='edit_user_ips_delete',
163 163 pattern='/users/{user_id:\d+}/edit/ips/delete')
164 164
165 # user perms
166 config.add_route(
167 name='edit_user_perms_summary',
168 pattern='/users/{user_id:\d+}/edit/permissions_summary')
169 config.add_route(
170 name='edit_user_perms_summary_json',
171 pattern='/users/{user_id:\d+}/edit/permissions_summary/json')
172
165 173 # user groups management
166 174 config.add_route(
167 175 name='edit_user_groups_management',
168 176 pattern='/users/{user_id:\d+}/edit/groups_management')
169 177
170 178 config.add_route(
171 179 name='edit_user_groups_management_updates',
172 180 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates')
173 181
174 182 # user audit logs
175 183 config.add_route(
176 184 name='edit_user_audit_logs',
177 185 pattern='/users/{user_id:\d+}/edit/audit')
178 186
179 187 # user groups admin
180 188 config.add_route(
181 189 name='user_groups',
182 190 pattern='/user_groups')
183 191
184 192 config.add_route(
185 193 name='user_groups_data',
186 194 pattern='/user_groups_data')
187 195
188 196 config.add_route(
189 197 name='user_group_members_data',
190 198 pattern='/user_groups/{user_group_id:\d+}/members')
191 199
200 # user groups perms
201 config.add_route(
202 name='edit_user_group_perms_summary',
203 pattern='/user_groups/{user_group_id:\d+}/edit/permissions_summary')
204 config.add_route(
205 name='edit_user_group_perms_summary_json',
206 pattern='/user_groups/{user_group_id:\d+}/edit/permissions_summary/json')
207
192 208
193 209 def includeme(config):
194 210 settings = config.get_settings()
195 211
196 212 # Create admin navigation registry and add it to the pyramid registry.
197 213 labs_active = str2bool(settings.get('labs_settings_active', False))
198 214 navigation_registry = NavigationRegistry(labs_active=labs_active)
199 215 config.registry.registerUtility(navigation_registry)
200 216
201 217 # main admin routes
202 218 config.add_route(name='admin_home', pattern=ADMIN_PREFIX)
203 219 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
204 220
205 221 # Scan module for configuration decorators.
206 222 config.scan('.views', ignore='.tests')
@@ -1,203 +1,256 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import datetime
23 23
24 24 from pyramid.httpexceptions import HTTPFound
25 25 from pyramid.view import view_config
26 26
27 from rhodecode.lib.helpers import Page
28 27 from rhodecode.model.scm import UserGroupList
29 from rhodecode_tools.lib.ext_json import json
30 28
31 29 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 30 from rhodecode.lib.auth import (
33 31 LoginRequired, HasPermissionAllDecorator, CSRFRequired, NotAnonymous,
34 32 HasUserGroupPermissionAnyDecorator)
35 33 from rhodecode.lib import helpers as h
36 34 from rhodecode.lib.utils import PartialRenderer
37 35 from rhodecode.lib.utils2 import safe_int, safe_unicode
38 from rhodecode.model.auth_token import AuthTokenModel
39 from rhodecode.model.user import UserModel
40 36 from rhodecode.model.user_group import UserGroupModel
41 from rhodecode.model.db import User, UserGroup, UserGroupMember, or_, count
37 from rhodecode.model.db import (
38 joinedload, or_, count, User, UserGroup, UserGroupMember,
39 UserGroupRepoToPerm, UserGroupRepoGroupToPerm)
42 40 from rhodecode.model.meta import Session
43 41
44 42 log = logging.getLogger(__name__)
45 43
46 44
47 45 class AdminUserGroupsView(BaseAppView, DataGridAppView):
48 46
49 47 def load_default_context(self):
50 48 c = self._get_local_tmpl_context()
51 49 self._register_global_c(c)
52 50 return c
53 51
54 52 # permission check in data loading of
55 53 # `user_groups_list_data` via UserGroupList
56 54 @NotAnonymous()
57 55 @view_config(
58 56 route_name='user_groups', request_method='GET',
59 57 renderer='rhodecode:templates/admin/user_groups/user_groups.mako')
60 58 def user_groups_list(self):
61 59 c = self.load_default_context()
62 60 return self._get_template_context(c)
63 61
64 62 # permission check inside
65 63 @NotAnonymous()
66 64 @view_config(
67 65 route_name='user_groups_data', request_method='GET',
68 66 renderer='json_ext', xhr=True)
69 67 def user_groups_list_data(self):
70 68 column_map = {
71 69 'active': 'users_group_active',
72 70 'description': 'user_group_description',
73 71 'members': 'members_total',
74 72 'owner': 'user_username',
75 73 'sync': 'group_data'
76 74 }
77 75 draw, start, limit = self._extract_chunk(self.request)
78 76 search_q, order_by, order_dir = self._extract_ordering(
79 77 self.request, column_map=column_map)
80 78
81 79 _render = PartialRenderer('data_table/_dt_elements.mako')
82 80
83 81 def user_group_name(user_group_id, user_group_name):
84 82 return _render("user_group_name", user_group_id, user_group_name)
85 83
86 84 def user_group_actions(user_group_id, user_group_name):
87 85 return _render("user_group_actions", user_group_id, user_group_name)
88 86
89 87 def user_profile(username):
90 88 return _render('user_profile', username)
91 89
92 90 auth_user_group_list = UserGroupList(
93 91 UserGroup.query().all(), perm_set=['usergroup.admin'])
94 92
95 93 allowed_ids = []
96 94 for user_group in auth_user_group_list:
97 95 allowed_ids.append(user_group.users_group_id)
98 96
99 97 user_groups_data_total_count = UserGroup.query()\
100 98 .filter(UserGroup.users_group_id.in_(allowed_ids))\
101 99 .count()
102 100
103 101 member_count = count(UserGroupMember.user_id)
104 102 base_q = Session.query(
105 103 UserGroup.users_group_name,
106 104 UserGroup.user_group_description,
107 105 UserGroup.users_group_active,
108 106 UserGroup.users_group_id,
109 107 UserGroup.group_data,
110 108 User,
111 109 member_count.label('member_count')
112 110 ) \
113 111 .filter(UserGroup.users_group_id.in_(allowed_ids)) \
114 112 .outerjoin(UserGroupMember) \
115 113 .join(User, User.user_id == UserGroup.user_id) \
116 114 .group_by(UserGroup, User)
117 115
118 116 if search_q:
119 117 like_expression = u'%{}%'.format(safe_unicode(search_q))
120 118 base_q = base_q.filter(or_(
121 119 UserGroup.users_group_name.ilike(like_expression),
122 120 ))
123 121
124 122 user_groups_data_total_filtered_count = base_q.count()
125 123
126 124 if order_by == 'members_total':
127 125 sort_col = member_count
128 126 elif order_by == 'user_username':
129 127 sort_col = User.username
130 128 else:
131 129 sort_col = getattr(UserGroup, order_by, None)
132 130
133 131 if isinstance(sort_col, count) or sort_col:
134 132 if order_dir == 'asc':
135 133 sort_col = sort_col.asc()
136 134 else:
137 135 sort_col = sort_col.desc()
138 136
139 137 base_q = base_q.order_by(sort_col)
140 138 base_q = base_q.offset(start).limit(limit)
141 139
142 140 # authenticated access to user groups
143 141 auth_user_group_list = base_q.all()
144 142
145 143 user_groups_data = []
146 144 for user_gr in auth_user_group_list:
147 145 user_groups_data.append({
148 146 "users_group_name": user_group_name(
149 147 user_gr.users_group_id, h.escape(user_gr.users_group_name)),
150 148 "name_raw": h.escape(user_gr.users_group_name),
151 149 "description": h.escape(user_gr.user_group_description),
152 150 "members": user_gr.member_count,
153 151 # NOTE(marcink): because of advanced query we
154 152 # need to load it like that
155 153 "sync": UserGroup._load_group_data(
156 154 user_gr.group_data).get('extern_type'),
157 155 "active": h.bool2icon(user_gr.users_group_active),
158 156 "owner": user_profile(user_gr.User.username),
159 157 "action": user_group_actions(
160 158 user_gr.users_group_id, user_gr.users_group_name)
161 159 })
162 160
163 161 data = ({
164 162 'draw': draw,
165 163 'data': user_groups_data,
166 164 'recordsTotal': user_groups_data_total_count,
167 165 'recordsFiltered': user_groups_data_total_filtered_count,
168 166 })
169 167
170 168 return data
171 169
172 170 @LoginRequired()
173 171 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
174 172 @view_config(
175 173 route_name='user_group_members_data', request_method='GET',
176 174 renderer='json_ext', xhr=True)
177 175 def user_group_members(self):
178 176 """
179 177 Return members of given user group
180 178 """
181 179 user_group_id = self.request.matchdict['user_group_id']
182 180 user_group = UserGroup.get_or_404(user_group_id)
183 181 group_members_obj = sorted((x.user for x in user_group.members),
184 182 key=lambda u: u.username.lower())
185 183
186 184 group_members = [
187 185 {
188 186 'id': user.user_id,
189 187 'first_name': user.first_name,
190 188 'last_name': user.last_name,
191 189 'username': user.username,
192 190 'icon_link': h.gravatar_url(user.email, 30),
193 191 'value_display': h.person(user.email),
194 192 'value': user.username,
195 193 'value_type': 'user',
196 194 'active': user.active,
197 195 }
198 196 for user in group_members_obj
199 197 ]
200 198
201 199 return {
202 200 'members': group_members
203 201 }
202
203 def _get_perms_summary(self, user_group_id):
204 permissions = {
205 'repositories': {},
206 'repositories_groups': {},
207 }
208 ugroup_repo_perms = UserGroupRepoToPerm.query()\
209 .options(joinedload(UserGroupRepoToPerm.permission))\
210 .options(joinedload(UserGroupRepoToPerm.repository))\
211 .filter(UserGroupRepoToPerm.users_group_id == user_group_id)\
212 .all()
213
214 for gr in ugroup_repo_perms:
215 permissions['repositories'][gr.repository.repo_name] \
216 = gr.permission.permission_name
217
218 ugroup_group_perms = UserGroupRepoGroupToPerm.query()\
219 .options(joinedload(UserGroupRepoGroupToPerm.permission))\
220 .options(joinedload(UserGroupRepoGroupToPerm.group))\
221 .filter(UserGroupRepoGroupToPerm.users_group_id == user_group_id)\
222 .all()
223
224 for gr in ugroup_group_perms:
225 permissions['repositories_groups'][gr.group.group_name] \
226 = gr.permission.permission_name
227 return permissions
228
229 @LoginRequired()
230 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
231 @view_config(
232 route_name='edit_user_group_perms_summary', request_method='GET',
233 renderer='rhodecode:templates/admin/user_groups/user_group_edit.mako')
234 def user_group_perms_summary(self):
235 c = self.load_default_context()
236
237 user_group_id = self.request.matchdict.get('user_group_id')
238 c.user_group = UserGroup.get_or_404(user_group_id)
239
240 c.active = 'perms_summary'
241
242 c.permissions = self._get_perms_summary(c.user_group.users_group_id)
243 return self._get_template_context(c)
244
245 @LoginRequired()
246 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
247 @view_config(
248 route_name='edit_user_group_perms_summary_json', request_method='GET',
249 renderer='json_ext')
250 def user_group_perms_summary(self):
251 self.load_default_context()
252
253 user_group_id = self.request.matchdict.get('user_group_id')
254 user_group = UserGroup.get_or_404(user_group_id)
255
256 return self._get_perms_summary(user_group.users_group_id)
@@ -1,637 +1,670 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import datetime
23 23 import formencode
24 24
25 25 from pyramid.httpexceptions import HTTPFound
26 26 from pyramid.view import view_config
27 27 from sqlalchemy.sql.functions import coalesce
28 28 from sqlalchemy.exc import IntegrityError
29 29
30 30 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 31 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
32 32 from rhodecode.events import trigger
33 33
34 34 from rhodecode.lib import audit_logger
35 35 from rhodecode.lib.ext_json import json
36 36 from rhodecode.lib.auth import (
37 37 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
38 38 from rhodecode.lib import helpers as h
39 39 from rhodecode.lib.utils2 import safe_int, safe_unicode
40 40 from rhodecode.model.auth_token import AuthTokenModel
41 41 from rhodecode.model.ssh_key import SshKeyModel
42 42 from rhodecode.model.user import UserModel
43 43 from rhodecode.model.user_group import UserGroupModel
44 44 from rhodecode.model.db import (
45 45 or_, User, UserIpMap, UserEmailMap, UserApiKeys, UserSshKeys)
46 46 from rhodecode.model.meta import Session
47 47
48 48 log = logging.getLogger(__name__)
49 49
50 50
51 51 class AdminUsersView(BaseAppView, DataGridAppView):
52 52 ALLOW_SCOPED_TOKENS = False
53 53 """
54 54 This view has alternative version inside EE, if modified please take a look
55 55 in there as well.
56 56 """
57 57
58 58 def load_default_context(self):
59 59 c = self._get_local_tmpl_context()
60 60 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
61 61 self._register_global_c(c)
62 62 return c
63 63
64 64 def _redirect_for_default_user(self, username):
65 65 _ = self.request.translate
66 66 if username == User.DEFAULT_USER:
67 67 h.flash(_("You can't edit this user"), category='warning')
68 68 # TODO(marcink): redirect to 'users' admin panel once this
69 69 # is a pyramid view
70 70 raise HTTPFound('/')
71 71
72 72 @HasPermissionAllDecorator('hg.admin')
73 73 @view_config(
74 74 route_name='users', request_method='GET',
75 75 renderer='rhodecode:templates/admin/users/users.mako')
76 76 def users_list(self):
77 77 c = self.load_default_context()
78 78 return self._get_template_context(c)
79 79
80 80 @HasPermissionAllDecorator('hg.admin')
81 81 @view_config(
82 82 # renderer defined below
83 83 route_name='users_data', request_method='GET',
84 84 renderer='json_ext', xhr=True)
85 85 def users_list_data(self):
86 86 column_map = {
87 87 'first_name': 'name',
88 88 'last_name': 'lastname',
89 89 }
90 90 draw, start, limit = self._extract_chunk(self.request)
91 91 search_q, order_by, order_dir = self._extract_ordering(
92 92 self.request, column_map=column_map)
93 93
94 94 _render = self.request.get_partial_renderer(
95 95 'data_table/_dt_elements.mako')
96 96
97 97 def user_actions(user_id, username):
98 98 return _render("user_actions", user_id, username)
99 99
100 100 users_data_total_count = User.query()\
101 101 .filter(User.username != User.DEFAULT_USER) \
102 102 .count()
103 103
104 104 # json generate
105 105 base_q = User.query().filter(User.username != User.DEFAULT_USER)
106 106
107 107 if search_q:
108 108 like_expression = u'%{}%'.format(safe_unicode(search_q))
109 109 base_q = base_q.filter(or_(
110 110 User.username.ilike(like_expression),
111 111 User._email.ilike(like_expression),
112 112 User.name.ilike(like_expression),
113 113 User.lastname.ilike(like_expression),
114 114 ))
115 115
116 116 users_data_total_filtered_count = base_q.count()
117 117
118 118 sort_col = getattr(User, order_by, None)
119 119 if sort_col:
120 120 if order_dir == 'asc':
121 121 # handle null values properly to order by NULL last
122 122 if order_by in ['last_activity']:
123 123 sort_col = coalesce(sort_col, datetime.date.max)
124 124 sort_col = sort_col.asc()
125 125 else:
126 126 # handle null values properly to order by NULL last
127 127 if order_by in ['last_activity']:
128 128 sort_col = coalesce(sort_col, datetime.date.min)
129 129 sort_col = sort_col.desc()
130 130
131 131 base_q = base_q.order_by(sort_col)
132 132 base_q = base_q.offset(start).limit(limit)
133 133
134 134 users_list = base_q.all()
135 135
136 136 users_data = []
137 137 for user in users_list:
138 138 users_data.append({
139 139 "username": h.gravatar_with_user(self.request, user.username),
140 140 "email": user.email,
141 141 "first_name": user.first_name,
142 142 "last_name": user.last_name,
143 143 "last_login": h.format_date(user.last_login),
144 144 "last_activity": h.format_date(user.last_activity),
145 145 "active": h.bool2icon(user.active),
146 146 "active_raw": user.active,
147 147 "admin": h.bool2icon(user.admin),
148 148 "extern_type": user.extern_type,
149 149 "extern_name": user.extern_name,
150 150 "action": user_actions(user.user_id, user.username),
151 151 })
152 152
153 153 data = ({
154 154 'draw': draw,
155 155 'data': users_data,
156 156 'recordsTotal': users_data_total_count,
157 157 'recordsFiltered': users_data_total_filtered_count,
158 158 })
159 159
160 160 return data
161 161
162 162 @LoginRequired()
163 163 @HasPermissionAllDecorator('hg.admin')
164 164 @view_config(
165 165 route_name='edit_user_auth_tokens', request_method='GET',
166 166 renderer='rhodecode:templates/admin/users/user_edit.mako')
167 167 def auth_tokens(self):
168 168 _ = self.request.translate
169 169 c = self.load_default_context()
170 170
171 171 user_id = self.request.matchdict.get('user_id')
172 172 c.user = User.get_or_404(user_id)
173 173 self._redirect_for_default_user(c.user.username)
174 174
175 175 c.active = 'auth_tokens'
176 176
177 177 c.lifetime_values = [
178 178 (str(-1), _('forever')),
179 179 (str(5), _('5 minutes')),
180 180 (str(60), _('1 hour')),
181 181 (str(60 * 24), _('1 day')),
182 182 (str(60 * 24 * 30), _('1 month')),
183 183 ]
184 184 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
185 185 c.role_values = [
186 186 (x, AuthTokenModel.cls._get_role_name(x))
187 187 for x in AuthTokenModel.cls.ROLES]
188 188 c.role_options = [(c.role_values, _("Role"))]
189 189 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
190 190 c.user.user_id, show_expired=True)
191 191 return self._get_template_context(c)
192 192
193 193 def maybe_attach_token_scope(self, token):
194 194 # implemented in EE edition
195 195 pass
196 196
197 197 @LoginRequired()
198 198 @HasPermissionAllDecorator('hg.admin')
199 199 @CSRFRequired()
200 200 @view_config(
201 201 route_name='edit_user_auth_tokens_add', request_method='POST')
202 202 def auth_tokens_add(self):
203 203 _ = self.request.translate
204 204 c = self.load_default_context()
205 205
206 206 user_id = self.request.matchdict.get('user_id')
207 207 c.user = User.get_or_404(user_id)
208 208
209 209 self._redirect_for_default_user(c.user.username)
210 210
211 211 user_data = c.user.get_api_data()
212 212 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
213 213 description = self.request.POST.get('description')
214 214 role = self.request.POST.get('role')
215 215
216 216 token = AuthTokenModel().create(
217 217 c.user.user_id, description, lifetime, role)
218 218 token_data = token.get_api_data()
219 219
220 220 self.maybe_attach_token_scope(token)
221 221 audit_logger.store_web(
222 222 'user.edit.token.add', action_data={
223 223 'data': {'token': token_data, 'user': user_data}},
224 224 user=self._rhodecode_user, )
225 225 Session().commit()
226 226
227 227 h.flash(_("Auth token successfully created"), category='success')
228 228 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
229 229
230 230 @LoginRequired()
231 231 @HasPermissionAllDecorator('hg.admin')
232 232 @CSRFRequired()
233 233 @view_config(
234 234 route_name='edit_user_auth_tokens_delete', request_method='POST')
235 235 def auth_tokens_delete(self):
236 236 _ = self.request.translate
237 237 c = self.load_default_context()
238 238
239 239 user_id = self.request.matchdict.get('user_id')
240 240 c.user = User.get_or_404(user_id)
241 241 self._redirect_for_default_user(c.user.username)
242 242 user_data = c.user.get_api_data()
243 243
244 244 del_auth_token = self.request.POST.get('del_auth_token')
245 245
246 246 if del_auth_token:
247 247 token = UserApiKeys.get_or_404(del_auth_token)
248 248 token_data = token.get_api_data()
249 249
250 250 AuthTokenModel().delete(del_auth_token, c.user.user_id)
251 251 audit_logger.store_web(
252 252 'user.edit.token.delete', action_data={
253 253 'data': {'token': token_data, 'user': user_data}},
254 254 user=self._rhodecode_user,)
255 255 Session().commit()
256 256 h.flash(_("Auth token successfully deleted"), category='success')
257 257
258 258 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
259 259
260 260 @LoginRequired()
261 261 @HasPermissionAllDecorator('hg.admin')
262 262 @view_config(
263 263 route_name='edit_user_ssh_keys', request_method='GET',
264 264 renderer='rhodecode:templates/admin/users/user_edit.mako')
265 265 def ssh_keys(self):
266 266 _ = self.request.translate
267 267 c = self.load_default_context()
268 268
269 269 user_id = self.request.matchdict.get('user_id')
270 270 c.user = User.get_or_404(user_id)
271 271 self._redirect_for_default_user(c.user.username)
272 272
273 273 c.active = 'ssh_keys'
274 274 c.default_key = self.request.GET.get('default_key')
275 275 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
276 276 return self._get_template_context(c)
277 277
278 278 @LoginRequired()
279 279 @HasPermissionAllDecorator('hg.admin')
280 280 @view_config(
281 281 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
282 282 renderer='rhodecode:templates/admin/users/user_edit.mako')
283 283 def ssh_keys_generate_keypair(self):
284 284 _ = self.request.translate
285 285 c = self.load_default_context()
286 286
287 287 user_id = self.request.matchdict.get('user_id')
288 288 c.user = User.get_or_404(user_id)
289 289 self._redirect_for_default_user(c.user.username)
290 290
291 291 c.active = 'ssh_keys_generate'
292 292 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
293 293 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
294 294
295 295 return self._get_template_context(c)
296 296
297 297 @LoginRequired()
298 298 @HasPermissionAllDecorator('hg.admin')
299 299 @CSRFRequired()
300 300 @view_config(
301 301 route_name='edit_user_ssh_keys_add', request_method='POST')
302 302 def ssh_keys_add(self):
303 303 _ = self.request.translate
304 304 c = self.load_default_context()
305 305
306 306 user_id = self.request.matchdict.get('user_id')
307 307 c.user = User.get_or_404(user_id)
308 308
309 309 self._redirect_for_default_user(c.user.username)
310 310
311 311 user_data = c.user.get_api_data()
312 312 key_data = self.request.POST.get('key_data')
313 313 description = self.request.POST.get('description')
314 314
315 315 try:
316 316 if not key_data:
317 317 raise ValueError('Please add a valid public key')
318 318
319 319 key = SshKeyModel().parse_key(key_data.strip())
320 320 fingerprint = key.hash_md5()
321 321
322 322 ssh_key = SshKeyModel().create(
323 323 c.user.user_id, fingerprint, key_data, description)
324 324 ssh_key_data = ssh_key.get_api_data()
325 325
326 326 audit_logger.store_web(
327 327 'user.edit.ssh_key.add', action_data={
328 328 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
329 329 user=self._rhodecode_user, )
330 330 Session().commit()
331 331
332 332 # Trigger an event on change of keys.
333 333 trigger(SshKeyFileChangeEvent(), self.request.registry)
334 334
335 335 h.flash(_("Ssh Key successfully created"), category='success')
336 336
337 337 except IntegrityError:
338 338 log.exception("Exception during ssh key saving")
339 339 h.flash(_('An error occurred during ssh key saving: {}').format(
340 340 'Such key already exists, please use a different one'),
341 341 category='error')
342 342 except Exception as e:
343 343 log.exception("Exception during ssh key saving")
344 344 h.flash(_('An error occurred during ssh key saving: {}').format(e),
345 345 category='error')
346 346
347 347 return HTTPFound(
348 348 h.route_path('edit_user_ssh_keys', user_id=user_id))
349 349
350 350 @LoginRequired()
351 351 @HasPermissionAllDecorator('hg.admin')
352 352 @CSRFRequired()
353 353 @view_config(
354 354 route_name='edit_user_ssh_keys_delete', request_method='POST')
355 355 def ssh_keys_delete(self):
356 356 _ = self.request.translate
357 357 c = self.load_default_context()
358 358
359 359 user_id = self.request.matchdict.get('user_id')
360 360 c.user = User.get_or_404(user_id)
361 361 self._redirect_for_default_user(c.user.username)
362 362 user_data = c.user.get_api_data()
363 363
364 364 del_ssh_key = self.request.POST.get('del_ssh_key')
365 365
366 366 if del_ssh_key:
367 367 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
368 368 ssh_key_data = ssh_key.get_api_data()
369 369
370 370 SshKeyModel().delete(del_ssh_key, c.user.user_id)
371 371 audit_logger.store_web(
372 372 'user.edit.ssh_key.delete', action_data={
373 373 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
374 374 user=self._rhodecode_user,)
375 375 Session().commit()
376 376 # Trigger an event on change of keys.
377 377 trigger(SshKeyFileChangeEvent(), self.request.registry)
378 378 h.flash(_("Ssh key successfully deleted"), category='success')
379 379
380 380 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
381 381
382 382 @LoginRequired()
383 383 @HasPermissionAllDecorator('hg.admin')
384 384 @view_config(
385 385 route_name='edit_user_emails', request_method='GET',
386 386 renderer='rhodecode:templates/admin/users/user_edit.mako')
387 387 def emails(self):
388 388 _ = self.request.translate
389 389 c = self.load_default_context()
390 390
391 391 user_id = self.request.matchdict.get('user_id')
392 392 c.user = User.get_or_404(user_id)
393 393 self._redirect_for_default_user(c.user.username)
394 394
395 395 c.active = 'emails'
396 396 c.user_email_map = UserEmailMap.query() \
397 397 .filter(UserEmailMap.user == c.user).all()
398 398
399 399 return self._get_template_context(c)
400 400
401 401 @LoginRequired()
402 402 @HasPermissionAllDecorator('hg.admin')
403 403 @CSRFRequired()
404 404 @view_config(
405 405 route_name='edit_user_emails_add', request_method='POST')
406 406 def emails_add(self):
407 407 _ = self.request.translate
408 408 c = self.load_default_context()
409 409
410 410 user_id = self.request.matchdict.get('user_id')
411 411 c.user = User.get_or_404(user_id)
412 412 self._redirect_for_default_user(c.user.username)
413 413
414 414 email = self.request.POST.get('new_email')
415 415 user_data = c.user.get_api_data()
416 416 try:
417 417 UserModel().add_extra_email(c.user.user_id, email)
418 418 audit_logger.store_web(
419 419 'user.edit.email.add', action_data={'email': email, 'user': user_data},
420 420 user=self._rhodecode_user)
421 421 Session().commit()
422 422 h.flash(_("Added new email address `%s` for user account") % email,
423 423 category='success')
424 424 except formencode.Invalid as error:
425 425 h.flash(h.escape(error.error_dict['email']), category='error')
426 426 except Exception:
427 427 log.exception("Exception during email saving")
428 428 h.flash(_('An error occurred during email saving'),
429 429 category='error')
430 430 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
431 431
432 432 @LoginRequired()
433 433 @HasPermissionAllDecorator('hg.admin')
434 434 @CSRFRequired()
435 435 @view_config(
436 436 route_name='edit_user_emails_delete', request_method='POST')
437 437 def emails_delete(self):
438 438 _ = self.request.translate
439 439 c = self.load_default_context()
440 440
441 441 user_id = self.request.matchdict.get('user_id')
442 442 c.user = User.get_or_404(user_id)
443 443 self._redirect_for_default_user(c.user.username)
444 444
445 445 email_id = self.request.POST.get('del_email_id')
446 446 user_model = UserModel()
447 447
448 448 email = UserEmailMap.query().get(email_id).email
449 449 user_data = c.user.get_api_data()
450 450 user_model.delete_extra_email(c.user.user_id, email_id)
451 451 audit_logger.store_web(
452 452 'user.edit.email.delete', action_data={'email': email, 'user': user_data},
453 453 user=self._rhodecode_user)
454 454 Session().commit()
455 455 h.flash(_("Removed email address from user account"),
456 456 category='success')
457 457 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
458 458
459 459 @LoginRequired()
460 460 @HasPermissionAllDecorator('hg.admin')
461 461 @view_config(
462 462 route_name='edit_user_ips', request_method='GET',
463 463 renderer='rhodecode:templates/admin/users/user_edit.mako')
464 464 def ips(self):
465 465 _ = self.request.translate
466 466 c = self.load_default_context()
467 467
468 468 user_id = self.request.matchdict.get('user_id')
469 469 c.user = User.get_or_404(user_id)
470 470 self._redirect_for_default_user(c.user.username)
471 471
472 472 c.active = 'ips'
473 473 c.user_ip_map = UserIpMap.query() \
474 474 .filter(UserIpMap.user == c.user).all()
475 475
476 476 c.inherit_default_ips = c.user.inherit_default_permissions
477 477 c.default_user_ip_map = UserIpMap.query() \
478 478 .filter(UserIpMap.user == User.get_default_user()).all()
479 479
480 480 return self._get_template_context(c)
481 481
482 482 @LoginRequired()
483 483 @HasPermissionAllDecorator('hg.admin')
484 484 @CSRFRequired()
485 485 @view_config(
486 486 route_name='edit_user_ips_add', request_method='POST')
487 487 def ips_add(self):
488 488 _ = self.request.translate
489 489 c = self.load_default_context()
490 490
491 491 user_id = self.request.matchdict.get('user_id')
492 492 c.user = User.get_or_404(user_id)
493 493 # NOTE(marcink): this view is allowed for default users, as we can
494 494 # edit their IP white list
495 495
496 496 user_model = UserModel()
497 497 desc = self.request.POST.get('description')
498 498 try:
499 499 ip_list = user_model.parse_ip_range(
500 500 self.request.POST.get('new_ip'))
501 501 except Exception as e:
502 502 ip_list = []
503 503 log.exception("Exception during ip saving")
504 504 h.flash(_('An error occurred during ip saving:%s' % (e,)),
505 505 category='error')
506 506 added = []
507 507 user_data = c.user.get_api_data()
508 508 for ip in ip_list:
509 509 try:
510 510 user_model.add_extra_ip(c.user.user_id, ip, desc)
511 511 audit_logger.store_web(
512 512 'user.edit.ip.add', action_data={'ip': ip, 'user': user_data},
513 513 user=self._rhodecode_user)
514 514 Session().commit()
515 515 added.append(ip)
516 516 except formencode.Invalid as error:
517 517 msg = error.error_dict['ip']
518 518 h.flash(msg, category='error')
519 519 except Exception:
520 520 log.exception("Exception during ip saving")
521 521 h.flash(_('An error occurred during ip saving'),
522 522 category='error')
523 523 if added:
524 524 h.flash(
525 525 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
526 526 category='success')
527 527 if 'default_user' in self.request.POST:
528 528 # case for editing global IP list we do it for 'DEFAULT' user
529 529 raise HTTPFound(h.route_path('admin_permissions_ips'))
530 530 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
531 531
532 532 @LoginRequired()
533 533 @HasPermissionAllDecorator('hg.admin')
534 534 @CSRFRequired()
535 535 @view_config(
536 536 route_name='edit_user_ips_delete', request_method='POST')
537 537 def ips_delete(self):
538 538 _ = self.request.translate
539 539 c = self.load_default_context()
540 540
541 541 user_id = self.request.matchdict.get('user_id')
542 542 c.user = User.get_or_404(user_id)
543 543 # NOTE(marcink): this view is allowed for default users, as we can
544 544 # edit their IP white list
545 545
546 546 ip_id = self.request.POST.get('del_ip_id')
547 547 user_model = UserModel()
548 548 user_data = c.user.get_api_data()
549 549 ip = UserIpMap.query().get(ip_id).ip_addr
550 550 user_model.delete_extra_ip(c.user.user_id, ip_id)
551 551 audit_logger.store_web(
552 552 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
553 553 user=self._rhodecode_user)
554 554 Session().commit()
555 555 h.flash(_("Removed ip address from user whitelist"), category='success')
556 556
557 557 if 'default_user' in self.request.POST:
558 558 # case for editing global IP list we do it for 'DEFAULT' user
559 559 raise HTTPFound(h.route_path('admin_permissions_ips'))
560 560 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
561 561
562 562 @LoginRequired()
563 563 @HasPermissionAllDecorator('hg.admin')
564 564 @view_config(
565 565 route_name='edit_user_groups_management', request_method='GET',
566 566 renderer='rhodecode:templates/admin/users/user_edit.mako')
567 567 def groups_management(self):
568 568 c = self.load_default_context()
569 569
570 570 user_id = self.request.matchdict.get('user_id')
571 571 c.user = User.get_or_404(user_id)
572 572 c.data = c.user.group_member
573 573 self._redirect_for_default_user(c.user.username)
574 574 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
575 575 for group in c.user.group_member]
576 576 c.groups = json.dumps(groups)
577 577 c.active = 'groups'
578 578
579 579 return self._get_template_context(c)
580 580
581 581 @LoginRequired()
582 582 @HasPermissionAllDecorator('hg.admin')
583 583 @CSRFRequired()
584 584 @view_config(
585 585 route_name='edit_user_groups_management_updates', request_method='POST')
586 586 def groups_management_updates(self):
587 587 _ = self.request.translate
588 588 c = self.load_default_context()
589 589
590 590 user_id = self.request.matchdict.get('user_id')
591 591 c.user = User.get_or_404(user_id)
592 592 self._redirect_for_default_user(c.user.username)
593 593
594 594 users_groups = set(self.request.POST.getall('users_group_id'))
595 595 users_groups_model = []
596 596
597 597 for ugid in users_groups:
598 598 users_groups_model.append(UserGroupModel().get_group(safe_int(ugid)))
599 599 user_group_model = UserGroupModel()
600 600 user_group_model.change_groups(c.user, users_groups_model)
601 601
602 602 Session().commit()
603 603 c.active = 'user_groups_management'
604 604 h.flash(_("Groups successfully changed"), category='success')
605 605
606 606 return HTTPFound(h.route_path(
607 607 'edit_user_groups_management', user_id=user_id))
608 608
609 609 @LoginRequired()
610 610 @HasPermissionAllDecorator('hg.admin')
611 611 @view_config(
612 612 route_name='edit_user_audit_logs', request_method='GET',
613 613 renderer='rhodecode:templates/admin/users/user_edit.mako')
614 614 def user_audit_logs(self):
615 615 _ = self.request.translate
616 616 c = self.load_default_context()
617 617
618 618 user_id = self.request.matchdict.get('user_id')
619 619 c.user = User.get_or_404(user_id)
620 620 self._redirect_for_default_user(c.user.username)
621 621 c.active = 'audit'
622 622
623 623 p = safe_int(self.request.GET.get('page', 1), 1)
624 624
625 625 filter_term = self.request.GET.get('filter')
626 626 user_log = UserModel().get_user_log(c.user, filter_term)
627 627
628 628 def url_generator(**kw):
629 629 if filter_term:
630 630 kw['filter'] = filter_term
631 631 return self.request.current_route_path(_query=kw)
632 632
633 633 c.audit_logs = h.Page(
634 634 user_log, page=p, items_per_page=10, url=url_generator)
635 635 c.filter_term = filter_term
636 636 return self._get_template_context(c)
637 637
638 @LoginRequired()
639 @HasPermissionAllDecorator('hg.admin')
640 @view_config(
641 route_name='edit_user_perms_summary', request_method='GET',
642 renderer='rhodecode:templates/admin/users/user_edit.mako')
643 def user_perms_summary(self):
644 _ = self.request.translate
645 c = self.load_default_context()
646
647 user_id = self.request.matchdict.get('user_id')
648 c.user = User.get_or_404(user_id)
649 self._redirect_for_default_user(c.user.username)
650
651 c.active = 'perms_summary'
652 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
653
654 return self._get_template_context(c)
655
656 @LoginRequired()
657 @HasPermissionAllDecorator('hg.admin')
658 @view_config(
659 route_name='edit_user_perms_summary_json', request_method='GET',
660 renderer='json_ext')
661 def user_perms_summary_json(self):
662 self.load_default_context()
663
664 user_id = self.request.matchdict.get('user_id')
665 user = User.get_or_404(user_id)
666 self._redirect_for_default_user(user.username)
667
668 perm_user = user.AuthUser(ip_addr=self.request.remote_addr)
669
670 return perm_user.permissions
@@ -1,492 +1,486 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Routes configuration
23 23
24 24 The more specific and detailed routes should be defined first so they
25 25 may take precedent over the more generic routes. For more information
26 26 refer to the routes manual at http://routes.groovie.org/docs/
27 27
28 28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
29 29 and _route_name variable which uses some of stored naming here to do redirects.
30 30 """
31 31 import os
32 32 import re
33 33 from routes import Mapper
34 34
35 35 # prefix for non repository related links needs to be prefixed with `/`
36 36 ADMIN_PREFIX = '/_admin'
37 37 STATIC_FILE_PREFIX = '/_static'
38 38
39 39 # Default requirements for URL parts
40 40 URL_NAME_REQUIREMENTS = {
41 41 # group name can have a slash in them, but they must not end with a slash
42 42 'group_name': r'.*?[^/]',
43 43 'repo_group_name': r'.*?[^/]',
44 44 # repo names can have a slash in them, but they must not end with a slash
45 45 'repo_name': r'.*?[^/]',
46 46 # file path eats up everything at the end
47 47 'f_path': r'.*',
48 48 # reference types
49 49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
50 50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
51 51 }
52 52
53 53
54 54 class JSRoutesMapper(Mapper):
55 55 """
56 56 Wrapper for routes.Mapper to make pyroutes compatible url definitions
57 57 """
58 58 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
59 59 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
60 60 def __init__(self, *args, **kw):
61 61 super(JSRoutesMapper, self).__init__(*args, **kw)
62 62 self._jsroutes = []
63 63
64 64 def connect(self, *args, **kw):
65 65 """
66 66 Wrapper for connect to take an extra argument jsroute=True
67 67
68 68 :param jsroute: boolean, if True will add the route to the pyroutes list
69 69 """
70 70 if kw.pop('jsroute', False):
71 71 if not self._named_route_regex.match(args[0]):
72 72 raise Exception('only named routes can be added to pyroutes')
73 73 self._jsroutes.append(args[0])
74 74
75 75 super(JSRoutesMapper, self).connect(*args, **kw)
76 76
77 77 def _extract_route_information(self, route):
78 78 """
79 79 Convert a route into tuple(name, path, args), eg:
80 80 ('show_user', '/profile/%(username)s', ['username'])
81 81 """
82 82 routepath = route.routepath
83 83 def replace(matchobj):
84 84 if matchobj.group(1):
85 85 return "%%(%s)s" % matchobj.group(1).split(':')[0]
86 86 else:
87 87 return "%%(%s)s" % matchobj.group(2)
88 88
89 89 routepath = self._argument_prog.sub(replace, routepath)
90 90 return (
91 91 route.name,
92 92 routepath,
93 93 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
94 94 for arg in self._argument_prog.findall(route.routepath)]
95 95 )
96 96
97 97 def jsroutes(self):
98 98 """
99 99 Return a list of pyroutes.js compatible routes
100 100 """
101 101 for route_name in self._jsroutes:
102 102 yield self._extract_route_information(self._routenames[route_name])
103 103
104 104
105 105 def make_map(config):
106 106 """Create, configure and return the routes Mapper"""
107 107 rmap = JSRoutesMapper(
108 108 directory=config['pylons.paths']['controllers'],
109 109 always_scan=config['debug'])
110 110 rmap.minimization = False
111 111 rmap.explicit = False
112 112
113 113 from rhodecode.lib.utils2 import str2bool
114 114 from rhodecode.model import repo, repo_group
115 115
116 116 def check_repo(environ, match_dict):
117 117 """
118 118 check for valid repository for proper 404 handling
119 119
120 120 :param environ:
121 121 :param match_dict:
122 122 """
123 123 repo_name = match_dict.get('repo_name')
124 124
125 125 if match_dict.get('f_path'):
126 126 # fix for multiple initial slashes that causes errors
127 127 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
128 128 repo_model = repo.RepoModel()
129 129 by_name_match = repo_model.get_by_repo_name(repo_name)
130 130 # if we match quickly from database, short circuit the operation,
131 131 # and validate repo based on the type.
132 132 if by_name_match:
133 133 return True
134 134
135 135 by_id_match = repo_model.get_repo_by_id(repo_name)
136 136 if by_id_match:
137 137 repo_name = by_id_match.repo_name
138 138 match_dict['repo_name'] = repo_name
139 139 return True
140 140
141 141 return False
142 142
143 143 def check_group(environ, match_dict):
144 144 """
145 145 check for valid repository group path for proper 404 handling
146 146
147 147 :param environ:
148 148 :param match_dict:
149 149 """
150 150 repo_group_name = match_dict.get('group_name')
151 151 repo_group_model = repo_group.RepoGroupModel()
152 152 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
153 153 if by_name_match:
154 154 return True
155 155
156 156 return False
157 157
158 158 def check_user_group(environ, match_dict):
159 159 """
160 160 check for valid user group for proper 404 handling
161 161
162 162 :param environ:
163 163 :param match_dict:
164 164 """
165 165 return True
166 166
167 167 def check_int(environ, match_dict):
168 168 return match_dict.get('id').isdigit()
169 169
170 170
171 171 #==========================================================================
172 172 # CUSTOM ROUTES HERE
173 173 #==========================================================================
174 174
175 175 # ping and pylons error test
176 176 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
177 177 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
178 178
179 179 # ADMIN REPOSITORY ROUTES
180 180 with rmap.submapper(path_prefix=ADMIN_PREFIX,
181 181 controller='admin/repos') as m:
182 182 m.connect('repos', '/repos',
183 183 action='create', conditions={'method': ['POST']})
184 184 m.connect('repos', '/repos',
185 185 action='index', conditions={'method': ['GET']})
186 186 m.connect('new_repo', '/create_repository', jsroute=True,
187 187 action='create_repository', conditions={'method': ['GET']})
188 188 m.connect('delete_repo', '/repos/{repo_name}',
189 189 action='delete', conditions={'method': ['DELETE']},
190 190 requirements=URL_NAME_REQUIREMENTS)
191 191 m.connect('repo', '/repos/{repo_name}',
192 192 action='show', conditions={'method': ['GET'],
193 193 'function': check_repo},
194 194 requirements=URL_NAME_REQUIREMENTS)
195 195
196 196 # ADMIN REPOSITORY GROUPS ROUTES
197 197 with rmap.submapper(path_prefix=ADMIN_PREFIX,
198 198 controller='admin/repo_groups') as m:
199 199 m.connect('repo_groups', '/repo_groups',
200 200 action='create', conditions={'method': ['POST']})
201 201 m.connect('repo_groups', '/repo_groups',
202 202 action='index', conditions={'method': ['GET']})
203 203 m.connect('new_repo_group', '/repo_groups/new',
204 204 action='new', conditions={'method': ['GET']})
205 205 m.connect('update_repo_group', '/repo_groups/{group_name}',
206 206 action='update', conditions={'method': ['PUT'],
207 207 'function': check_group},
208 208 requirements=URL_NAME_REQUIREMENTS)
209 209
210 210 # EXTRAS REPO GROUP ROUTES
211 211 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
212 212 action='edit',
213 213 conditions={'method': ['GET'], 'function': check_group},
214 214 requirements=URL_NAME_REQUIREMENTS)
215 215 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
216 216 action='edit',
217 217 conditions={'method': ['PUT'], 'function': check_group},
218 218 requirements=URL_NAME_REQUIREMENTS)
219 219
220 220 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
221 221 action='edit_repo_group_advanced',
222 222 conditions={'method': ['GET'], 'function': check_group},
223 223 requirements=URL_NAME_REQUIREMENTS)
224 224 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
225 225 action='edit_repo_group_advanced',
226 226 conditions={'method': ['PUT'], 'function': check_group},
227 227 requirements=URL_NAME_REQUIREMENTS)
228 228
229 229 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
230 230 action='edit_repo_group_perms',
231 231 conditions={'method': ['GET'], 'function': check_group},
232 232 requirements=URL_NAME_REQUIREMENTS)
233 233 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
234 234 action='update_perms',
235 235 conditions={'method': ['PUT'], 'function': check_group},
236 236 requirements=URL_NAME_REQUIREMENTS)
237 237
238 238 m.connect('delete_repo_group', '/repo_groups/{group_name}',
239 239 action='delete', conditions={'method': ['DELETE'],
240 240 'function': check_group},
241 241 requirements=URL_NAME_REQUIREMENTS)
242 242
243 243 # ADMIN USER ROUTES
244 244 with rmap.submapper(path_prefix=ADMIN_PREFIX,
245 245 controller='admin/users') as m:
246 246 m.connect('users', '/users',
247 247 action='create', conditions={'method': ['POST']})
248 248 m.connect('new_user', '/users/new',
249 249 action='new', conditions={'method': ['GET']})
250 250 m.connect('update_user', '/users/{user_id}',
251 251 action='update', conditions={'method': ['PUT']})
252 252 m.connect('delete_user', '/users/{user_id}',
253 253 action='delete', conditions={'method': ['DELETE']})
254 254 m.connect('edit_user', '/users/{user_id}/edit',
255 255 action='edit', conditions={'method': ['GET']}, jsroute=True)
256 256 m.connect('user', '/users/{user_id}',
257 257 action='show', conditions={'method': ['GET']})
258 258 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
259 259 action='reset_password', conditions={'method': ['POST']})
260 260 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
261 261 action='create_personal_repo_group', conditions={'method': ['POST']})
262 262
263 263 # EXTRAS USER ROUTES
264 264 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
265 265 action='edit_advanced', conditions={'method': ['GET']})
266 266 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
267 267 action='update_advanced', conditions={'method': ['PUT']})
268 268
269 269 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
270 270 action='edit_global_perms', conditions={'method': ['GET']})
271 271 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
272 272 action='update_global_perms', conditions={'method': ['PUT']})
273 273
274 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
275 action='edit_perms_summary', conditions={'method': ['GET']})
276
277 274 # ADMIN USER GROUPS REST ROUTES
278 275 with rmap.submapper(path_prefix=ADMIN_PREFIX,
279 276 controller='admin/user_groups') as m:
280 277 m.connect('users_groups', '/user_groups',
281 278 action='create', conditions={'method': ['POST']})
282 279 m.connect('new_users_group', '/user_groups/new',
283 280 action='new', conditions={'method': ['GET']})
284 281 m.connect('update_users_group', '/user_groups/{user_group_id}',
285 282 action='update', conditions={'method': ['PUT']})
286 283 m.connect('delete_users_group', '/user_groups/{user_group_id}',
287 284 action='delete', conditions={'method': ['DELETE']})
288 285 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
289 286 action='edit', conditions={'method': ['GET']},
290 287 function=check_user_group)
291 288
292 289 # EXTRAS USER GROUP ROUTES
293 290 m.connect('edit_user_group_global_perms',
294 291 '/user_groups/{user_group_id}/edit/global_permissions',
295 292 action='edit_global_perms', conditions={'method': ['GET']})
296 293 m.connect('edit_user_group_global_perms',
297 294 '/user_groups/{user_group_id}/edit/global_permissions',
298 295 action='update_global_perms', conditions={'method': ['PUT']})
299 m.connect('edit_user_group_perms_summary',
300 '/user_groups/{user_group_id}/edit/permissions_summary',
301 action='edit_perms_summary', conditions={'method': ['GET']})
302 296
303 297 m.connect('edit_user_group_perms',
304 298 '/user_groups/{user_group_id}/edit/permissions',
305 299 action='edit_perms', conditions={'method': ['GET']})
306 300 m.connect('edit_user_group_perms',
307 301 '/user_groups/{user_group_id}/edit/permissions',
308 302 action='update_perms', conditions={'method': ['PUT']})
309 303
310 304 m.connect('edit_user_group_advanced',
311 305 '/user_groups/{user_group_id}/edit/advanced',
312 306 action='edit_advanced', conditions={'method': ['GET']})
313 307
314 308 m.connect('edit_user_group_advanced_sync',
315 309 '/user_groups/{user_group_id}/edit/advanced/sync',
316 310 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
317 311
318 312 # ADMIN DEFAULTS REST ROUTES
319 313 with rmap.submapper(path_prefix=ADMIN_PREFIX,
320 314 controller='admin/defaults') as m:
321 315 m.connect('admin_defaults_repositories', '/defaults/repositories',
322 316 action='update_repository_defaults', conditions={'method': ['POST']})
323 317 m.connect('admin_defaults_repositories', '/defaults/repositories',
324 318 action='index', conditions={'method': ['GET']})
325 319
326 320 # ADMIN SETTINGS ROUTES
327 321 with rmap.submapper(path_prefix=ADMIN_PREFIX,
328 322 controller='admin/settings') as m:
329 323
330 324 # default
331 325 m.connect('admin_settings', '/settings',
332 326 action='settings_global_update',
333 327 conditions={'method': ['POST']})
334 328 m.connect('admin_settings', '/settings',
335 329 action='settings_global', conditions={'method': ['GET']})
336 330
337 331 m.connect('admin_settings_vcs', '/settings/vcs',
338 332 action='settings_vcs_update',
339 333 conditions={'method': ['POST']})
340 334 m.connect('admin_settings_vcs', '/settings/vcs',
341 335 action='settings_vcs',
342 336 conditions={'method': ['GET']})
343 337 m.connect('admin_settings_vcs', '/settings/vcs',
344 338 action='delete_svn_pattern',
345 339 conditions={'method': ['DELETE']})
346 340
347 341 m.connect('admin_settings_mapping', '/settings/mapping',
348 342 action='settings_mapping_update',
349 343 conditions={'method': ['POST']})
350 344 m.connect('admin_settings_mapping', '/settings/mapping',
351 345 action='settings_mapping', conditions={'method': ['GET']})
352 346
353 347 m.connect('admin_settings_global', '/settings/global',
354 348 action='settings_global_update',
355 349 conditions={'method': ['POST']})
356 350 m.connect('admin_settings_global', '/settings/global',
357 351 action='settings_global', conditions={'method': ['GET']})
358 352
359 353 m.connect('admin_settings_visual', '/settings/visual',
360 354 action='settings_visual_update',
361 355 conditions={'method': ['POST']})
362 356 m.connect('admin_settings_visual', '/settings/visual',
363 357 action='settings_visual', conditions={'method': ['GET']})
364 358
365 359 m.connect('admin_settings_issuetracker',
366 360 '/settings/issue-tracker', action='settings_issuetracker',
367 361 conditions={'method': ['GET']})
368 362 m.connect('admin_settings_issuetracker_save',
369 363 '/settings/issue-tracker/save',
370 364 action='settings_issuetracker_save',
371 365 conditions={'method': ['POST']})
372 366 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
373 367 action='settings_issuetracker_test',
374 368 conditions={'method': ['POST']})
375 369 m.connect('admin_issuetracker_delete',
376 370 '/settings/issue-tracker/delete',
377 371 action='settings_issuetracker_delete',
378 372 conditions={'method': ['DELETE']})
379 373
380 374 m.connect('admin_settings_email', '/settings/email',
381 375 action='settings_email_update',
382 376 conditions={'method': ['POST']})
383 377 m.connect('admin_settings_email', '/settings/email',
384 378 action='settings_email', conditions={'method': ['GET']})
385 379
386 380 m.connect('admin_settings_hooks', '/settings/hooks',
387 381 action='settings_hooks_update',
388 382 conditions={'method': ['POST', 'DELETE']})
389 383 m.connect('admin_settings_hooks', '/settings/hooks',
390 384 action='settings_hooks', conditions={'method': ['GET']})
391 385
392 386 m.connect('admin_settings_search', '/settings/search',
393 387 action='settings_search', conditions={'method': ['GET']})
394 388
395 389 m.connect('admin_settings_supervisor', '/settings/supervisor',
396 390 action='settings_supervisor', conditions={'method': ['GET']})
397 391 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
398 392 action='settings_supervisor_log', conditions={'method': ['GET']})
399 393
400 394 m.connect('admin_settings_labs', '/settings/labs',
401 395 action='settings_labs_update',
402 396 conditions={'method': ['POST']})
403 397 m.connect('admin_settings_labs', '/settings/labs',
404 398 action='settings_labs', conditions={'method': ['GET']})
405 399
406 400 # ADMIN MY ACCOUNT
407 401 with rmap.submapper(path_prefix=ADMIN_PREFIX,
408 402 controller='admin/my_account') as m:
409 403
410 404 # NOTE(marcink): this needs to be kept for password force flag to be
411 405 # handled in pylons controllers, remove after full migration to pyramid
412 406 m.connect('my_account_password', '/my_account/password',
413 407 action='my_account_password', conditions={'method': ['GET']})
414 408
415 409 #==========================================================================
416 410 # REPOSITORY ROUTES
417 411 #==========================================================================
418 412
419 413 # repo edit options
420 414 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
421 415 controller='admin/repos', action='edit_fields',
422 416 conditions={'method': ['GET'], 'function': check_repo},
423 417 requirements=URL_NAME_REQUIREMENTS)
424 418 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
425 419 controller='admin/repos', action='create_repo_field',
426 420 conditions={'method': ['PUT'], 'function': check_repo},
427 421 requirements=URL_NAME_REQUIREMENTS)
428 422 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
429 423 controller='admin/repos', action='delete_repo_field',
430 424 conditions={'method': ['DELETE'], 'function': check_repo},
431 425 requirements=URL_NAME_REQUIREMENTS)
432 426
433 427 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
434 428 controller='admin/repos', action='toggle_locking',
435 429 conditions={'method': ['GET'], 'function': check_repo},
436 430 requirements=URL_NAME_REQUIREMENTS)
437 431
438 432 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
439 433 controller='admin/repos', action='edit_remote_form',
440 434 conditions={'method': ['GET'], 'function': check_repo},
441 435 requirements=URL_NAME_REQUIREMENTS)
442 436 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
443 437 controller='admin/repos', action='edit_remote',
444 438 conditions={'method': ['PUT'], 'function': check_repo},
445 439 requirements=URL_NAME_REQUIREMENTS)
446 440
447 441 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
448 442 controller='admin/repos', action='edit_statistics_form',
449 443 conditions={'method': ['GET'], 'function': check_repo},
450 444 requirements=URL_NAME_REQUIREMENTS)
451 445 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
452 446 controller='admin/repos', action='edit_statistics',
453 447 conditions={'method': ['PUT'], 'function': check_repo},
454 448 requirements=URL_NAME_REQUIREMENTS)
455 449 rmap.connect('repo_settings_issuetracker',
456 450 '/{repo_name}/settings/issue-tracker',
457 451 controller='admin/repos', action='repo_issuetracker',
458 452 conditions={'method': ['GET'], 'function': check_repo},
459 453 requirements=URL_NAME_REQUIREMENTS)
460 454 rmap.connect('repo_issuetracker_test',
461 455 '/{repo_name}/settings/issue-tracker/test',
462 456 controller='admin/repos', action='repo_issuetracker_test',
463 457 conditions={'method': ['POST'], 'function': check_repo},
464 458 requirements=URL_NAME_REQUIREMENTS)
465 459 rmap.connect('repo_issuetracker_delete',
466 460 '/{repo_name}/settings/issue-tracker/delete',
467 461 controller='admin/repos', action='repo_issuetracker_delete',
468 462 conditions={'method': ['DELETE'], 'function': check_repo},
469 463 requirements=URL_NAME_REQUIREMENTS)
470 464 rmap.connect('repo_issuetracker_save',
471 465 '/{repo_name}/settings/issue-tracker/save',
472 466 controller='admin/repos', action='repo_issuetracker_save',
473 467 conditions={'method': ['POST'], 'function': check_repo},
474 468 requirements=URL_NAME_REQUIREMENTS)
475 469 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
476 470 controller='admin/repos', action='repo_settings_vcs_update',
477 471 conditions={'method': ['POST'], 'function': check_repo},
478 472 requirements=URL_NAME_REQUIREMENTS)
479 473 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
480 474 controller='admin/repos', action='repo_settings_vcs',
481 475 conditions={'method': ['GET'], 'function': check_repo},
482 476 requirements=URL_NAME_REQUIREMENTS)
483 477 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
484 478 controller='admin/repos', action='repo_delete_svn_pattern',
485 479 conditions={'method': ['DELETE'], 'function': check_repo},
486 480 requirements=URL_NAME_REQUIREMENTS)
487 481 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
488 482 controller='admin/repos', action='repo_settings_pullrequest',
489 483 conditions={'method': ['GET', 'POST'], 'function': check_repo},
490 484 requirements=URL_NAME_REQUIREMENTS)
491 485
492 486 return rmap
@@ -1,448 +1,419 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 User Groups crud controller for pylons
23 23 """
24 24
25 25 import logging
26 26 import formencode
27 27
28 28 import peppercorn
29 29 from formencode import htmlfill
30 30 from pylons import request, tmpl_context as c, url, config
31 31 from pylons.controllers.util import redirect
32 32 from pylons.i18n.translation import _
33 33
34 34 from sqlalchemy.orm import joinedload
35 35
36 36 from rhodecode.lib import auth
37 37 from rhodecode.lib import helpers as h
38 38 from rhodecode.lib import audit_logger
39 39 from rhodecode.lib.ext_json import json
40 40 from rhodecode.lib.exceptions import UserGroupAssignedException,\
41 41 RepoGroupAssignmentError
42 42 from rhodecode.lib.utils import jsonify
43 43 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
44 44 from rhodecode.lib.auth import (
45 45 LoginRequired, NotAnonymous, HasUserGroupPermissionAnyDecorator,
46 46 HasPermissionAnyDecorator, XHRRequired)
47 47 from rhodecode.lib.base import BaseController, render
48 48 from rhodecode.model.permission import PermissionModel
49 49 from rhodecode.model.scm import UserGroupList
50 50 from rhodecode.model.user_group import UserGroupModel
51 51 from rhodecode.model.db import (
52 52 User, UserGroup, UserGroupRepoToPerm, UserGroupRepoGroupToPerm)
53 53 from rhodecode.model.forms import (
54 54 UserGroupForm, UserGroupPermsForm, UserIndividualPermissionsForm,
55 55 UserPermissionsForm)
56 56 from rhodecode.model.meta import Session
57 57
58 58
59 59 log = logging.getLogger(__name__)
60 60
61 61
62 62 class UserGroupsController(BaseController):
63 63 """REST Controller styled on the Atom Publishing Protocol"""
64 64
65 65 @LoginRequired()
66 66 def __before__(self):
67 67 super(UserGroupsController, self).__before__()
68 68 c.available_permissions = config['available_permissions']
69 69 PermissionModel().set_global_permission_choices(c, gettext_translator=_)
70 70
71 71 def __load_data(self, user_group_id):
72 72 c.group_members_obj = [x.user for x in c.user_group.members]
73 73 c.group_members_obj.sort(key=lambda u: u.username.lower())
74 74 c.group_members = [(x.user_id, x.username) for x in c.group_members_obj]
75 75
76 76 def __load_defaults(self, user_group_id):
77 77 """
78 78 Load defaults settings for edit, and update
79 79
80 80 :param user_group_id:
81 81 """
82 82 user_group = UserGroup.get_or_404(user_group_id)
83 83 data = user_group.get_dict()
84 84 # fill owner
85 85 if user_group.user:
86 86 data.update({'user': user_group.user.username})
87 87 else:
88 88 replacement_user = User.get_first_super_admin().username
89 89 data.update({'user': replacement_user})
90 90 return data
91 91
92 92 def _revoke_perms_on_yourself(self, form_result):
93 93 _updates = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
94 94 form_result['perm_updates'])
95 95 _additions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
96 96 form_result['perm_additions'])
97 97 _deletions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
98 98 form_result['perm_deletions'])
99 99 admin_perm = 'usergroup.admin'
100 100 if _updates and _updates[0][1] != admin_perm or \
101 101 _additions and _additions[0][1] != admin_perm or \
102 102 _deletions and _deletions[0][1] != admin_perm:
103 103 return True
104 104 return False
105 105
106 106 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
107 107 @auth.CSRFRequired()
108 108 def create(self):
109 109
110 110 users_group_form = UserGroupForm()()
111 111 try:
112 112 form_result = users_group_form.to_python(dict(request.POST))
113 113 user_group = UserGroupModel().create(
114 114 name=form_result['users_group_name'],
115 115 description=form_result['user_group_description'],
116 116 owner=c.rhodecode_user.user_id,
117 117 active=form_result['users_group_active'])
118 118 Session().flush()
119 119 creation_data = user_group.get_api_data()
120 120 user_group_name = form_result['users_group_name']
121 121
122 122 audit_logger.store_web(
123 123 'user_group.create', action_data={'data': creation_data},
124 124 user=c.rhodecode_user)
125 125
126 126 user_group_link = h.link_to(
127 127 h.escape(user_group_name),
128 128 url('edit_users_group', user_group_id=user_group.users_group_id))
129 129 h.flash(h.literal(_('Created user group %(user_group_link)s')
130 130 % {'user_group_link': user_group_link}),
131 131 category='success')
132 132 Session().commit()
133 133 except formencode.Invalid as errors:
134 134 return htmlfill.render(
135 135 render('admin/user_groups/user_group_add.mako'),
136 136 defaults=errors.value,
137 137 errors=errors.error_dict or {},
138 138 prefix_error=False,
139 139 encoding="UTF-8",
140 140 force_defaults=False)
141 141 except Exception:
142 142 log.exception("Exception creating user group")
143 143 h.flash(_('Error occurred during creation of user group %s') \
144 144 % request.POST.get('users_group_name'), category='error')
145 145
146 146 return redirect(
147 147 url('edit_users_group', user_group_id=user_group.users_group_id))
148 148
149 149 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
150 150 def new(self):
151 151 """GET /user_groups/new: Form to create a new item"""
152 152 # url('new_users_group')
153 153 return render('admin/user_groups/user_group_add.mako')
154 154
155 155 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
156 156 @auth.CSRFRequired()
157 157 def update(self, user_group_id):
158 158
159 159 user_group_id = safe_int(user_group_id)
160 160 c.user_group = UserGroup.get_or_404(user_group_id)
161 161 c.active = 'settings'
162 162 self.__load_data(user_group_id)
163 163
164 164 users_group_form = UserGroupForm(
165 165 edit=True, old_data=c.user_group.get_dict(), allow_disabled=True)()
166 166
167 167 old_values = c.user_group.get_api_data()
168 168 try:
169 169 form_result = users_group_form.to_python(request.POST)
170 170 pstruct = peppercorn.parse(request.POST.items())
171 171 form_result['users_group_members'] = pstruct['user_group_members']
172 172
173 173 user_group, added_members, removed_members = \
174 174 UserGroupModel().update(c.user_group, form_result)
175 175 updated_user_group = form_result['users_group_name']
176 176
177 177 audit_logger.store_web(
178 178 'user_group.edit', action_data={'old_data': old_values},
179 179 user=c.rhodecode_user)
180 180
181 181 # TODO(marcink): use added/removed to set user_group.edit.member.add
182 182
183 183 h.flash(_('Updated user group %s') % updated_user_group,
184 184 category='success')
185 185 Session().commit()
186 186 except formencode.Invalid as errors:
187 187 defaults = errors.value
188 188 e = errors.error_dict or {}
189 189
190 190 return htmlfill.render(
191 191 render('admin/user_groups/user_group_edit.mako'),
192 192 defaults=defaults,
193 193 errors=e,
194 194 prefix_error=False,
195 195 encoding="UTF-8",
196 196 force_defaults=False)
197 197 except Exception:
198 198 log.exception("Exception during update of user group")
199 199 h.flash(_('Error occurred during update of user group %s')
200 200 % request.POST.get('users_group_name'), category='error')
201 201
202 202 return redirect(url('edit_users_group', user_group_id=user_group_id))
203 203
204 204 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
205 205 @auth.CSRFRequired()
206 206 def delete(self, user_group_id):
207 207 user_group_id = safe_int(user_group_id)
208 208 c.user_group = UserGroup.get_or_404(user_group_id)
209 209 force = str2bool(request.POST.get('force'))
210 210
211 211 old_values = c.user_group.get_api_data()
212 212 try:
213 213 UserGroupModel().delete(c.user_group, force=force)
214 214 audit_logger.store_web(
215 215 'user.delete', action_data={'old_data': old_values},
216 216 user=c.rhodecode_user)
217 217 Session().commit()
218 218 h.flash(_('Successfully deleted user group'), category='success')
219 219 except UserGroupAssignedException as e:
220 220 h.flash(str(e), category='error')
221 221 except Exception:
222 222 log.exception("Exception during deletion of user group")
223 223 h.flash(_('An error occurred during deletion of user group'),
224 224 category='error')
225 225 return redirect(url('users_groups'))
226 226
227 227 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
228 228 def edit(self, user_group_id):
229 229 """GET /user_groups/user_group_id/edit: Form to edit an existing item"""
230 230 # url('edit_users_group', user_group_id=ID)
231 231
232 232 user_group_id = safe_int(user_group_id)
233 233 c.user_group = UserGroup.get_or_404(user_group_id)
234 234 c.active = 'settings'
235 235 self.__load_data(user_group_id)
236 236
237 237 defaults = self.__load_defaults(user_group_id)
238 238
239 239 return htmlfill.render(
240 240 render('admin/user_groups/user_group_edit.mako'),
241 241 defaults=defaults,
242 242 encoding="UTF-8",
243 243 force_defaults=False
244 244 )
245 245
246 246 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
247 247 def edit_perms(self, user_group_id):
248 248 user_group_id = safe_int(user_group_id)
249 249 c.user_group = UserGroup.get_or_404(user_group_id)
250 250 c.active = 'perms'
251 251
252 252 defaults = {}
253 253 # fill user group users
254 254 for p in c.user_group.user_user_group_to_perm:
255 255 defaults.update({'u_perm_%s' % p.user.user_id:
256 256 p.permission.permission_name})
257 257
258 258 for p in c.user_group.user_group_user_group_to_perm:
259 259 defaults.update({'g_perm_%s' % p.user_group.users_group_id:
260 260 p.permission.permission_name})
261 261
262 262 return htmlfill.render(
263 263 render('admin/user_groups/user_group_edit.mako'),
264 264 defaults=defaults,
265 265 encoding="UTF-8",
266 266 force_defaults=False
267 267 )
268 268
269 269 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
270 270 @auth.CSRFRequired()
271 271 def update_perms(self, user_group_id):
272 272 """
273 273 grant permission for given usergroup
274 274
275 275 :param user_group_id:
276 276 """
277 277 user_group_id = safe_int(user_group_id)
278 278 c.user_group = UserGroup.get_or_404(user_group_id)
279 279 form = UserGroupPermsForm()().to_python(request.POST)
280 280
281 281 if not c.rhodecode_user.is_admin:
282 282 if self._revoke_perms_on_yourself(form):
283 283 msg = _('Cannot change permission for yourself as admin')
284 284 h.flash(msg, category='warning')
285 285 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
286 286
287 287 try:
288 288 UserGroupModel().update_permissions(user_group_id,
289 289 form['perm_additions'], form['perm_updates'], form['perm_deletions'])
290 290 except RepoGroupAssignmentError:
291 291 h.flash(_('Target group cannot be the same'), category='error')
292 292 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
293 293
294 294 # TODO(marcink): implement global permissions
295 295 # audit_log.store_web('user_group.edit.permissions')
296 296 Session().commit()
297 297 h.flash(_('User Group permissions updated'), category='success')
298 298 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
299 299
300 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
301 def edit_perms_summary(self, user_group_id):
302 user_group_id = safe_int(user_group_id)
303 c.user_group = UserGroup.get_or_404(user_group_id)
304 c.active = 'perms_summary'
305 permissions = {
306 'repositories': {},
307 'repositories_groups': {},
308 }
309 ugroup_repo_perms = UserGroupRepoToPerm.query()\
310 .options(joinedload(UserGroupRepoToPerm.permission))\
311 .options(joinedload(UserGroupRepoToPerm.repository))\
312 .filter(UserGroupRepoToPerm.users_group_id == user_group_id)\
313 .all()
314 300
315 for gr in ugroup_repo_perms:
316 permissions['repositories'][gr.repository.repo_name] \
317 = gr.permission.permission_name
318
319 ugroup_group_perms = UserGroupRepoGroupToPerm.query()\
320 .options(joinedload(UserGroupRepoGroupToPerm.permission))\
321 .options(joinedload(UserGroupRepoGroupToPerm.group))\
322 .filter(UserGroupRepoGroupToPerm.users_group_id == user_group_id)\
323 .all()
324
325 for gr in ugroup_group_perms:
326 permissions['repositories_groups'][gr.group.group_name] \
327 = gr.permission.permission_name
328 c.permissions = permissions
329 return render('admin/user_groups/user_group_edit.mako')
330 301
331 302 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
332 303 def edit_global_perms(self, user_group_id):
333 304 user_group_id = safe_int(user_group_id)
334 305 c.user_group = UserGroup.get_or_404(user_group_id)
335 306 c.active = 'global_perms'
336 307
337 308 c.default_user = User.get_default_user()
338 309 defaults = c.user_group.get_dict()
339 310 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
340 311 defaults.update(c.user_group.get_default_perms())
341 312
342 313 return htmlfill.render(
343 314 render('admin/user_groups/user_group_edit.mako'),
344 315 defaults=defaults,
345 316 encoding="UTF-8",
346 317 force_defaults=False
347 318 )
348 319
349 320 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
350 321 @auth.CSRFRequired()
351 322 def update_global_perms(self, user_group_id):
352 323 user_group_id = safe_int(user_group_id)
353 324 user_group = UserGroup.get_or_404(user_group_id)
354 325 c.active = 'global_perms'
355 326
356 327 try:
357 328 # first stage that verifies the checkbox
358 329 _form = UserIndividualPermissionsForm()
359 330 form_result = _form.to_python(dict(request.POST))
360 331 inherit_perms = form_result['inherit_default_permissions']
361 332 user_group.inherit_default_permissions = inherit_perms
362 333 Session().add(user_group)
363 334
364 335 if not inherit_perms:
365 336 # only update the individual ones if we un check the flag
366 337 _form = UserPermissionsForm(
367 338 [x[0] for x in c.repo_create_choices],
368 339 [x[0] for x in c.repo_create_on_write_choices],
369 340 [x[0] for x in c.repo_group_create_choices],
370 341 [x[0] for x in c.user_group_create_choices],
371 342 [x[0] for x in c.fork_choices],
372 343 [x[0] for x in c.inherit_default_permission_choices])()
373 344
374 345 form_result = _form.to_python(dict(request.POST))
375 346 form_result.update({'perm_user_group_id': user_group.users_group_id})
376 347
377 348 PermissionModel().update_user_group_permissions(form_result)
378 349
379 350 Session().commit()
380 351 h.flash(_('User Group global permissions updated successfully'),
381 352 category='success')
382 353
383 354 except formencode.Invalid as errors:
384 355 defaults = errors.value
385 356 c.user_group = user_group
386 357 return htmlfill.render(
387 358 render('admin/user_groups/user_group_edit.mako'),
388 359 defaults=defaults,
389 360 errors=errors.error_dict or {},
390 361 prefix_error=False,
391 362 encoding="UTF-8",
392 363 force_defaults=False)
393 364 except Exception:
394 365 log.exception("Exception during permissions saving")
395 366 h.flash(_('An error occurred during permissions saving'),
396 367 category='error')
397 368
398 369 return redirect(url('edit_user_group_global_perms', user_group_id=user_group_id))
399 370
400 371 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
401 372 def edit_advanced(self, user_group_id):
402 373 user_group_id = safe_int(user_group_id)
403 374 c.user_group = UserGroup.get_or_404(user_group_id)
404 375 c.active = 'advanced'
405 376 c.group_members_obj = sorted(
406 377 (x.user for x in c.user_group.members),
407 378 key=lambda u: u.username.lower())
408 379
409 380 c.group_to_repos = sorted(
410 381 (x.repository for x in c.user_group.users_group_repo_to_perm),
411 382 key=lambda u: u.repo_name.lower())
412 383
413 384 c.group_to_repo_groups = sorted(
414 385 (x.group for x in c.user_group.users_group_repo_group_to_perm),
415 386 key=lambda u: u.group_name.lower())
416 387
417 388 return render('admin/user_groups/user_group_edit.mako')
418 389
419 390 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
420 391 def edit_advanced_set_synchronization(self, user_group_id):
421 392 user_group_id = safe_int(user_group_id)
422 393 user_group = UserGroup.get_or_404(user_group_id)
423 394
424 395 existing = user_group.group_data.get('extern_type')
425 396
426 397 if existing:
427 398 new_state = user_group.group_data
428 399 new_state['extern_type'] = None
429 400 else:
430 401 new_state = user_group.group_data
431 402 new_state['extern_type'] = 'manual'
432 403 new_state['extern_type_set_by'] = c.rhodecode_user.username
433 404
434 405 try:
435 406 user_group.group_data = new_state
436 407 Session().add(user_group)
437 408 Session().commit()
438 409
439 410 h.flash(_('User Group synchronization updated successfully'),
440 411 category='success')
441 412 except Exception:
442 413 log.exception("Exception during sync settings saving")
443 414 h.flash(_('An error occurred during synchronization update'),
444 415 category='error')
445 416
446 417 return redirect(
447 418 url('edit_user_group_advanced', user_group_id=user_group_id))
448 419
@@ -1,503 +1,491 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Users crud controller for pylons
23 23 """
24 24
25 25 import logging
26 26 import formencode
27 27
28 28 from formencode import htmlfill
29 29 from pylons import request, tmpl_context as c, url, config
30 30 from pylons.controllers.util import redirect
31 31 from pylons.i18n.translation import _
32 32
33 33 from rhodecode.authentication.plugins import auth_rhodecode
34 34
35 35 from rhodecode.lib import helpers as h
36 36 from rhodecode.lib import auth
37 37 from rhodecode.lib import audit_logger
38 38 from rhodecode.lib.auth import (
39 39 LoginRequired, HasPermissionAllDecorator, AuthUser)
40 40 from rhodecode.lib.base import BaseController, render
41 41 from rhodecode.lib.exceptions import (
42 42 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
43 43 UserOwnsUserGroupsException, UserCreationError)
44 44 from rhodecode.lib.utils2 import safe_int, AttributeDict
45 45
46 46 from rhodecode.model.db import (
47 47 PullRequestReviewers, User, UserEmailMap, UserIpMap, RepoGroup)
48 48 from rhodecode.model.forms import (
49 49 UserForm, UserPermissionsForm, UserIndividualPermissionsForm)
50 50 from rhodecode.model.repo_group import RepoGroupModel
51 51 from rhodecode.model.user import UserModel
52 52 from rhodecode.model.meta import Session
53 53 from rhodecode.model.permission import PermissionModel
54 54
55 55 log = logging.getLogger(__name__)
56 56
57 57
58 58 class UsersController(BaseController):
59 59 """REST Controller styled on the Atom Publishing Protocol"""
60 60
61 61 @LoginRequired()
62 62 def __before__(self):
63 63 super(UsersController, self).__before__()
64 64 c.available_permissions = config['available_permissions']
65 65 c.allowed_languages = [
66 66 ('en', 'English (en)'),
67 67 ('de', 'German (de)'),
68 68 ('fr', 'French (fr)'),
69 69 ('it', 'Italian (it)'),
70 70 ('ja', 'Japanese (ja)'),
71 71 ('pl', 'Polish (pl)'),
72 72 ('pt', 'Portuguese (pt)'),
73 73 ('ru', 'Russian (ru)'),
74 74 ('zh', 'Chinese (zh)'),
75 75 ]
76 76 PermissionModel().set_global_permission_choices(c, gettext_translator=_)
77 77
78 78 def _get_personal_repo_group_template_vars(self):
79 79 DummyUser = AttributeDict({
80 80 'username': '${username}',
81 81 'user_id': '${user_id}',
82 82 })
83 83 c.default_create_repo_group = RepoGroupModel() \
84 84 .get_default_create_personal_repo_group()
85 85 c.personal_repo_group_name = RepoGroupModel() \
86 86 .get_personal_group_name(DummyUser)
87 87
88 88 @HasPermissionAllDecorator('hg.admin')
89 89 @auth.CSRFRequired()
90 90 def create(self):
91 91 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
92 92 user_model = UserModel()
93 93 user_form = UserForm()()
94 94 try:
95 95 form_result = user_form.to_python(dict(request.POST))
96 96 user = user_model.create(form_result)
97 97 Session().flush()
98 98 creation_data = user.get_api_data()
99 99 username = form_result['username']
100 100
101 101 audit_logger.store_web(
102 102 'user.create', action_data={'data': creation_data},
103 103 user=c.rhodecode_user)
104 104
105 105 user_link = h.link_to(h.escape(username),
106 106 url('edit_user',
107 107 user_id=user.user_id))
108 108 h.flash(h.literal(_('Created user %(user_link)s')
109 109 % {'user_link': user_link}), category='success')
110 110 Session().commit()
111 111 except formencode.Invalid as errors:
112 112 self._get_personal_repo_group_template_vars()
113 113 return htmlfill.render(
114 114 render('admin/users/user_add.mako'),
115 115 defaults=errors.value,
116 116 errors=errors.error_dict or {},
117 117 prefix_error=False,
118 118 encoding="UTF-8",
119 119 force_defaults=False)
120 120 except UserCreationError as e:
121 121 h.flash(e, 'error')
122 122 except Exception:
123 123 log.exception("Exception creation of user")
124 124 h.flash(_('Error occurred during creation of user %s')
125 125 % request.POST.get('username'), category='error')
126 126 return redirect(h.route_path('users'))
127 127
128 128 @HasPermissionAllDecorator('hg.admin')
129 129 def new(self):
130 130 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
131 131 self._get_personal_repo_group_template_vars()
132 132 return render('admin/users/user_add.mako')
133 133
134 134 @HasPermissionAllDecorator('hg.admin')
135 135 @auth.CSRFRequired()
136 136 def update(self, user_id):
137 137
138 138 user_id = safe_int(user_id)
139 139 c.user = User.get_or_404(user_id)
140 140 c.active = 'profile'
141 141 c.extern_type = c.user.extern_type
142 142 c.extern_name = c.user.extern_name
143 143 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
144 144 available_languages = [x[0] for x in c.allowed_languages]
145 145 _form = UserForm(edit=True, available_languages=available_languages,
146 146 old_data={'user_id': user_id,
147 147 'email': c.user.email})()
148 148 form_result = {}
149 149 old_values = c.user.get_api_data()
150 150 try:
151 151 form_result = _form.to_python(dict(request.POST))
152 152 skip_attrs = ['extern_type', 'extern_name']
153 153 # TODO: plugin should define if username can be updated
154 154 if c.extern_type != "rhodecode":
155 155 # forbid updating username for external accounts
156 156 skip_attrs.append('username')
157 157
158 158 UserModel().update_user(
159 159 user_id, skip_attrs=skip_attrs, **form_result)
160 160
161 161 audit_logger.store_web(
162 162 'user.edit', action_data={'old_data': old_values},
163 163 user=c.rhodecode_user)
164 164
165 165 Session().commit()
166 166 h.flash(_('User updated successfully'), category='success')
167 167 except formencode.Invalid as errors:
168 168 defaults = errors.value
169 169 e = errors.error_dict or {}
170 170
171 171 return htmlfill.render(
172 172 render('admin/users/user_edit.mako'),
173 173 defaults=defaults,
174 174 errors=e,
175 175 prefix_error=False,
176 176 encoding="UTF-8",
177 177 force_defaults=False)
178 178 except UserCreationError as e:
179 179 h.flash(e, 'error')
180 180 except Exception:
181 181 log.exception("Exception updating user")
182 182 h.flash(_('Error occurred during update of user %s')
183 183 % form_result.get('username'), category='error')
184 184 return redirect(url('edit_user', user_id=user_id))
185 185
186 186 @HasPermissionAllDecorator('hg.admin')
187 187 @auth.CSRFRequired()
188 188 def delete(self, user_id):
189 189 user_id = safe_int(user_id)
190 190 c.user = User.get_or_404(user_id)
191 191
192 192 _repos = c.user.repositories
193 193 _repo_groups = c.user.repository_groups
194 194 _user_groups = c.user.user_groups
195 195
196 196 handle_repos = None
197 197 handle_repo_groups = None
198 198 handle_user_groups = None
199 199 # dummy call for flash of handle
200 200 set_handle_flash_repos = lambda: None
201 201 set_handle_flash_repo_groups = lambda: None
202 202 set_handle_flash_user_groups = lambda: None
203 203
204 204 if _repos and request.POST.get('user_repos'):
205 205 do = request.POST['user_repos']
206 206 if do == 'detach':
207 207 handle_repos = 'detach'
208 208 set_handle_flash_repos = lambda: h.flash(
209 209 _('Detached %s repositories') % len(_repos),
210 210 category='success')
211 211 elif do == 'delete':
212 212 handle_repos = 'delete'
213 213 set_handle_flash_repos = lambda: h.flash(
214 214 _('Deleted %s repositories') % len(_repos),
215 215 category='success')
216 216
217 217 if _repo_groups and request.POST.get('user_repo_groups'):
218 218 do = request.POST['user_repo_groups']
219 219 if do == 'detach':
220 220 handle_repo_groups = 'detach'
221 221 set_handle_flash_repo_groups = lambda: h.flash(
222 222 _('Detached %s repository groups') % len(_repo_groups),
223 223 category='success')
224 224 elif do == 'delete':
225 225 handle_repo_groups = 'delete'
226 226 set_handle_flash_repo_groups = lambda: h.flash(
227 227 _('Deleted %s repository groups') % len(_repo_groups),
228 228 category='success')
229 229
230 230 if _user_groups and request.POST.get('user_user_groups'):
231 231 do = request.POST['user_user_groups']
232 232 if do == 'detach':
233 233 handle_user_groups = 'detach'
234 234 set_handle_flash_user_groups = lambda: h.flash(
235 235 _('Detached %s user groups') % len(_user_groups),
236 236 category='success')
237 237 elif do == 'delete':
238 238 handle_user_groups = 'delete'
239 239 set_handle_flash_user_groups = lambda: h.flash(
240 240 _('Deleted %s user groups') % len(_user_groups),
241 241 category='success')
242 242
243 243 old_values = c.user.get_api_data()
244 244 try:
245 245 UserModel().delete(c.user, handle_repos=handle_repos,
246 246 handle_repo_groups=handle_repo_groups,
247 247 handle_user_groups=handle_user_groups)
248 248
249 249 audit_logger.store_web(
250 250 'user.delete', action_data={'old_data': old_values},
251 251 user=c.rhodecode_user)
252 252
253 253 Session().commit()
254 254 set_handle_flash_repos()
255 255 set_handle_flash_repo_groups()
256 256 set_handle_flash_user_groups()
257 257 h.flash(_('Successfully deleted user'), category='success')
258 258 except (UserOwnsReposException, UserOwnsRepoGroupsException,
259 259 UserOwnsUserGroupsException, DefaultUserException) as e:
260 260 h.flash(e, category='warning')
261 261 except Exception:
262 262 log.exception("Exception during deletion of user")
263 263 h.flash(_('An error occurred during deletion of user'),
264 264 category='error')
265 265 return redirect(h.route_path('users'))
266 266
267 267 @HasPermissionAllDecorator('hg.admin')
268 268 @auth.CSRFRequired()
269 269 def reset_password(self, user_id):
270 270 """
271 271 toggle reset password flag for this user
272 272 """
273 273 user_id = safe_int(user_id)
274 274 c.user = User.get_or_404(user_id)
275 275 try:
276 276 old_value = c.user.user_data.get('force_password_change')
277 277 c.user.update_userdata(force_password_change=not old_value)
278 278
279 279 if old_value:
280 280 msg = _('Force password change disabled for user')
281 281 audit_logger.store_web(
282 282 'user.edit.password_reset.disabled',
283 283 user=c.rhodecode_user)
284 284 else:
285 285 msg = _('Force password change enabled for user')
286 286 audit_logger.store_web(
287 287 'user.edit.password_reset.enabled',
288 288 user=c.rhodecode_user)
289 289
290 290 Session().commit()
291 291 h.flash(msg, category='success')
292 292 except Exception:
293 293 log.exception("Exception during password reset for user")
294 294 h.flash(_('An error occurred during password reset for user'),
295 295 category='error')
296 296
297 297 return redirect(url('edit_user_advanced', user_id=user_id))
298 298
299 299 @HasPermissionAllDecorator('hg.admin')
300 300 @auth.CSRFRequired()
301 301 def create_personal_repo_group(self, user_id):
302 302 """
303 303 Create personal repository group for this user
304 304 """
305 305 from rhodecode.model.repo_group import RepoGroupModel
306 306
307 307 user_id = safe_int(user_id)
308 308 c.user = User.get_or_404(user_id)
309 309 personal_repo_group = RepoGroup.get_user_personal_repo_group(
310 310 c.user.user_id)
311 311 if personal_repo_group:
312 312 return redirect(url('edit_user_advanced', user_id=user_id))
313 313
314 314 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
315 315 c.user)
316 316 named_personal_group = RepoGroup.get_by_group_name(
317 317 personal_repo_group_name)
318 318 try:
319 319
320 320 if named_personal_group and named_personal_group.user_id == c.user.user_id:
321 321 # migrate the same named group, and mark it as personal
322 322 named_personal_group.personal = True
323 323 Session().add(named_personal_group)
324 324 Session().commit()
325 325 msg = _('Linked repository group `%s` as personal' % (
326 326 personal_repo_group_name,))
327 327 h.flash(msg, category='success')
328 328 elif not named_personal_group:
329 329 RepoGroupModel().create_personal_repo_group(c.user)
330 330
331 331 msg = _('Created repository group `%s`' % (
332 332 personal_repo_group_name,))
333 333 h.flash(msg, category='success')
334 334 else:
335 335 msg = _('Repository group `%s` is already taken' % (
336 336 personal_repo_group_name,))
337 337 h.flash(msg, category='warning')
338 338 except Exception:
339 339 log.exception("Exception during repository group creation")
340 340 msg = _(
341 341 'An error occurred during repository group creation for user')
342 342 h.flash(msg, category='error')
343 343 Session().rollback()
344 344
345 345 return redirect(url('edit_user_advanced', user_id=user_id))
346 346
347 347 @HasPermissionAllDecorator('hg.admin')
348 348 def show(self, user_id):
349 349 """GET /users/user_id: Show a specific item"""
350 350 # url('user', user_id=ID)
351 351 User.get_or_404(-1)
352 352
353 353 @HasPermissionAllDecorator('hg.admin')
354 354 def edit(self, user_id):
355 355 """GET /users/user_id/edit: Form to edit an existing item"""
356 356 # url('edit_user', user_id=ID)
357 357 user_id = safe_int(user_id)
358 358 c.user = User.get_or_404(user_id)
359 359 if c.user.username == User.DEFAULT_USER:
360 360 h.flash(_("You can't edit this user"), category='warning')
361 361 return redirect(h.route_path('users'))
362 362
363 363 c.active = 'profile'
364 364 c.extern_type = c.user.extern_type
365 365 c.extern_name = c.user.extern_name
366 366 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
367 367
368 368 defaults = c.user.get_dict()
369 369 defaults.update({'language': c.user.user_data.get('language')})
370 370 return htmlfill.render(
371 371 render('admin/users/user_edit.mako'),
372 372 defaults=defaults,
373 373 encoding="UTF-8",
374 374 force_defaults=False)
375 375
376 376 @HasPermissionAllDecorator('hg.admin')
377 377 def edit_advanced(self, user_id):
378 378 user_id = safe_int(user_id)
379 379 user = c.user = User.get_or_404(user_id)
380 380 if user.username == User.DEFAULT_USER:
381 381 h.flash(_("You can't edit this user"), category='warning')
382 382 return redirect(h.route_path('users'))
383 383
384 384 c.active = 'advanced'
385 385 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
386 386 c.personal_repo_group_name = RepoGroupModel()\
387 387 .get_personal_group_name(user)
388 388 c.first_admin = User.get_first_super_admin()
389 389 defaults = user.get_dict()
390 390
391 391 # Interim workaround if the user participated on any pull requests as a
392 392 # reviewer.
393 393 has_review = len(user.reviewer_pull_requests)
394 394 c.can_delete_user = not has_review
395 395 c.can_delete_user_message = ''
396 396 inactive_link = h.link_to(
397 397 'inactive', h.url('edit_user', user_id=user_id, anchor='active'))
398 398 if has_review == 1:
399 399 c.can_delete_user_message = h.literal(_(
400 400 'The user participates as reviewer in {} pull request and '
401 401 'cannot be deleted. \nYou can set the user to '
402 402 '"{}" instead of deleting it.').format(
403 403 has_review, inactive_link))
404 404 elif has_review:
405 405 c.can_delete_user_message = h.literal(_(
406 406 'The user participates as reviewer in {} pull requests and '
407 407 'cannot be deleted. \nYou can set the user to '
408 408 '"{}" instead of deleting it.').format(
409 409 has_review, inactive_link))
410 410
411 411 return htmlfill.render(
412 412 render('admin/users/user_edit.mako'),
413 413 defaults=defaults,
414 414 encoding="UTF-8",
415 415 force_defaults=False)
416 416
417 417 @HasPermissionAllDecorator('hg.admin')
418 418 def edit_global_perms(self, user_id):
419 419 user_id = safe_int(user_id)
420 420 c.user = User.get_or_404(user_id)
421 421 if c.user.username == User.DEFAULT_USER:
422 422 h.flash(_("You can't edit this user"), category='warning')
423 423 return redirect(h.route_path('users'))
424 424
425 425 c.active = 'global_perms'
426 426
427 427 c.default_user = User.get_default_user()
428 428 defaults = c.user.get_dict()
429 429 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
430 430 defaults.update(c.default_user.get_default_perms())
431 431 defaults.update(c.user.get_default_perms())
432 432
433 433 return htmlfill.render(
434 434 render('admin/users/user_edit.mako'),
435 435 defaults=defaults,
436 436 encoding="UTF-8",
437 437 force_defaults=False)
438 438
439 439 @HasPermissionAllDecorator('hg.admin')
440 440 @auth.CSRFRequired()
441 441 def update_global_perms(self, user_id):
442 442 user_id = safe_int(user_id)
443 443 user = User.get_or_404(user_id)
444 444 c.active = 'global_perms'
445 445 try:
446 446 # first stage that verifies the checkbox
447 447 _form = UserIndividualPermissionsForm()
448 448 form_result = _form.to_python(dict(request.POST))
449 449 inherit_perms = form_result['inherit_default_permissions']
450 450 user.inherit_default_permissions = inherit_perms
451 451 Session().add(user)
452 452
453 453 if not inherit_perms:
454 454 # only update the individual ones if we un check the flag
455 455 _form = UserPermissionsForm(
456 456 [x[0] for x in c.repo_create_choices],
457 457 [x[0] for x in c.repo_create_on_write_choices],
458 458 [x[0] for x in c.repo_group_create_choices],
459 459 [x[0] for x in c.user_group_create_choices],
460 460 [x[0] for x in c.fork_choices],
461 461 [x[0] for x in c.inherit_default_permission_choices])()
462 462
463 463 form_result = _form.to_python(dict(request.POST))
464 464 form_result.update({'perm_user_id': user.user_id})
465 465
466 466 PermissionModel().update_user_permissions(form_result)
467 467
468 468 # TODO(marcink): implement global permissions
469 469 # audit_log.store_web('user.edit.permissions')
470 470
471 471 Session().commit()
472 472 h.flash(_('User global permissions updated successfully'),
473 473 category='success')
474 474
475 475 except formencode.Invalid as errors:
476 476 defaults = errors.value
477 477 c.user = user
478 478 return htmlfill.render(
479 479 render('admin/users/user_edit.mako'),
480 480 defaults=defaults,
481 481 errors=errors.error_dict or {},
482 482 prefix_error=False,
483 483 encoding="UTF-8",
484 484 force_defaults=False)
485 485 except Exception:
486 486 log.exception("Exception during permissions saving")
487 487 h.flash(_('An error occurred during permissions saving'),
488 488 category='error')
489 489 return redirect(url('edit_user_global_perms', user_id=user_id))
490 490
491 @HasPermissionAllDecorator('hg.admin')
492 def edit_perms_summary(self, user_id):
493 user_id = safe_int(user_id)
494 c.user = User.get_or_404(user_id)
495 if c.user.username == User.DEFAULT_USER:
496 h.flash(_("You can't edit this user"), category='warning')
497 return redirect(h.route_path('users'))
498 491
499 c.active = 'perms_summary'
500 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
501
502 return render('admin/users/user_edit.mako')
503
@@ -1,230 +1,234 b''
1 1
2 2 /******************************************************************************
3 3 * *
4 4 * DO NOT CHANGE THIS FILE MANUALLY *
5 5 * *
6 6 * *
7 7 * This file is automatically generated when the app starts up with *
8 8 * generate_js_files = true *
9 9 * *
10 10 * To add a route here pass jsroute=True to the route definition in the app *
11 11 * *
12 12 ******************************************************************************/
13 13 function registerRCRoutes() {
14 14 // routes registration
15 15 pyroutes.register('new_repo', '/_admin/create_repository', []);
16 16 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
17 17 pyroutes.register('favicon', '/favicon.ico', []);
18 18 pyroutes.register('robots', '/robots.txt', []);
19 19 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
20 20 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
21 21 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
22 22 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
23 23 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
24 24 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
25 25 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/settings/integrations', ['repo_group_name']);
26 26 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
27 27 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
28 28 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
29 29 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
30 30 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
31 31 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
32 32 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
33 33 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
34 34 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
35 35 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
36 36 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
37 37 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
38 38 pyroutes.register('admin_home', '/_admin', []);
39 39 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
40 40 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
41 41 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
42 42 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
43 43 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
44 44 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
45 45 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
46 46 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
47 47 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
48 48 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
49 49 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
50 50 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
51 51 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
52 52 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
53 53 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
54 54 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
55 55 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
56 56 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
57 57 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
58 58 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
59 59 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
60 60 pyroutes.register('users', '/_admin/users', []);
61 61 pyroutes.register('users_data', '/_admin/users_data', []);
62 62 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
63 63 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
64 64 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
65 65 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
66 66 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
67 67 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
68 68 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
69 69 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
70 70 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
71 71 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
72 72 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
73 73 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
74 74 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
75 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
76 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
75 77 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
76 78 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
77 79 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
78 80 pyroutes.register('user_groups', '/_admin/user_groups', []);
79 81 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
80 82 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
83 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
84 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
81 85 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
82 86 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
83 87 pyroutes.register('channelstream_proxy', '/_channelstream', []);
84 88 pyroutes.register('login', '/_admin/login', []);
85 89 pyroutes.register('logout', '/_admin/logout', []);
86 90 pyroutes.register('register', '/_admin/register', []);
87 91 pyroutes.register('reset_password', '/_admin/password_reset', []);
88 92 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
89 93 pyroutes.register('home', '/', []);
90 94 pyroutes.register('user_autocomplete_data', '/_users', []);
91 95 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
92 96 pyroutes.register('repo_list_data', '/_repos', []);
93 97 pyroutes.register('goto_switcher_data', '/_goto_data', []);
94 98 pyroutes.register('journal', '/_admin/journal', []);
95 99 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
96 100 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
97 101 pyroutes.register('journal_public', '/_admin/public_journal', []);
98 102 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
99 103 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
100 104 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
101 105 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
102 106 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
103 107 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
104 108 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
105 109 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
106 110 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
107 111 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
108 112 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
109 113 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
110 114 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
111 115 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
112 116 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
113 117 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
114 118 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
115 119 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
116 120 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
117 121 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
118 122 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
119 123 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
120 124 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
121 125 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
122 126 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
123 127 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
124 128 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
125 129 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
126 130 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
127 131 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
128 132 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
129 133 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
130 134 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
131 135 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
132 136 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
133 137 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
134 138 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
135 139 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
136 140 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
137 141 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
138 142 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
139 143 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
140 144 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
141 145 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
142 146 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
143 147 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
144 148 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
145 149 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
146 150 pyroutes.register('repo_changelog_elements', '/%(repo_name)s/changelog_elements', ['repo_name']);
147 151 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
148 152 pyroutes.register('repo_compare', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
149 153 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
150 154 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
151 155 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
152 156 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
153 157 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
154 158 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
155 159 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
156 160 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
157 161 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
158 162 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
159 163 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
160 164 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
161 165 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
162 166 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
163 167 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
164 168 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
165 169 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
166 170 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
167 171 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']);
168 172 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
169 173 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
170 174 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
171 175 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
172 176 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
173 177 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
174 178 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
175 179 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
176 180 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
177 181 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
178 182 pyroutes.register('repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
179 183 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
180 184 pyroutes.register('strip', '/%(repo_name)s/settings/strip', ['repo_name']);
181 185 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
182 186 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
183 187 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);
184 188 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
185 189 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
186 190 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
187 191 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
188 192 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
189 193 pyroutes.register('search', '/_admin/search', []);
190 194 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
191 195 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
192 196 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
193 197 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
194 198 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
195 199 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
196 200 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
197 201 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
198 202 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
199 203 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
200 204 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
201 205 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
202 206 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
203 207 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
204 208 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
205 209 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
206 210 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
207 211 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
208 212 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
209 213 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
210 214 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
211 215 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
212 216 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
213 217 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
214 218 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
215 219 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
216 220 pyroutes.register('gists_show', '/_admin/gists', []);
217 221 pyroutes.register('gists_new', '/_admin/gists/new', []);
218 222 pyroutes.register('gists_create', '/_admin/gists/create', []);
219 223 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
220 224 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
221 225 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
222 226 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
223 227 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
224 228 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
225 229 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
226 230 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
227 231 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
228 232 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
229 233 pyroutes.register('apiv2', '/_admin/api', []);
230 234 }
@@ -1,46 +1,46 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.mako"/>
3 3
4 4 <%def name="title()">
5 5 ${_('%s user group settings') % c.user_group.users_group_name}
6 6 %if c.rhodecode_name:
7 7 &middot; ${h.branding(c.rhodecode_name)}
8 8 %endif
9 9 </%def>
10 10
11 11 <%def name="breadcrumbs_links()">
12 12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 13 &raquo;
14 14 ${h.link_to(_('User Groups'),h.url('users_groups'))}
15 15 &raquo;
16 16 ${c.user_group.users_group_name}
17 17 </%def>
18 18
19 19 <%def name="menu_bar_nav()">
20 20 ${self.menu_items(active='admin')}
21 21 </%def>
22 22
23 23 <%def name="main()">
24 24 <div class="box">
25 25 <div class="title">
26 26 ${self.breadcrumbs()}
27 27 </div>
28 28
29 29 ##main
30 30 <div class="sidebar-col-wrapper">
31 31 <div class="sidebar">
32 32 <ul class="nav nav-pills nav-stacked">
33 33 <li class="${'active' if c.active=='settings' else ''}"><a href="${h.url('edit_users_group', user_group_id=c.user_group.users_group_id)}">${_('Settings')}</a></li>
34 34 <li class="${'active' if c.active=='perms' else ''}"><a href="${h.url('edit_user_group_perms', user_group_id=c.user_group.users_group_id)}">${_('Permissions')}</a></li>
35 35 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.url('edit_user_group_advanced', user_group_id=c.user_group.users_group_id)}">${_('Advanced')}</a></li>
36 36 <li class="${'active' if c.active=='global_perms' else ''}"><a href="${h.url('edit_user_group_global_perms', user_group_id=c.user_group.users_group_id)}">${_('Global permissions')}</a></li>
37 <li class="${'active' if c.active=='perms_summary' else ''}"><a href="${h.url('edit_user_group_perms_summary', user_group_id=c.user_group.users_group_id)}">${_('Permissions summary')}</a></li>
37 <li class="${'active' if c.active=='perms_summary' else ''}"><a href="${h.route_path('edit_user_group_perms_summary', user_group_id=c.user_group.users_group_id)}">${_('Permissions summary')}</a></li>
38 38 </ul>
39 39 </div>
40 40
41 41 <div class="main-content-full-width">
42 42 <%include file="/admin/user_groups/user_group_edit_${c.active}.mako"/>
43 43 </div>
44 44 </div>
45 45 </div>
46 46 </%def>
@@ -1,3 +1,3 b''
1 1 ## permissions overview
2 2 <%namespace name="p" file="/base/perms_summary.mako"/>
3 ${p.perms_summary(c.permissions)}
3 ${p.perms_summary(c.permissions, side_link=h.route_path('edit_user_group_perms_summary_json', user_group_id=c.user_group.users_group_id))}
@@ -1,57 +1,57 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.mako"/>
3 3
4 4 <%def name="title()">
5 5 ${_('%s user settings') % c.user.username}
6 6 %if c.rhodecode_name:
7 7 &middot; ${h.branding(c.rhodecode_name)}
8 8 %endif
9 9 </%def>
10 10
11 11 <%def name="breadcrumbs_links()">
12 12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 13 &raquo;
14 14 ${h.link_to(_('Users'),h.route_path('users'))}
15 15 &raquo;
16 16 % if c.user.active:
17 17 ${c.user.username}
18 18 % else:
19 19 <strike title="${_('This user is set as disabled')}">${c.user.username}</strike>
20 20 % endif
21 21
22 22 </%def>
23 23
24 24 <%def name="menu_bar_nav()">
25 25 ${self.menu_items(active='admin')}
26 26 </%def>
27 27
28 28 <%def name="main()">
29 29 <div class="box user_settings">
30 30 <div class="title">
31 31 ${self.breadcrumbs()}
32 32 </div>
33 33
34 34 ##main
35 35 <div class="sidebar-col-wrapper">
36 36 <div class="sidebar">
37 37 <ul class="nav nav-pills nav-stacked">
38 38 <li class="${'active' if c.active=='profile' else ''}"><a href="${h.url('edit_user', user_id=c.user.user_id)}">${_('User Profile')}</a></li>
39 39 <li class="${'active' if c.active=='auth_tokens' else ''}"><a href="${h.route_path('edit_user_auth_tokens', user_id=c.user.user_id)}">${_('Auth tokens')}</a></li>
40 40 <li class="${'active' if c.active in ['ssh_keys','ssh_keys_generate'] else ''}"><a href="${h.route_path('edit_user_ssh_keys', user_id=c.user.user_id)}">${_('SSH Keys')}</a></li>
41 41 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.url('edit_user_advanced', user_id=c.user.user_id)}">${_('Advanced')}</a></li>
42 42 <li class="${'active' if c.active=='global_perms' else ''}"><a href="${h.url('edit_user_global_perms', user_id=c.user.user_id)}">${_('Global permissions')}</a></li>
43 <li class="${'active' if c.active=='perms_summary' else ''}"><a href="${h.url('edit_user_perms_summary', user_id=c.user.user_id)}">${_('Permissions summary')}</a></li>
43 <li class="${'active' if c.active=='perms_summary' else ''}"><a href="${h.route_path('edit_user_perms_summary', user_id=c.user.user_id)}">${_('Permissions summary')}</a></li>
44 44 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.route_path('edit_user_emails', user_id=c.user.user_id)}">${_('Emails')}</a></li>
45 45 <li class="${'active' if c.active=='ips' else ''}"><a href="${h.route_path('edit_user_ips', user_id=c.user.user_id)}">${_('Ip Whitelist')}</a></li>
46 46 <li class="${'active' if c.active=='groups' else ''}"><a href="${h.route_path('edit_user_groups_management', user_id=c.user.user_id)}">${_('User Groups Management')}</a></li>
47 47 <li class="${'active' if c.active=='audit' else ''}"><a href="${h.route_path('edit_user_audit_logs', user_id=c.user.user_id)}">${_('User audit')}</a></li>
48 48 </ul>
49 49 </div>
50 50
51 51 <div class="main-content-full-width">
52 52 <%include file="/admin/users/user_edit_${c.active}.mako"/>
53 53 </div>
54 54 </div>
55 55 </div>
56 56
57 57 </%def>
@@ -1,3 +1,3 b''
1 1 ## permissions overview
2 2 <%namespace name="p" file="/base/perms_summary.mako"/>
3 ${p.perms_summary(c.perm_user.permissions, show_all=True)}
3 ${p.perms_summary(c.perm_user.permissions, show_all=True, side_link=h.route_path('edit_user_perms_summary_json', user_id=c.user.user_id))}
@@ -1,207 +1,212 b''
1 1 ## snippet for displaying permissions overview for users
2 2 ## usage:
3 3 ## <%namespace name="p" file="/base/perms_summary.mako"/>
4 4 ## ${p.perms_summary(c.perm_user.permissions)}
5 5
6 <%def name="perms_summary(permissions, show_all=False, actions=True)">
6 <%def name="perms_summary(permissions, show_all=False, actions=True, side_link=None)">
7 7 <div id="perms" class="table fields">
8 8 %for section in sorted(permissions.keys()):
9 9 <div class="panel panel-default">
10 10 <div class="panel-heading">
11 11 <h3 class="panel-title">${section.replace("_"," ").capitalize()}</h3>
12 % if side_link:
13 <div class="pull-right">
14 <a href="${side_link}">${_('in JSON format')}</a>
15 </div>
16 % endif
12 17 </div>
13 18 <div class="panel-body">
14 19 <div class="perms_section_head field">
15 20 <div class="radios">
16 21 %if section != 'global':
17 22 <span class="permissions_boxes">
18 23 <span class="desc">${_('show')}: </span>
19 24 ${h.checkbox('perms_filter_none_%s' % section, 'none', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='none')} <label for="${'perms_filter_none_%s' % section}"><span class="perm_tag none">${_('none')}</span></label>
20 25 ${h.checkbox('perms_filter_read_%s' % section, 'read', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='read')} <label for="${'perms_filter_read_%s' % section}"><span class="perm_tag read">${_('read')}</span></label>
21 26 ${h.checkbox('perms_filter_write_%s' % section, 'write', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='write')} <label for="${'perms_filter_write_%s' % section}"> <span class="perm_tag write">${_('write')}</span></label>
22 27 ${h.checkbox('perms_filter_admin_%s' % section, 'admin', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='admin')} <label for="${'perms_filter_admin_%s' % section}"><span class="perm_tag admin">${_('admin')}</span></label>
23 28 </span>
24 29 %endif
25 30 </div>
26 31 </div>
27 32 <div class="field">
28 33 %if not permissions[section]:
29 34 <p class="empty_data help-block">${_('No permissions defined')}</p>
30 35 %else:
31 36 <div id='tbl_list_wrap_${section}'>
32 37 <table id="tbl_list_${section}" class="rctable">
33 38 ## global permission box
34 39 %if section == 'global':
35 40 <thead>
36 41 <tr>
37 42 <th colspan="2" class="left">${_('Permission')}</th>
38 43 %if actions:
39 44 <th>${_('Edit Permission')}</th>
40 45 %endif
41 46 </thead>
42 47 <tbody>
43 48
44 49 <%
45 50 def get_section_perms(prefix, opts):
46 51 _selected = []
47 52 for op in opts:
48 53 if op.startswith(prefix) and not op.startswith('hg.create.write_on_repogroup'):
49 54 _selected.append(op)
50 55 admin = 'hg.admin' in opts
51 56 _selected_vals = [x.partition(prefix)[-1] for x in _selected]
52 57 return admin, _selected_vals, _selected
53 58 %>
54 59 <%def name="glob(lbl, val, val_lbl=None, custom_url=None)">
55 60 <tr>
56 61 <td class="td-tags">
57 62 ${lbl}
58 63 </td>
59 64 <td class="td-tags">
60 65 %if val[0]:
61 66 %if not val_lbl:
62 67 ${h.bool2icon(True)}
63 68 %else:
64 69 <span class="perm_tag admin">${val_lbl}.admin</span>
65 70 %endif
66 71 %else:
67 72 %if not val_lbl:
68 73 ${h.bool2icon({'false': False,
69 74 'true': True,
70 75 'none': False,
71 76 'repository': True}.get(val[1][0] if 0 < len(val[1]) else 'false'))}
72 77 %else:
73 78 <span class="perm_tag ${val[1][0]}">${val_lbl}.${val[1][0]}</span>
74 79 %endif
75 80 %endif
76 81 </td>
77 82 %if actions:
78 83 <td class="td-action">
79 84 <a href="${custom_url or h.route_path('admin_permissions_global')}">${_('edit')}</a>
80 85 </td>
81 86 %endif
82 87 </tr>
83 88 </%def>
84 89
85 90 ${glob(_('Super admin'), get_section_perms('hg.admin', permissions[section]))}
86 91
87 92 ${glob(_('Repository default permission'), get_section_perms('repository.', permissions[section]), 'repository', h.route_path('admin_permissions_object'))}
88 93 ${glob(_('Repository group default permission'), get_section_perms('group.', permissions[section]), 'group', h.route_path('admin_permissions_object'))}
89 94 ${glob(_('User group default permission'), get_section_perms('usergroup.', permissions[section]), 'usergroup', h.route_path('admin_permissions_object'))}
90 95
91 96 ${glob(_('Create repositories'), get_section_perms('hg.create.', permissions[section]), custom_url=h.route_path('admin_permissions_global'))}
92 97 ${glob(_('Fork repositories'), get_section_perms('hg.fork.', permissions[section]), custom_url=h.route_path('admin_permissions_global'))}
93 98 ${glob(_('Create repository groups'), get_section_perms('hg.repogroup.create.', permissions[section]), custom_url=h.route_path('admin_permissions_global'))}
94 99 ${glob(_('Create user groups'), get_section_perms('hg.usergroup.create.', permissions[section]), custom_url=h.route_path('admin_permissions_global'))}
95 100
96 101
97 102 </tbody>
98 103 %else:
99 104 ## none/read/write/admin permissions on groups/repos etc
100 105 <thead>
101 106 <tr>
102 107 <th>${_('Name')}</th>
103 108 <th>${_('Permission')}</th>
104 109 %if actions:
105 110 <th>${_('Edit Permission')}</th>
106 111 %endif
107 112 </thead>
108 113 <tbody class="section_${section}">
109 114 <%
110 115 def sorter(permissions):
111 116 def custom_sorter(item):
112 117 ## read/write/admin
113 118 section = item[1].split('.')[-1]
114 119 section_importance = {'none': u'0',
115 120 'read': u'1',
116 121 'write':u'2',
117 122 'admin':u'3'}.get(section)
118 123 ## sort by group importance+name
119 124 return section_importance+item[0]
120 125 return sorted(permissions, key=custom_sorter)
121 126 %>
122 127 %for k, section_perm in sorter(permissions[section].items()):
123 128 %if section_perm.split('.')[-1] != 'none' or show_all:
124 129 <tr class="perm_row ${'%s_%s' % (section, section_perm.split('.')[-1])}">
125 130 <td class="td-componentname">
126 131 %if section == 'repositories':
127 132 <a href="${h.route_path('repo_summary',repo_name=k)}">${k}</a>
128 133 %elif section == 'repositories_groups':
129 134 <a href="${h.route_path('repo_group_home', repo_group_name=k)}">${k}</a>
130 135 %elif section == 'user_groups':
131 136 ##<a href="${h.url('edit_users_group',user_group_id=k)}">${k}</a>
132 137 ${k}
133 138 %endif
134 139 </td>
135 140 <td class="td-tags">
136 141 %if hasattr(permissions[section], 'perm_origin_stack'):
137 142 %for i, (perm, origin) in enumerate(reversed(permissions[section].perm_origin_stack[k])):
138 143 <span class="${i > 0 and 'perm_overriden' or ''} perm_tag ${perm.split('.')[-1]}">
139 144 ${perm} (${origin})
140 145 </span>
141 146 %endfor
142 147 %else:
143 148 <span class="perm_tag ${section_perm.split('.')[-1]}">${section_perm}</span>
144 149 %endif
145 150 </td>
146 151 %if actions:
147 152 <td class="td-action">
148 153 %if section == 'repositories':
149 154 <a href="${h.route_path('edit_repo_perms',repo_name=k,_anchor='permissions_manage')}">${_('edit')}</a>
150 155 %elif section == 'repositories_groups':
151 156 <a href="${h.url('edit_repo_group_perms',group_name=k,anchor='permissions_manage')}">${_('edit')}</a>
152 157 %elif section == 'user_groups':
153 158 ##<a href="${h.url('edit_users_group',user_group_id=k)}">${_('edit')}</a>
154 159 %endif
155 160 </td>
156 161 %endif
157 162 </tr>
158 163 %endif
159 164 %endfor
160 165
161 166 <tr id="empty_${section}" class="noborder" style="display:none;">
162 167 <td colspan="6">${_('No permission defined')}</td>
163 168 </tr>
164 169
165 170 </tbody>
166 171 %endif
167 172 </table>
168 173 </div>
169 174 %endif
170 175 </div>
171 176 </div>
172 177 </div>
173 178 %endfor
174 179 </div>
175 180
176 181 <script>
177 182 $(document).ready(function(){
178 183 var show_empty = function(section){
179 184 var visible = $('.section_{0} tr.perm_row:visible'.format(section)).length;
180 185 if(visible == 0){
181 186 $('#empty_{0}'.format(section)).show();
182 187 }
183 188 else{
184 189 $('#empty_{0}'.format(section)).hide();
185 190 }
186 191 };
187 192 $('.perm_filter').on('change', function(e){
188 193 var self = this;
189 194 var section = $(this).attr('section');
190 195
191 196 var opts = {};
192 197 var elems = $('.filter_' + section).each(function(el){
193 198 var perm_type = $(this).attr('perm_type');
194 199 var checked = this.checked;
195 200 opts[perm_type] = checked;
196 201 if(checked){
197 202 $('.'+section+'_'+perm_type).show();
198 203 }
199 204 else{
200 205 $('.'+section+'_'+perm_type).hide();
201 206 }
202 207 });
203 208 show_empty(section);
204 209 })
205 210 })
206 211 </script>
207 212 </%def>
General Comments 0
You need to be logged in to leave comments. Login now