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