##// END OF EJS Templates
apps: switch import of ADMIN_PREFIX from routing into base app....
marcink -
r2309:4e0e53bc default
parent child Browse files
Show More
@@ -1,325 +1,325 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.apps._base 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='admin_audit_log_entry',
37 name='admin_audit_log_entry',
38 pattern='/audit_logs/{audit_log_id}')
38 pattern='/audit_logs/{audit_log_id}')
39
39
40 config.add_route(
40 config.add_route(
41 name='pull_requests_global_0', # backward compat
41 name='pull_requests_global_0', # backward compat
42 pattern='/pull_requests/{pull_request_id:\d+}')
42 pattern='/pull_requests/{pull_request_id:\d+}')
43 config.add_route(
43 config.add_route(
44 name='pull_requests_global_1', # backward compat
44 name='pull_requests_global_1', # backward compat
45 pattern='/pull-requests/{pull_request_id:\d+}')
45 pattern='/pull-requests/{pull_request_id:\d+}')
46 config.add_route(
46 config.add_route(
47 name='pull_requests_global',
47 name='pull_requests_global',
48 pattern='/pull-request/{pull_request_id:\d+}')
48 pattern='/pull-request/{pull_request_id:\d+}')
49
49
50 config.add_route(
50 config.add_route(
51 name='admin_settings_open_source',
51 name='admin_settings_open_source',
52 pattern='/settings/open_source')
52 pattern='/settings/open_source')
53 config.add_route(
53 config.add_route(
54 name='admin_settings_vcs_svn_generate_cfg',
54 name='admin_settings_vcs_svn_generate_cfg',
55 pattern='/settings/vcs/svn_generate_cfg')
55 pattern='/settings/vcs/svn_generate_cfg')
56
56
57 config.add_route(
57 config.add_route(
58 name='admin_settings_system',
58 name='admin_settings_system',
59 pattern='/settings/system')
59 pattern='/settings/system')
60 config.add_route(
60 config.add_route(
61 name='admin_settings_system_update',
61 name='admin_settings_system_update',
62 pattern='/settings/system/updates')
62 pattern='/settings/system/updates')
63
63
64 config.add_route(
64 config.add_route(
65 name='admin_settings_sessions',
65 name='admin_settings_sessions',
66 pattern='/settings/sessions')
66 pattern='/settings/sessions')
67 config.add_route(
67 config.add_route(
68 name='admin_settings_sessions_cleanup',
68 name='admin_settings_sessions_cleanup',
69 pattern='/settings/sessions/cleanup')
69 pattern='/settings/sessions/cleanup')
70
70
71 config.add_route(
71 config.add_route(
72 name='admin_settings_process_management',
72 name='admin_settings_process_management',
73 pattern='/settings/process_management')
73 pattern='/settings/process_management')
74 config.add_route(
74 config.add_route(
75 name='admin_settings_process_management_signal',
75 name='admin_settings_process_management_signal',
76 pattern='/settings/process_management/signal')
76 pattern='/settings/process_management/signal')
77
77
78 # default settings
78 # default settings
79 config.add_route(
79 config.add_route(
80 name='admin_defaults_repositories',
80 name='admin_defaults_repositories',
81 pattern='/defaults/repositories')
81 pattern='/defaults/repositories')
82 config.add_route(
82 config.add_route(
83 name='admin_defaults_repositories_update',
83 name='admin_defaults_repositories_update',
84 pattern='/defaults/repositories/update')
84 pattern='/defaults/repositories/update')
85
85
86 # global permissions
86 # global permissions
87
87
88 config.add_route(
88 config.add_route(
89 name='admin_permissions_application',
89 name='admin_permissions_application',
90 pattern='/permissions/application')
90 pattern='/permissions/application')
91 config.add_route(
91 config.add_route(
92 name='admin_permissions_application_update',
92 name='admin_permissions_application_update',
93 pattern='/permissions/application/update')
93 pattern='/permissions/application/update')
94
94
95 config.add_route(
95 config.add_route(
96 name='admin_permissions_global',
96 name='admin_permissions_global',
97 pattern='/permissions/global')
97 pattern='/permissions/global')
98 config.add_route(
98 config.add_route(
99 name='admin_permissions_global_update',
99 name='admin_permissions_global_update',
100 pattern='/permissions/global/update')
100 pattern='/permissions/global/update')
101
101
102 config.add_route(
102 config.add_route(
103 name='admin_permissions_object',
103 name='admin_permissions_object',
104 pattern='/permissions/object')
104 pattern='/permissions/object')
105 config.add_route(
105 config.add_route(
106 name='admin_permissions_object_update',
106 name='admin_permissions_object_update',
107 pattern='/permissions/object/update')
107 pattern='/permissions/object/update')
108
108
109 config.add_route(
109 config.add_route(
110 name='admin_permissions_ips',
110 name='admin_permissions_ips',
111 pattern='/permissions/ips')
111 pattern='/permissions/ips')
112
112
113 config.add_route(
113 config.add_route(
114 name='admin_permissions_overview',
114 name='admin_permissions_overview',
115 pattern='/permissions/overview')
115 pattern='/permissions/overview')
116
116
117 config.add_route(
117 config.add_route(
118 name='admin_permissions_auth_token_access',
118 name='admin_permissions_auth_token_access',
119 pattern='/permissions/auth_token_access')
119 pattern='/permissions/auth_token_access')
120
120
121 config.add_route(
121 config.add_route(
122 name='admin_permissions_ssh_keys',
122 name='admin_permissions_ssh_keys',
123 pattern='/permissions/ssh_keys')
123 pattern='/permissions/ssh_keys')
124 config.add_route(
124 config.add_route(
125 name='admin_permissions_ssh_keys_data',
125 name='admin_permissions_ssh_keys_data',
126 pattern='/permissions/ssh_keys/data')
126 pattern='/permissions/ssh_keys/data')
127 config.add_route(
127 config.add_route(
128 name='admin_permissions_ssh_keys_update',
128 name='admin_permissions_ssh_keys_update',
129 pattern='/permissions/ssh_keys/update')
129 pattern='/permissions/ssh_keys/update')
130
130
131 # users admin
131 # users admin
132 config.add_route(
132 config.add_route(
133 name='users',
133 name='users',
134 pattern='/users')
134 pattern='/users')
135
135
136 config.add_route(
136 config.add_route(
137 name='users_data',
137 name='users_data',
138 pattern='/users_data')
138 pattern='/users_data')
139
139
140 config.add_route(
140 config.add_route(
141 name='users_create',
141 name='users_create',
142 pattern='/users/create')
142 pattern='/users/create')
143
143
144 config.add_route(
144 config.add_route(
145 name='users_new',
145 name='users_new',
146 pattern='/users/new')
146 pattern='/users/new')
147
147
148 # user management
148 # user management
149 config.add_route(
149 config.add_route(
150 name='user_edit',
150 name='user_edit',
151 pattern='/users/{user_id:\d+}/edit',
151 pattern='/users/{user_id:\d+}/edit',
152 user_route=True)
152 user_route=True)
153 config.add_route(
153 config.add_route(
154 name='user_edit_advanced',
154 name='user_edit_advanced',
155 pattern='/users/{user_id:\d+}/edit/advanced',
155 pattern='/users/{user_id:\d+}/edit/advanced',
156 user_route=True)
156 user_route=True)
157 config.add_route(
157 config.add_route(
158 name='user_edit_global_perms',
158 name='user_edit_global_perms',
159 pattern='/users/{user_id:\d+}/edit/global_permissions',
159 pattern='/users/{user_id:\d+}/edit/global_permissions',
160 user_route=True)
160 user_route=True)
161 config.add_route(
161 config.add_route(
162 name='user_edit_global_perms_update',
162 name='user_edit_global_perms_update',
163 pattern='/users/{user_id:\d+}/edit/global_permissions/update',
163 pattern='/users/{user_id:\d+}/edit/global_permissions/update',
164 user_route=True)
164 user_route=True)
165 config.add_route(
165 config.add_route(
166 name='user_update',
166 name='user_update',
167 pattern='/users/{user_id:\d+}/update',
167 pattern='/users/{user_id:\d+}/update',
168 user_route=True)
168 user_route=True)
169 config.add_route(
169 config.add_route(
170 name='user_delete',
170 name='user_delete',
171 pattern='/users/{user_id:\d+}/delete',
171 pattern='/users/{user_id:\d+}/delete',
172 user_route=True)
172 user_route=True)
173 config.add_route(
173 config.add_route(
174 name='user_force_password_reset',
174 name='user_force_password_reset',
175 pattern='/users/{user_id:\d+}/password_reset',
175 pattern='/users/{user_id:\d+}/password_reset',
176 user_route=True)
176 user_route=True)
177 config.add_route(
177 config.add_route(
178 name='user_create_personal_repo_group',
178 name='user_create_personal_repo_group',
179 pattern='/users/{user_id:\d+}/create_repo_group',
179 pattern='/users/{user_id:\d+}/create_repo_group',
180 user_route=True)
180 user_route=True)
181
181
182 # user auth tokens
182 # user auth tokens
183 config.add_route(
183 config.add_route(
184 name='edit_user_auth_tokens',
184 name='edit_user_auth_tokens',
185 pattern='/users/{user_id:\d+}/edit/auth_tokens',
185 pattern='/users/{user_id:\d+}/edit/auth_tokens',
186 user_route=True)
186 user_route=True)
187 config.add_route(
187 config.add_route(
188 name='edit_user_auth_tokens_add',
188 name='edit_user_auth_tokens_add',
189 pattern='/users/{user_id:\d+}/edit/auth_tokens/new',
189 pattern='/users/{user_id:\d+}/edit/auth_tokens/new',
190 user_route=True)
190 user_route=True)
191 config.add_route(
191 config.add_route(
192 name='edit_user_auth_tokens_delete',
192 name='edit_user_auth_tokens_delete',
193 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete',
193 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete',
194 user_route=True)
194 user_route=True)
195
195
196 # user ssh keys
196 # user ssh keys
197 config.add_route(
197 config.add_route(
198 name='edit_user_ssh_keys',
198 name='edit_user_ssh_keys',
199 pattern='/users/{user_id:\d+}/edit/ssh_keys',
199 pattern='/users/{user_id:\d+}/edit/ssh_keys',
200 user_route=True)
200 user_route=True)
201 config.add_route(
201 config.add_route(
202 name='edit_user_ssh_keys_generate_keypair',
202 name='edit_user_ssh_keys_generate_keypair',
203 pattern='/users/{user_id:\d+}/edit/ssh_keys/generate',
203 pattern='/users/{user_id:\d+}/edit/ssh_keys/generate',
204 user_route=True)
204 user_route=True)
205 config.add_route(
205 config.add_route(
206 name='edit_user_ssh_keys_add',
206 name='edit_user_ssh_keys_add',
207 pattern='/users/{user_id:\d+}/edit/ssh_keys/new',
207 pattern='/users/{user_id:\d+}/edit/ssh_keys/new',
208 user_route=True)
208 user_route=True)
209 config.add_route(
209 config.add_route(
210 name='edit_user_ssh_keys_delete',
210 name='edit_user_ssh_keys_delete',
211 pattern='/users/{user_id:\d+}/edit/ssh_keys/delete',
211 pattern='/users/{user_id:\d+}/edit/ssh_keys/delete',
212 user_route=True)
212 user_route=True)
213
213
214 # user emails
214 # user emails
215 config.add_route(
215 config.add_route(
216 name='edit_user_emails',
216 name='edit_user_emails',
217 pattern='/users/{user_id:\d+}/edit/emails',
217 pattern='/users/{user_id:\d+}/edit/emails',
218 user_route=True)
218 user_route=True)
219 config.add_route(
219 config.add_route(
220 name='edit_user_emails_add',
220 name='edit_user_emails_add',
221 pattern='/users/{user_id:\d+}/edit/emails/new',
221 pattern='/users/{user_id:\d+}/edit/emails/new',
222 user_route=True)
222 user_route=True)
223 config.add_route(
223 config.add_route(
224 name='edit_user_emails_delete',
224 name='edit_user_emails_delete',
225 pattern='/users/{user_id:\d+}/edit/emails/delete',
225 pattern='/users/{user_id:\d+}/edit/emails/delete',
226 user_route=True)
226 user_route=True)
227
227
228 # user IPs
228 # user IPs
229 config.add_route(
229 config.add_route(
230 name='edit_user_ips',
230 name='edit_user_ips',
231 pattern='/users/{user_id:\d+}/edit/ips',
231 pattern='/users/{user_id:\d+}/edit/ips',
232 user_route=True)
232 user_route=True)
233 config.add_route(
233 config.add_route(
234 name='edit_user_ips_add',
234 name='edit_user_ips_add',
235 pattern='/users/{user_id:\d+}/edit/ips/new',
235 pattern='/users/{user_id:\d+}/edit/ips/new',
236 user_route_with_default=True) # enabled for default user too
236 user_route_with_default=True) # enabled for default user too
237 config.add_route(
237 config.add_route(
238 name='edit_user_ips_delete',
238 name='edit_user_ips_delete',
239 pattern='/users/{user_id:\d+}/edit/ips/delete',
239 pattern='/users/{user_id:\d+}/edit/ips/delete',
240 user_route_with_default=True) # enabled for default user too
240 user_route_with_default=True) # enabled for default user too
241
241
242 # user perms
242 # user perms
243 config.add_route(
243 config.add_route(
244 name='edit_user_perms_summary',
244 name='edit_user_perms_summary',
245 pattern='/users/{user_id:\d+}/edit/permissions_summary',
245 pattern='/users/{user_id:\d+}/edit/permissions_summary',
246 user_route=True)
246 user_route=True)
247 config.add_route(
247 config.add_route(
248 name='edit_user_perms_summary_json',
248 name='edit_user_perms_summary_json',
249 pattern='/users/{user_id:\d+}/edit/permissions_summary/json',
249 pattern='/users/{user_id:\d+}/edit/permissions_summary/json',
250 user_route=True)
250 user_route=True)
251
251
252 # user user groups management
252 # user user groups management
253 config.add_route(
253 config.add_route(
254 name='edit_user_groups_management',
254 name='edit_user_groups_management',
255 pattern='/users/{user_id:\d+}/edit/groups_management',
255 pattern='/users/{user_id:\d+}/edit/groups_management',
256 user_route=True)
256 user_route=True)
257
257
258 config.add_route(
258 config.add_route(
259 name='edit_user_groups_management_updates',
259 name='edit_user_groups_management_updates',
260 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates',
260 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates',
261 user_route=True)
261 user_route=True)
262
262
263 # user audit logs
263 # user audit logs
264 config.add_route(
264 config.add_route(
265 name='edit_user_audit_logs',
265 name='edit_user_audit_logs',
266 pattern='/users/{user_id:\d+}/edit/audit', user_route=True)
266 pattern='/users/{user_id:\d+}/edit/audit', user_route=True)
267
267
268 # user-groups admin
268 # user-groups admin
269 config.add_route(
269 config.add_route(
270 name='user_groups',
270 name='user_groups',
271 pattern='/user_groups')
271 pattern='/user_groups')
272
272
273 config.add_route(
273 config.add_route(
274 name='user_groups_data',
274 name='user_groups_data',
275 pattern='/user_groups_data')
275 pattern='/user_groups_data')
276
276
277 config.add_route(
277 config.add_route(
278 name='user_groups_new',
278 name='user_groups_new',
279 pattern='/user_groups/new')
279 pattern='/user_groups/new')
280
280
281 config.add_route(
281 config.add_route(
282 name='user_groups_create',
282 name='user_groups_create',
283 pattern='/user_groups/create')
283 pattern='/user_groups/create')
284
284
285 # repos admin
285 # repos admin
286 config.add_route(
286 config.add_route(
287 name='repos',
287 name='repos',
288 pattern='/repos')
288 pattern='/repos')
289
289
290 config.add_route(
290 config.add_route(
291 name='repo_new',
291 name='repo_new',
292 pattern='/repos/new')
292 pattern='/repos/new')
293
293
294 config.add_route(
294 config.add_route(
295 name='repo_create',
295 name='repo_create',
296 pattern='/repos/create')
296 pattern='/repos/create')
297
297
298 # repo groups admin
298 # repo groups admin
299 config.add_route(
299 config.add_route(
300 name='repo_groups',
300 name='repo_groups',
301 pattern='/repo_groups')
301 pattern='/repo_groups')
302
302
303 config.add_route(
303 config.add_route(
304 name='repo_group_new',
304 name='repo_group_new',
305 pattern='/repo_group/new')
305 pattern='/repo_group/new')
306
306
307 config.add_route(
307 config.add_route(
308 name='repo_group_create',
308 name='repo_group_create',
309 pattern='/repo_group/create')
309 pattern='/repo_group/create')
310
310
311
311
312 def includeme(config):
312 def includeme(config):
313 settings = config.get_settings()
313 settings = config.get_settings()
314
314
315 # Create admin navigation registry and add it to the pyramid registry.
315 # Create admin navigation registry and add it to the pyramid registry.
316 labs_active = str2bool(settings.get('labs_settings_active', False))
316 labs_active = str2bool(settings.get('labs_settings_active', False))
317 navigation_registry = NavigationRegistry(labs_active=labs_active)
317 navigation_registry = NavigationRegistry(labs_active=labs_active)
318 config.registry.registerUtility(navigation_registry)
318 config.registry.registerUtility(navigation_registry)
319
319
320 # main admin routes
320 # main admin routes
321 config.add_route(name='admin_home', pattern=ADMIN_PREFIX)
321 config.add_route(name='admin_home', pattern=ADMIN_PREFIX)
322 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
322 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
323
323
324 # Scan module for configuration decorators.
324 # Scan module for configuration decorators.
325 config.scan('.views', ignore='.tests')
325 config.scan('.views', ignore='.tests')
@@ -1,96 +1,96 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 import os
21 import os
22
22
23 from pyramid.events import ApplicationCreated
23 from pyramid.events import ApplicationCreated
24 from pyramid.settings import asbool
24 from pyramid.settings import asbool
25
25
26 from rhodecode.config.routing import ADMIN_PREFIX
26 from rhodecode.apps._base import ADMIN_PREFIX
27 from rhodecode.lib.ext_json import json
27 from rhodecode.lib.ext_json import json
28
28
29
29
30 def url_gen(request):
30 def url_gen(request):
31 registry = request.registry
31 registry = request.registry
32 longpoll_url = registry.settings.get('channelstream.longpoll_url', '')
32 longpoll_url = registry.settings.get('channelstream.longpoll_url', '')
33 ws_url = registry.settings.get('channelstream.ws_url', '')
33 ws_url = registry.settings.get('channelstream.ws_url', '')
34 proxy_url = request.route_url('channelstream_proxy')
34 proxy_url = request.route_url('channelstream_proxy')
35 urls = {
35 urls = {
36 'connect': request.route_path('channelstream_connect'),
36 'connect': request.route_path('channelstream_connect'),
37 'subscribe': request.route_path('channelstream_subscribe'),
37 'subscribe': request.route_path('channelstream_subscribe'),
38 'longpoll': longpoll_url or proxy_url,
38 'longpoll': longpoll_url or proxy_url,
39 'ws': ws_url or proxy_url.replace('http', 'ws')
39 'ws': ws_url or proxy_url.replace('http', 'ws')
40 }
40 }
41 return json.dumps(urls)
41 return json.dumps(urls)
42
42
43
43
44 PLUGIN_DEFINITION = {
44 PLUGIN_DEFINITION = {
45 'name': 'channelstream',
45 'name': 'channelstream',
46 'config': {
46 'config': {
47 'javascript': [],
47 'javascript': [],
48 'css': [],
48 'css': [],
49 'template_hooks': {
49 'template_hooks': {
50 'plugin_init_template': 'rhodecode:templates/channelstream/plugin_init.mako'
50 'plugin_init_template': 'rhodecode:templates/channelstream/plugin_init.mako'
51 },
51 },
52 'url_gen': url_gen,
52 'url_gen': url_gen,
53 'static': None,
53 'static': None,
54 'enabled': False,
54 'enabled': False,
55 'server': '',
55 'server': '',
56 'secret': ''
56 'secret': ''
57 }
57 }
58 }
58 }
59
59
60
60
61 def maybe_create_history_store(event):
61 def maybe_create_history_store(event):
62 # create plugin history location
62 # create plugin history location
63 settings = event.app.registry.settings
63 settings = event.app.registry.settings
64 history_dir = settings.get('channelstream.history.location', '')
64 history_dir = settings.get('channelstream.history.location', '')
65 if history_dir and not os.path.exists(history_dir):
65 if history_dir and not os.path.exists(history_dir):
66 os.makedirs(history_dir, 0750)
66 os.makedirs(history_dir, 0750)
67
67
68
68
69 def includeme(config):
69 def includeme(config):
70 settings = config.registry.settings
70 settings = config.registry.settings
71 PLUGIN_DEFINITION['config']['enabled'] = asbool(
71 PLUGIN_DEFINITION['config']['enabled'] = asbool(
72 settings.get('channelstream.enabled'))
72 settings.get('channelstream.enabled'))
73 PLUGIN_DEFINITION['config']['server'] = settings.get(
73 PLUGIN_DEFINITION['config']['server'] = settings.get(
74 'channelstream.server', '')
74 'channelstream.server', '')
75 PLUGIN_DEFINITION['config']['secret'] = settings.get(
75 PLUGIN_DEFINITION['config']['secret'] = settings.get(
76 'channelstream.secret', '')
76 'channelstream.secret', '')
77 PLUGIN_DEFINITION['config']['history.location'] = settings.get(
77 PLUGIN_DEFINITION['config']['history.location'] = settings.get(
78 'channelstream.history.location', '')
78 'channelstream.history.location', '')
79 config.register_rhodecode_plugin(
79 config.register_rhodecode_plugin(
80 PLUGIN_DEFINITION['name'],
80 PLUGIN_DEFINITION['name'],
81 PLUGIN_DEFINITION['config']
81 PLUGIN_DEFINITION['config']
82 )
82 )
83 config.add_subscriber(maybe_create_history_store, ApplicationCreated)
83 config.add_subscriber(maybe_create_history_store, ApplicationCreated)
84
84
85 config.add_route(
85 config.add_route(
86 name='channelstream_connect',
86 name='channelstream_connect',
87 pattern=ADMIN_PREFIX + '/channelstream/connect')
87 pattern=ADMIN_PREFIX + '/channelstream/connect')
88 config.add_route(
88 config.add_route(
89 name='channelstream_subscribe',
89 name='channelstream_subscribe',
90 pattern=ADMIN_PREFIX + '/channelstream/subscribe')
90 pattern=ADMIN_PREFIX + '/channelstream/subscribe')
91 config.add_route(
91 config.add_route(
92 name='channelstream_proxy',
92 name='channelstream_proxy',
93 pattern=settings.get('channelstream.proxy_path') or '/_channelstream')
93 pattern=settings.get('channelstream.proxy_path') or '/_channelstream')
94
94
95 # Scan module for configuration decorators.
95 # Scan module for configuration decorators.
96 config.scan('.views', ignore='.tests')
96 config.scan('.views', ignore='.tests')
@@ -1,44 +1,44 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.config.routing import ADMIN_PREFIX
22 from rhodecode.apps._base import ADMIN_PREFIX
23
23
24
24
25 def includeme(config):
25 def includeme(config):
26
26
27 config.add_route(
27 config.add_route(
28 name='login',
28 name='login',
29 pattern=ADMIN_PREFIX + '/login')
29 pattern=ADMIN_PREFIX + '/login')
30 config.add_route(
30 config.add_route(
31 name='logout',
31 name='logout',
32 pattern=ADMIN_PREFIX + '/logout')
32 pattern=ADMIN_PREFIX + '/logout')
33 config.add_route(
33 config.add_route(
34 name='register',
34 name='register',
35 pattern=ADMIN_PREFIX + '/register')
35 pattern=ADMIN_PREFIX + '/register')
36 config.add_route(
36 config.add_route(
37 name='reset_password',
37 name='reset_password',
38 pattern=ADMIN_PREFIX + '/password_reset')
38 pattern=ADMIN_PREFIX + '/password_reset')
39 config.add_route(
39 config.add_route(
40 name='reset_password_confirmation',
40 name='reset_password_confirmation',
41 pattern=ADMIN_PREFIX + '/password_reset_confirmation')
41 pattern=ADMIN_PREFIX + '/password_reset_confirmation')
42
42
43 # Scan module for configuration decorators.
43 # Scan module for configuration decorators.
44 config.scan('.views', ignore='.tests')
44 config.scan('.views', ignore='.tests')
@@ -1,133 +1,133 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 import mock
22 import mock
23 import pytest
23 import pytest
24
24
25 from rhodecode.apps._base import ADMIN_PREFIX
25 from rhodecode.apps.login.views import LoginView, CaptchaData
26 from rhodecode.apps.login.views import LoginView, CaptchaData
26 from rhodecode.config.routing import ADMIN_PREFIX
27 from rhodecode.model.settings import SettingsModel
27 from rhodecode.lib.utils2 import AttributeDict
28 from rhodecode.lib.utils2 import AttributeDict
28 from rhodecode.model.settings import SettingsModel
29 from rhodecode.tests.utils import AssertResponse
29 from rhodecode.tests.utils import AssertResponse
30
30
31
31
32 class RhodeCodeSetting(object):
32 class RhodeCodeSetting(object):
33 def __init__(self, name, value):
33 def __init__(self, name, value):
34 self.name = name
34 self.name = name
35 self.value = value
35 self.value = value
36
36
37 def __enter__(self):
37 def __enter__(self):
38 from rhodecode.model.settings import SettingsModel
38 from rhodecode.model.settings import SettingsModel
39 model = SettingsModel()
39 model = SettingsModel()
40 self.old_setting = model.get_setting_by_name(self.name)
40 self.old_setting = model.get_setting_by_name(self.name)
41 model.create_or_update_setting(name=self.name, val=self.value)
41 model.create_or_update_setting(name=self.name, val=self.value)
42 return self
42 return self
43
43
44 def __exit__(self, exc_type, exc_val, exc_tb):
44 def __exit__(self, exc_type, exc_val, exc_tb):
45 model = SettingsModel()
45 model = SettingsModel()
46 if self.old_setting:
46 if self.old_setting:
47 model.create_or_update_setting(
47 model.create_or_update_setting(
48 name=self.name, val=self.old_setting.app_settings_value)
48 name=self.name, val=self.old_setting.app_settings_value)
49 else:
49 else:
50 model.create_or_update_setting(name=self.name)
50 model.create_or_update_setting(name=self.name)
51
51
52
52
53 class TestRegisterCaptcha(object):
53 class TestRegisterCaptcha(object):
54
54
55 @pytest.mark.parametrize('private_key, public_key, expected', [
55 @pytest.mark.parametrize('private_key, public_key, expected', [
56 ('', '', CaptchaData(False, '', '')),
56 ('', '', CaptchaData(False, '', '')),
57 ('', 'pubkey', CaptchaData(False, '', 'pubkey')),
57 ('', 'pubkey', CaptchaData(False, '', 'pubkey')),
58 ('privkey', '', CaptchaData(True, 'privkey', '')),
58 ('privkey', '', CaptchaData(True, 'privkey', '')),
59 ('privkey', 'pubkey', CaptchaData(True, 'privkey', 'pubkey')),
59 ('privkey', 'pubkey', CaptchaData(True, 'privkey', 'pubkey')),
60 ])
60 ])
61 def test_get_captcha_data(self, private_key, public_key, expected, db,
61 def test_get_captcha_data(self, private_key, public_key, expected, db,
62 request_stub, user_util):
62 request_stub, user_util):
63 request_stub.user = user_util.create_user().AuthUser()
63 request_stub.user = user_util.create_user().AuthUser()
64 request_stub.matched_route = AttributeDict({'name': 'login'})
64 request_stub.matched_route = AttributeDict({'name': 'login'})
65 login_view = LoginView(mock.Mock(), request_stub)
65 login_view = LoginView(mock.Mock(), request_stub)
66
66
67 with RhodeCodeSetting('captcha_private_key', private_key):
67 with RhodeCodeSetting('captcha_private_key', private_key):
68 with RhodeCodeSetting('captcha_public_key', public_key):
68 with RhodeCodeSetting('captcha_public_key', public_key):
69 captcha = login_view._get_captcha_data()
69 captcha = login_view._get_captcha_data()
70 assert captcha == expected
70 assert captcha == expected
71
71
72 @pytest.mark.parametrize('active', [False, True])
72 @pytest.mark.parametrize('active', [False, True])
73 @mock.patch.object(LoginView, '_get_captcha_data')
73 @mock.patch.object(LoginView, '_get_captcha_data')
74 def test_private_key_does_not_leak_to_html(
74 def test_private_key_does_not_leak_to_html(
75 self, m_get_captcha_data, active, app):
75 self, m_get_captcha_data, active, app):
76 captcha = CaptchaData(
76 captcha = CaptchaData(
77 active=active, private_key='PRIVATE_KEY', public_key='PUBLIC_KEY')
77 active=active, private_key='PRIVATE_KEY', public_key='PUBLIC_KEY')
78 m_get_captcha_data.return_value = captcha
78 m_get_captcha_data.return_value = captcha
79
79
80 response = app.get(ADMIN_PREFIX + '/register')
80 response = app.get(ADMIN_PREFIX + '/register')
81 assert 'PRIVATE_KEY' not in response
81 assert 'PRIVATE_KEY' not in response
82
82
83 @pytest.mark.parametrize('active', [False, True])
83 @pytest.mark.parametrize('active', [False, True])
84 @mock.patch.object(LoginView, '_get_captcha_data')
84 @mock.patch.object(LoginView, '_get_captcha_data')
85 def test_register_view_renders_captcha(
85 def test_register_view_renders_captcha(
86 self, m_get_captcha_data, active, app):
86 self, m_get_captcha_data, active, app):
87 captcha = CaptchaData(
87 captcha = CaptchaData(
88 active=active, private_key='PRIVATE_KEY', public_key='PUBLIC_KEY')
88 active=active, private_key='PRIVATE_KEY', public_key='PUBLIC_KEY')
89 m_get_captcha_data.return_value = captcha
89 m_get_captcha_data.return_value = captcha
90
90
91 response = app.get(ADMIN_PREFIX + '/register')
91 response = app.get(ADMIN_PREFIX + '/register')
92
92
93 assertr = AssertResponse(response)
93 assertr = AssertResponse(response)
94 if active:
94 if active:
95 assertr.one_element_exists('#recaptcha_field')
95 assertr.one_element_exists('#recaptcha_field')
96 else:
96 else:
97 assertr.no_element_exists('#recaptcha_field')
97 assertr.no_element_exists('#recaptcha_field')
98
98
99 @pytest.mark.parametrize('valid', [False, True])
99 @pytest.mark.parametrize('valid', [False, True])
100 @mock.patch('rhodecode.apps.login.views.submit')
100 @mock.patch('rhodecode.apps.login.views.submit')
101 @mock.patch.object(LoginView, '_get_captcha_data')
101 @mock.patch.object(LoginView, '_get_captcha_data')
102 def test_register_with_active_captcha(
102 def test_register_with_active_captcha(
103 self, m_get_captcha_data, m_submit, valid, app, csrf_token):
103 self, m_get_captcha_data, m_submit, valid, app, csrf_token):
104 captcha = CaptchaData(
104 captcha = CaptchaData(
105 active=True, private_key='PRIVATE_KEY', public_key='PUBLIC_KEY')
105 active=True, private_key='PRIVATE_KEY', public_key='PUBLIC_KEY')
106 m_get_captcha_data.return_value = captcha
106 m_get_captcha_data.return_value = captcha
107 m_response = mock.Mock()
107 m_response = mock.Mock()
108 m_response.is_valid = valid
108 m_response.is_valid = valid
109 m_submit.return_value = m_response
109 m_submit.return_value = m_response
110
110
111 params = {
111 params = {
112 'csrf_token': csrf_token,
112 'csrf_token': csrf_token,
113 'email': 'pytest@example.com',
113 'email': 'pytest@example.com',
114 'firstname': 'pytest-firstname',
114 'firstname': 'pytest-firstname',
115 'lastname': 'pytest-lastname',
115 'lastname': 'pytest-lastname',
116 'password': 'secret',
116 'password': 'secret',
117 'password_confirmation': 'secret',
117 'password_confirmation': 'secret',
118 'username': 'pytest',
118 'username': 'pytest',
119 }
119 }
120 response = app.post(ADMIN_PREFIX + '/register', params=params)
120 response = app.post(ADMIN_PREFIX + '/register', params=params)
121
121
122 if valid:
122 if valid:
123 # If we provided a valid captcha input we expect a successful
123 # If we provided a valid captcha input we expect a successful
124 # registration and redirect to the login page.
124 # registration and redirect to the login page.
125 assert response.status_int == 302
125 assert response.status_int == 302
126 assert 'location' in response.headers
126 assert 'location' in response.headers
127 assert ADMIN_PREFIX + '/login' in response.headers['location']
127 assert ADMIN_PREFIX + '/login' in response.headers['location']
128 else:
128 else:
129 # If captche input is invalid we expect to stay on the registration
129 # If captche input is invalid we expect to stay on the registration
130 # page with an error message displayed.
130 # page with an error message displayed.
131 assertr = AssertResponse(response)
131 assertr = AssertResponse(response)
132 assert response.status_int == 200
132 assert response.status_int == 200
133 assertr.one_element_exists('#recaptcha_field ~ span.error-message')
133 assertr.one_element_exists('#recaptcha_field ~ span.error-message')
@@ -1,46 +1,46 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 from rhodecode.config.routing import ADMIN_PREFIX
21 from rhodecode.apps._base import ADMIN_PREFIX
22
22
23
23
24 def admin_routes(config):
24 def admin_routes(config):
25 config.add_route(
25 config.add_route(
26 name='ops_ping',
26 name='ops_ping',
27 pattern='/ping')
27 pattern='/ping')
28 config.add_route(
28 config.add_route(
29 name='ops_error_test',
29 name='ops_error_test',
30 pattern='/error')
30 pattern='/error')
31 config.add_route(
31 config.add_route(
32 name='ops_redirect_test',
32 name='ops_redirect_test',
33 pattern='/redirect')
33 pattern='/redirect')
34
34
35
35
36 def includeme(config):
36 def includeme(config):
37
37
38 config.include(admin_routes, route_prefix=ADMIN_PREFIX + '/ops')
38 config.include(admin_routes, route_prefix=ADMIN_PREFIX + '/ops')
39 # make OLD entries from pylons work
39 # make OLD entries from pylons work
40 config.add_route(
40 config.add_route(
41 name='ops_ping_legacy', pattern=ADMIN_PREFIX + '/ping')
41 name='ops_ping_legacy', pattern=ADMIN_PREFIX + '/ping')
42 config.add_route(
42 config.add_route(
43 name='ops_error_test_legacy', pattern=ADMIN_PREFIX + '/error_test')
43 name='ops_error_test_legacy', pattern=ADMIN_PREFIX + '/error_test')
44
44
45 # Scan module for configuration decorators.
45 # Scan module for configuration decorators.
46 config.scan('.views', ignore='.tests')
46 config.scan('.views', ignore='.tests')
@@ -1,101 +1,101 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.apps._base 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 User groups /_admin prefixed routes
29 User groups /_admin prefixed routes
30 """
30 """
31
31
32 config.add_route(
32 config.add_route(
33 name='user_group_members_data',
33 name='user_group_members_data',
34 pattern='/user_groups/{user_group_id:\d+}/members',
34 pattern='/user_groups/{user_group_id:\d+}/members',
35 user_group_route=True)
35 user_group_route=True)
36
36
37 # user groups perms
37 # user groups perms
38 config.add_route(
38 config.add_route(
39 name='edit_user_group_perms_summary',
39 name='edit_user_group_perms_summary',
40 pattern='/user_groups/{user_group_id:\d+}/edit/permissions_summary',
40 pattern='/user_groups/{user_group_id:\d+}/edit/permissions_summary',
41 user_group_route=True)
41 user_group_route=True)
42 config.add_route(
42 config.add_route(
43 name='edit_user_group_perms_summary_json',
43 name='edit_user_group_perms_summary_json',
44 pattern='/user_groups/{user_group_id:\d+}/edit/permissions_summary/json',
44 pattern='/user_groups/{user_group_id:\d+}/edit/permissions_summary/json',
45 user_group_route=True)
45 user_group_route=True)
46
46
47 # user groups edit
47 # user groups edit
48 config.add_route(
48 config.add_route(
49 name='edit_user_group',
49 name='edit_user_group',
50 pattern='/user_groups/{user_group_id:\d+}/edit',
50 pattern='/user_groups/{user_group_id:\d+}/edit',
51 user_group_route=True)
51 user_group_route=True)
52
52
53 # user groups update
53 # user groups update
54 config.add_route(
54 config.add_route(
55 name='user_groups_update',
55 name='user_groups_update',
56 pattern='/user_groups/{user_group_id:\d+}/update',
56 pattern='/user_groups/{user_group_id:\d+}/update',
57 user_group_route=True)
57 user_group_route=True)
58
58
59 config.add_route(
59 config.add_route(
60 name='edit_user_group_global_perms',
60 name='edit_user_group_global_perms',
61 pattern='/user_groups/{user_group_id:\d+}/edit/global_permissions',
61 pattern='/user_groups/{user_group_id:\d+}/edit/global_permissions',
62 user_group_route=True)
62 user_group_route=True)
63
63
64 config.add_route(
64 config.add_route(
65 name='edit_user_group_global_perms_update',
65 name='edit_user_group_global_perms_update',
66 pattern='/user_groups/{user_group_id:\d+}/edit/global_permissions/update',
66 pattern='/user_groups/{user_group_id:\d+}/edit/global_permissions/update',
67 user_group_route=True)
67 user_group_route=True)
68
68
69 config.add_route(
69 config.add_route(
70 name='edit_user_group_perms',
70 name='edit_user_group_perms',
71 pattern='/user_groups/{user_group_id:\d+}/edit/permissions',
71 pattern='/user_groups/{user_group_id:\d+}/edit/permissions',
72 user_group_route=True)
72 user_group_route=True)
73
73
74 config.add_route(
74 config.add_route(
75 name='edit_user_group_perms_update',
75 name='edit_user_group_perms_update',
76 pattern='/user_groups/{user_group_id:\d+}/edit/permissions/update',
76 pattern='/user_groups/{user_group_id:\d+}/edit/permissions/update',
77 user_group_route=True)
77 user_group_route=True)
78
78
79 config.add_route(
79 config.add_route(
80 name='edit_user_group_advanced',
80 name='edit_user_group_advanced',
81 pattern='/user_groups/{user_group_id:\d+}/edit/advanced',
81 pattern='/user_groups/{user_group_id:\d+}/edit/advanced',
82 user_group_route=True)
82 user_group_route=True)
83
83
84 config.add_route(
84 config.add_route(
85 name='edit_user_group_advanced_sync',
85 name='edit_user_group_advanced_sync',
86 pattern='/user_groups/{user_group_id:\d+}/edit/advanced/sync',
86 pattern='/user_groups/{user_group_id:\d+}/edit/advanced/sync',
87 user_group_route=True)
87 user_group_route=True)
88
88
89 # user groups delete
89 # user groups delete
90 config.add_route(
90 config.add_route(
91 name='user_groups_delete',
91 name='user_groups_delete',
92 pattern='/user_groups/{user_group_id:\d+}/delete',
92 pattern='/user_groups/{user_group_id:\d+}/delete',
93 user_group_route=True)
93 user_group_route=True)
94
94
95
95
96 def includeme(config):
96 def includeme(config):
97 # main admin routes
97 # main admin routes
98 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
98 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
99
99
100 # Scan module for configuration decorators.
100 # Scan module for configuration decorators.
101 config.scan('.views', ignore='.tests')
101 config.scan('.views', ignore='.tests')
@@ -1,122 +1,122 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2017 RhodeCode GmbH
3 # Copyright (C) 2012-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 importlib
22 import importlib
23
23
24 from pkg_resources import iter_entry_points
24 from pkg_resources import iter_entry_points
25 from pyramid.authentication import SessionAuthenticationPolicy
25 from pyramid.authentication import SessionAuthenticationPolicy
26
26
27 from rhodecode.authentication.registry import AuthenticationPluginRegistry
27 from rhodecode.authentication.registry import AuthenticationPluginRegistry
28 from rhodecode.authentication.routes import root_factory
28 from rhodecode.authentication.routes import root_factory
29 from rhodecode.authentication.routes import AuthnRootResource
29 from rhodecode.authentication.routes import AuthnRootResource
30 from rhodecode.config.routing import ADMIN_PREFIX
30 from rhodecode.apps._base import ADMIN_PREFIX
31 from rhodecode.model.settings import SettingsModel
31 from rhodecode.model.settings import SettingsModel
32
32
33
33
34 log = logging.getLogger(__name__)
34 log = logging.getLogger(__name__)
35
35
36 # Plugin ID prefixes to distinct between normal and legacy plugins.
36 # Plugin ID prefixes to distinct between normal and legacy plugins.
37 plugin_prefix = 'egg:'
37 plugin_prefix = 'egg:'
38 legacy_plugin_prefix = 'py:'
38 legacy_plugin_prefix = 'py:'
39
39
40
40
41 # TODO: Currently this is only used to discover the authentication plugins.
41 # TODO: Currently this is only used to discover the authentication plugins.
42 # Later on this may be used in a generic way to look up and include all kinds
42 # Later on this may be used in a generic way to look up and include all kinds
43 # of supported enterprise plugins. Therefore this has to be moved and
43 # of supported enterprise plugins. Therefore this has to be moved and
44 # refactored to a real 'plugin look up' machinery.
44 # refactored to a real 'plugin look up' machinery.
45 # TODO: When refactoring this think about splitting it up into distinct
45 # TODO: When refactoring this think about splitting it up into distinct
46 # discover, load and include phases.
46 # discover, load and include phases.
47 def _discover_plugins(config, entry_point='enterprise.plugins1'):
47 def _discover_plugins(config, entry_point='enterprise.plugins1'):
48 for ep in iter_entry_points(entry_point):
48 for ep in iter_entry_points(entry_point):
49 plugin_id = '{}{}#{}'.format(
49 plugin_id = '{}{}#{}'.format(
50 plugin_prefix, ep.dist.project_name, ep.name)
50 plugin_prefix, ep.dist.project_name, ep.name)
51 log.debug('Plugin discovered: "%s"', plugin_id)
51 log.debug('Plugin discovered: "%s"', plugin_id)
52 try:
52 try:
53 module = ep.load()
53 module = ep.load()
54 plugin = module(plugin_id=plugin_id)
54 plugin = module(plugin_id=plugin_id)
55 config.include(plugin.includeme)
55 config.include(plugin.includeme)
56 except Exception as e:
56 except Exception as e:
57 log.exception(
57 log.exception(
58 'Exception while loading authentication plugin '
58 'Exception while loading authentication plugin '
59 '"{}": {}'.format(plugin_id, e.message))
59 '"{}": {}'.format(plugin_id, e.message))
60
60
61
61
62 def _import_legacy_plugin(plugin_id):
62 def _import_legacy_plugin(plugin_id):
63 module_name = plugin_id.split(legacy_plugin_prefix, 1)[-1]
63 module_name = plugin_id.split(legacy_plugin_prefix, 1)[-1]
64 module = importlib.import_module(module_name)
64 module = importlib.import_module(module_name)
65 return module.plugin_factory(plugin_id=plugin_id)
65 return module.plugin_factory(plugin_id=plugin_id)
66
66
67
67
68 def _discover_legacy_plugins(config, prefix=legacy_plugin_prefix):
68 def _discover_legacy_plugins(config, prefix=legacy_plugin_prefix):
69 """
69 """
70 Function that imports the legacy plugins stored in the 'auth_plugins'
70 Function that imports the legacy plugins stored in the 'auth_plugins'
71 setting in database which are using the specified prefix. Normally 'py:' is
71 setting in database which are using the specified prefix. Normally 'py:' is
72 used for the legacy plugins.
72 used for the legacy plugins.
73 """
73 """
74 auth_plugins = SettingsModel().get_setting_by_name('auth_plugins')
74 auth_plugins = SettingsModel().get_setting_by_name('auth_plugins')
75 enabled_plugins = auth_plugins.app_settings_value
75 enabled_plugins = auth_plugins.app_settings_value
76 legacy_plugins = [id_ for id_ in enabled_plugins if id_.startswith(prefix)]
76 legacy_plugins = [id_ for id_ in enabled_plugins if id_.startswith(prefix)]
77
77
78 for plugin_id in legacy_plugins:
78 for plugin_id in legacy_plugins:
79 log.debug('Legacy plugin discovered: "%s"', plugin_id)
79 log.debug('Legacy plugin discovered: "%s"', plugin_id)
80 try:
80 try:
81 plugin = _import_legacy_plugin(plugin_id)
81 plugin = _import_legacy_plugin(plugin_id)
82 config.include(plugin.includeme)
82 config.include(plugin.includeme)
83 except Exception as e:
83 except Exception as e:
84 log.exception(
84 log.exception(
85 'Exception while loading legacy authentication plugin '
85 'Exception while loading legacy authentication plugin '
86 '"{}": {}'.format(plugin_id, e.message))
86 '"{}": {}'.format(plugin_id, e.message))
87
87
88
88
89 def includeme(config):
89 def includeme(config):
90 # Set authentication policy.
90 # Set authentication policy.
91 authn_policy = SessionAuthenticationPolicy()
91 authn_policy = SessionAuthenticationPolicy()
92 config.set_authentication_policy(authn_policy)
92 config.set_authentication_policy(authn_policy)
93
93
94 # Create authentication plugin registry and add it to the pyramid registry.
94 # Create authentication plugin registry and add it to the pyramid registry.
95 authn_registry = AuthenticationPluginRegistry(config.get_settings())
95 authn_registry = AuthenticationPluginRegistry(config.get_settings())
96 config.add_directive('add_authn_plugin', authn_registry.add_authn_plugin)
96 config.add_directive('add_authn_plugin', authn_registry.add_authn_plugin)
97 config.registry.registerUtility(authn_registry)
97 config.registry.registerUtility(authn_registry)
98
98
99 # Create authentication traversal root resource.
99 # Create authentication traversal root resource.
100 authn_root_resource = root_factory()
100 authn_root_resource = root_factory()
101 config.add_directive('add_authn_resource',
101 config.add_directive('add_authn_resource',
102 authn_root_resource.add_authn_resource)
102 authn_root_resource.add_authn_resource)
103
103
104 # Add the authentication traversal route.
104 # Add the authentication traversal route.
105 config.add_route('auth_home',
105 config.add_route('auth_home',
106 ADMIN_PREFIX + '/auth*traverse',
106 ADMIN_PREFIX + '/auth*traverse',
107 factory=root_factory)
107 factory=root_factory)
108 # Add the authentication settings root views.
108 # Add the authentication settings root views.
109 config.add_view('rhodecode.authentication.views.AuthSettingsView',
109 config.add_view('rhodecode.authentication.views.AuthSettingsView',
110 attr='index',
110 attr='index',
111 request_method='GET',
111 request_method='GET',
112 route_name='auth_home',
112 route_name='auth_home',
113 context=AuthnRootResource)
113 context=AuthnRootResource)
114 config.add_view('rhodecode.authentication.views.AuthSettingsView',
114 config.add_view('rhodecode.authentication.views.AuthSettingsView',
115 attr='auth_settings',
115 attr='auth_settings',
116 request_method='POST',
116 request_method='POST',
117 route_name='auth_home',
117 route_name='auth_home',
118 context=AuthnRootResource)
118 context=AuthnRootResource)
119
119
120 # Auto discover authentication plugins and include their configuration.
120 # Auto discover authentication plugins and include their configuration.
121 _discover_plugins(config)
121 _discover_plugins(config)
122 _discover_legacy_plugins(config)
122 _discover_legacy_plugins(config)
@@ -1,77 +1,77 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 import pytest
22 import pytest
23
23
24 from rhodecode.authentication.tests.conftest import (
24 from rhodecode.authentication.tests.conftest import (
25 EnabledAuthPlugin, DisabledAuthPlugin)
25 EnabledAuthPlugin, DisabledAuthPlugin)
26 from rhodecode.config.routing import ADMIN_PREFIX
26 from rhodecode.apps._base import ADMIN_PREFIX
27
27
28
28
29 @pytest.mark.usefixtures('autologin_user', 'app')
29 @pytest.mark.usefixtures('autologin_user', 'app')
30 class TestAuthenticationSettings:
30 class TestAuthenticationSettings:
31
31
32 def test_auth_settings_global_view_get(self, app):
32 def test_auth_settings_global_view_get(self, app):
33 url = '{prefix}/auth/'.format(prefix=ADMIN_PREFIX)
33 url = '{prefix}/auth/'.format(prefix=ADMIN_PREFIX)
34 response = app.get(url)
34 response = app.get(url)
35 assert response.status_code == 200
35 assert response.status_code == 200
36
36
37 def test_plugin_settings_view_get(self, app, auth_plugin):
37 def test_plugin_settings_view_get(self, app, auth_plugin):
38 url = '{prefix}/auth/{name}'.format(
38 url = '{prefix}/auth/{name}'.format(
39 prefix=ADMIN_PREFIX,
39 prefix=ADMIN_PREFIX,
40 name=auth_plugin.name)
40 name=auth_plugin.name)
41 with EnabledAuthPlugin(auth_plugin):
41 with EnabledAuthPlugin(auth_plugin):
42 response = app.get(url)
42 response = app.get(url)
43 assert response.status_code == 200
43 assert response.status_code == 200
44
44
45 def test_plugin_settings_view_post(self, app, auth_plugin, csrf_token):
45 def test_plugin_settings_view_post(self, app, auth_plugin, csrf_token):
46 url = '{prefix}/auth/{name}'.format(
46 url = '{prefix}/auth/{name}'.format(
47 prefix=ADMIN_PREFIX,
47 prefix=ADMIN_PREFIX,
48 name=auth_plugin.name)
48 name=auth_plugin.name)
49 params = {
49 params = {
50 'enabled': True,
50 'enabled': True,
51 'cache_ttl': 0,
51 'cache_ttl': 0,
52 'csrf_token': csrf_token,
52 'csrf_token': csrf_token,
53 }
53 }
54 with EnabledAuthPlugin(auth_plugin):
54 with EnabledAuthPlugin(auth_plugin):
55 response = app.post(url, params=params)
55 response = app.post(url, params=params)
56 assert response.status_code in [200, 302]
56 assert response.status_code in [200, 302]
57
57
58 def test_plugin_settings_view_get_404(self, app, auth_plugin):
58 def test_plugin_settings_view_get_404(self, app, auth_plugin):
59 url = '{prefix}/auth/{name}'.format(
59 url = '{prefix}/auth/{name}'.format(
60 prefix=ADMIN_PREFIX,
60 prefix=ADMIN_PREFIX,
61 name=auth_plugin.name)
61 name=auth_plugin.name)
62 with DisabledAuthPlugin(auth_plugin):
62 with DisabledAuthPlugin(auth_plugin):
63 response = app.get(url, status=404)
63 response = app.get(url, status=404)
64 assert response.status_code == 404
64 assert response.status_code == 404
65
65
66 def test_plugin_settings_view_post_404(self, app, auth_plugin, csrf_token):
66 def test_plugin_settings_view_post_404(self, app, auth_plugin, csrf_token):
67 url = '{prefix}/auth/{name}'.format(
67 url = '{prefix}/auth/{name}'.format(
68 prefix=ADMIN_PREFIX,
68 prefix=ADMIN_PREFIX,
69 name=auth_plugin.name)
69 name=auth_plugin.name)
70 params = {
70 params = {
71 'enabled': True,
71 'enabled': True,
72 'cache_ttl': 0,
72 'cache_ttl': 0,
73 'csrf_token': csrf_token,
73 'csrf_token': csrf_token,
74 }
74 }
75 with DisabledAuthPlugin(auth_plugin):
75 with DisabledAuthPlugin(auth_plugin):
76 response = app.post(url, params=params, status=404)
76 response = app.post(url, params=params, status=404)
77 assert response.status_code == 404
77 assert response.status_code == 404
@@ -1,1122 +1,1122 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 Set of generic validators
22 Set of generic validators
23 """
23 """
24
24
25 import logging
25 import logging
26 import os
26 import os
27 import re
27 import re
28 from collections import defaultdict
28 from collections import defaultdict
29
29
30 import formencode
30 import formencode
31 import ipaddress
31 import ipaddress
32 from formencode.validators import (
32 from formencode.validators import (
33 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set,
33 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set,
34 NotEmpty, IPAddress, CIDR, String, FancyValidator
34 NotEmpty, IPAddress, CIDR, String, FancyValidator
35 )
35 )
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37 from sqlalchemy.sql.expression import true
37 from sqlalchemy.sql.expression import true
38 from sqlalchemy.util import OrderedSet
38 from sqlalchemy.util import OrderedSet
39 from webhelpers.pylonslib.secure_form import authentication_token
39 from webhelpers.pylonslib.secure_form import authentication_token
40
40
41 from rhodecode.authentication import (
41 from rhodecode.authentication import (
42 legacy_plugin_prefix, _import_legacy_plugin)
42 legacy_plugin_prefix, _import_legacy_plugin)
43 from rhodecode.authentication.base import loadplugin
43 from rhodecode.authentication.base import loadplugin
44 from rhodecode.config.routing import ADMIN_PREFIX
44 from rhodecode.apps._base import ADMIN_PREFIX
45 from rhodecode.lib.auth import HasRepoGroupPermissionAny, HasPermissionAny
45 from rhodecode.lib.auth import HasRepoGroupPermissionAny, HasPermissionAny
46 from rhodecode.lib.utils import repo_name_slug, make_db_config
46 from rhodecode.lib.utils import repo_name_slug, make_db_config
47 from rhodecode.lib.utils2 import safe_int, str2bool, aslist, md5, safe_unicode
47 from rhodecode.lib.utils2 import safe_int, str2bool, aslist, md5, safe_unicode
48 from rhodecode.lib.vcs.backends.git.repository import GitRepository
48 from rhodecode.lib.vcs.backends.git.repository import GitRepository
49 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
49 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
50 from rhodecode.lib.vcs.backends.svn.repository import SubversionRepository
50 from rhodecode.lib.vcs.backends.svn.repository import SubversionRepository
51 from rhodecode.model.db import (
51 from rhodecode.model.db import (
52 RepoGroup, Repository, UserGroup, User, ChangesetStatus, Gist)
52 RepoGroup, Repository, UserGroup, User, ChangesetStatus, Gist)
53 from rhodecode.model.settings import VcsSettingsModel
53 from rhodecode.model.settings import VcsSettingsModel
54
54
55 # silence warnings and pylint
55 # silence warnings and pylint
56 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \
56 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \
57 NotEmpty, IPAddress, CIDR, String, FancyValidator
57 NotEmpty, IPAddress, CIDR, String, FancyValidator
58
58
59 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
60
60
61
61
62 class _Missing(object):
62 class _Missing(object):
63 pass
63 pass
64
64
65 Missing = _Missing()
65 Missing = _Missing()
66
66
67
67
68 class StateObj(object):
68 class StateObj(object):
69 """
69 """
70 this is needed to translate the messages using _() in validators
70 this is needed to translate the messages using _() in validators
71 """
71 """
72 _ = staticmethod(_)
72 _ = staticmethod(_)
73
73
74
74
75 def M(self, key, state=None, **kwargs):
75 def M(self, key, state=None, **kwargs):
76 """
76 """
77 returns string from self.message based on given key,
77 returns string from self.message based on given key,
78 passed kw params are used to substitute %(named)s params inside
78 passed kw params are used to substitute %(named)s params inside
79 translated strings
79 translated strings
80
80
81 :param msg:
81 :param msg:
82 :param state:
82 :param state:
83 """
83 """
84 if state is None:
84 if state is None:
85 state = StateObj()
85 state = StateObj()
86 else:
86 else:
87 state._ = staticmethod(_)
87 state._ = staticmethod(_)
88 # inject validator into state object
88 # inject validator into state object
89 return self.message(key, state, **kwargs)
89 return self.message(key, state, **kwargs)
90
90
91
91
92 def UniqueList(convert=None):
92 def UniqueList(convert=None):
93 class _UniqueList(formencode.FancyValidator):
93 class _UniqueList(formencode.FancyValidator):
94 """
94 """
95 Unique List !
95 Unique List !
96 """
96 """
97 messages = {
97 messages = {
98 'empty': _(u'Value cannot be an empty list'),
98 'empty': _(u'Value cannot be an empty list'),
99 'missing_value': _(u'Value cannot be an empty list'),
99 'missing_value': _(u'Value cannot be an empty list'),
100 }
100 }
101
101
102 def _to_python(self, value, state):
102 def _to_python(self, value, state):
103 ret_val = []
103 ret_val = []
104
104
105 def make_unique(value):
105 def make_unique(value):
106 seen = []
106 seen = []
107 return [c for c in value if not (c in seen or seen.append(c))]
107 return [c for c in value if not (c in seen or seen.append(c))]
108
108
109 if isinstance(value, list):
109 if isinstance(value, list):
110 ret_val = make_unique(value)
110 ret_val = make_unique(value)
111 elif isinstance(value, set):
111 elif isinstance(value, set):
112 ret_val = make_unique(list(value))
112 ret_val = make_unique(list(value))
113 elif isinstance(value, tuple):
113 elif isinstance(value, tuple):
114 ret_val = make_unique(list(value))
114 ret_val = make_unique(list(value))
115 elif value is None:
115 elif value is None:
116 ret_val = []
116 ret_val = []
117 else:
117 else:
118 ret_val = [value]
118 ret_val = [value]
119
119
120 if convert:
120 if convert:
121 ret_val = map(convert, ret_val)
121 ret_val = map(convert, ret_val)
122 return ret_val
122 return ret_val
123
123
124 def empty_value(self, value):
124 def empty_value(self, value):
125 return []
125 return []
126
126
127 return _UniqueList
127 return _UniqueList
128
128
129
129
130 def UniqueListFromString():
130 def UniqueListFromString():
131 class _UniqueListFromString(UniqueList()):
131 class _UniqueListFromString(UniqueList()):
132 def _to_python(self, value, state):
132 def _to_python(self, value, state):
133 if isinstance(value, basestring):
133 if isinstance(value, basestring):
134 value = aslist(value, ',')
134 value = aslist(value, ',')
135 return super(_UniqueListFromString, self)._to_python(value, state)
135 return super(_UniqueListFromString, self)._to_python(value, state)
136 return _UniqueListFromString
136 return _UniqueListFromString
137
137
138
138
139 def ValidSvnPattern(section, repo_name=None):
139 def ValidSvnPattern(section, repo_name=None):
140 class _validator(formencode.validators.FancyValidator):
140 class _validator(formencode.validators.FancyValidator):
141 messages = {
141 messages = {
142 'pattern_exists': _(u'Pattern already exists'),
142 'pattern_exists': _(u'Pattern already exists'),
143 }
143 }
144
144
145 def validate_python(self, value, state):
145 def validate_python(self, value, state):
146 if not value:
146 if not value:
147 return
147 return
148 model = VcsSettingsModel(repo=repo_name)
148 model = VcsSettingsModel(repo=repo_name)
149 ui_settings = model.get_svn_patterns(section=section)
149 ui_settings = model.get_svn_patterns(section=section)
150 for entry in ui_settings:
150 for entry in ui_settings:
151 if value == entry.value:
151 if value == entry.value:
152 msg = M(self, 'pattern_exists', state)
152 msg = M(self, 'pattern_exists', state)
153 raise formencode.Invalid(msg, value, state)
153 raise formencode.Invalid(msg, value, state)
154 return _validator
154 return _validator
155
155
156
156
157 def ValidUsername(edit=False, old_data={}):
157 def ValidUsername(edit=False, old_data={}):
158 class _validator(formencode.validators.FancyValidator):
158 class _validator(formencode.validators.FancyValidator):
159 messages = {
159 messages = {
160 'username_exists': _(u'Username "%(username)s" already exists'),
160 'username_exists': _(u'Username "%(username)s" already exists'),
161 'system_invalid_username':
161 'system_invalid_username':
162 _(u'Username "%(username)s" is forbidden'),
162 _(u'Username "%(username)s" is forbidden'),
163 'invalid_username':
163 'invalid_username':
164 _(u'Username may only contain alphanumeric characters '
164 _(u'Username may only contain alphanumeric characters '
165 u'underscores, periods or dashes and must begin with '
165 u'underscores, periods or dashes and must begin with '
166 u'alphanumeric character or underscore')
166 u'alphanumeric character or underscore')
167 }
167 }
168
168
169 def validate_python(self, value, state):
169 def validate_python(self, value, state):
170 if value in ['default', 'new_user']:
170 if value in ['default', 'new_user']:
171 msg = M(self, 'system_invalid_username', state, username=value)
171 msg = M(self, 'system_invalid_username', state, username=value)
172 raise formencode.Invalid(msg, value, state)
172 raise formencode.Invalid(msg, value, state)
173 # check if user is unique
173 # check if user is unique
174 old_un = None
174 old_un = None
175 if edit:
175 if edit:
176 old_un = User.get(old_data.get('user_id')).username
176 old_un = User.get(old_data.get('user_id')).username
177
177
178 if old_un != value or not edit:
178 if old_un != value or not edit:
179 if User.get_by_username(value, case_insensitive=True):
179 if User.get_by_username(value, case_insensitive=True):
180 msg = M(self, 'username_exists', state, username=value)
180 msg = M(self, 'username_exists', state, username=value)
181 raise formencode.Invalid(msg, value, state)
181 raise formencode.Invalid(msg, value, state)
182
182
183 if (re.match(r'^[\w]{1}[\w\-\.]{0,254}$', value)
183 if (re.match(r'^[\w]{1}[\w\-\.]{0,254}$', value)
184 is None):
184 is None):
185 msg = M(self, 'invalid_username', state)
185 msg = M(self, 'invalid_username', state)
186 raise formencode.Invalid(msg, value, state)
186 raise formencode.Invalid(msg, value, state)
187 return _validator
187 return _validator
188
188
189
189
190 def ValidRegex(msg=None):
190 def ValidRegex(msg=None):
191 class _validator(formencode.validators.Regex):
191 class _validator(formencode.validators.Regex):
192 messages = {'invalid': msg or _(u'The input is not valid')}
192 messages = {'invalid': msg or _(u'The input is not valid')}
193 return _validator
193 return _validator
194
194
195
195
196 def ValidRepoUser(allow_disabled=False):
196 def ValidRepoUser(allow_disabled=False):
197 class _validator(formencode.validators.FancyValidator):
197 class _validator(formencode.validators.FancyValidator):
198 messages = {
198 messages = {
199 'invalid_username': _(u'Username %(username)s is not valid'),
199 'invalid_username': _(u'Username %(username)s is not valid'),
200 'disabled_username': _(u'Username %(username)s is disabled')
200 'disabled_username': _(u'Username %(username)s is disabled')
201 }
201 }
202
202
203 def validate_python(self, value, state):
203 def validate_python(self, value, state):
204 try:
204 try:
205 user = User.query().filter(User.username == value).one()
205 user = User.query().filter(User.username == value).one()
206 except Exception:
206 except Exception:
207 msg = M(self, 'invalid_username', state, username=value)
207 msg = M(self, 'invalid_username', state, username=value)
208 raise formencode.Invalid(
208 raise formencode.Invalid(
209 msg, value, state, error_dict={'username': msg}
209 msg, value, state, error_dict={'username': msg}
210 )
210 )
211 if user and (not allow_disabled and not user.active):
211 if user and (not allow_disabled and not user.active):
212 msg = M(self, 'disabled_username', state, username=value)
212 msg = M(self, 'disabled_username', state, username=value)
213 raise formencode.Invalid(
213 raise formencode.Invalid(
214 msg, value, state, error_dict={'username': msg}
214 msg, value, state, error_dict={'username': msg}
215 )
215 )
216
216
217 return _validator
217 return _validator
218
218
219
219
220 def ValidUserGroup(edit=False, old_data={}):
220 def ValidUserGroup(edit=False, old_data={}):
221 class _validator(formencode.validators.FancyValidator):
221 class _validator(formencode.validators.FancyValidator):
222 messages = {
222 messages = {
223 'invalid_group': _(u'Invalid user group name'),
223 'invalid_group': _(u'Invalid user group name'),
224 'group_exist': _(u'User group `%(usergroup)s` already exists'),
224 'group_exist': _(u'User group `%(usergroup)s` already exists'),
225 'invalid_usergroup_name':
225 'invalid_usergroup_name':
226 _(u'user group name may only contain alphanumeric '
226 _(u'user group name may only contain alphanumeric '
227 u'characters underscores, periods or dashes and must begin '
227 u'characters underscores, periods or dashes and must begin '
228 u'with alphanumeric character')
228 u'with alphanumeric character')
229 }
229 }
230
230
231 def validate_python(self, value, state):
231 def validate_python(self, value, state):
232 if value in ['default']:
232 if value in ['default']:
233 msg = M(self, 'invalid_group', state)
233 msg = M(self, 'invalid_group', state)
234 raise formencode.Invalid(
234 raise formencode.Invalid(
235 msg, value, state, error_dict={'users_group_name': msg}
235 msg, value, state, error_dict={'users_group_name': msg}
236 )
236 )
237 # check if group is unique
237 # check if group is unique
238 old_ugname = None
238 old_ugname = None
239 if edit:
239 if edit:
240 old_id = old_data.get('users_group_id')
240 old_id = old_data.get('users_group_id')
241 old_ugname = UserGroup.get(old_id).users_group_name
241 old_ugname = UserGroup.get(old_id).users_group_name
242
242
243 if old_ugname != value or not edit:
243 if old_ugname != value or not edit:
244 is_existing_group = UserGroup.get_by_group_name(
244 is_existing_group = UserGroup.get_by_group_name(
245 value, case_insensitive=True)
245 value, case_insensitive=True)
246 if is_existing_group:
246 if is_existing_group:
247 msg = M(self, 'group_exist', state, usergroup=value)
247 msg = M(self, 'group_exist', state, usergroup=value)
248 raise formencode.Invalid(
248 raise formencode.Invalid(
249 msg, value, state, error_dict={'users_group_name': msg}
249 msg, value, state, error_dict={'users_group_name': msg}
250 )
250 )
251
251
252 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
252 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
253 msg = M(self, 'invalid_usergroup_name', state)
253 msg = M(self, 'invalid_usergroup_name', state)
254 raise formencode.Invalid(
254 raise formencode.Invalid(
255 msg, value, state, error_dict={'users_group_name': msg}
255 msg, value, state, error_dict={'users_group_name': msg}
256 )
256 )
257
257
258 return _validator
258 return _validator
259
259
260
260
261 def ValidRepoGroup(edit=False, old_data={}, can_create_in_root=False):
261 def ValidRepoGroup(edit=False, old_data={}, can_create_in_root=False):
262 class _validator(formencode.validators.FancyValidator):
262 class _validator(formencode.validators.FancyValidator):
263 messages = {
263 messages = {
264 'group_parent_id': _(u'Cannot assign this group as parent'),
264 'group_parent_id': _(u'Cannot assign this group as parent'),
265 'group_exists': _(u'Group "%(group_name)s" already exists'),
265 'group_exists': _(u'Group "%(group_name)s" already exists'),
266 'repo_exists': _(u'Repository with name "%(group_name)s" '
266 'repo_exists': _(u'Repository with name "%(group_name)s" '
267 u'already exists'),
267 u'already exists'),
268 'permission_denied': _(u"no permission to store repository group"
268 'permission_denied': _(u"no permission to store repository group"
269 u"in this location"),
269 u"in this location"),
270 'permission_denied_root': _(
270 'permission_denied_root': _(
271 u"no permission to store repository group "
271 u"no permission to store repository group "
272 u"in root location")
272 u"in root location")
273 }
273 }
274
274
275 def _to_python(self, value, state):
275 def _to_python(self, value, state):
276 group_name = repo_name_slug(value.get('group_name', ''))
276 group_name = repo_name_slug(value.get('group_name', ''))
277 group_parent_id = safe_int(value.get('group_parent_id'))
277 group_parent_id = safe_int(value.get('group_parent_id'))
278 gr = RepoGroup.get(group_parent_id)
278 gr = RepoGroup.get(group_parent_id)
279 if gr:
279 if gr:
280 parent_group_path = gr.full_path
280 parent_group_path = gr.full_path
281 # value needs to be aware of group name in order to check
281 # value needs to be aware of group name in order to check
282 # db key This is an actual just the name to store in the
282 # db key This is an actual just the name to store in the
283 # database
283 # database
284 group_name_full = (
284 group_name_full = (
285 parent_group_path + RepoGroup.url_sep() + group_name)
285 parent_group_path + RepoGroup.url_sep() + group_name)
286 else:
286 else:
287 group_name_full = group_name
287 group_name_full = group_name
288
288
289 value['group_name'] = group_name
289 value['group_name'] = group_name
290 value['group_name_full'] = group_name_full
290 value['group_name_full'] = group_name_full
291 value['group_parent_id'] = group_parent_id
291 value['group_parent_id'] = group_parent_id
292 return value
292 return value
293
293
294 def validate_python(self, value, state):
294 def validate_python(self, value, state):
295
295
296 old_group_name = None
296 old_group_name = None
297 group_name = value.get('group_name')
297 group_name = value.get('group_name')
298 group_name_full = value.get('group_name_full')
298 group_name_full = value.get('group_name_full')
299 group_parent_id = safe_int(value.get('group_parent_id'))
299 group_parent_id = safe_int(value.get('group_parent_id'))
300 if group_parent_id == -1:
300 if group_parent_id == -1:
301 group_parent_id = None
301 group_parent_id = None
302
302
303 group_obj = RepoGroup.get(old_data.get('group_id'))
303 group_obj = RepoGroup.get(old_data.get('group_id'))
304 parent_group_changed = False
304 parent_group_changed = False
305 if edit:
305 if edit:
306 old_group_name = group_obj.group_name
306 old_group_name = group_obj.group_name
307 old_group_parent_id = group_obj.group_parent_id
307 old_group_parent_id = group_obj.group_parent_id
308
308
309 if group_parent_id != old_group_parent_id:
309 if group_parent_id != old_group_parent_id:
310 parent_group_changed = True
310 parent_group_changed = True
311
311
312 # TODO: mikhail: the following if statement is not reached
312 # TODO: mikhail: the following if statement is not reached
313 # since group_parent_id's OneOf validation fails before.
313 # since group_parent_id's OneOf validation fails before.
314 # Can be removed.
314 # Can be removed.
315
315
316 # check against setting a parent of self
316 # check against setting a parent of self
317 parent_of_self = (
317 parent_of_self = (
318 old_data['group_id'] == group_parent_id
318 old_data['group_id'] == group_parent_id
319 if group_parent_id else False
319 if group_parent_id else False
320 )
320 )
321 if parent_of_self:
321 if parent_of_self:
322 msg = M(self, 'group_parent_id', state)
322 msg = M(self, 'group_parent_id', state)
323 raise formencode.Invalid(
323 raise formencode.Invalid(
324 msg, value, state, error_dict={'group_parent_id': msg}
324 msg, value, state, error_dict={'group_parent_id': msg}
325 )
325 )
326
326
327 # group we're moving current group inside
327 # group we're moving current group inside
328 child_group = None
328 child_group = None
329 if group_parent_id:
329 if group_parent_id:
330 child_group = RepoGroup.query().filter(
330 child_group = RepoGroup.query().filter(
331 RepoGroup.group_id == group_parent_id).scalar()
331 RepoGroup.group_id == group_parent_id).scalar()
332
332
333 # do a special check that we cannot move a group to one of
333 # do a special check that we cannot move a group to one of
334 # it's children
334 # it's children
335 if edit and child_group:
335 if edit and child_group:
336 parents = [x.group_id for x in child_group.parents]
336 parents = [x.group_id for x in child_group.parents]
337 move_to_children = old_data['group_id'] in parents
337 move_to_children = old_data['group_id'] in parents
338 if move_to_children:
338 if move_to_children:
339 msg = M(self, 'group_parent_id', state)
339 msg = M(self, 'group_parent_id', state)
340 raise formencode.Invalid(
340 raise formencode.Invalid(
341 msg, value, state, error_dict={'group_parent_id': msg})
341 msg, value, state, error_dict={'group_parent_id': msg})
342
342
343 # Check if we have permission to store in the parent.
343 # Check if we have permission to store in the parent.
344 # Only check if the parent group changed.
344 # Only check if the parent group changed.
345 if parent_group_changed:
345 if parent_group_changed:
346 if child_group is None:
346 if child_group is None:
347 if not can_create_in_root:
347 if not can_create_in_root:
348 msg = M(self, 'permission_denied_root', state)
348 msg = M(self, 'permission_denied_root', state)
349 raise formencode.Invalid(
349 raise formencode.Invalid(
350 msg, value, state,
350 msg, value, state,
351 error_dict={'group_parent_id': msg})
351 error_dict={'group_parent_id': msg})
352 else:
352 else:
353 valid = HasRepoGroupPermissionAny('group.admin')
353 valid = HasRepoGroupPermissionAny('group.admin')
354 forbidden = not valid(
354 forbidden = not valid(
355 child_group.group_name, 'can create group validator')
355 child_group.group_name, 'can create group validator')
356 if forbidden:
356 if forbidden:
357 msg = M(self, 'permission_denied', state)
357 msg = M(self, 'permission_denied', state)
358 raise formencode.Invalid(
358 raise formencode.Invalid(
359 msg, value, state,
359 msg, value, state,
360 error_dict={'group_parent_id': msg})
360 error_dict={'group_parent_id': msg})
361
361
362 # if we change the name or it's new group, check for existing names
362 # if we change the name or it's new group, check for existing names
363 # or repositories with the same name
363 # or repositories with the same name
364 if old_group_name != group_name_full or not edit:
364 if old_group_name != group_name_full or not edit:
365 # check group
365 # check group
366 gr = RepoGroup.get_by_group_name(group_name_full)
366 gr = RepoGroup.get_by_group_name(group_name_full)
367 if gr:
367 if gr:
368 msg = M(self, 'group_exists', state, group_name=group_name)
368 msg = M(self, 'group_exists', state, group_name=group_name)
369 raise formencode.Invalid(
369 raise formencode.Invalid(
370 msg, value, state, error_dict={'group_name': msg})
370 msg, value, state, error_dict={'group_name': msg})
371
371
372 # check for same repo
372 # check for same repo
373 repo = Repository.get_by_repo_name(group_name_full)
373 repo = Repository.get_by_repo_name(group_name_full)
374 if repo:
374 if repo:
375 msg = M(self, 'repo_exists', state, group_name=group_name)
375 msg = M(self, 'repo_exists', state, group_name=group_name)
376 raise formencode.Invalid(
376 raise formencode.Invalid(
377 msg, value, state, error_dict={'group_name': msg})
377 msg, value, state, error_dict={'group_name': msg})
378
378
379 return _validator
379 return _validator
380
380
381
381
382 def ValidPassword():
382 def ValidPassword():
383 class _validator(formencode.validators.FancyValidator):
383 class _validator(formencode.validators.FancyValidator):
384 messages = {
384 messages = {
385 'invalid_password':
385 'invalid_password':
386 _(u'Invalid characters (non-ascii) in password')
386 _(u'Invalid characters (non-ascii) in password')
387 }
387 }
388
388
389 def validate_python(self, value, state):
389 def validate_python(self, value, state):
390 try:
390 try:
391 (value or '').decode('ascii')
391 (value or '').decode('ascii')
392 except UnicodeError:
392 except UnicodeError:
393 msg = M(self, 'invalid_password', state)
393 msg = M(self, 'invalid_password', state)
394 raise formencode.Invalid(msg, value, state,)
394 raise formencode.Invalid(msg, value, state,)
395 return _validator
395 return _validator
396
396
397
397
398 def ValidOldPassword(username):
398 def ValidOldPassword(username):
399 class _validator(formencode.validators.FancyValidator):
399 class _validator(formencode.validators.FancyValidator):
400 messages = {
400 messages = {
401 'invalid_password': _(u'Invalid old password')
401 'invalid_password': _(u'Invalid old password')
402 }
402 }
403
403
404 def validate_python(self, value, state):
404 def validate_python(self, value, state):
405 from rhodecode.authentication.base import authenticate, HTTP_TYPE
405 from rhodecode.authentication.base import authenticate, HTTP_TYPE
406 if not authenticate(username, value, '', HTTP_TYPE):
406 if not authenticate(username, value, '', HTTP_TYPE):
407 msg = M(self, 'invalid_password', state)
407 msg = M(self, 'invalid_password', state)
408 raise formencode.Invalid(
408 raise formencode.Invalid(
409 msg, value, state, error_dict={'current_password': msg}
409 msg, value, state, error_dict={'current_password': msg}
410 )
410 )
411 return _validator
411 return _validator
412
412
413
413
414 def ValidPasswordsMatch(
414 def ValidPasswordsMatch(
415 passwd='new_password', passwd_confirmation='password_confirmation'):
415 passwd='new_password', passwd_confirmation='password_confirmation'):
416 class _validator(formencode.validators.FancyValidator):
416 class _validator(formencode.validators.FancyValidator):
417 messages = {
417 messages = {
418 'password_mismatch': _(u'Passwords do not match'),
418 'password_mismatch': _(u'Passwords do not match'),
419 }
419 }
420
420
421 def validate_python(self, value, state):
421 def validate_python(self, value, state):
422
422
423 pass_val = value.get('password') or value.get(passwd)
423 pass_val = value.get('password') or value.get(passwd)
424 if pass_val != value[passwd_confirmation]:
424 if pass_val != value[passwd_confirmation]:
425 msg = M(self, 'password_mismatch', state)
425 msg = M(self, 'password_mismatch', state)
426 raise formencode.Invalid(
426 raise formencode.Invalid(
427 msg, value, state,
427 msg, value, state,
428 error_dict={passwd: msg, passwd_confirmation: msg}
428 error_dict={passwd: msg, passwd_confirmation: msg}
429 )
429 )
430 return _validator
430 return _validator
431
431
432
432
433 def ValidAuth():
433 def ValidAuth():
434 class _validator(formencode.validators.FancyValidator):
434 class _validator(formencode.validators.FancyValidator):
435 messages = {
435 messages = {
436 'invalid_password': _(u'invalid password'),
436 'invalid_password': _(u'invalid password'),
437 'invalid_username': _(u'invalid user name'),
437 'invalid_username': _(u'invalid user name'),
438 'disabled_account': _(u'Your account is disabled')
438 'disabled_account': _(u'Your account is disabled')
439 }
439 }
440
440
441 def validate_python(self, value, state):
441 def validate_python(self, value, state):
442 from rhodecode.authentication.base import authenticate, HTTP_TYPE
442 from rhodecode.authentication.base import authenticate, HTTP_TYPE
443
443
444 password = value['password']
444 password = value['password']
445 username = value['username']
445 username = value['username']
446
446
447 if not authenticate(username, password, '', HTTP_TYPE,
447 if not authenticate(username, password, '', HTTP_TYPE,
448 skip_missing=True):
448 skip_missing=True):
449 user = User.get_by_username(username)
449 user = User.get_by_username(username)
450 if user and not user.active:
450 if user and not user.active:
451 log.warning('user %s is disabled', username)
451 log.warning('user %s is disabled', username)
452 msg = M(self, 'disabled_account', state)
452 msg = M(self, 'disabled_account', state)
453 raise formencode.Invalid(
453 raise formencode.Invalid(
454 msg, value, state, error_dict={'username': msg}
454 msg, value, state, error_dict={'username': msg}
455 )
455 )
456 else:
456 else:
457 log.warning('user `%s` failed to authenticate', username)
457 log.warning('user `%s` failed to authenticate', username)
458 msg = M(self, 'invalid_username', state)
458 msg = M(self, 'invalid_username', state)
459 msg2 = M(self, 'invalid_password', state)
459 msg2 = M(self, 'invalid_password', state)
460 raise formencode.Invalid(
460 raise formencode.Invalid(
461 msg, value, state,
461 msg, value, state,
462 error_dict={'username': msg, 'password': msg2}
462 error_dict={'username': msg, 'password': msg2}
463 )
463 )
464 return _validator
464 return _validator
465
465
466
466
467 def ValidAuthToken():
467 def ValidAuthToken():
468 class _validator(formencode.validators.FancyValidator):
468 class _validator(formencode.validators.FancyValidator):
469 messages = {
469 messages = {
470 'invalid_token': _(u'Token mismatch')
470 'invalid_token': _(u'Token mismatch')
471 }
471 }
472
472
473 def validate_python(self, value, state):
473 def validate_python(self, value, state):
474 if value != authentication_token():
474 if value != authentication_token():
475 msg = M(self, 'invalid_token', state)
475 msg = M(self, 'invalid_token', state)
476 raise formencode.Invalid(msg, value, state)
476 raise formencode.Invalid(msg, value, state)
477 return _validator
477 return _validator
478
478
479
479
480 def ValidRepoName(edit=False, old_data={}):
480 def ValidRepoName(edit=False, old_data={}):
481 class _validator(formencode.validators.FancyValidator):
481 class _validator(formencode.validators.FancyValidator):
482 messages = {
482 messages = {
483 'invalid_repo_name':
483 'invalid_repo_name':
484 _(u'Repository name %(repo)s is disallowed'),
484 _(u'Repository name %(repo)s is disallowed'),
485 # top level
485 # top level
486 'repository_exists': _(u'Repository with name %(repo)s '
486 'repository_exists': _(u'Repository with name %(repo)s '
487 u'already exists'),
487 u'already exists'),
488 'group_exists': _(u'Repository group with name "%(repo)s" '
488 'group_exists': _(u'Repository group with name "%(repo)s" '
489 u'already exists'),
489 u'already exists'),
490 # inside a group
490 # inside a group
491 'repository_in_group_exists': _(u'Repository with name %(repo)s '
491 'repository_in_group_exists': _(u'Repository with name %(repo)s '
492 u'exists in group "%(group)s"'),
492 u'exists in group "%(group)s"'),
493 'group_in_group_exists': _(
493 'group_in_group_exists': _(
494 u'Repository group with name "%(repo)s" '
494 u'Repository group with name "%(repo)s" '
495 u'exists in group "%(group)s"'),
495 u'exists in group "%(group)s"'),
496 }
496 }
497
497
498 def _to_python(self, value, state):
498 def _to_python(self, value, state):
499 repo_name = repo_name_slug(value.get('repo_name', ''))
499 repo_name = repo_name_slug(value.get('repo_name', ''))
500 repo_group = value.get('repo_group')
500 repo_group = value.get('repo_group')
501 if repo_group:
501 if repo_group:
502 gr = RepoGroup.get(repo_group)
502 gr = RepoGroup.get(repo_group)
503 group_path = gr.full_path
503 group_path = gr.full_path
504 group_name = gr.group_name
504 group_name = gr.group_name
505 # value needs to be aware of group name in order to check
505 # value needs to be aware of group name in order to check
506 # db key This is an actual just the name to store in the
506 # db key This is an actual just the name to store in the
507 # database
507 # database
508 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
508 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
509 else:
509 else:
510 group_name = group_path = ''
510 group_name = group_path = ''
511 repo_name_full = repo_name
511 repo_name_full = repo_name
512
512
513 value['repo_name'] = repo_name
513 value['repo_name'] = repo_name
514 value['repo_name_full'] = repo_name_full
514 value['repo_name_full'] = repo_name_full
515 value['group_path'] = group_path
515 value['group_path'] = group_path
516 value['group_name'] = group_name
516 value['group_name'] = group_name
517 return value
517 return value
518
518
519 def validate_python(self, value, state):
519 def validate_python(self, value, state):
520
520
521 repo_name = value.get('repo_name')
521 repo_name = value.get('repo_name')
522 repo_name_full = value.get('repo_name_full')
522 repo_name_full = value.get('repo_name_full')
523 group_path = value.get('group_path')
523 group_path = value.get('group_path')
524 group_name = value.get('group_name')
524 group_name = value.get('group_name')
525
525
526 if repo_name in [ADMIN_PREFIX, '']:
526 if repo_name in [ADMIN_PREFIX, '']:
527 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
527 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
528 raise formencode.Invalid(
528 raise formencode.Invalid(
529 msg, value, state, error_dict={'repo_name': msg})
529 msg, value, state, error_dict={'repo_name': msg})
530
530
531 rename = old_data.get('repo_name') != repo_name_full
531 rename = old_data.get('repo_name') != repo_name_full
532 create = not edit
532 create = not edit
533 if rename or create:
533 if rename or create:
534
534
535 if group_path:
535 if group_path:
536 if Repository.get_by_repo_name(repo_name_full):
536 if Repository.get_by_repo_name(repo_name_full):
537 msg = M(self, 'repository_in_group_exists', state,
537 msg = M(self, 'repository_in_group_exists', state,
538 repo=repo_name, group=group_name)
538 repo=repo_name, group=group_name)
539 raise formencode.Invalid(
539 raise formencode.Invalid(
540 msg, value, state, error_dict={'repo_name': msg})
540 msg, value, state, error_dict={'repo_name': msg})
541 if RepoGroup.get_by_group_name(repo_name_full):
541 if RepoGroup.get_by_group_name(repo_name_full):
542 msg = M(self, 'group_in_group_exists', state,
542 msg = M(self, 'group_in_group_exists', state,
543 repo=repo_name, group=group_name)
543 repo=repo_name, group=group_name)
544 raise formencode.Invalid(
544 raise formencode.Invalid(
545 msg, value, state, error_dict={'repo_name': msg})
545 msg, value, state, error_dict={'repo_name': msg})
546 else:
546 else:
547 if RepoGroup.get_by_group_name(repo_name_full):
547 if RepoGroup.get_by_group_name(repo_name_full):
548 msg = M(self, 'group_exists', state, repo=repo_name)
548 msg = M(self, 'group_exists', state, repo=repo_name)
549 raise formencode.Invalid(
549 raise formencode.Invalid(
550 msg, value, state, error_dict={'repo_name': msg})
550 msg, value, state, error_dict={'repo_name': msg})
551
551
552 if Repository.get_by_repo_name(repo_name_full):
552 if Repository.get_by_repo_name(repo_name_full):
553 msg = M(
553 msg = M(
554 self, 'repository_exists', state, repo=repo_name)
554 self, 'repository_exists', state, repo=repo_name)
555 raise formencode.Invalid(
555 raise formencode.Invalid(
556 msg, value, state, error_dict={'repo_name': msg})
556 msg, value, state, error_dict={'repo_name': msg})
557 return value
557 return value
558 return _validator
558 return _validator
559
559
560
560
561 def ValidForkName(*args, **kwargs):
561 def ValidForkName(*args, **kwargs):
562 return ValidRepoName(*args, **kwargs)
562 return ValidRepoName(*args, **kwargs)
563
563
564
564
565 def SlugifyName():
565 def SlugifyName():
566 class _validator(formencode.validators.FancyValidator):
566 class _validator(formencode.validators.FancyValidator):
567
567
568 def _to_python(self, value, state):
568 def _to_python(self, value, state):
569 return repo_name_slug(value)
569 return repo_name_slug(value)
570
570
571 def validate_python(self, value, state):
571 def validate_python(self, value, state):
572 pass
572 pass
573
573
574 return _validator
574 return _validator
575
575
576
576
577 def CannotHaveGitSuffix():
577 def CannotHaveGitSuffix():
578 class _validator(formencode.validators.FancyValidator):
578 class _validator(formencode.validators.FancyValidator):
579 messages = {
579 messages = {
580 'has_git_suffix':
580 'has_git_suffix':
581 _(u'Repository name cannot end with .git'),
581 _(u'Repository name cannot end with .git'),
582 }
582 }
583
583
584 def _to_python(self, value, state):
584 def _to_python(self, value, state):
585 return value
585 return value
586
586
587 def validate_python(self, value, state):
587 def validate_python(self, value, state):
588 if value and value.endswith('.git'):
588 if value and value.endswith('.git'):
589 msg = M(
589 msg = M(
590 self, 'has_git_suffix', state)
590 self, 'has_git_suffix', state)
591 raise formencode.Invalid(
591 raise formencode.Invalid(
592 msg, value, state, error_dict={'repo_name': msg})
592 msg, value, state, error_dict={'repo_name': msg})
593
593
594 return _validator
594 return _validator
595
595
596
596
597 def ValidCloneUri():
597 def ValidCloneUri():
598 class InvalidCloneUrl(Exception):
598 class InvalidCloneUrl(Exception):
599 allowed_prefixes = ()
599 allowed_prefixes = ()
600
600
601 def url_handler(repo_type, url):
601 def url_handler(repo_type, url):
602 config = make_db_config(clear_session=False)
602 config = make_db_config(clear_session=False)
603 if repo_type == 'hg':
603 if repo_type == 'hg':
604 allowed_prefixes = ('http', 'svn+http', 'git+http')
604 allowed_prefixes = ('http', 'svn+http', 'git+http')
605
605
606 if 'http' in url[:4]:
606 if 'http' in url[:4]:
607 # initially check if it's at least the proper URL
607 # initially check if it's at least the proper URL
608 # or does it pass basic auth
608 # or does it pass basic auth
609 MercurialRepository.check_url(url, config)
609 MercurialRepository.check_url(url, config)
610 elif 'svn+http' in url[:8]: # svn->hg import
610 elif 'svn+http' in url[:8]: # svn->hg import
611 SubversionRepository.check_url(url, config)
611 SubversionRepository.check_url(url, config)
612 elif 'git+http' in url[:8]: # git->hg import
612 elif 'git+http' in url[:8]: # git->hg import
613 raise NotImplementedError()
613 raise NotImplementedError()
614 else:
614 else:
615 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
615 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
616 'Allowed url must start with one of %s'
616 'Allowed url must start with one of %s'
617 % (url, ','.join(allowed_prefixes)))
617 % (url, ','.join(allowed_prefixes)))
618 exc.allowed_prefixes = allowed_prefixes
618 exc.allowed_prefixes = allowed_prefixes
619 raise exc
619 raise exc
620
620
621 elif repo_type == 'git':
621 elif repo_type == 'git':
622 allowed_prefixes = ('http', 'svn+http', 'hg+http')
622 allowed_prefixes = ('http', 'svn+http', 'hg+http')
623 if 'http' in url[:4]:
623 if 'http' in url[:4]:
624 # initially check if it's at least the proper URL
624 # initially check if it's at least the proper URL
625 # or does it pass basic auth
625 # or does it pass basic auth
626 GitRepository.check_url(url, config)
626 GitRepository.check_url(url, config)
627 elif 'svn+http' in url[:8]: # svn->git import
627 elif 'svn+http' in url[:8]: # svn->git import
628 raise NotImplementedError()
628 raise NotImplementedError()
629 elif 'hg+http' in url[:8]: # hg->git import
629 elif 'hg+http' in url[:8]: # hg->git import
630 raise NotImplementedError()
630 raise NotImplementedError()
631 else:
631 else:
632 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
632 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
633 'Allowed url must start with one of %s'
633 'Allowed url must start with one of %s'
634 % (url, ','.join(allowed_prefixes)))
634 % (url, ','.join(allowed_prefixes)))
635 exc.allowed_prefixes = allowed_prefixes
635 exc.allowed_prefixes = allowed_prefixes
636 raise exc
636 raise exc
637
637
638 class _validator(formencode.validators.FancyValidator):
638 class _validator(formencode.validators.FancyValidator):
639 messages = {
639 messages = {
640 'clone_uri': _(u'invalid clone url for %(rtype)s repository'),
640 'clone_uri': _(u'invalid clone url for %(rtype)s repository'),
641 'invalid_clone_uri': _(
641 'invalid_clone_uri': _(
642 u'Invalid clone url, provide a valid clone '
642 u'Invalid clone url, provide a valid clone '
643 u'url starting with one of %(allowed_prefixes)s')
643 u'url starting with one of %(allowed_prefixes)s')
644 }
644 }
645
645
646 def validate_python(self, value, state):
646 def validate_python(self, value, state):
647 repo_type = value.get('repo_type')
647 repo_type = value.get('repo_type')
648 url = value.get('clone_uri')
648 url = value.get('clone_uri')
649
649
650 if url:
650 if url:
651 try:
651 try:
652 url_handler(repo_type, url)
652 url_handler(repo_type, url)
653 except InvalidCloneUrl as e:
653 except InvalidCloneUrl as e:
654 log.warning(e)
654 log.warning(e)
655 msg = M(self, 'invalid_clone_uri', rtype=repo_type,
655 msg = M(self, 'invalid_clone_uri', rtype=repo_type,
656 allowed_prefixes=','.join(e.allowed_prefixes))
656 allowed_prefixes=','.join(e.allowed_prefixes))
657 raise formencode.Invalid(msg, value, state,
657 raise formencode.Invalid(msg, value, state,
658 error_dict={'clone_uri': msg})
658 error_dict={'clone_uri': msg})
659 except Exception:
659 except Exception:
660 log.exception('Url validation failed')
660 log.exception('Url validation failed')
661 msg = M(self, 'clone_uri', rtype=repo_type)
661 msg = M(self, 'clone_uri', rtype=repo_type)
662 raise formencode.Invalid(msg, value, state,
662 raise formencode.Invalid(msg, value, state,
663 error_dict={'clone_uri': msg})
663 error_dict={'clone_uri': msg})
664 return _validator
664 return _validator
665
665
666
666
667 def ValidForkType(old_data={}):
667 def ValidForkType(old_data={}):
668 class _validator(formencode.validators.FancyValidator):
668 class _validator(formencode.validators.FancyValidator):
669 messages = {
669 messages = {
670 'invalid_fork_type': _(u'Fork have to be the same type as parent')
670 'invalid_fork_type': _(u'Fork have to be the same type as parent')
671 }
671 }
672
672
673 def validate_python(self, value, state):
673 def validate_python(self, value, state):
674 if old_data['repo_type'] != value:
674 if old_data['repo_type'] != value:
675 msg = M(self, 'invalid_fork_type', state)
675 msg = M(self, 'invalid_fork_type', state)
676 raise formencode.Invalid(
676 raise formencode.Invalid(
677 msg, value, state, error_dict={'repo_type': msg}
677 msg, value, state, error_dict={'repo_type': msg}
678 )
678 )
679 return _validator
679 return _validator
680
680
681
681
682 def CanWriteGroup(old_data=None):
682 def CanWriteGroup(old_data=None):
683 class _validator(formencode.validators.FancyValidator):
683 class _validator(formencode.validators.FancyValidator):
684 messages = {
684 messages = {
685 'permission_denied': _(
685 'permission_denied': _(
686 u"You do not have the permission "
686 u"You do not have the permission "
687 u"to create repositories in this group."),
687 u"to create repositories in this group."),
688 'permission_denied_root': _(
688 'permission_denied_root': _(
689 u"You do not have the permission to store repositories in "
689 u"You do not have the permission to store repositories in "
690 u"the root location.")
690 u"the root location.")
691 }
691 }
692
692
693 def _to_python(self, value, state):
693 def _to_python(self, value, state):
694 # root location
694 # root location
695 if value in [-1, "-1"]:
695 if value in [-1, "-1"]:
696 return None
696 return None
697 return value
697 return value
698
698
699 def validate_python(self, value, state):
699 def validate_python(self, value, state):
700 gr = RepoGroup.get(value)
700 gr = RepoGroup.get(value)
701 gr_name = gr.group_name if gr else None # None means ROOT location
701 gr_name = gr.group_name if gr else None # None means ROOT location
702 # create repositories with write permission on group is set to true
702 # create repositories with write permission on group is set to true
703 create_on_write = HasPermissionAny(
703 create_on_write = HasPermissionAny(
704 'hg.create.write_on_repogroup.true')()
704 'hg.create.write_on_repogroup.true')()
705 group_admin = HasRepoGroupPermissionAny('group.admin')(
705 group_admin = HasRepoGroupPermissionAny('group.admin')(
706 gr_name, 'can write into group validator')
706 gr_name, 'can write into group validator')
707 group_write = HasRepoGroupPermissionAny('group.write')(
707 group_write = HasRepoGroupPermissionAny('group.write')(
708 gr_name, 'can write into group validator')
708 gr_name, 'can write into group validator')
709 forbidden = not (group_admin or (group_write and create_on_write))
709 forbidden = not (group_admin or (group_write and create_on_write))
710 can_create_repos = HasPermissionAny(
710 can_create_repos = HasPermissionAny(
711 'hg.admin', 'hg.create.repository')
711 'hg.admin', 'hg.create.repository')
712 gid = (old_data['repo_group'].get('group_id')
712 gid = (old_data['repo_group'].get('group_id')
713 if (old_data and 'repo_group' in old_data) else None)
713 if (old_data and 'repo_group' in old_data) else None)
714 value_changed = gid != safe_int(value)
714 value_changed = gid != safe_int(value)
715 new = not old_data
715 new = not old_data
716 # do check if we changed the value, there's a case that someone got
716 # do check if we changed the value, there's a case that someone got
717 # revoked write permissions to a repository, he still created, we
717 # revoked write permissions to a repository, he still created, we
718 # don't need to check permission if he didn't change the value of
718 # don't need to check permission if he didn't change the value of
719 # groups in form box
719 # groups in form box
720 if value_changed or new:
720 if value_changed or new:
721 # parent group need to be existing
721 # parent group need to be existing
722 if gr and forbidden:
722 if gr and forbidden:
723 msg = M(self, 'permission_denied', state)
723 msg = M(self, 'permission_denied', state)
724 raise formencode.Invalid(
724 raise formencode.Invalid(
725 msg, value, state, error_dict={'repo_type': msg}
725 msg, value, state, error_dict={'repo_type': msg}
726 )
726 )
727 # check if we can write to root location !
727 # check if we can write to root location !
728 elif gr is None and not can_create_repos():
728 elif gr is None and not can_create_repos():
729 msg = M(self, 'permission_denied_root', state)
729 msg = M(self, 'permission_denied_root', state)
730 raise formencode.Invalid(
730 raise formencode.Invalid(
731 msg, value, state, error_dict={'repo_type': msg}
731 msg, value, state, error_dict={'repo_type': msg}
732 )
732 )
733
733
734 return _validator
734 return _validator
735
735
736
736
737 def ValidPerms(type_='repo'):
737 def ValidPerms(type_='repo'):
738 if type_ == 'repo_group':
738 if type_ == 'repo_group':
739 EMPTY_PERM = 'group.none'
739 EMPTY_PERM = 'group.none'
740 elif type_ == 'repo':
740 elif type_ == 'repo':
741 EMPTY_PERM = 'repository.none'
741 EMPTY_PERM = 'repository.none'
742 elif type_ == 'user_group':
742 elif type_ == 'user_group':
743 EMPTY_PERM = 'usergroup.none'
743 EMPTY_PERM = 'usergroup.none'
744
744
745 class _validator(formencode.validators.FancyValidator):
745 class _validator(formencode.validators.FancyValidator):
746 messages = {
746 messages = {
747 'perm_new_member_name':
747 'perm_new_member_name':
748 _(u'This username or user group name is not valid')
748 _(u'This username or user group name is not valid')
749 }
749 }
750
750
751 def _to_python(self, value, state):
751 def _to_python(self, value, state):
752 perm_updates = OrderedSet()
752 perm_updates = OrderedSet()
753 perm_additions = OrderedSet()
753 perm_additions = OrderedSet()
754 perm_deletions = OrderedSet()
754 perm_deletions = OrderedSet()
755 # build a list of permission to update/delete and new permission
755 # build a list of permission to update/delete and new permission
756
756
757 # Read the perm_new_member/perm_del_member attributes and group
757 # Read the perm_new_member/perm_del_member attributes and group
758 # them by they IDs
758 # them by they IDs
759 new_perms_group = defaultdict(dict)
759 new_perms_group = defaultdict(dict)
760 del_perms_group = defaultdict(dict)
760 del_perms_group = defaultdict(dict)
761 for k, v in value.copy().iteritems():
761 for k, v in value.copy().iteritems():
762 if k.startswith('perm_del_member'):
762 if k.startswith('perm_del_member'):
763 # delete from org storage so we don't process that later
763 # delete from org storage so we don't process that later
764 del value[k]
764 del value[k]
765 # part is `id`, `type`
765 # part is `id`, `type`
766 _type, part = k.split('perm_del_member_')
766 _type, part = k.split('perm_del_member_')
767 args = part.split('_')
767 args = part.split('_')
768 if len(args) == 2:
768 if len(args) == 2:
769 _key, pos = args
769 _key, pos = args
770 del_perms_group[pos][_key] = v
770 del_perms_group[pos][_key] = v
771 if k.startswith('perm_new_member'):
771 if k.startswith('perm_new_member'):
772 # delete from org storage so we don't process that later
772 # delete from org storage so we don't process that later
773 del value[k]
773 del value[k]
774 # part is `id`, `type`, `perm`
774 # part is `id`, `type`, `perm`
775 _type, part = k.split('perm_new_member_')
775 _type, part = k.split('perm_new_member_')
776 args = part.split('_')
776 args = part.split('_')
777 if len(args) == 2:
777 if len(args) == 2:
778 _key, pos = args
778 _key, pos = args
779 new_perms_group[pos][_key] = v
779 new_perms_group[pos][_key] = v
780
780
781 # store the deletes
781 # store the deletes
782 for k in sorted(del_perms_group.keys()):
782 for k in sorted(del_perms_group.keys()):
783 perm_dict = del_perms_group[k]
783 perm_dict = del_perms_group[k]
784 del_member = perm_dict.get('id')
784 del_member = perm_dict.get('id')
785 del_type = perm_dict.get('type')
785 del_type = perm_dict.get('type')
786 if del_member and del_type:
786 if del_member and del_type:
787 perm_deletions.add(
787 perm_deletions.add(
788 (del_member, None, del_type))
788 (del_member, None, del_type))
789
789
790 # store additions in order of how they were added in web form
790 # store additions in order of how they were added in web form
791 for k in sorted(new_perms_group.keys()):
791 for k in sorted(new_perms_group.keys()):
792 perm_dict = new_perms_group[k]
792 perm_dict = new_perms_group[k]
793 new_member = perm_dict.get('id')
793 new_member = perm_dict.get('id')
794 new_type = perm_dict.get('type')
794 new_type = perm_dict.get('type')
795 new_perm = perm_dict.get('perm')
795 new_perm = perm_dict.get('perm')
796 if new_member and new_perm and new_type:
796 if new_member and new_perm and new_type:
797 perm_additions.add(
797 perm_additions.add(
798 (new_member, new_perm, new_type))
798 (new_member, new_perm, new_type))
799
799
800 # get updates of permissions
800 # get updates of permissions
801 # (read the existing radio button states)
801 # (read the existing radio button states)
802 default_user_id = User.get_default_user().user_id
802 default_user_id = User.get_default_user().user_id
803 for k, update_value in value.iteritems():
803 for k, update_value in value.iteritems():
804 if k.startswith('u_perm_') or k.startswith('g_perm_'):
804 if k.startswith('u_perm_') or k.startswith('g_perm_'):
805 member = k[7:]
805 member = k[7:]
806 update_type = {'u': 'user',
806 update_type = {'u': 'user',
807 'g': 'users_group'}[k[0]]
807 'g': 'users_group'}[k[0]]
808
808
809 if safe_int(member) == default_user_id:
809 if safe_int(member) == default_user_id:
810 if str2bool(value.get('repo_private')):
810 if str2bool(value.get('repo_private')):
811 # prevent from updating default user permissions
811 # prevent from updating default user permissions
812 # when this repository is marked as private
812 # when this repository is marked as private
813 update_value = EMPTY_PERM
813 update_value = EMPTY_PERM
814
814
815 perm_updates.add(
815 perm_updates.add(
816 (member, update_value, update_type))
816 (member, update_value, update_type))
817
817
818 value['perm_additions'] = [] # propagated later
818 value['perm_additions'] = [] # propagated later
819 value['perm_updates'] = list(perm_updates)
819 value['perm_updates'] = list(perm_updates)
820 value['perm_deletions'] = list(perm_deletions)
820 value['perm_deletions'] = list(perm_deletions)
821
821
822 updates_map = dict(
822 updates_map = dict(
823 (x[0], (x[1], x[2])) for x in value['perm_updates'])
823 (x[0], (x[1], x[2])) for x in value['perm_updates'])
824 # make sure Additions don't override updates.
824 # make sure Additions don't override updates.
825 for member_id, perm, member_type in list(perm_additions):
825 for member_id, perm, member_type in list(perm_additions):
826 if member_id in updates_map:
826 if member_id in updates_map:
827 perm = updates_map[member_id][0]
827 perm = updates_map[member_id][0]
828 value['perm_additions'].append((member_id, perm, member_type))
828 value['perm_additions'].append((member_id, perm, member_type))
829
829
830 # on new entries validate users they exist and they are active !
830 # on new entries validate users they exist and they are active !
831 # this leaves feedback to the form
831 # this leaves feedback to the form
832 try:
832 try:
833 if member_type == 'user':
833 if member_type == 'user':
834 User.query()\
834 User.query()\
835 .filter(User.active == true())\
835 .filter(User.active == true())\
836 .filter(User.user_id == member_id).one()
836 .filter(User.user_id == member_id).one()
837 if member_type == 'users_group':
837 if member_type == 'users_group':
838 UserGroup.query()\
838 UserGroup.query()\
839 .filter(UserGroup.users_group_active == true())\
839 .filter(UserGroup.users_group_active == true())\
840 .filter(UserGroup.users_group_id == member_id)\
840 .filter(UserGroup.users_group_id == member_id)\
841 .one()
841 .one()
842
842
843 except Exception:
843 except Exception:
844 log.exception('Updated permission failed: org_exc:')
844 log.exception('Updated permission failed: org_exc:')
845 msg = M(self, 'perm_new_member_type', state)
845 msg = M(self, 'perm_new_member_type', state)
846 raise formencode.Invalid(
846 raise formencode.Invalid(
847 msg, value, state, error_dict={
847 msg, value, state, error_dict={
848 'perm_new_member_name': msg}
848 'perm_new_member_name': msg}
849 )
849 )
850 return value
850 return value
851 return _validator
851 return _validator
852
852
853
853
854 def ValidSettings():
854 def ValidSettings():
855 class _validator(formencode.validators.FancyValidator):
855 class _validator(formencode.validators.FancyValidator):
856 def _to_python(self, value, state):
856 def _to_python(self, value, state):
857 # settings form for users that are not admin
857 # settings form for users that are not admin
858 # can't edit certain parameters, it's extra backup if they mangle
858 # can't edit certain parameters, it's extra backup if they mangle
859 # with forms
859 # with forms
860
860
861 forbidden_params = [
861 forbidden_params = [
862 'user', 'repo_type', 'repo_enable_locking',
862 'user', 'repo_type', 'repo_enable_locking',
863 'repo_enable_downloads', 'repo_enable_statistics'
863 'repo_enable_downloads', 'repo_enable_statistics'
864 ]
864 ]
865
865
866 for param in forbidden_params:
866 for param in forbidden_params:
867 if param in value:
867 if param in value:
868 del value[param]
868 del value[param]
869 return value
869 return value
870
870
871 def validate_python(self, value, state):
871 def validate_python(self, value, state):
872 pass
872 pass
873 return _validator
873 return _validator
874
874
875
875
876 def ValidPath():
876 def ValidPath():
877 class _validator(formencode.validators.FancyValidator):
877 class _validator(formencode.validators.FancyValidator):
878 messages = {
878 messages = {
879 'invalid_path': _(u'This is not a valid path')
879 'invalid_path': _(u'This is not a valid path')
880 }
880 }
881
881
882 def validate_python(self, value, state):
882 def validate_python(self, value, state):
883 if not os.path.isdir(value):
883 if not os.path.isdir(value):
884 msg = M(self, 'invalid_path', state)
884 msg = M(self, 'invalid_path', state)
885 raise formencode.Invalid(
885 raise formencode.Invalid(
886 msg, value, state, error_dict={'paths_root_path': msg}
886 msg, value, state, error_dict={'paths_root_path': msg}
887 )
887 )
888 return _validator
888 return _validator
889
889
890
890
891 def UniqSystemEmail(old_data={}):
891 def UniqSystemEmail(old_data={}):
892 class _validator(formencode.validators.FancyValidator):
892 class _validator(formencode.validators.FancyValidator):
893 messages = {
893 messages = {
894 'email_taken': _(u'This e-mail address is already taken')
894 'email_taken': _(u'This e-mail address is already taken')
895 }
895 }
896
896
897 def _to_python(self, value, state):
897 def _to_python(self, value, state):
898 return value.lower()
898 return value.lower()
899
899
900 def validate_python(self, value, state):
900 def validate_python(self, value, state):
901 if (old_data.get('email') or '').lower() != value:
901 if (old_data.get('email') or '').lower() != value:
902 user = User.get_by_email(value, case_insensitive=True)
902 user = User.get_by_email(value, case_insensitive=True)
903 if user:
903 if user:
904 msg = M(self, 'email_taken', state)
904 msg = M(self, 'email_taken', state)
905 raise formencode.Invalid(
905 raise formencode.Invalid(
906 msg, value, state, error_dict={'email': msg}
906 msg, value, state, error_dict={'email': msg}
907 )
907 )
908 return _validator
908 return _validator
909
909
910
910
911 def ValidSystemEmail():
911 def ValidSystemEmail():
912 class _validator(formencode.validators.FancyValidator):
912 class _validator(formencode.validators.FancyValidator):
913 messages = {
913 messages = {
914 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
914 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
915 }
915 }
916
916
917 def _to_python(self, value, state):
917 def _to_python(self, value, state):
918 return value.lower()
918 return value.lower()
919
919
920 def validate_python(self, value, state):
920 def validate_python(self, value, state):
921 user = User.get_by_email(value, case_insensitive=True)
921 user = User.get_by_email(value, case_insensitive=True)
922 if user is None:
922 if user is None:
923 msg = M(self, 'non_existing_email', state, email=value)
923 msg = M(self, 'non_existing_email', state, email=value)
924 raise formencode.Invalid(
924 raise formencode.Invalid(
925 msg, value, state, error_dict={'email': msg}
925 msg, value, state, error_dict={'email': msg}
926 )
926 )
927
927
928 return _validator
928 return _validator
929
929
930
930
931 def NotReviewedRevisions(repo_id):
931 def NotReviewedRevisions(repo_id):
932 class _validator(formencode.validators.FancyValidator):
932 class _validator(formencode.validators.FancyValidator):
933 messages = {
933 messages = {
934 'rev_already_reviewed':
934 'rev_already_reviewed':
935 _(u'Revisions %(revs)s are already part of pull request '
935 _(u'Revisions %(revs)s are already part of pull request '
936 u'or have set status'),
936 u'or have set status'),
937 }
937 }
938
938
939 def validate_python(self, value, state):
939 def validate_python(self, value, state):
940 # check revisions if they are not reviewed, or a part of another
940 # check revisions if they are not reviewed, or a part of another
941 # pull request
941 # pull request
942 statuses = ChangesetStatus.query()\
942 statuses = ChangesetStatus.query()\
943 .filter(ChangesetStatus.revision.in_(value))\
943 .filter(ChangesetStatus.revision.in_(value))\
944 .filter(ChangesetStatus.repo_id == repo_id)\
944 .filter(ChangesetStatus.repo_id == repo_id)\
945 .all()
945 .all()
946
946
947 errors = []
947 errors = []
948 for status in statuses:
948 for status in statuses:
949 if status.pull_request_id:
949 if status.pull_request_id:
950 errors.append(['pull_req', status.revision[:12]])
950 errors.append(['pull_req', status.revision[:12]])
951 elif status.status:
951 elif status.status:
952 errors.append(['status', status.revision[:12]])
952 errors.append(['status', status.revision[:12]])
953
953
954 if errors:
954 if errors:
955 revs = ','.join([x[1] for x in errors])
955 revs = ','.join([x[1] for x in errors])
956 msg = M(self, 'rev_already_reviewed', state, revs=revs)
956 msg = M(self, 'rev_already_reviewed', state, revs=revs)
957 raise formencode.Invalid(
957 raise formencode.Invalid(
958 msg, value, state, error_dict={'revisions': revs})
958 msg, value, state, error_dict={'revisions': revs})
959
959
960 return _validator
960 return _validator
961
961
962
962
963 def ValidIp():
963 def ValidIp():
964 class _validator(CIDR):
964 class _validator(CIDR):
965 messages = {
965 messages = {
966 'badFormat': _(u'Please enter a valid IPv4 or IpV6 address'),
966 'badFormat': _(u'Please enter a valid IPv4 or IpV6 address'),
967 'illegalBits': _(
967 'illegalBits': _(
968 u'The network size (bits) must be within the range '
968 u'The network size (bits) must be within the range '
969 u'of 0-32 (not %(bits)r)'),
969 u'of 0-32 (not %(bits)r)'),
970 }
970 }
971
971
972 # we ovveride the default to_python() call
972 # we ovveride the default to_python() call
973 def to_python(self, value, state):
973 def to_python(self, value, state):
974 v = super(_validator, self).to_python(value, state)
974 v = super(_validator, self).to_python(value, state)
975 v = safe_unicode(v.strip())
975 v = safe_unicode(v.strip())
976 net = ipaddress.ip_network(address=v, strict=False)
976 net = ipaddress.ip_network(address=v, strict=False)
977 return str(net)
977 return str(net)
978
978
979 def validate_python(self, value, state):
979 def validate_python(self, value, state):
980 try:
980 try:
981 addr = safe_unicode(value.strip())
981 addr = safe_unicode(value.strip())
982 # this raises an ValueError if address is not IpV4 or IpV6
982 # this raises an ValueError if address is not IpV4 or IpV6
983 ipaddress.ip_network(addr, strict=False)
983 ipaddress.ip_network(addr, strict=False)
984 except ValueError:
984 except ValueError:
985 raise formencode.Invalid(self.message('badFormat', state),
985 raise formencode.Invalid(self.message('badFormat', state),
986 value, state)
986 value, state)
987
987
988 return _validator
988 return _validator
989
989
990
990
991 def FieldKey():
991 def FieldKey():
992 class _validator(formencode.validators.FancyValidator):
992 class _validator(formencode.validators.FancyValidator):
993 messages = {
993 messages = {
994 'badFormat': _(
994 'badFormat': _(
995 u'Key name can only consist of letters, '
995 u'Key name can only consist of letters, '
996 u'underscore, dash or numbers'),
996 u'underscore, dash or numbers'),
997 }
997 }
998
998
999 def validate_python(self, value, state):
999 def validate_python(self, value, state):
1000 if not re.match('[a-zA-Z0-9_-]+$', value):
1000 if not re.match('[a-zA-Z0-9_-]+$', value):
1001 raise formencode.Invalid(self.message('badFormat', state),
1001 raise formencode.Invalid(self.message('badFormat', state),
1002 value, state)
1002 value, state)
1003 return _validator
1003 return _validator
1004
1004
1005
1005
1006 def ValidAuthPlugins():
1006 def ValidAuthPlugins():
1007 class _validator(formencode.validators.FancyValidator):
1007 class _validator(formencode.validators.FancyValidator):
1008 messages = {
1008 messages = {
1009 'import_duplicate': _(
1009 'import_duplicate': _(
1010 u'Plugins %(loaded)s and %(next_to_load)s '
1010 u'Plugins %(loaded)s and %(next_to_load)s '
1011 u'both export the same name'),
1011 u'both export the same name'),
1012 'missing_includeme': _(
1012 'missing_includeme': _(
1013 u'The plugin "%(plugin_id)s" is missing an includeme '
1013 u'The plugin "%(plugin_id)s" is missing an includeme '
1014 u'function.'),
1014 u'function.'),
1015 'import_error': _(
1015 'import_error': _(
1016 u'Can not load plugin "%(plugin_id)s"'),
1016 u'Can not load plugin "%(plugin_id)s"'),
1017 'no_plugin': _(
1017 'no_plugin': _(
1018 u'No plugin available with ID "%(plugin_id)s"'),
1018 u'No plugin available with ID "%(plugin_id)s"'),
1019 }
1019 }
1020
1020
1021 def _to_python(self, value, state):
1021 def _to_python(self, value, state):
1022 # filter empty values
1022 # filter empty values
1023 return filter(lambda s: s not in [None, ''], value)
1023 return filter(lambda s: s not in [None, ''], value)
1024
1024
1025 def _validate_legacy_plugin_id(self, plugin_id, value, state):
1025 def _validate_legacy_plugin_id(self, plugin_id, value, state):
1026 """
1026 """
1027 Validates that the plugin import works. It also checks that the
1027 Validates that the plugin import works. It also checks that the
1028 plugin has an includeme attribute.
1028 plugin has an includeme attribute.
1029 """
1029 """
1030 try:
1030 try:
1031 plugin = _import_legacy_plugin(plugin_id)
1031 plugin = _import_legacy_plugin(plugin_id)
1032 except Exception as e:
1032 except Exception as e:
1033 log.exception(
1033 log.exception(
1034 'Exception during import of auth legacy plugin "{}"'
1034 'Exception during import of auth legacy plugin "{}"'
1035 .format(plugin_id))
1035 .format(plugin_id))
1036 msg = M(self, 'import_error', plugin_id=plugin_id)
1036 msg = M(self, 'import_error', plugin_id=plugin_id)
1037 raise formencode.Invalid(msg, value, state)
1037 raise formencode.Invalid(msg, value, state)
1038
1038
1039 if not hasattr(plugin, 'includeme'):
1039 if not hasattr(plugin, 'includeme'):
1040 msg = M(self, 'missing_includeme', plugin_id=plugin_id)
1040 msg = M(self, 'missing_includeme', plugin_id=plugin_id)
1041 raise formencode.Invalid(msg, value, state)
1041 raise formencode.Invalid(msg, value, state)
1042
1042
1043 return plugin
1043 return plugin
1044
1044
1045 def _validate_plugin_id(self, plugin_id, value, state):
1045 def _validate_plugin_id(self, plugin_id, value, state):
1046 """
1046 """
1047 Plugins are already imported during app start up. Therefore this
1047 Plugins are already imported during app start up. Therefore this
1048 validation only retrieves the plugin from the plugin registry and
1048 validation only retrieves the plugin from the plugin registry and
1049 if it returns something not None everything is OK.
1049 if it returns something not None everything is OK.
1050 """
1050 """
1051 plugin = loadplugin(plugin_id)
1051 plugin = loadplugin(plugin_id)
1052
1052
1053 if plugin is None:
1053 if plugin is None:
1054 msg = M(self, 'no_plugin', plugin_id=plugin_id)
1054 msg = M(self, 'no_plugin', plugin_id=plugin_id)
1055 raise formencode.Invalid(msg, value, state)
1055 raise formencode.Invalid(msg, value, state)
1056
1056
1057 return plugin
1057 return plugin
1058
1058
1059 def validate_python(self, value, state):
1059 def validate_python(self, value, state):
1060 unique_names = {}
1060 unique_names = {}
1061 for plugin_id in value:
1061 for plugin_id in value:
1062
1062
1063 # Validate legacy or normal plugin.
1063 # Validate legacy or normal plugin.
1064 if plugin_id.startswith(legacy_plugin_prefix):
1064 if plugin_id.startswith(legacy_plugin_prefix):
1065 plugin = self._validate_legacy_plugin_id(
1065 plugin = self._validate_legacy_plugin_id(
1066 plugin_id, value, state)
1066 plugin_id, value, state)
1067 else:
1067 else:
1068 plugin = self._validate_plugin_id(plugin_id, value, state)
1068 plugin = self._validate_plugin_id(plugin_id, value, state)
1069
1069
1070 # Only allow unique plugin names.
1070 # Only allow unique plugin names.
1071 if plugin.name in unique_names:
1071 if plugin.name in unique_names:
1072 msg = M(self, 'import_duplicate', state,
1072 msg = M(self, 'import_duplicate', state,
1073 loaded=unique_names[plugin.name],
1073 loaded=unique_names[plugin.name],
1074 next_to_load=plugin)
1074 next_to_load=plugin)
1075 raise formencode.Invalid(msg, value, state)
1075 raise formencode.Invalid(msg, value, state)
1076 unique_names[plugin.name] = plugin
1076 unique_names[plugin.name] = plugin
1077
1077
1078 return _validator
1078 return _validator
1079
1079
1080
1080
1081 def ValidPattern():
1081 def ValidPattern():
1082
1082
1083 class _Validator(formencode.validators.FancyValidator):
1083 class _Validator(formencode.validators.FancyValidator):
1084
1084
1085 def _to_python(self, value, state):
1085 def _to_python(self, value, state):
1086 patterns = []
1086 patterns = []
1087
1087
1088 prefix = 'new_pattern'
1088 prefix = 'new_pattern'
1089 for name, v in value.iteritems():
1089 for name, v in value.iteritems():
1090 pattern_name = '_'.join((prefix, 'pattern'))
1090 pattern_name = '_'.join((prefix, 'pattern'))
1091 if name.startswith(pattern_name):
1091 if name.startswith(pattern_name):
1092 new_item_id = name[len(pattern_name)+1:]
1092 new_item_id = name[len(pattern_name)+1:]
1093
1093
1094 def _field(name):
1094 def _field(name):
1095 return '%s_%s_%s' % (prefix, name, new_item_id)
1095 return '%s_%s_%s' % (prefix, name, new_item_id)
1096
1096
1097 values = {
1097 values = {
1098 'issuetracker_pat': value.get(_field('pattern')),
1098 'issuetracker_pat': value.get(_field('pattern')),
1099 'issuetracker_pat': value.get(_field('pattern')),
1099 'issuetracker_pat': value.get(_field('pattern')),
1100 'issuetracker_url': value.get(_field('url')),
1100 'issuetracker_url': value.get(_field('url')),
1101 'issuetracker_pref': value.get(_field('prefix')),
1101 'issuetracker_pref': value.get(_field('prefix')),
1102 'issuetracker_desc': value.get(_field('description'))
1102 'issuetracker_desc': value.get(_field('description'))
1103 }
1103 }
1104 new_uid = md5(values['issuetracker_pat'])
1104 new_uid = md5(values['issuetracker_pat'])
1105
1105
1106 has_required_fields = (
1106 has_required_fields = (
1107 values['issuetracker_pat']
1107 values['issuetracker_pat']
1108 and values['issuetracker_url'])
1108 and values['issuetracker_url'])
1109
1109
1110 if has_required_fields:
1110 if has_required_fields:
1111 settings = [
1111 settings = [
1112 ('_'.join((key, new_uid)), values[key], 'unicode')
1112 ('_'.join((key, new_uid)), values[key], 'unicode')
1113 for key in values]
1113 for key in values]
1114 patterns.append(settings)
1114 patterns.append(settings)
1115
1115
1116 value['patterns'] = patterns
1116 value['patterns'] = patterns
1117 delete_patterns = value.get('uid') or []
1117 delete_patterns = value.get('uid') or []
1118 if not isinstance(delete_patterns, (list, tuple)):
1118 if not isinstance(delete_patterns, (list, tuple)):
1119 delete_patterns = [delete_patterns]
1119 delete_patterns = [delete_patterns]
1120 value['delete_patterns'] = delete_patterns
1120 value['delete_patterns'] = delete_patterns
1121 return value
1121 return value
1122 return _Validator
1122 return _Validator
@@ -1,670 +1,670 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 import mock
21 import mock
22 import pytest
22 import pytest
23
23
24 import rhodecode
24 import rhodecode
25 from rhodecode.config.routing import ADMIN_PREFIX
25 from rhodecode.apps._base import ADMIN_PREFIX
26 from rhodecode.lib.utils2 import md5
26 from rhodecode.lib.utils2 import md5
27 from rhodecode.model.db import RhodeCodeUi
27 from rhodecode.model.db import RhodeCodeUi
28 from rhodecode.model.meta import Session
28 from rhodecode.model.meta import Session
29 from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
29 from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
30 from rhodecode.tests import url, assert_session_flash
30 from rhodecode.tests import url, assert_session_flash
31 from rhodecode.tests.utils import AssertResponse
31 from rhodecode.tests.utils import AssertResponse
32
32
33
33
34 UPDATE_DATA_QUALNAME = (
34 UPDATE_DATA_QUALNAME = (
35 'rhodecode.apps.admin.views.system_info.AdminSystemInfoSettingsView.get_update_data')
35 'rhodecode.apps.admin.views.system_info.AdminSystemInfoSettingsView.get_update_data')
36
36
37
37
38 @pytest.mark.usefixtures('autologin_user', 'app')
38 @pytest.mark.usefixtures('autologin_user', 'app')
39 class TestAdminSettingsController(object):
39 class TestAdminSettingsController(object):
40
40
41 @pytest.mark.parametrize('urlname', [
41 @pytest.mark.parametrize('urlname', [
42 'admin_settings_vcs',
42 'admin_settings_vcs',
43 'admin_settings_mapping',
43 'admin_settings_mapping',
44 'admin_settings_global',
44 'admin_settings_global',
45 'admin_settings_visual',
45 'admin_settings_visual',
46 'admin_settings_email',
46 'admin_settings_email',
47 'admin_settings_hooks',
47 'admin_settings_hooks',
48 'admin_settings_search',
48 'admin_settings_search',
49 ])
49 ])
50 def test_simple_get(self, urlname, app):
50 def test_simple_get(self, urlname, app):
51 app.get(url(urlname))
51 app.get(url(urlname))
52
52
53 def test_create_custom_hook(self, csrf_token):
53 def test_create_custom_hook(self, csrf_token):
54 response = self.app.post(
54 response = self.app.post(
55 url('admin_settings_hooks'),
55 url('admin_settings_hooks'),
56 params={
56 params={
57 'new_hook_ui_key': 'test_hooks_1',
57 'new_hook_ui_key': 'test_hooks_1',
58 'new_hook_ui_value': 'cd /tmp',
58 'new_hook_ui_value': 'cd /tmp',
59 'csrf_token': csrf_token})
59 'csrf_token': csrf_token})
60
60
61 response = response.follow()
61 response = response.follow()
62 response.mustcontain('test_hooks_1')
62 response.mustcontain('test_hooks_1')
63 response.mustcontain('cd /tmp')
63 response.mustcontain('cd /tmp')
64
64
65 def test_create_custom_hook_delete(self, csrf_token):
65 def test_create_custom_hook_delete(self, csrf_token):
66 response = self.app.post(
66 response = self.app.post(
67 url('admin_settings_hooks'),
67 url('admin_settings_hooks'),
68 params={
68 params={
69 'new_hook_ui_key': 'test_hooks_2',
69 'new_hook_ui_key': 'test_hooks_2',
70 'new_hook_ui_value': 'cd /tmp2',
70 'new_hook_ui_value': 'cd /tmp2',
71 'csrf_token': csrf_token})
71 'csrf_token': csrf_token})
72
72
73 response = response.follow()
73 response = response.follow()
74 response.mustcontain('test_hooks_2')
74 response.mustcontain('test_hooks_2')
75 response.mustcontain('cd /tmp2')
75 response.mustcontain('cd /tmp2')
76
76
77 hook_id = SettingsModel().get_ui_by_key('test_hooks_2').ui_id
77 hook_id = SettingsModel().get_ui_by_key('test_hooks_2').ui_id
78
78
79 # delete
79 # delete
80 self.app.post(
80 self.app.post(
81 url('admin_settings_hooks'),
81 url('admin_settings_hooks'),
82 params={'hook_id': hook_id, 'csrf_token': csrf_token})
82 params={'hook_id': hook_id, 'csrf_token': csrf_token})
83 response = self.app.get(url('admin_settings_hooks'))
83 response = self.app.get(url('admin_settings_hooks'))
84 response.mustcontain(no=['test_hooks_2'])
84 response.mustcontain(no=['test_hooks_2'])
85 response.mustcontain(no=['cd /tmp2'])
85 response.mustcontain(no=['cd /tmp2'])
86
86
87
87
88 @pytest.mark.usefixtures('autologin_user', 'app')
88 @pytest.mark.usefixtures('autologin_user', 'app')
89 class TestAdminSettingsGlobal(object):
89 class TestAdminSettingsGlobal(object):
90
90
91 def test_pre_post_code_code_active(self, csrf_token):
91 def test_pre_post_code_code_active(self, csrf_token):
92 pre_code = 'rc-pre-code-187652122'
92 pre_code = 'rc-pre-code-187652122'
93 post_code = 'rc-postcode-98165231'
93 post_code = 'rc-postcode-98165231'
94
94
95 response = self.post_and_verify_settings({
95 response = self.post_and_verify_settings({
96 'rhodecode_pre_code': pre_code,
96 'rhodecode_pre_code': pre_code,
97 'rhodecode_post_code': post_code,
97 'rhodecode_post_code': post_code,
98 'csrf_token': csrf_token,
98 'csrf_token': csrf_token,
99 })
99 })
100
100
101 response = response.follow()
101 response = response.follow()
102 response.mustcontain(pre_code, post_code)
102 response.mustcontain(pre_code, post_code)
103
103
104 def test_pre_post_code_code_inactive(self, csrf_token):
104 def test_pre_post_code_code_inactive(self, csrf_token):
105 pre_code = 'rc-pre-code-187652122'
105 pre_code = 'rc-pre-code-187652122'
106 post_code = 'rc-postcode-98165231'
106 post_code = 'rc-postcode-98165231'
107 response = self.post_and_verify_settings({
107 response = self.post_and_verify_settings({
108 'rhodecode_pre_code': '',
108 'rhodecode_pre_code': '',
109 'rhodecode_post_code': '',
109 'rhodecode_post_code': '',
110 'csrf_token': csrf_token,
110 'csrf_token': csrf_token,
111 })
111 })
112
112
113 response = response.follow()
113 response = response.follow()
114 response.mustcontain(no=[pre_code, post_code])
114 response.mustcontain(no=[pre_code, post_code])
115
115
116 def test_captcha_activate(self, csrf_token):
116 def test_captcha_activate(self, csrf_token):
117 self.post_and_verify_settings({
117 self.post_and_verify_settings({
118 'rhodecode_captcha_private_key': '1234567890',
118 'rhodecode_captcha_private_key': '1234567890',
119 'rhodecode_captcha_public_key': '1234567890',
119 'rhodecode_captcha_public_key': '1234567890',
120 'csrf_token': csrf_token,
120 'csrf_token': csrf_token,
121 })
121 })
122
122
123 response = self.app.get(ADMIN_PREFIX + '/register')
123 response = self.app.get(ADMIN_PREFIX + '/register')
124 response.mustcontain('captcha')
124 response.mustcontain('captcha')
125
125
126 def test_captcha_deactivate(self, csrf_token):
126 def test_captcha_deactivate(self, csrf_token):
127 self.post_and_verify_settings({
127 self.post_and_verify_settings({
128 'rhodecode_captcha_private_key': '',
128 'rhodecode_captcha_private_key': '',
129 'rhodecode_captcha_public_key': '1234567890',
129 'rhodecode_captcha_public_key': '1234567890',
130 'csrf_token': csrf_token,
130 'csrf_token': csrf_token,
131 })
131 })
132
132
133 response = self.app.get(ADMIN_PREFIX + '/register')
133 response = self.app.get(ADMIN_PREFIX + '/register')
134 response.mustcontain(no=['captcha'])
134 response.mustcontain(no=['captcha'])
135
135
136 def test_title_change(self, csrf_token):
136 def test_title_change(self, csrf_token):
137 old_title = 'RhodeCode'
137 old_title = 'RhodeCode'
138 new_title = old_title + '_changed'
138 new_title = old_title + '_changed'
139
139
140 for new_title in ['Changed', 'Ε»Γ³Ε‚wik', old_title]:
140 for new_title in ['Changed', 'Ε»Γ³Ε‚wik', old_title]:
141 response = self.post_and_verify_settings({
141 response = self.post_and_verify_settings({
142 'rhodecode_title': new_title,
142 'rhodecode_title': new_title,
143 'csrf_token': csrf_token,
143 'csrf_token': csrf_token,
144 })
144 })
145
145
146 response = response.follow()
146 response = response.follow()
147 response.mustcontain(
147 response.mustcontain(
148 """<div class="branding">- %s</div>""" % new_title)
148 """<div class="branding">- %s</div>""" % new_title)
149
149
150 def post_and_verify_settings(self, settings):
150 def post_and_verify_settings(self, settings):
151 old_title = 'RhodeCode'
151 old_title = 'RhodeCode'
152 old_realm = 'RhodeCode authentication'
152 old_realm = 'RhodeCode authentication'
153 params = {
153 params = {
154 'rhodecode_title': old_title,
154 'rhodecode_title': old_title,
155 'rhodecode_realm': old_realm,
155 'rhodecode_realm': old_realm,
156 'rhodecode_pre_code': '',
156 'rhodecode_pre_code': '',
157 'rhodecode_post_code': '',
157 'rhodecode_post_code': '',
158 'rhodecode_captcha_private_key': '',
158 'rhodecode_captcha_private_key': '',
159 'rhodecode_captcha_public_key': '',
159 'rhodecode_captcha_public_key': '',
160 'rhodecode_create_personal_repo_group': False,
160 'rhodecode_create_personal_repo_group': False,
161 'rhodecode_personal_repo_group_pattern': '${username}',
161 'rhodecode_personal_repo_group_pattern': '${username}',
162 }
162 }
163 params.update(settings)
163 params.update(settings)
164 response = self.app.post(url('admin_settings_global'), params=params)
164 response = self.app.post(url('admin_settings_global'), params=params)
165
165
166 assert_session_flash(response, 'Updated application settings')
166 assert_session_flash(response, 'Updated application settings')
167 app_settings = SettingsModel().get_all_settings()
167 app_settings = SettingsModel().get_all_settings()
168 del settings['csrf_token']
168 del settings['csrf_token']
169 for key, value in settings.iteritems():
169 for key, value in settings.iteritems():
170 assert app_settings[key] == value.decode('utf-8')
170 assert app_settings[key] == value.decode('utf-8')
171
171
172 return response
172 return response
173
173
174
174
175 @pytest.mark.usefixtures('autologin_user', 'app')
175 @pytest.mark.usefixtures('autologin_user', 'app')
176 class TestAdminSettingsVcs(object):
176 class TestAdminSettingsVcs(object):
177
177
178 def test_contains_svn_default_patterns(self, app):
178 def test_contains_svn_default_patterns(self, app):
179 response = app.get(url('admin_settings_vcs'))
179 response = app.get(url('admin_settings_vcs'))
180 expected_patterns = [
180 expected_patterns = [
181 '/trunk',
181 '/trunk',
182 '/branches/*',
182 '/branches/*',
183 '/tags/*',
183 '/tags/*',
184 ]
184 ]
185 for pattern in expected_patterns:
185 for pattern in expected_patterns:
186 response.mustcontain(pattern)
186 response.mustcontain(pattern)
187
187
188 def test_add_new_svn_branch_and_tag_pattern(
188 def test_add_new_svn_branch_and_tag_pattern(
189 self, app, backend_svn, form_defaults, disable_sql_cache,
189 self, app, backend_svn, form_defaults, disable_sql_cache,
190 csrf_token):
190 csrf_token):
191 form_defaults.update({
191 form_defaults.update({
192 'new_svn_branch': '/exp/branches/*',
192 'new_svn_branch': '/exp/branches/*',
193 'new_svn_tag': '/important_tags/*',
193 'new_svn_tag': '/important_tags/*',
194 'csrf_token': csrf_token,
194 'csrf_token': csrf_token,
195 })
195 })
196
196
197 response = app.post(
197 response = app.post(
198 url('admin_settings_vcs'), params=form_defaults, status=302)
198 url('admin_settings_vcs'), params=form_defaults, status=302)
199 response = response.follow()
199 response = response.follow()
200
200
201 # Expect to find the new values on the page
201 # Expect to find the new values on the page
202 response.mustcontain('/exp/branches/*')
202 response.mustcontain('/exp/branches/*')
203 response.mustcontain('/important_tags/*')
203 response.mustcontain('/important_tags/*')
204
204
205 # Expect that those patterns are used to match branches and tags now
205 # Expect that those patterns are used to match branches and tags now
206 repo = backend_svn['svn-simple-layout'].scm_instance()
206 repo = backend_svn['svn-simple-layout'].scm_instance()
207 assert 'exp/branches/exp-sphinx-docs' in repo.branches
207 assert 'exp/branches/exp-sphinx-docs' in repo.branches
208 assert 'important_tags/v0.5' in repo.tags
208 assert 'important_tags/v0.5' in repo.tags
209
209
210 def test_add_same_svn_value_twice_shows_an_error_message(
210 def test_add_same_svn_value_twice_shows_an_error_message(
211 self, app, form_defaults, csrf_token, settings_util):
211 self, app, form_defaults, csrf_token, settings_util):
212 settings_util.create_rhodecode_ui('vcs_svn_branch', '/test')
212 settings_util.create_rhodecode_ui('vcs_svn_branch', '/test')
213 settings_util.create_rhodecode_ui('vcs_svn_tag', '/test')
213 settings_util.create_rhodecode_ui('vcs_svn_tag', '/test')
214
214
215 response = app.post(
215 response = app.post(
216 url('admin_settings_vcs'),
216 url('admin_settings_vcs'),
217 params={
217 params={
218 'paths_root_path': form_defaults['paths_root_path'],
218 'paths_root_path': form_defaults['paths_root_path'],
219 'new_svn_branch': '/test',
219 'new_svn_branch': '/test',
220 'new_svn_tag': '/test',
220 'new_svn_tag': '/test',
221 'csrf_token': csrf_token,
221 'csrf_token': csrf_token,
222 },
222 },
223 status=200)
223 status=200)
224
224
225 response.mustcontain("Pattern already exists")
225 response.mustcontain("Pattern already exists")
226 response.mustcontain("Some form inputs contain invalid data.")
226 response.mustcontain("Some form inputs contain invalid data.")
227
227
228 @pytest.mark.parametrize('section', [
228 @pytest.mark.parametrize('section', [
229 'vcs_svn_branch',
229 'vcs_svn_branch',
230 'vcs_svn_tag',
230 'vcs_svn_tag',
231 ])
231 ])
232 def test_delete_svn_patterns(
232 def test_delete_svn_patterns(
233 self, section, app, csrf_token, settings_util):
233 self, section, app, csrf_token, settings_util):
234 setting = settings_util.create_rhodecode_ui(
234 setting = settings_util.create_rhodecode_ui(
235 section, '/test_delete', cleanup=False)
235 section, '/test_delete', cleanup=False)
236
236
237 app.post(
237 app.post(
238 url('admin_settings_vcs'),
238 url('admin_settings_vcs'),
239 params={
239 params={
240 '_method': 'delete',
240 '_method': 'delete',
241 'delete_svn_pattern': setting.ui_id,
241 'delete_svn_pattern': setting.ui_id,
242 'csrf_token': csrf_token},
242 'csrf_token': csrf_token},
243 headers={'X-REQUESTED-WITH': 'XMLHttpRequest'})
243 headers={'X-REQUESTED-WITH': 'XMLHttpRequest'})
244
244
245 @pytest.mark.parametrize('section', [
245 @pytest.mark.parametrize('section', [
246 'vcs_svn_branch',
246 'vcs_svn_branch',
247 'vcs_svn_tag',
247 'vcs_svn_tag',
248 ])
248 ])
249 def test_delete_svn_patterns_raises_400_when_no_xhr(
249 def test_delete_svn_patterns_raises_400_when_no_xhr(
250 self, section, app, csrf_token, settings_util):
250 self, section, app, csrf_token, settings_util):
251 setting = settings_util.create_rhodecode_ui(section, '/test_delete')
251 setting = settings_util.create_rhodecode_ui(section, '/test_delete')
252
252
253 app.post(
253 app.post(
254 url('admin_settings_vcs'),
254 url('admin_settings_vcs'),
255 params={
255 params={
256 '_method': 'delete',
256 '_method': 'delete',
257 'delete_svn_pattern': setting.ui_id,
257 'delete_svn_pattern': setting.ui_id,
258 'csrf_token': csrf_token},
258 'csrf_token': csrf_token},
259 status=400)
259 status=400)
260
260
261 def test_extensions_hgsubversion(self, app, form_defaults, csrf_token):
261 def test_extensions_hgsubversion(self, app, form_defaults, csrf_token):
262 form_defaults.update({
262 form_defaults.update({
263 'csrf_token': csrf_token,
263 'csrf_token': csrf_token,
264 'extensions_hgsubversion': 'True',
264 'extensions_hgsubversion': 'True',
265 })
265 })
266 response = app.post(
266 response = app.post(
267 url('admin_settings_vcs'),
267 url('admin_settings_vcs'),
268 params=form_defaults,
268 params=form_defaults,
269 status=302)
269 status=302)
270
270
271 response = response.follow()
271 response = response.follow()
272 extensions_input = (
272 extensions_input = (
273 '<input id="extensions_hgsubversion" '
273 '<input id="extensions_hgsubversion" '
274 'name="extensions_hgsubversion" type="checkbox" '
274 'name="extensions_hgsubversion" type="checkbox" '
275 'value="True" checked="checked" />')
275 'value="True" checked="checked" />')
276 response.mustcontain(extensions_input)
276 response.mustcontain(extensions_input)
277
277
278 def test_extensions_hgevolve(self, app, form_defaults, csrf_token):
278 def test_extensions_hgevolve(self, app, form_defaults, csrf_token):
279 form_defaults.update({
279 form_defaults.update({
280 'csrf_token': csrf_token,
280 'csrf_token': csrf_token,
281 'extensions_evolve': 'True',
281 'extensions_evolve': 'True',
282 })
282 })
283 response = app.post(
283 response = app.post(
284 url('admin_settings_vcs'),
284 url('admin_settings_vcs'),
285 params=form_defaults,
285 params=form_defaults,
286 status=302)
286 status=302)
287
287
288 response = response.follow()
288 response = response.follow()
289 extensions_input = (
289 extensions_input = (
290 '<input id="extensions_evolve" '
290 '<input id="extensions_evolve" '
291 'name="extensions_evolve" type="checkbox" '
291 'name="extensions_evolve" type="checkbox" '
292 'value="True" checked="checked" />')
292 'value="True" checked="checked" />')
293 response.mustcontain(extensions_input)
293 response.mustcontain(extensions_input)
294
294
295 def test_has_a_section_for_pull_request_settings(self, app):
295 def test_has_a_section_for_pull_request_settings(self, app):
296 response = app.get(url('admin_settings_vcs'))
296 response = app.get(url('admin_settings_vcs'))
297 response.mustcontain('Pull Request Settings')
297 response.mustcontain('Pull Request Settings')
298
298
299 def test_has_an_input_for_invalidation_of_inline_comments(
299 def test_has_an_input_for_invalidation_of_inline_comments(
300 self, app):
300 self, app):
301 response = app.get(url('admin_settings_vcs'))
301 response = app.get(url('admin_settings_vcs'))
302 assert_response = AssertResponse(response)
302 assert_response = AssertResponse(response)
303 assert_response.one_element_exists(
303 assert_response.one_element_exists(
304 '[name=rhodecode_use_outdated_comments]')
304 '[name=rhodecode_use_outdated_comments]')
305
305
306 @pytest.mark.parametrize('new_value', [True, False])
306 @pytest.mark.parametrize('new_value', [True, False])
307 def test_allows_to_change_invalidation_of_inline_comments(
307 def test_allows_to_change_invalidation_of_inline_comments(
308 self, app, form_defaults, csrf_token, new_value):
308 self, app, form_defaults, csrf_token, new_value):
309 setting_key = 'use_outdated_comments'
309 setting_key = 'use_outdated_comments'
310 setting = SettingsModel().create_or_update_setting(
310 setting = SettingsModel().create_or_update_setting(
311 setting_key, not new_value, 'bool')
311 setting_key, not new_value, 'bool')
312 Session().add(setting)
312 Session().add(setting)
313 Session().commit()
313 Session().commit()
314
314
315 form_defaults.update({
315 form_defaults.update({
316 'csrf_token': csrf_token,
316 'csrf_token': csrf_token,
317 'rhodecode_use_outdated_comments': str(new_value),
317 'rhodecode_use_outdated_comments': str(new_value),
318 })
318 })
319 response = app.post(
319 response = app.post(
320 url('admin_settings_vcs'),
320 url('admin_settings_vcs'),
321 params=form_defaults,
321 params=form_defaults,
322 status=302)
322 status=302)
323 response = response.follow()
323 response = response.follow()
324 setting = SettingsModel().get_setting_by_name(setting_key)
324 setting = SettingsModel().get_setting_by_name(setting_key)
325 assert setting.app_settings_value is new_value
325 assert setting.app_settings_value is new_value
326
326
327 @pytest.mark.parametrize('new_value', [True, False])
327 @pytest.mark.parametrize('new_value', [True, False])
328 def test_allows_to_change_hg_rebase_merge_strategy(
328 def test_allows_to_change_hg_rebase_merge_strategy(
329 self, app, form_defaults, csrf_token, new_value):
329 self, app, form_defaults, csrf_token, new_value):
330 setting_key = 'hg_use_rebase_for_merging'
330 setting_key = 'hg_use_rebase_for_merging'
331
331
332 form_defaults.update({
332 form_defaults.update({
333 'csrf_token': csrf_token,
333 'csrf_token': csrf_token,
334 'rhodecode_' + setting_key: str(new_value),
334 'rhodecode_' + setting_key: str(new_value),
335 })
335 })
336
336
337 with mock.patch.dict(
337 with mock.patch.dict(
338 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
338 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
339 app.post(
339 app.post(
340 url('admin_settings_vcs'),
340 url('admin_settings_vcs'),
341 params=form_defaults,
341 params=form_defaults,
342 status=302)
342 status=302)
343
343
344 setting = SettingsModel().get_setting_by_name(setting_key)
344 setting = SettingsModel().get_setting_by_name(setting_key)
345 assert setting.app_settings_value is new_value
345 assert setting.app_settings_value is new_value
346
346
347 @pytest.fixture
347 @pytest.fixture
348 def disable_sql_cache(self, request):
348 def disable_sql_cache(self, request):
349 patcher = mock.patch(
349 patcher = mock.patch(
350 'rhodecode.lib.caching_query.FromCache.process_query')
350 'rhodecode.lib.caching_query.FromCache.process_query')
351 request.addfinalizer(patcher.stop)
351 request.addfinalizer(patcher.stop)
352 patcher.start()
352 patcher.start()
353
353
354 @pytest.fixture
354 @pytest.fixture
355 def form_defaults(self):
355 def form_defaults(self):
356 from rhodecode.controllers.admin.settings import SettingsController
356 from rhodecode.controllers.admin.settings import SettingsController
357 controller = SettingsController()
357 controller = SettingsController()
358 return controller._form_defaults()
358 return controller._form_defaults()
359
359
360 # TODO: johbo: What we really want is to checkpoint before a test run and
360 # TODO: johbo: What we really want is to checkpoint before a test run and
361 # reset the session afterwards.
361 # reset the session afterwards.
362 @pytest.fixture(scope='class', autouse=True)
362 @pytest.fixture(scope='class', autouse=True)
363 def cleanup_settings(self, request, pylonsapp):
363 def cleanup_settings(self, request, pylonsapp):
364 ui_id = RhodeCodeUi.ui_id
364 ui_id = RhodeCodeUi.ui_id
365 original_ids = list(
365 original_ids = list(
366 r.ui_id for r in RhodeCodeUi.query().values(ui_id))
366 r.ui_id for r in RhodeCodeUi.query().values(ui_id))
367
367
368 @request.addfinalizer
368 @request.addfinalizer
369 def cleanup():
369 def cleanup():
370 RhodeCodeUi.query().filter(
370 RhodeCodeUi.query().filter(
371 ui_id.notin_(original_ids)).delete(False)
371 ui_id.notin_(original_ids)).delete(False)
372
372
373
373
374 @pytest.mark.usefixtures('autologin_user', 'app')
374 @pytest.mark.usefixtures('autologin_user', 'app')
375 class TestLabsSettings(object):
375 class TestLabsSettings(object):
376 def test_get_settings_page_disabled(self):
376 def test_get_settings_page_disabled(self):
377 with mock.patch.dict(rhodecode.CONFIG,
377 with mock.patch.dict(rhodecode.CONFIG,
378 {'labs_settings_active': 'false'}):
378 {'labs_settings_active': 'false'}):
379 response = self.app.get(url('admin_settings_labs'), status=302)
379 response = self.app.get(url('admin_settings_labs'), status=302)
380
380
381 assert response.location.endswith(url('admin_settings'))
381 assert response.location.endswith(url('admin_settings'))
382
382
383 def test_get_settings_page_enabled(self):
383 def test_get_settings_page_enabled(self):
384 from rhodecode.controllers.admin import settings
384 from rhodecode.controllers.admin import settings
385 lab_settings = [
385 lab_settings = [
386 settings.LabSetting(
386 settings.LabSetting(
387 key='rhodecode_bool',
387 key='rhodecode_bool',
388 type='bool',
388 type='bool',
389 group='bool group',
389 group='bool group',
390 label='bool label',
390 label='bool label',
391 help='bool help'
391 help='bool help'
392 ),
392 ),
393 settings.LabSetting(
393 settings.LabSetting(
394 key='rhodecode_text',
394 key='rhodecode_text',
395 type='unicode',
395 type='unicode',
396 group='text group',
396 group='text group',
397 label='text label',
397 label='text label',
398 help='text help'
398 help='text help'
399 ),
399 ),
400 ]
400 ]
401 with mock.patch.dict(rhodecode.CONFIG,
401 with mock.patch.dict(rhodecode.CONFIG,
402 {'labs_settings_active': 'true'}):
402 {'labs_settings_active': 'true'}):
403 with mock.patch.object(settings, '_LAB_SETTINGS', lab_settings):
403 with mock.patch.object(settings, '_LAB_SETTINGS', lab_settings):
404 response = self.app.get(url('admin_settings_labs'))
404 response = self.app.get(url('admin_settings_labs'))
405
405
406 assert '<label>bool group:</label>' in response
406 assert '<label>bool group:</label>' in response
407 assert '<label for="rhodecode_bool">bool label</label>' in response
407 assert '<label for="rhodecode_bool">bool label</label>' in response
408 assert '<p class="help-block">bool help</p>' in response
408 assert '<p class="help-block">bool help</p>' in response
409 assert 'name="rhodecode_bool" type="checkbox"' in response
409 assert 'name="rhodecode_bool" type="checkbox"' in response
410
410
411 assert '<label>text group:</label>' in response
411 assert '<label>text group:</label>' in response
412 assert '<label for="rhodecode_text">text label</label>' in response
412 assert '<label for="rhodecode_text">text label</label>' in response
413 assert '<p class="help-block">text help</p>' in response
413 assert '<p class="help-block">text help</p>' in response
414 assert 'name="rhodecode_text" size="60" type="text"' in response
414 assert 'name="rhodecode_text" size="60" type="text"' in response
415
415
416
416
417 @pytest.mark.usefixtures('app')
417 @pytest.mark.usefixtures('app')
418 class TestOpenSourceLicenses(object):
418 class TestOpenSourceLicenses(object):
419
419
420 def _get_url(self):
420 def _get_url(self):
421 return ADMIN_PREFIX + '/settings/open_source'
421 return ADMIN_PREFIX + '/settings/open_source'
422
422
423 def test_records_are_displayed(self, autologin_user):
423 def test_records_are_displayed(self, autologin_user):
424 sample_licenses = {
424 sample_licenses = {
425 "python2.7-pytest-2.7.1": {
425 "python2.7-pytest-2.7.1": {
426 "UNKNOWN": None
426 "UNKNOWN": None
427 },
427 },
428 "python2.7-Markdown-2.6.2": {
428 "python2.7-Markdown-2.6.2": {
429 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
429 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
430 }
430 }
431 }
431 }
432 read_licenses_patch = mock.patch(
432 read_licenses_patch = mock.patch(
433 'rhodecode.apps.admin.views.open_source_licenses.read_opensource_licenses',
433 'rhodecode.apps.admin.views.open_source_licenses.read_opensource_licenses',
434 return_value=sample_licenses)
434 return_value=sample_licenses)
435 with read_licenses_patch:
435 with read_licenses_patch:
436 response = self.app.get(self._get_url(), status=200)
436 response = self.app.get(self._get_url(), status=200)
437
437
438 assert_response = AssertResponse(response)
438 assert_response = AssertResponse(response)
439 assert_response.element_contains(
439 assert_response.element_contains(
440 '.panel-heading', 'Licenses of Third Party Packages')
440 '.panel-heading', 'Licenses of Third Party Packages')
441 for name in sample_licenses:
441 for name in sample_licenses:
442 response.mustcontain(name)
442 response.mustcontain(name)
443 for license in sample_licenses[name]:
443 for license in sample_licenses[name]:
444 assert_response.element_contains('.panel-body', license)
444 assert_response.element_contains('.panel-body', license)
445
445
446 def test_records_can_be_read(self, autologin_user):
446 def test_records_can_be_read(self, autologin_user):
447 response = self.app.get(self._get_url(), status=200)
447 response = self.app.get(self._get_url(), status=200)
448 assert_response = AssertResponse(response)
448 assert_response = AssertResponse(response)
449 assert_response.element_contains(
449 assert_response.element_contains(
450 '.panel-heading', 'Licenses of Third Party Packages')
450 '.panel-heading', 'Licenses of Third Party Packages')
451
451
452 def test_forbidden_when_normal_user(self, autologin_regular_user):
452 def test_forbidden_when_normal_user(self, autologin_regular_user):
453 self.app.get(self._get_url(), status=404)
453 self.app.get(self._get_url(), status=404)
454
454
455
455
456 @pytest.mark.usefixtures('app')
456 @pytest.mark.usefixtures('app')
457 class TestUserSessions(object):
457 class TestUserSessions(object):
458
458
459 def _get_url(self, name='admin_settings_sessions'):
459 def _get_url(self, name='admin_settings_sessions'):
460 return {
460 return {
461 'admin_settings_sessions': ADMIN_PREFIX + '/settings/sessions',
461 'admin_settings_sessions': ADMIN_PREFIX + '/settings/sessions',
462 'admin_settings_sessions_cleanup': ADMIN_PREFIX + '/settings/sessions/cleanup'
462 'admin_settings_sessions_cleanup': ADMIN_PREFIX + '/settings/sessions/cleanup'
463 }[name]
463 }[name]
464
464
465 def test_forbidden_when_normal_user(self, autologin_regular_user):
465 def test_forbidden_when_normal_user(self, autologin_regular_user):
466 self.app.get(self._get_url(), status=404)
466 self.app.get(self._get_url(), status=404)
467
467
468 def test_show_sessions_page(self, autologin_user):
468 def test_show_sessions_page(self, autologin_user):
469 response = self.app.get(self._get_url(), status=200)
469 response = self.app.get(self._get_url(), status=200)
470 response.mustcontain('file')
470 response.mustcontain('file')
471
471
472 def test_cleanup_old_sessions(self, autologin_user, csrf_token):
472 def test_cleanup_old_sessions(self, autologin_user, csrf_token):
473
473
474 post_data = {
474 post_data = {
475 'csrf_token': csrf_token,
475 'csrf_token': csrf_token,
476 'expire_days': '60'
476 'expire_days': '60'
477 }
477 }
478 response = self.app.post(
478 response = self.app.post(
479 self._get_url('admin_settings_sessions_cleanup'), params=post_data,
479 self._get_url('admin_settings_sessions_cleanup'), params=post_data,
480 status=302)
480 status=302)
481 assert_session_flash(response, 'Cleaned up old sessions')
481 assert_session_flash(response, 'Cleaned up old sessions')
482
482
483
483
484 @pytest.mark.usefixtures('app')
484 @pytest.mark.usefixtures('app')
485 class TestAdminSystemInfo(object):
485 class TestAdminSystemInfo(object):
486 def _get_url(self, name='admin_settings_system'):
486 def _get_url(self, name='admin_settings_system'):
487 return {
487 return {
488 'admin_settings_system': ADMIN_PREFIX + '/settings/system',
488 'admin_settings_system': ADMIN_PREFIX + '/settings/system',
489 'admin_settings_system_update': ADMIN_PREFIX + '/settings/system/updates',
489 'admin_settings_system_update': ADMIN_PREFIX + '/settings/system/updates',
490 }[name]
490 }[name]
491
491
492 def test_forbidden_when_normal_user(self, autologin_regular_user):
492 def test_forbidden_when_normal_user(self, autologin_regular_user):
493 self.app.get(self._get_url(), status=404)
493 self.app.get(self._get_url(), status=404)
494
494
495 def test_system_info_page(self, autologin_user):
495 def test_system_info_page(self, autologin_user):
496 response = self.app.get(self._get_url())
496 response = self.app.get(self._get_url())
497 response.mustcontain('RhodeCode Community Edition, version {}'.format(
497 response.mustcontain('RhodeCode Community Edition, version {}'.format(
498 rhodecode.__version__))
498 rhodecode.__version__))
499
499
500 def test_system_update_new_version(self, autologin_user):
500 def test_system_update_new_version(self, autologin_user):
501 update_data = {
501 update_data = {
502 'versions': [
502 'versions': [
503 {
503 {
504 'version': '100.3.1415926535',
504 'version': '100.3.1415926535',
505 'general': 'The latest version we are ever going to ship'
505 'general': 'The latest version we are ever going to ship'
506 },
506 },
507 {
507 {
508 'version': '0.0.0',
508 'version': '0.0.0',
509 'general': 'The first version we ever shipped'
509 'general': 'The first version we ever shipped'
510 }
510 }
511 ]
511 ]
512 }
512 }
513 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
513 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
514 response = self.app.get(self._get_url('admin_settings_system_update'))
514 response = self.app.get(self._get_url('admin_settings_system_update'))
515 response.mustcontain('A <b>new version</b> is available')
515 response.mustcontain('A <b>new version</b> is available')
516
516
517 def test_system_update_nothing_new(self, autologin_user):
517 def test_system_update_nothing_new(self, autologin_user):
518 update_data = {
518 update_data = {
519 'versions': [
519 'versions': [
520 {
520 {
521 'version': '0.0.0',
521 'version': '0.0.0',
522 'general': 'The first version we ever shipped'
522 'general': 'The first version we ever shipped'
523 }
523 }
524 ]
524 ]
525 }
525 }
526 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
526 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
527 response = self.app.get(self._get_url('admin_settings_system_update'))
527 response = self.app.get(self._get_url('admin_settings_system_update'))
528 response.mustcontain(
528 response.mustcontain(
529 'You already have the <b>latest</b> stable version.')
529 'You already have the <b>latest</b> stable version.')
530
530
531 def test_system_update_bad_response(self, autologin_user):
531 def test_system_update_bad_response(self, autologin_user):
532 with mock.patch(UPDATE_DATA_QUALNAME, side_effect=ValueError('foo')):
532 with mock.patch(UPDATE_DATA_QUALNAME, side_effect=ValueError('foo')):
533 response = self.app.get(self._get_url('admin_settings_system_update'))
533 response = self.app.get(self._get_url('admin_settings_system_update'))
534 response.mustcontain(
534 response.mustcontain(
535 'Bad data sent from update server')
535 'Bad data sent from update server')
536
536
537
537
538 @pytest.mark.usefixtures("app")
538 @pytest.mark.usefixtures("app")
539 class TestAdminSettingsIssueTracker(object):
539 class TestAdminSettingsIssueTracker(object):
540 RC_PREFIX = 'rhodecode_'
540 RC_PREFIX = 'rhodecode_'
541 SHORT_PATTERN_KEY = 'issuetracker_pat_'
541 SHORT_PATTERN_KEY = 'issuetracker_pat_'
542 PATTERN_KEY = RC_PREFIX + SHORT_PATTERN_KEY
542 PATTERN_KEY = RC_PREFIX + SHORT_PATTERN_KEY
543
543
544 def test_issuetracker_index(self, autologin_user):
544 def test_issuetracker_index(self, autologin_user):
545 response = self.app.get(url('admin_settings_issuetracker'))
545 response = self.app.get(url('admin_settings_issuetracker'))
546 assert response.status_code == 200
546 assert response.status_code == 200
547
547
548 def test_add_empty_issuetracker_pattern(
548 def test_add_empty_issuetracker_pattern(
549 self, request, autologin_user, csrf_token):
549 self, request, autologin_user, csrf_token):
550 post_url = url('admin_settings_issuetracker_save')
550 post_url = url('admin_settings_issuetracker_save')
551 post_data = {
551 post_data = {
552 'csrf_token': csrf_token
552 'csrf_token': csrf_token
553 }
553 }
554 self.app.post(post_url, post_data, status=302)
554 self.app.post(post_url, post_data, status=302)
555
555
556 def test_add_issuetracker_pattern(
556 def test_add_issuetracker_pattern(
557 self, request, autologin_user, csrf_token):
557 self, request, autologin_user, csrf_token):
558 pattern = 'issuetracker_pat'
558 pattern = 'issuetracker_pat'
559 another_pattern = pattern+'1'
559 another_pattern = pattern+'1'
560 post_url = url('admin_settings_issuetracker_save')
560 post_url = url('admin_settings_issuetracker_save')
561 post_data = {
561 post_data = {
562 'new_pattern_pattern_0': pattern,
562 'new_pattern_pattern_0': pattern,
563 'new_pattern_url_0': 'url',
563 'new_pattern_url_0': 'url',
564 'new_pattern_prefix_0': 'prefix',
564 'new_pattern_prefix_0': 'prefix',
565 'new_pattern_description_0': 'description',
565 'new_pattern_description_0': 'description',
566 'new_pattern_pattern_1': another_pattern,
566 'new_pattern_pattern_1': another_pattern,
567 'new_pattern_url_1': 'url1',
567 'new_pattern_url_1': 'url1',
568 'new_pattern_prefix_1': 'prefix1',
568 'new_pattern_prefix_1': 'prefix1',
569 'new_pattern_description_1': 'description1',
569 'new_pattern_description_1': 'description1',
570 'csrf_token': csrf_token
570 'csrf_token': csrf_token
571 }
571 }
572 self.app.post(post_url, post_data, status=302)
572 self.app.post(post_url, post_data, status=302)
573 settings = SettingsModel().get_all_settings()
573 settings = SettingsModel().get_all_settings()
574 self.uid = md5(pattern)
574 self.uid = md5(pattern)
575 assert settings[self.PATTERN_KEY+self.uid] == pattern
575 assert settings[self.PATTERN_KEY+self.uid] == pattern
576 self.another_uid = md5(another_pattern)
576 self.another_uid = md5(another_pattern)
577 assert settings[self.PATTERN_KEY+self.another_uid] == another_pattern
577 assert settings[self.PATTERN_KEY+self.another_uid] == another_pattern
578
578
579 @request.addfinalizer
579 @request.addfinalizer
580 def cleanup():
580 def cleanup():
581 defaults = SettingsModel().get_all_settings()
581 defaults = SettingsModel().get_all_settings()
582
582
583 entries = [name for name in defaults if (
583 entries = [name for name in defaults if (
584 (self.uid in name) or (self.another_uid) in name)]
584 (self.uid in name) or (self.another_uid) in name)]
585 start = len(self.RC_PREFIX)
585 start = len(self.RC_PREFIX)
586 for del_key in entries:
586 for del_key in entries:
587 # TODO: anderson: get_by_name needs name without prefix
587 # TODO: anderson: get_by_name needs name without prefix
588 entry = SettingsModel().get_setting_by_name(del_key[start:])
588 entry = SettingsModel().get_setting_by_name(del_key[start:])
589 Session().delete(entry)
589 Session().delete(entry)
590
590
591 Session().commit()
591 Session().commit()
592
592
593 def test_edit_issuetracker_pattern(
593 def test_edit_issuetracker_pattern(
594 self, autologin_user, backend, csrf_token, request):
594 self, autologin_user, backend, csrf_token, request):
595 old_pattern = 'issuetracker_pat'
595 old_pattern = 'issuetracker_pat'
596 old_uid = md5(old_pattern)
596 old_uid = md5(old_pattern)
597 pattern = 'issuetracker_pat_new'
597 pattern = 'issuetracker_pat_new'
598 self.new_uid = md5(pattern)
598 self.new_uid = md5(pattern)
599
599
600 SettingsModel().create_or_update_setting(
600 SettingsModel().create_or_update_setting(
601 self.SHORT_PATTERN_KEY+old_uid, old_pattern, 'unicode')
601 self.SHORT_PATTERN_KEY+old_uid, old_pattern, 'unicode')
602
602
603 post_url = url('admin_settings_issuetracker_save')
603 post_url = url('admin_settings_issuetracker_save')
604 post_data = {
604 post_data = {
605 'new_pattern_pattern_0': pattern,
605 'new_pattern_pattern_0': pattern,
606 'new_pattern_url_0': 'url',
606 'new_pattern_url_0': 'url',
607 'new_pattern_prefix_0': 'prefix',
607 'new_pattern_prefix_0': 'prefix',
608 'new_pattern_description_0': 'description',
608 'new_pattern_description_0': 'description',
609 'uid': old_uid,
609 'uid': old_uid,
610 'csrf_token': csrf_token
610 'csrf_token': csrf_token
611 }
611 }
612 self.app.post(post_url, post_data, status=302)
612 self.app.post(post_url, post_data, status=302)
613 settings = SettingsModel().get_all_settings()
613 settings = SettingsModel().get_all_settings()
614 assert settings[self.PATTERN_KEY+self.new_uid] == pattern
614 assert settings[self.PATTERN_KEY+self.new_uid] == pattern
615 assert self.PATTERN_KEY+old_uid not in settings
615 assert self.PATTERN_KEY+old_uid not in settings
616
616
617 @request.addfinalizer
617 @request.addfinalizer
618 def cleanup():
618 def cleanup():
619 IssueTrackerSettingsModel().delete_entries(self.new_uid)
619 IssueTrackerSettingsModel().delete_entries(self.new_uid)
620
620
621 def test_replace_issuetracker_pattern_description(
621 def test_replace_issuetracker_pattern_description(
622 self, autologin_user, csrf_token, request, settings_util):
622 self, autologin_user, csrf_token, request, settings_util):
623 prefix = 'issuetracker'
623 prefix = 'issuetracker'
624 pattern = 'issuetracker_pat'
624 pattern = 'issuetracker_pat'
625 self.uid = md5(pattern)
625 self.uid = md5(pattern)
626 pattern_key = '_'.join([prefix, 'pat', self.uid])
626 pattern_key = '_'.join([prefix, 'pat', self.uid])
627 rc_pattern_key = '_'.join(['rhodecode', pattern_key])
627 rc_pattern_key = '_'.join(['rhodecode', pattern_key])
628 desc_key = '_'.join([prefix, 'desc', self.uid])
628 desc_key = '_'.join([prefix, 'desc', self.uid])
629 rc_desc_key = '_'.join(['rhodecode', desc_key])
629 rc_desc_key = '_'.join(['rhodecode', desc_key])
630 new_description = 'new_description'
630 new_description = 'new_description'
631
631
632 settings_util.create_rhodecode_setting(
632 settings_util.create_rhodecode_setting(
633 pattern_key, pattern, 'unicode', cleanup=False)
633 pattern_key, pattern, 'unicode', cleanup=False)
634 settings_util.create_rhodecode_setting(
634 settings_util.create_rhodecode_setting(
635 desc_key, 'old description', 'unicode', cleanup=False)
635 desc_key, 'old description', 'unicode', cleanup=False)
636
636
637 post_url = url('admin_settings_issuetracker_save')
637 post_url = url('admin_settings_issuetracker_save')
638 post_data = {
638 post_data = {
639 'new_pattern_pattern_0': pattern,
639 'new_pattern_pattern_0': pattern,
640 'new_pattern_url_0': 'url',
640 'new_pattern_url_0': 'url',
641 'new_pattern_prefix_0': 'prefix',
641 'new_pattern_prefix_0': 'prefix',
642 'new_pattern_description_0': new_description,
642 'new_pattern_description_0': new_description,
643 'uid': self.uid,
643 'uid': self.uid,
644 'csrf_token': csrf_token
644 'csrf_token': csrf_token
645 }
645 }
646 self.app.post(post_url, post_data, status=302)
646 self.app.post(post_url, post_data, status=302)
647 settings = SettingsModel().get_all_settings()
647 settings = SettingsModel().get_all_settings()
648 assert settings[rc_pattern_key] == pattern
648 assert settings[rc_pattern_key] == pattern
649 assert settings[rc_desc_key] == new_description
649 assert settings[rc_desc_key] == new_description
650
650
651 @request.addfinalizer
651 @request.addfinalizer
652 def cleanup():
652 def cleanup():
653 IssueTrackerSettingsModel().delete_entries(self.uid)
653 IssueTrackerSettingsModel().delete_entries(self.uid)
654
654
655 def test_delete_issuetracker_pattern(
655 def test_delete_issuetracker_pattern(
656 self, autologin_user, backend, csrf_token, settings_util):
656 self, autologin_user, backend, csrf_token, settings_util):
657 pattern = 'issuetracker_pat'
657 pattern = 'issuetracker_pat'
658 uid = md5(pattern)
658 uid = md5(pattern)
659 settings_util.create_rhodecode_setting(
659 settings_util.create_rhodecode_setting(
660 self.SHORT_PATTERN_KEY+uid, pattern, 'unicode', cleanup=False)
660 self.SHORT_PATTERN_KEY+uid, pattern, 'unicode', cleanup=False)
661
661
662 post_url = url('admin_issuetracker_delete')
662 post_url = url('admin_issuetracker_delete')
663 post_data = {
663 post_data = {
664 '_method': 'delete',
664 '_method': 'delete',
665 'uid': uid,
665 'uid': uid,
666 'csrf_token': csrf_token
666 'csrf_token': csrf_token
667 }
667 }
668 self.app.post(post_url, post_data, status=302)
668 self.app.post(post_url, post_data, status=302)
669 settings = SettingsModel().get_all_settings()
669 settings = SettingsModel().get_all_settings()
670 assert 'rhodecode_%s%s' % (self.SHORT_PATTERN_KEY, uid) not in settings
670 assert 'rhodecode_%s%s' % (self.SHORT_PATTERN_KEY, uid) not in settings
General Comments 0
You need to be logged in to leave comments. Login now