##// END OF EJS Templates
ssh-keys: added admin panel for managing globally all SSH Keys....
marcink -
r2042:e367c94a default
parent child Browse files
Show More
@@ -0,0 +1,91 b''
1
2 <div class="panel panel-default">
3 <div class="panel-heading">
4 <h3 class="panel-title">${_('SSH Keys')} - <span id="ssh_keys_count"></span></h3>
5
6 ${h.secure_form(h.route_path('admin_permissions_ssh_keys_update'), method='POST', request=request)}
7 <button class="btn btn-link pull-right" type="submit">${_('Update SSH keys file')}</button>
8 ${h.end_form()}
9 </div>
10 <div class="panel-body">
11 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
12
13 <div id="repos_list_wrap">
14 <table id="ssh_keys_table" class="display"></table>
15 </div>
16 </div>
17 </div>
18
19
20 <script type="text/javascript">
21
22 $(document).ready(function() {
23 var $sshKeyListTable = $('#ssh_keys_table');
24
25 var getDatatableCount = function(){
26 var table = $sshKeyListTable.dataTable();
27 var page = table.api().page.info();
28 var active = page.recordsDisplay;
29 var total = page.recordsTotal;
30
31 var _text = _gettext("{0} out of {1} ssh keys").format(active, total);
32 $('#ssh_keys_count').text(_text);
33 };
34
35 // user list
36 $sshKeyListTable.DataTable({
37 processing: true,
38 serverSide: true,
39 ajax: "${h.route_path('admin_permissions_ssh_keys_data')}",
40 dom: 'rtp',
41 pageLength: ${c.visual.admin_grid_items},
42 order: [[ 0, "asc" ]],
43 columns: [
44 { data: {"_": "username",
45 "sort": "username"}, title: "${_('Username')}", className: "td-user" },
46 { data: {"_": "fingerprint",
47 "sort": "fingerprint"}, title: "${_('Fingerprint')}", className: "td-type" },
48 { data: {"_": "description",
49 "sort": "description"}, title: "${_('Description')}", className: "td-type" },
50 { data: {"_": "created_on",
51 "sort": "created_on"}, title: "${_('Created on')}", className: "td-time" },
52 { data: {"_": "action",
53 "sort": "action"}, title: "${_('Action')}", className: "td-action", orderable: false }
54 ],
55 language: {
56 paginate: DEFAULT_GRID_PAGINATION,
57 sProcessing: _gettext('loading...'),
58 emptyTable: _gettext("No ssh keys available yet.")
59 },
60
61 "createdRow": function ( row, data, index ) {
62 if (!data['active_raw']){
63 $(row).addClass('closed')
64 }
65 }
66 });
67
68 $sshKeyListTable.on('xhr.dt', function(e, settings, json, xhr){
69 $sshKeyListTable.css('opacity', 1);
70 });
71
72 $sshKeyListTable.on('preXhr.dt', function(e, settings, data){
73 $sshKeyListTable.css('opacity', 0.3);
74 });
75
76 // refresh counters on draw
77 $sshKeyListTable.on('draw.dt', function(){
78 getDatatableCount();
79 });
80
81 // filter
82 $('#q_filter').on('keyup',
83 $.debounce(250, function() {
84 $sshKeyListTable.DataTable().search(
85 $('#q_filter').val()
86 ).draw();
87 })
88 );
89
90 });
91 </script>
@@ -1,235 +1,245 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 from rhodecode.apps.admin.navigation import NavigationRegistry
23 23 from rhodecode.config.routing import ADMIN_PREFIX
24 24 from rhodecode.lib.utils2 import str2bool
25 25
26 26
27 27 def admin_routes(config):
28 28 """
29 29 Admin prefixed routes
30 30 """
31 31
32 32 config.add_route(
33 33 name='admin_audit_logs',
34 34 pattern='/audit_logs')
35 35
36 36 config.add_route(
37 37 name='pull_requests_global_0', # backward compat
38 38 pattern='/pull_requests/{pull_request_id:\d+}')
39 39 config.add_route(
40 40 name='pull_requests_global_1', # backward compat
41 41 pattern='/pull-requests/{pull_request_id:\d+}')
42 42 config.add_route(
43 43 name='pull_requests_global',
44 44 pattern='/pull-request/{pull_request_id:\d+}')
45 45
46 46 config.add_route(
47 47 name='admin_settings_open_source',
48 48 pattern='/settings/open_source')
49 49 config.add_route(
50 50 name='admin_settings_vcs_svn_generate_cfg',
51 51 pattern='/settings/vcs/svn_generate_cfg')
52 52
53 53 config.add_route(
54 54 name='admin_settings_system',
55 55 pattern='/settings/system')
56 56 config.add_route(
57 57 name='admin_settings_system_update',
58 58 pattern='/settings/system/updates')
59 59
60 60 config.add_route(
61 61 name='admin_settings_sessions',
62 62 pattern='/settings/sessions')
63 63 config.add_route(
64 64 name='admin_settings_sessions_cleanup',
65 65 pattern='/settings/sessions/cleanup')
66 66
67 67 config.add_route(
68 68 name='admin_settings_process_management',
69 69 pattern='/settings/process_management')
70 70 config.add_route(
71 71 name='admin_settings_process_management_signal',
72 72 pattern='/settings/process_management/signal')
73 73
74 74 # global permissions
75 75
76 76 config.add_route(
77 77 name='admin_permissions_application',
78 78 pattern='/permissions/application')
79 79 config.add_route(
80 80 name='admin_permissions_application_update',
81 81 pattern='/permissions/application/update')
82 82
83 83 config.add_route(
84 84 name='admin_permissions_global',
85 85 pattern='/permissions/global')
86 86 config.add_route(
87 87 name='admin_permissions_global_update',
88 88 pattern='/permissions/global/update')
89 89
90 90 config.add_route(
91 91 name='admin_permissions_object',
92 92 pattern='/permissions/object')
93 93 config.add_route(
94 94 name='admin_permissions_object_update',
95 95 pattern='/permissions/object/update')
96 96
97 97 config.add_route(
98 98 name='admin_permissions_ips',
99 99 pattern='/permissions/ips')
100 100
101 101 config.add_route(
102 102 name='admin_permissions_overview',
103 103 pattern='/permissions/overview')
104 104
105 105 config.add_route(
106 106 name='admin_permissions_auth_token_access',
107 107 pattern='/permissions/auth_token_access')
108 108
109 config.add_route(
110 name='admin_permissions_ssh_keys',
111 pattern='/permissions/ssh_keys')
112 config.add_route(
113 name='admin_permissions_ssh_keys_data',
114 pattern='/permissions/ssh_keys/data')
115 config.add_route(
116 name='admin_permissions_ssh_keys_update',
117 pattern='/permissions/ssh_keys/update')
118
109 119 # users admin
110 120 config.add_route(
111 121 name='users',
112 122 pattern='/users')
113 123
114 124 config.add_route(
115 125 name='users_data',
116 126 pattern='/users_data')
117 127
118 128 # user auth tokens
119 129 config.add_route(
120 130 name='edit_user_auth_tokens',
121 131 pattern='/users/{user_id:\d+}/edit/auth_tokens')
122 132 config.add_route(
123 133 name='edit_user_auth_tokens_add',
124 134 pattern='/users/{user_id:\d+}/edit/auth_tokens/new')
125 135 config.add_route(
126 136 name='edit_user_auth_tokens_delete',
127 137 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete')
128 138
129 139 # user ssh keys
130 140 config.add_route(
131 141 name='edit_user_ssh_keys',
132 142 pattern='/users/{user_id:\d+}/edit/ssh_keys')
133 143 config.add_route(
134 144 name='edit_user_ssh_keys_generate_keypair',
135 145 pattern='/users/{user_id:\d+}/edit/ssh_keys/generate')
136 146 config.add_route(
137 147 name='edit_user_ssh_keys_add',
138 148 pattern='/users/{user_id:\d+}/edit/ssh_keys/new')
139 149 config.add_route(
140 150 name='edit_user_ssh_keys_delete',
141 151 pattern='/users/{user_id:\d+}/edit/ssh_keys/delete')
142 152
143 153 # user emails
144 154 config.add_route(
145 155 name='edit_user_emails',
146 156 pattern='/users/{user_id:\d+}/edit/emails')
147 157 config.add_route(
148 158 name='edit_user_emails_add',
149 159 pattern='/users/{user_id:\d+}/edit/emails/new')
150 160 config.add_route(
151 161 name='edit_user_emails_delete',
152 162 pattern='/users/{user_id:\d+}/edit/emails/delete')
153 163
154 164 # user IPs
155 165 config.add_route(
156 166 name='edit_user_ips',
157 167 pattern='/users/{user_id:\d+}/edit/ips')
158 168 config.add_route(
159 169 name='edit_user_ips_add',
160 170 pattern='/users/{user_id:\d+}/edit/ips/new')
161 171 config.add_route(
162 172 name='edit_user_ips_delete',
163 173 pattern='/users/{user_id:\d+}/edit/ips/delete')
164 174
165 175 # user perms
166 176 config.add_route(
167 177 name='edit_user_perms_summary',
168 178 pattern='/users/{user_id:\d+}/edit/permissions_summary')
169 179 config.add_route(
170 180 name='edit_user_perms_summary_json',
171 181 pattern='/users/{user_id:\d+}/edit/permissions_summary/json')
172 182
173 183 # user groups management
174 184 config.add_route(
175 185 name='edit_user_groups_management',
176 186 pattern='/users/{user_id:\d+}/edit/groups_management')
177 187
178 188 config.add_route(
179 189 name='edit_user_groups_management_updates',
180 190 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates')
181 191
182 192 # user audit logs
183 193 config.add_route(
184 194 name='edit_user_audit_logs',
185 195 pattern='/users/{user_id:\d+}/edit/audit')
186 196
187 197 # user groups admin
188 198 config.add_route(
189 199 name='user_groups',
190 200 pattern='/user_groups')
191 201
192 202 config.add_route(
193 203 name='user_groups_data',
194 204 pattern='/user_groups_data')
195 205
196 206 config.add_route(
197 207 name='user_group_members_data',
198 208 pattern='/user_groups/{user_group_id:\d+}/members')
199 209
200 210 # user groups perms
201 211 config.add_route(
202 212 name='edit_user_group_perms_summary',
203 213 pattern='/user_groups/{user_group_id:\d+}/edit/permissions_summary')
204 214 config.add_route(
205 215 name='edit_user_group_perms_summary_json',
206 216 pattern='/user_groups/{user_group_id:\d+}/edit/permissions_summary/json')
207 217
208 218 # repos admin
209 219 config.add_route(
210 220 name='repos',
211 221 pattern='/repos')
212 222
213 223 config.add_route(
214 224 name='repo_new',
215 225 pattern='/repos/new')
216 226
217 227 config.add_route(
218 228 name='repo_create',
219 229 pattern='/repos/create')
220 230
221 231
222 232 def includeme(config):
223 233 settings = config.get_settings()
224 234
225 235 # Create admin navigation registry and add it to the pyramid registry.
226 236 labs_active = str2bool(settings.get('labs_settings_active', False))
227 237 navigation_registry = NavigationRegistry(labs_active=labs_active)
228 238 config.registry.registerUtility(navigation_registry)
229 239
230 240 # main admin routes
231 241 config.add_route(name='admin_home', pattern=ADMIN_PREFIX)
232 242 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
233 243
234 244 # Scan module for configuration decorators.
235 245 config.scan('.views', ignore='.tests')
@@ -1,250 +1,286 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import pytest
22 22 from rhodecode.model.db import User, UserIpMap
23 from rhodecode.model.meta import Session
23 24 from rhodecode.model.permission import PermissionModel
25 from rhodecode.model.ssh_key import SshKeyModel
24 26 from rhodecode.tests import (
25 27 TestController, clear_all_caches, assert_session_flash)
26 28
27 29
28 30 def route_path(name, params=None, **kwargs):
29 31 import urllib
30 32 from rhodecode.apps._base import ADMIN_PREFIX
31 33
32 34 base_url = {
33 35 'edit_user_ips':
34 36 ADMIN_PREFIX + '/users/{user_id}/edit/ips',
35 37 'edit_user_ips_add':
36 38 ADMIN_PREFIX + '/users/{user_id}/edit/ips/new',
37 39 'edit_user_ips_delete':
38 40 ADMIN_PREFIX + '/users/{user_id}/edit/ips/delete',
39 41
40 42 'admin_permissions_application':
41 43 ADMIN_PREFIX + '/permissions/application',
42 44 'admin_permissions_application_update':
43 45 ADMIN_PREFIX + '/permissions/application/update',
44 46
45 47 'admin_permissions_global':
46 48 ADMIN_PREFIX + '/permissions/global',
47 49 'admin_permissions_global_update':
48 50 ADMIN_PREFIX + '/permissions/global/update',
49 51
50 52 'admin_permissions_object':
51 53 ADMIN_PREFIX + '/permissions/object',
52 54 'admin_permissions_object_update':
53 55 ADMIN_PREFIX + '/permissions/object/update',
54 56
55 57 'admin_permissions_ips':
56 58 ADMIN_PREFIX + '/permissions/ips',
57 59 'admin_permissions_overview':
58 ADMIN_PREFIX + '/permissions/overview'
60 ADMIN_PREFIX + '/permissions/overview',
61
62 'admin_permissions_ssh_keys':
63 ADMIN_PREFIX + '/permissions/ssh_keys',
64 'admin_permissions_ssh_keys_data':
65 ADMIN_PREFIX + '/permissions/ssh_keys/data',
66 'admin_permissions_ssh_keys_update':
67 ADMIN_PREFIX + '/permissions/ssh_keys/update'
59 68
60 69 }[name].format(**kwargs)
61 70
62 71 if params:
63 72 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
64 73 return base_url
65 74
66 75
67 76 class TestAdminPermissionsController(TestController):
68 77
69 78 @pytest.fixture(scope='class', autouse=True)
70 79 def prepare(self, request):
71 80 # cleanup and reset to default permissions after
72 81 @request.addfinalizer
73 82 def cleanup():
74 83 PermissionModel().create_default_user_permissions(
75 84 User.get_default_user(), force=True)
76 85
77 86 def test_index_application(self):
78 87 self.log_user()
79 88 self.app.get(route_path('admin_permissions_application'))
80 89
81 90 @pytest.mark.parametrize(
82 91 'anonymous, default_register, default_register_message, default_password_reset,'
83 92 'default_extern_activate, expect_error, expect_form_error', [
84 93 (True, 'hg.register.none', '', 'hg.password_reset.enabled', 'hg.extern_activate.manual',
85 94 False, False),
86 95 (True, 'hg.register.manual_activate', '', 'hg.password_reset.enabled', 'hg.extern_activate.auto',
87 96 False, False),
88 97 (True, 'hg.register.auto_activate', '', 'hg.password_reset.enabled', 'hg.extern_activate.manual',
89 98 False, False),
90 99 (True, 'hg.register.auto_activate', '', 'hg.password_reset.enabled', 'hg.extern_activate.manual',
91 100 False, False),
92 101 (True, 'hg.register.XXX', '', 'hg.password_reset.enabled', 'hg.extern_activate.manual',
93 102 False, True),
94 103 (True, '', '', 'hg.password_reset.enabled', '', True, False),
95 104 ])
96 105 def test_update_application_permissions(
97 106 self, anonymous, default_register, default_register_message, default_password_reset,
98 107 default_extern_activate, expect_error, expect_form_error):
99 108
100 109 self.log_user()
101 110
102 111 # TODO: anonymous access set here to False, breaks some other tests
103 112 params = {
104 113 'csrf_token': self.csrf_token,
105 114 'anonymous': anonymous,
106 115 'default_register': default_register,
107 116 'default_register_message': default_register_message,
108 117 'default_password_reset': default_password_reset,
109 118 'default_extern_activate': default_extern_activate,
110 119 }
111 120 response = self.app.post(route_path('admin_permissions_application_update'),
112 121 params=params)
113 122 if expect_form_error:
114 123 assert response.status_int == 200
115 124 response.mustcontain('Value must be one of')
116 125 else:
117 126 if expect_error:
118 127 msg = 'Error occurred during update of permissions'
119 128 else:
120 129 msg = 'Application permissions updated successfully'
121 130 assert_session_flash(response, msg)
122 131
123 132 def test_index_object(self):
124 133 self.log_user()
125 134 self.app.get(route_path('admin_permissions_object'))
126 135
127 136 @pytest.mark.parametrize(
128 137 'repo, repo_group, user_group, expect_error, expect_form_error', [
129 138 ('repository.none', 'group.none', 'usergroup.none', False, False),
130 139 ('repository.read', 'group.read', 'usergroup.read', False, False),
131 140 ('repository.write', 'group.write', 'usergroup.write',
132 141 False, False),
133 142 ('repository.admin', 'group.admin', 'usergroup.admin',
134 143 False, False),
135 144 ('repository.XXX', 'group.admin', 'usergroup.admin', False, True),
136 145 ('', '', '', True, False),
137 146 ])
138 147 def test_update_object_permissions(self, repo, repo_group, user_group,
139 148 expect_error, expect_form_error):
140 149 self.log_user()
141 150
142 151 params = {
143 152 'csrf_token': self.csrf_token,
144 153 'default_repo_perm': repo,
145 154 'overwrite_default_repo': False,
146 155 'default_group_perm': repo_group,
147 156 'overwrite_default_group': False,
148 157 'default_user_group_perm': user_group,
149 158 'overwrite_default_user_group': False,
150 159 }
151 160 response = self.app.post(route_path('admin_permissions_object_update'),
152 161 params=params)
153 162 if expect_form_error:
154 163 assert response.status_int == 200
155 164 response.mustcontain('Value must be one of')
156 165 else:
157 166 if expect_error:
158 167 msg = 'Error occurred during update of permissions'
159 168 else:
160 169 msg = 'Object permissions updated successfully'
161 170 assert_session_flash(response, msg)
162 171
163 172 def test_index_global(self):
164 173 self.log_user()
165 174 self.app.get(route_path('admin_permissions_global'))
166 175
167 176 @pytest.mark.parametrize(
168 177 'repo_create, repo_create_write, user_group_create, repo_group_create,'
169 178 'fork_create, inherit_default_permissions, expect_error,'
170 179 'expect_form_error', [
171 180 ('hg.create.none', 'hg.create.write_on_repogroup.false',
172 181 'hg.usergroup.create.false', 'hg.repogroup.create.false',
173 182 'hg.fork.none', 'hg.inherit_default_perms.false', False, False),
174 183 ('hg.create.repository', 'hg.create.write_on_repogroup.true',
175 184 'hg.usergroup.create.true', 'hg.repogroup.create.true',
176 185 'hg.fork.repository', 'hg.inherit_default_perms.false',
177 186 False, False),
178 187 ('hg.create.XXX', 'hg.create.write_on_repogroup.true',
179 188 'hg.usergroup.create.true', 'hg.repogroup.create.true',
180 189 'hg.fork.repository', 'hg.inherit_default_perms.false',
181 190 False, True),
182 191 ('', '', '', '', '', '', True, False),
183 192 ])
184 193 def test_update_global_permissions(
185 194 self, repo_create, repo_create_write, user_group_create,
186 195 repo_group_create, fork_create, inherit_default_permissions,
187 196 expect_error, expect_form_error):
188 197 self.log_user()
189 198
190 199 params = {
191 200 'csrf_token': self.csrf_token,
192 201 'default_repo_create': repo_create,
193 202 'default_repo_create_on_write': repo_create_write,
194 203 'default_user_group_create': user_group_create,
195 204 'default_repo_group_create': repo_group_create,
196 205 'default_fork_create': fork_create,
197 206 'default_inherit_default_permissions': inherit_default_permissions
198 207 }
199 208 response = self.app.post(route_path('admin_permissions_global_update'),
200 209 params=params)
201 210 if expect_form_error:
202 211 assert response.status_int == 200
203 212 response.mustcontain('Value must be one of')
204 213 else:
205 214 if expect_error:
206 215 msg = 'Error occurred during update of permissions'
207 216 else:
208 217 msg = 'Global permissions updated successfully'
209 218 assert_session_flash(response, msg)
210 219
211 220 def test_index_ips(self):
212 221 self.log_user()
213 222 response = self.app.get(route_path('admin_permissions_ips'))
214 223 # TODO: Test response...
215 224 response.mustcontain('All IP addresses are allowed')
216 225
217 226 def test_add_delete_ips(self):
218 227 self.log_user()
219 228 clear_all_caches()
220 229
221 230 # ADD
222 231 default_user_id = User.get_default_user().user_id
223 232 self.app.post(
224 233 route_path('edit_user_ips_add', user_id=default_user_id),
225 234 params={'new_ip': '127.0.0.0/24', 'csrf_token': self.csrf_token})
226 235
227 236 response = self.app.get(route_path('admin_permissions_ips'))
228 237 response.mustcontain('127.0.0.0/24')
229 238 response.mustcontain('127.0.0.0 - 127.0.0.255')
230 239
231 240 # DELETE
232 241 default_user_id = User.get_default_user().user_id
233 242 del_ip_id = UserIpMap.query().filter(UserIpMap.user_id ==
234 243 default_user_id).first().ip_id
235 244
236 245 response = self.app.post(
237 246 route_path('edit_user_ips_delete', user_id=default_user_id),
238 247 params={'del_ip_id': del_ip_id, 'csrf_token': self.csrf_token})
239 248
240 249 assert_session_flash(response, 'Removed ip address from user whitelist')
241 250
242 251 clear_all_caches()
243 252 response = self.app.get(route_path('admin_permissions_ips'))
244 253 response.mustcontain('All IP addresses are allowed')
245 254 response.mustcontain(no=['127.0.0.0/24'])
246 255 response.mustcontain(no=['127.0.0.0 - 127.0.0.255'])
247 256
248 257 def test_index_overview(self):
249 258 self.log_user()
250 259 self.app.get(route_path('admin_permissions_overview'))
260
261 def test_ssh_keys(self):
262 self.log_user()
263 self.app.get(route_path('admin_permissions_ssh_keys'), status=200)
264
265 def test_ssh_keys_data(self, user_util, xhr_header):
266 self.log_user()
267 response = self.app.get(route_path('admin_permissions_ssh_keys_data'),
268 extra_environ=xhr_header)
269 assert response.json == {u'data': [], u'draw': None,
270 u'recordsFiltered': 0, u'recordsTotal': 0}
271
272 dummy_user = user_util.create_user()
273 SshKeyModel().create(dummy_user, 'ab:cd:ef', 'KEYKEY', 'test_key')
274 Session().commit()
275 response = self.app.get(route_path('admin_permissions_ssh_keys_data'),
276 extra_environ=xhr_header)
277 assert response.json['data'][0]['fingerprint'] == 'ab:cd:ef'
278
279 def test_ssh_keys_update(self):
280 self.log_user()
281 response = self.app.post(
282 route_path('admin_permissions_ssh_keys_update'),
283 dict(csrf_token=self.csrf_token), status=302)
284
285 assert_session_flash(
286 response, 'SSH key support is disabled in .ini file')
@@ -1,369 +1,475 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import re
22 22 import logging
23 23 import formencode
24 import datetime
24 25 from pyramid.interfaces import IRoutesMapper
25 26
26 27 from pyramid.view import view_config
27 28 from pyramid.httpexceptions import HTTPFound
28 29 from pyramid.renderers import render
29 30 from pyramid.response import Response
30 31
31 from rhodecode.apps._base import BaseAppView
32 from rhodecode.apps._base import BaseAppView, DataGridAppView
33 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
34 from rhodecode.events import trigger
32 35
33 36 from rhodecode.lib import helpers as h
34 37 from rhodecode.lib.auth import (
35 38 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
36 from rhodecode.lib.utils2 import aslist
37 from rhodecode.model.db import User, UserIpMap
39 from rhodecode.lib.utils2 import aslist, safe_unicode
40 from rhodecode.model.db import or_, joinedload, coalesce, User, UserIpMap, UserSshKeys
38 41 from rhodecode.model.forms import (
39 42 ApplicationPermissionsForm, ObjectPermissionsForm, UserPermissionsForm)
40 43 from rhodecode.model.meta import Session
41 44 from rhodecode.model.permission import PermissionModel
42 45 from rhodecode.model.settings import SettingsModel
43 46
44 47
45 48 log = logging.getLogger(__name__)
46 49
47 50
48 class AdminPermissionsView(BaseAppView):
51 class AdminPermissionsView(BaseAppView, DataGridAppView):
49 52 def load_default_context(self):
50 53 c = self._get_local_tmpl_context()
51 54
52 55 self._register_global_c(c)
53 56 PermissionModel().set_global_permission_choices(
54 57 c, gettext_translator=self.request.translate)
55 58 return c
56 59
57 60 @LoginRequired()
58 61 @HasPermissionAllDecorator('hg.admin')
59 62 @view_config(
60 63 route_name='admin_permissions_application', request_method='GET',
61 64 renderer='rhodecode:templates/admin/permissions/permissions.mako')
62 65 def permissions_application(self):
63 66 c = self.load_default_context()
64 67 c.active = 'application'
65 68
66 69 c.user = User.get_default_user(refresh=True)
67 70
68 71 app_settings = SettingsModel().get_all_settings()
69 72 defaults = {
70 73 'anonymous': c.user.active,
71 74 'default_register_message': app_settings.get(
72 75 'rhodecode_register_message')
73 76 }
74 77 defaults.update(c.user.get_default_perms())
75 78
76 79 data = render('rhodecode:templates/admin/permissions/permissions.mako',
77 80 self._get_template_context(c), self.request)
78 81 html = formencode.htmlfill.render(
79 82 data,
80 83 defaults=defaults,
81 84 encoding="UTF-8",
82 85 force_defaults=False
83 86 )
84 87 return Response(html)
85 88
86 89 @LoginRequired()
87 90 @HasPermissionAllDecorator('hg.admin')
88 91 @CSRFRequired()
89 92 @view_config(
90 93 route_name='admin_permissions_application_update', request_method='POST',
91 94 renderer='rhodecode:templates/admin/permissions/permissions.mako')
92 95 def permissions_application_update(self):
93 96 _ = self.request.translate
94 97 c = self.load_default_context()
95 98 c.active = 'application'
96 99
97 100 _form = ApplicationPermissionsForm(
98 101 [x[0] for x in c.register_choices],
99 102 [x[0] for x in c.password_reset_choices],
100 103 [x[0] for x in c.extern_activate_choices])()
101 104
102 105 try:
103 106 form_result = _form.to_python(dict(self.request.POST))
104 107 form_result.update({'perm_user_name': User.DEFAULT_USER})
105 108 PermissionModel().update_application_permissions(form_result)
106 109
107 110 settings = [
108 111 ('register_message', 'default_register_message'),
109 112 ]
110 113 for setting, form_key in settings:
111 114 sett = SettingsModel().create_or_update_setting(
112 115 setting, form_result[form_key])
113 116 Session().add(sett)
114 117
115 118 Session().commit()
116 119 h.flash(_('Application permissions updated successfully'),
117 120 category='success')
118 121
119 122 except formencode.Invalid as errors:
120 123 defaults = errors.value
121 124
122 125 data = render(
123 126 'rhodecode:templates/admin/permissions/permissions.mako',
124 127 self._get_template_context(c), self.request)
125 128 html = formencode.htmlfill.render(
126 129 data,
127 130 defaults=defaults,
128 131 errors=errors.error_dict or {},
129 132 prefix_error=False,
130 133 encoding="UTF-8",
131 134 force_defaults=False
132 135 )
133 136 return Response(html)
134 137
135 138 except Exception:
136 139 log.exception("Exception during update of permissions")
137 140 h.flash(_('Error occurred during update of permissions'),
138 141 category='error')
139 142
140 143 raise HTTPFound(h.route_path('admin_permissions_application'))
141 144
142 145 @LoginRequired()
143 146 @HasPermissionAllDecorator('hg.admin')
144 147 @view_config(
145 148 route_name='admin_permissions_object', request_method='GET',
146 149 renderer='rhodecode:templates/admin/permissions/permissions.mako')
147 150 def permissions_objects(self):
148 151 c = self.load_default_context()
149 152 c.active = 'objects'
150 153
151 154 c.user = User.get_default_user(refresh=True)
152 155 defaults = {}
153 156 defaults.update(c.user.get_default_perms())
154 157
155 158 data = render(
156 159 'rhodecode:templates/admin/permissions/permissions.mako',
157 160 self._get_template_context(c), self.request)
158 161 html = formencode.htmlfill.render(
159 162 data,
160 163 defaults=defaults,
161 164 encoding="UTF-8",
162 165 force_defaults=False
163 166 )
164 167 return Response(html)
165 168
166 169 @LoginRequired()
167 170 @HasPermissionAllDecorator('hg.admin')
168 171 @CSRFRequired()
169 172 @view_config(
170 173 route_name='admin_permissions_object_update', request_method='POST',
171 174 renderer='rhodecode:templates/admin/permissions/permissions.mako')
172 175 def permissions_objects_update(self):
173 176 _ = self.request.translate
174 177 c = self.load_default_context()
175 178 c.active = 'objects'
176 179
177 180 _form = ObjectPermissionsForm(
178 181 [x[0] for x in c.repo_perms_choices],
179 182 [x[0] for x in c.group_perms_choices],
180 183 [x[0] for x in c.user_group_perms_choices])()
181 184
182 185 try:
183 186 form_result = _form.to_python(dict(self.request.POST))
184 187 form_result.update({'perm_user_name': User.DEFAULT_USER})
185 188 PermissionModel().update_object_permissions(form_result)
186 189
187 190 Session().commit()
188 191 h.flash(_('Object permissions updated successfully'),
189 192 category='success')
190 193
191 194 except formencode.Invalid as errors:
192 195 defaults = errors.value
193 196
194 197 data = render(
195 198 'rhodecode:templates/admin/permissions/permissions.mako',
196 199 self._get_template_context(c), self.request)
197 200 html = formencode.htmlfill.render(
198 201 data,
199 202 defaults=defaults,
200 203 errors=errors.error_dict or {},
201 204 prefix_error=False,
202 205 encoding="UTF-8",
203 206 force_defaults=False
204 207 )
205 208 return Response(html)
206 209 except Exception:
207 210 log.exception("Exception during update of permissions")
208 211 h.flash(_('Error occurred during update of permissions'),
209 212 category='error')
210 213
211 214 raise HTTPFound(h.route_path('admin_permissions_object'))
212 215
213 216 @LoginRequired()
214 217 @HasPermissionAllDecorator('hg.admin')
215 218 @view_config(
216 219 route_name='admin_permissions_global', request_method='GET',
217 220 renderer='rhodecode:templates/admin/permissions/permissions.mako')
218 221 def permissions_global(self):
219 222 c = self.load_default_context()
220 223 c.active = 'global'
221 224
222 225 c.user = User.get_default_user(refresh=True)
223 226 defaults = {}
224 227 defaults.update(c.user.get_default_perms())
225 228
226 229 data = render(
227 230 'rhodecode:templates/admin/permissions/permissions.mako',
228 231 self._get_template_context(c), self.request)
229 232 html = formencode.htmlfill.render(
230 233 data,
231 234 defaults=defaults,
232 235 encoding="UTF-8",
233 236 force_defaults=False
234 237 )
235 238 return Response(html)
236 239
237 240 @LoginRequired()
238 241 @HasPermissionAllDecorator('hg.admin')
239 242 @CSRFRequired()
240 243 @view_config(
241 244 route_name='admin_permissions_global_update', request_method='POST',
242 245 renderer='rhodecode:templates/admin/permissions/permissions.mako')
243 246 def permissions_global_update(self):
244 247 _ = self.request.translate
245 248 c = self.load_default_context()
246 249 c.active = 'global'
247 250
248 251 _form = UserPermissionsForm(
249 252 [x[0] for x in c.repo_create_choices],
250 253 [x[0] for x in c.repo_create_on_write_choices],
251 254 [x[0] for x in c.repo_group_create_choices],
252 255 [x[0] for x in c.user_group_create_choices],
253 256 [x[0] for x in c.fork_choices],
254 257 [x[0] for x in c.inherit_default_permission_choices])()
255 258
256 259 try:
257 260 form_result = _form.to_python(dict(self.request.POST))
258 261 form_result.update({'perm_user_name': User.DEFAULT_USER})
259 262 PermissionModel().update_user_permissions(form_result)
260 263
261 264 Session().commit()
262 265 h.flash(_('Global permissions updated successfully'),
263 266 category='success')
264 267
265 268 except formencode.Invalid as errors:
266 269 defaults = errors.value
267 270
268 271 data = render(
269 272 'rhodecode:templates/admin/permissions/permissions.mako',
270 273 self._get_template_context(c), self.request)
271 274 html = formencode.htmlfill.render(
272 275 data,
273 276 defaults=defaults,
274 277 errors=errors.error_dict or {},
275 278 prefix_error=False,
276 279 encoding="UTF-8",
277 280 force_defaults=False
278 281 )
279 282 return Response(html)
280 283 except Exception:
281 284 log.exception("Exception during update of permissions")
282 285 h.flash(_('Error occurred during update of permissions'),
283 286 category='error')
284 287
285 288 raise HTTPFound(h.route_path('admin_permissions_global'))
286 289
287 290 @LoginRequired()
288 291 @HasPermissionAllDecorator('hg.admin')
289 292 @view_config(
290 293 route_name='admin_permissions_ips', request_method='GET',
291 294 renderer='rhodecode:templates/admin/permissions/permissions.mako')
292 295 def permissions_ips(self):
293 296 c = self.load_default_context()
294 297 c.active = 'ips'
295 298
296 299 c.user = User.get_default_user(refresh=True)
297 300 c.user_ip_map = (
298 301 UserIpMap.query().filter(UserIpMap.user == c.user).all())
299 302
300 303 return self._get_template_context(c)
301 304
302 305 @LoginRequired()
303 306 @HasPermissionAllDecorator('hg.admin')
304 307 @view_config(
305 308 route_name='admin_permissions_overview', request_method='GET',
306 309 renderer='rhodecode:templates/admin/permissions/permissions.mako')
307 310 def permissions_overview(self):
308 311 c = self.load_default_context()
309 312 c.active = 'perms'
310 313
311 314 c.user = User.get_default_user(refresh=True)
312 315 c.perm_user = c.user.AuthUser()
313 316 return self._get_template_context(c)
314 317
315 318 @LoginRequired()
316 319 @HasPermissionAllDecorator('hg.admin')
317 320 @view_config(
318 321 route_name='admin_permissions_auth_token_access', request_method='GET',
319 322 renderer='rhodecode:templates/admin/permissions/permissions.mako')
320 323 def auth_token_access(self):
321 324 from rhodecode import CONFIG
322 325
323 326 c = self.load_default_context()
324 327 c.active = 'auth_token_access'
325 328
326 329 c.user = User.get_default_user(refresh=True)
327 330 c.perm_user = c.user.AuthUser()
328 331
329 332 mapper = self.request.registry.queryUtility(IRoutesMapper)
330 333 c.view_data = []
331 334
332 335 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
333 336 introspector = self.request.registry.introspector
334 337
335 338 view_intr = {}
336 339 for view_data in introspector.get_category('views'):
337 340 intr = view_data['introspectable']
338 341
339 342 if 'route_name' in intr and intr['attr']:
340 343 view_intr[intr['route_name']] = '{}:{}'.format(
341 344 str(intr['derived_callable'].func_name), intr['attr']
342 345 )
343 346
344 347 c.whitelist_key = 'api_access_controllers_whitelist'
345 348 c.whitelist_file = CONFIG.get('__file__')
346 349 whitelist_views = aslist(
347 350 CONFIG.get(c.whitelist_key), sep=',')
348 351
349 352 for route_info in mapper.get_routes():
350 353 if not route_info.name.startswith('__'):
351 354 routepath = route_info.pattern
352 355
353 356 def replace(matchobj):
354 357 if matchobj.group(1):
355 358 return "{%s}" % matchobj.group(1).split(':')[0]
356 359 else:
357 360 return "{%s}" % matchobj.group(2)
358 361
359 362 routepath = _argument_prog.sub(replace, routepath)
360 363
361 364 if not routepath.startswith('/'):
362 365 routepath = '/' + routepath
363 366
364 367 view_fqn = view_intr.get(route_info.name, 'NOT AVAILABLE')
365 368 active = view_fqn in whitelist_views
366 369 c.view_data.append((route_info.name, view_fqn, routepath, active))
367 370
368 371 c.whitelist_views = whitelist_views
369 372 return self._get_template_context(c)
373
374 @LoginRequired()
375 @HasPermissionAllDecorator('hg.admin')
376 @view_config(
377 route_name='admin_permissions_ssh_keys', request_method='GET',
378 renderer='rhodecode:templates/admin/permissions/permissions.mako')
379 def ssh_keys(self):
380 c = self.load_default_context()
381 c.active = 'ssh_keys'
382 return self._get_template_context(c)
383
384 @LoginRequired()
385 @HasPermissionAllDecorator('hg.admin')
386 @view_config(
387 route_name='admin_permissions_ssh_keys_data', request_method='GET',
388 renderer='json_ext', xhr=True)
389 def ssh_keys_data(self):
390 _ = self.request.translate
391 column_map = {
392 'fingerprint': 'ssh_key_fingerprint',
393 'username': User.username
394 }
395 draw, start, limit = self._extract_chunk(self.request)
396 search_q, order_by, order_dir = self._extract_ordering(
397 self.request, column_map=column_map)
398
399 ssh_keys_data_total_count = UserSshKeys.query()\
400 .count()
401
402 # json generate
403 base_q = UserSshKeys.query().join(UserSshKeys.user)
404
405 if search_q:
406 like_expression = u'%{}%'.format(safe_unicode(search_q))
407 base_q = base_q.filter(or_(
408 User.username.ilike(like_expression),
409 UserSshKeys.ssh_key_fingerprint.ilike(like_expression),
410 ))
411
412 users_data_total_filtered_count = base_q.count()
413
414 sort_col = self._get_order_col(order_by, UserSshKeys)
415 if sort_col:
416 if order_dir == 'asc':
417 # handle null values properly to order by NULL last
418 if order_by in ['created_on']:
419 sort_col = coalesce(sort_col, datetime.date.max)
420 sort_col = sort_col.asc()
421 else:
422 # handle null values properly to order by NULL last
423 if order_by in ['created_on']:
424 sort_col = coalesce(sort_col, datetime.date.min)
425 sort_col = sort_col.desc()
426
427 base_q = base_q.order_by(sort_col)
428 base_q = base_q.offset(start).limit(limit)
429
430 ssh_keys = base_q.all()
431
432 ssh_keys_data = []
433 for ssh_key in ssh_keys:
434 ssh_keys_data.append({
435 "username": h.gravatar_with_user(self.request, ssh_key.user.username),
436 "fingerprint": ssh_key.ssh_key_fingerprint,
437 "description": ssh_key.description,
438 "created_on": h.format_date(ssh_key.created_on),
439 "action": h.link_to(
440 _('Edit'), h.route_path('edit_user_ssh_keys',
441 user_id=ssh_key.user.user_id))
442 })
443
444 data = ({
445 'draw': draw,
446 'data': ssh_keys_data,
447 'recordsTotal': ssh_keys_data_total_count,
448 'recordsFiltered': users_data_total_filtered_count,
449 })
450
451 return data
452
453 @LoginRequired()
454 @HasPermissionAllDecorator('hg.admin')
455 @CSRFRequired()
456 @view_config(
457 route_name='admin_permissions_ssh_keys_update', request_method='POST',
458 renderer='rhodecode:templates/admin/permissions/permissions.mako')
459 def ssh_keys_update(self):
460 _ = self.request.translate
461 self.load_default_context()
462
463 ssh_enabled = self.request.registry.settings.get(
464 'ssh.generate_authorized_keyfile')
465 key_file = self.request.registry.settings.get(
466 'ssh.authorized_keys_file_path')
467 if ssh_enabled:
468 trigger(SshKeyFileChangeEvent(), self.request.registry)
469 h.flash(_('Updated SSH keys file: {}').format(key_file),
470 category='success')
471 else:
472 h.flash(_('SSH key support is disabled in .ini file'),
473 category='warning')
474
475 raise HTTPFound(h.route_path('admin_permissions_ssh_keys'))
@@ -1,251 +1,254 b''
1 1
2 2 /******************************************************************************
3 3 * *
4 4 * DO NOT CHANGE THIS FILE MANUALLY *
5 5 * *
6 6 * *
7 7 * This file is automatically generated when the app starts up with *
8 8 * generate_js_files = true *
9 9 * *
10 10 * To add a route here pass jsroute=True to the route definition in the app *
11 11 * *
12 12 ******************************************************************************/
13 13 function registerRCRoutes() {
14 14 // routes registration
15 15 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
16 16 pyroutes.register('favicon', '/favicon.ico', []);
17 17 pyroutes.register('robots', '/robots.txt', []);
18 18 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
19 19 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
20 20 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
21 21 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
22 22 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
23 23 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
24 24 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/settings/integrations', ['repo_group_name']);
25 25 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
26 26 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
27 27 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
28 28 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
29 29 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
30 30 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
31 31 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
32 32 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
33 33 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
34 34 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
35 35 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
36 36 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
37 37 pyroutes.register('admin_home', '/_admin', []);
38 38 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
39 39 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
40 40 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
41 41 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
42 42 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
43 43 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
44 44 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
45 45 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
46 46 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
47 47 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
48 48 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
49 49 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
50 50 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
51 51 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
52 52 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
53 53 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
54 54 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
55 55 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
56 56 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
57 57 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
58 58 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
59 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
60 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
61 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
59 62 pyroutes.register('users', '/_admin/users', []);
60 63 pyroutes.register('users_data', '/_admin/users_data', []);
61 64 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
62 65 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
63 66 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
64 67 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
65 68 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
66 69 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
67 70 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
68 71 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
69 72 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
70 73 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
71 74 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
72 75 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
73 76 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
74 77 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
75 78 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
76 79 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
77 80 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
78 81 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
79 82 pyroutes.register('user_groups', '/_admin/user_groups', []);
80 83 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
81 84 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
82 85 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
83 86 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
84 87 pyroutes.register('repos', '/_admin/repos', []);
85 88 pyroutes.register('repo_new', '/_admin/repos/new', []);
86 89 pyroutes.register('repo_create', '/_admin/repos/create', []);
87 90 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
88 91 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
89 92 pyroutes.register('channelstream_proxy', '/_channelstream', []);
90 93 pyroutes.register('login', '/_admin/login', []);
91 94 pyroutes.register('logout', '/_admin/logout', []);
92 95 pyroutes.register('register', '/_admin/register', []);
93 96 pyroutes.register('reset_password', '/_admin/password_reset', []);
94 97 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
95 98 pyroutes.register('home', '/', []);
96 99 pyroutes.register('user_autocomplete_data', '/_users', []);
97 100 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
98 101 pyroutes.register('repo_list_data', '/_repos', []);
99 102 pyroutes.register('goto_switcher_data', '/_goto_data', []);
100 103 pyroutes.register('journal', '/_admin/journal', []);
101 104 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
102 105 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
103 106 pyroutes.register('journal_public', '/_admin/public_journal', []);
104 107 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
105 108 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
106 109 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
107 110 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
108 111 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
109 112 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
110 113 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
111 114 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
112 115 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
113 116 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
114 117 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
115 118 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
116 119 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
117 120 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
118 121 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
119 122 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
120 123 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
121 124 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
122 125 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
123 126 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
124 127 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
125 128 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
126 129 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
127 130 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
128 131 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
129 132 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
130 133 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
131 134 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
132 135 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
133 136 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
134 137 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
135 138 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
136 139 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
137 140 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
138 141 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
139 142 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
140 143 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
141 144 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
142 145 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
143 146 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
144 147 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
145 148 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
146 149 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
147 150 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
148 151 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
149 152 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
150 153 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
151 154 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
152 155 pyroutes.register('repo_changelog_elements', '/%(repo_name)s/changelog_elements', ['repo_name']);
153 156 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
154 157 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']);
155 158 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
156 159 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
157 160 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
158 161 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
159 162 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
160 163 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
161 164 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
162 165 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
163 166 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
164 167 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
165 168 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
166 169 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
167 170 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
168 171 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
169 172 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
170 173 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
171 174 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
172 175 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
173 176 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']);
174 177 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
175 178 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
176 179 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
177 180 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
178 181 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
179 182 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
180 183 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
181 184 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
182 185 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
183 186 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
184 187 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
185 188 pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
186 189 pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
187 190 pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']);
188 191 pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
189 192 pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
190 193 pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
191 194 pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
192 195 pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
193 196 pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
194 197 pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
195 198 pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
196 199 pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
197 200 pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
198 201 pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
199 202 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
200 203 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
201 204 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
202 205 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
203 206 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
204 207 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);
205 208 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
206 209 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
207 210 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
208 211 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
209 212 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
210 213 pyroutes.register('search', '/_admin/search', []);
211 214 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
212 215 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
213 216 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
214 217 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
215 218 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
216 219 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
217 220 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
218 221 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
219 222 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
220 223 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
221 224 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
222 225 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
223 226 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
224 227 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
225 228 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
226 229 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
227 230 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
228 231 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
229 232 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
230 233 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
231 234 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
232 235 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
233 236 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
234 237 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
235 238 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
236 239 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
237 240 pyroutes.register('gists_show', '/_admin/gists', []);
238 241 pyroutes.register('gists_new', '/_admin/gists/new', []);
239 242 pyroutes.register('gists_create', '/_admin/gists/create', []);
240 243 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
241 244 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
242 245 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
243 246 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
244 247 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
245 248 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
246 249 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
247 250 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
248 251 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
249 252 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
250 253 pyroutes.register('apiv2', '/_admin/api', []);
251 254 }
@@ -1,59 +1,62 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.mako"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Permissions Administration')}
6 6 %if c.rhodecode_name:
7 7 &middot; ${h.branding(c.rhodecode_name)}
8 8 %endif
9 9 </%def>
10 10
11 11 <%def name="breadcrumbs_links()">
12 12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 13 &raquo;
14 14 ${_('Permissions')}
15 15 </%def>
16 16
17 17 <%def name="menu_bar_nav()">
18 18 ${self.menu_items(active='admin')}
19 19 </%def>
20 20
21 21
22 22 <%def name="main()">
23 23 <div class="box">
24 24 <div class="title">
25 25 ${self.breadcrumbs()}
26 26 </div>
27 27
28 28 <div class="sidebar-col-wrapper scw-small">
29 29 ##main
30 30 <div class="sidebar">
31 31 <ul class="nav nav-pills nav-stacked">
32 32 <li class="${'active' if c.active=='application' else ''}">
33 33 <a href="${h.route_path('admin_permissions_application')}">${_('Application')}</a>
34 34 </li>
35 35 <li class="${'active' if c.active=='global' else ''}">
36 36 <a href="${h.route_path('admin_permissions_global')}">${_('Global')}</a>
37 37 </li>
38 38 <li class="${'active' if c.active=='objects' else ''}">
39 39 <a href="${h.route_path('admin_permissions_object')}">${_('Object')}</a>
40 40 </li>
41 41 <li class="${'active' if c.active=='ips' else ''}">
42 42 <a href="${h.route_path('admin_permissions_ips')}">${_('IP Whitelist')}</a>
43 43 </li>
44 44 <li class="${'active' if c.active=='auth_token_access' else ''}">
45 45 <a href="${h.route_path('admin_permissions_auth_token_access')}">${_('AuthToken Access')}</a>
46 46 </li>
47 <li class="${'active' if c.active=='ssh_keys' else ''}">
48 <a href="${h.route_path('admin_permissions_ssh_keys')}">${_('SSH Keys')}</a>
49 </li>
47 50 <li class="${'active' if c.active=='perms' else ''}">
48 51 <a href="${h.route_path('admin_permissions_overview')}">${_('Overview')}</a>
49 52 </li>
50 53 </ul>
51 54 </div>
52 55
53 56 <div class="main-content-full-width">
54 57 <%include file="/admin/permissions/permissions_${c.active}.mako"/>
55 58 </div>
56 59 </div>
57 60 </div>
58 61
59 62 </%def>
General Comments 0
You need to be logged in to leave comments. Login now