##// END OF EJS Templates
Pass in old groups data to CanWriteToGroup validator for later skipping group checks....
marcink -
r3525:0cef54d3 default
parent child Browse files
Show More
@@ -1,480 +1,482 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.repos
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Repositories controller for RhodeCode
7 7
8 8 :created_on: Apr 7, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28 import formencode
29 29 from formencode import htmlfill
30 30
31 31 from webob.exc import HTTPInternalServerError
32 32 from pylons import request, session, tmpl_context as c, url
33 33 from pylons.controllers.util import redirect
34 34 from pylons.i18n.translation import _
35 35 from sqlalchemy.exc import IntegrityError
36 36
37 37 import rhodecode
38 38 from rhodecode.lib import helpers as h
39 39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 40 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator
41 41 from rhodecode.lib.base import BaseController, render
42 42 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
43 43 from rhodecode.lib.helpers import get_token
44 44 from rhodecode.model.meta import Session
45 45 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup,\
46 46 RhodeCodeSetting
47 47 from rhodecode.model.forms import RepoForm
48 48 from rhodecode.model.scm import ScmModel
49 49 from rhodecode.model.repo import RepoModel
50 50 from rhodecode.lib.compat import json
51 51 from sqlalchemy.sql.expression import func
52 52
53 53 log = logging.getLogger(__name__)
54 54
55 55
56 56 class ReposController(BaseController):
57 57 """
58 58 REST Controller styled on the Atom Publishing Protocol"""
59 59 # To properly map this controller, ensure your config/routing.py
60 60 # file has a resource setup:
61 61 # map.resource('repo', 'repos')
62 62
63 63 @LoginRequired()
64 64 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
65 65 def __before__(self):
66 66 c.admin_user = session.get('admin_user')
67 67 c.admin_username = session.get('admin_username')
68 68 super(ReposController, self).__before__()
69 69
70 70 def __load_defaults(self):
71 71 c.repo_groups = RepoGroup.groups_choices(check_perms=True)
72 72 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
73 73
74 74 repo_model = RepoModel()
75 75 c.users_array = repo_model.get_users_js()
76 76 c.users_groups_array = repo_model.get_users_groups_js()
77 77 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
78 78 c.landing_revs_choices = choices
79 79
80 80 def __load_data(self, repo_name=None):
81 81 """
82 82 Load defaults settings for edit, and update
83 83
84 84 :param repo_name:
85 85 """
86 86 self.__load_defaults()
87 87
88 88 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
89 89 repo = db_repo.scm_instance
90 90
91 91 if c.repo_info is None:
92 92 h.not_mapped_error(repo_name)
93 93 return redirect(url('repos'))
94 94
95 95 ##override defaults for exact repo info here git/hg etc
96 96 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
97 97 c.landing_revs_choices = choices
98 98
99 99 c.default_user_id = User.get_by_username('default').user_id
100 100 c.in_public_journal = UserFollowing.query()\
101 101 .filter(UserFollowing.user_id == c.default_user_id)\
102 102 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
103 103
104 104 if c.repo_info.stats:
105 105 # this is on what revision we ended up so we add +1 for count
106 106 last_rev = c.repo_info.stats.stat_on_revision + 1
107 107 else:
108 108 last_rev = 0
109 109 c.stats_revision = last_rev
110 110
111 111 c.repo_last_rev = repo.count() if repo.revisions else 0
112 112
113 113 if last_rev == 0 or c.repo_last_rev == 0:
114 114 c.stats_percentage = 0
115 115 else:
116 116 c.stats_percentage = '%.2f' % ((float((last_rev)) /
117 117 c.repo_last_rev) * 100)
118 118
119 119 defaults = RepoModel()._get_defaults(repo_name)
120 120
121 121 c.repos_list = [('', _('--REMOVE FORK--'))]
122 122 c.repos_list += [(x.repo_id, x.repo_name) for x in
123 123 Repository.query().order_by(Repository.repo_name).all()
124 124 if x.repo_id != c.repo_info.repo_id]
125 125
126 126 defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
127 127 return defaults
128 128
129 129 @HasPermissionAllDecorator('hg.admin')
130 130 def index(self, format='html'):
131 131 """GET /repos: All items in the collection"""
132 132 # url('repos')
133 133
134 134 c.repos_list = Repository.query()\
135 135 .order_by(func.lower(Repository.repo_name))\
136 136 .all()
137 137
138 138 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
139 139 admin=True)
140 140 #json used to render the grid
141 141 c.data = json.dumps(repos_data)
142 142
143 143 return render('admin/repos/repos.html')
144 144
145 145 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
146 146 def create(self):
147 147 """
148 148 POST /repos: Create a new item"""
149 149 # url('repos')
150 150
151 151 self.__load_defaults()
152 152 form_result = {}
153 153 try:
154 154 form_result = RepoForm(repo_groups=c.repo_groups_choices,
155 155 landing_revs=c.landing_revs_choices)()\
156 156 .to_python(dict(request.POST))
157 157 new_repo = RepoModel().create(form_result,
158 158 self.rhodecode_user.user_id)
159 159 if form_result['clone_uri']:
160 160 h.flash(_('created repository %s from %s') \
161 161 % (form_result['repo_name'], form_result['clone_uri']),
162 162 category='success')
163 163 else:
164 164 h.flash(_('created repository %s') % form_result['repo_name'],
165 165 category='success')
166 166
167 167 if request.POST.get('user_created'):
168 168 # created by regular non admin user
169 169 action_logger(self.rhodecode_user, 'user_created_repo',
170 170 form_result['repo_name_full'], self.ip_addr,
171 171 self.sa)
172 172 else:
173 173 action_logger(self.rhodecode_user, 'admin_created_repo',
174 174 form_result['repo_name_full'], self.ip_addr,
175 175 self.sa)
176 176 Session().commit()
177 177 except formencode.Invalid, errors:
178 178
179 179 c.new_repo = errors.value['repo_name']
180 180
181 181 if request.POST.get('user_created'):
182 182 r = render('admin/repos/repo_add_create_repository.html')
183 183 else:
184 184 r = render('admin/repos/repo_add.html')
185 185
186 186 return htmlfill.render(
187 187 r,
188 188 defaults=errors.value,
189 189 errors=errors.error_dict or {},
190 190 prefix_error=False,
191 191 encoding="UTF-8")
192 192
193 193 except Exception:
194 194 log.error(traceback.format_exc())
195 195 msg = _('error occurred during creation of repository %s') \
196 196 % form_result.get('repo_name')
197 197 h.flash(msg, category='error')
198 198 return redirect(url('repos'))
199 199 #redirect to our new repo !
200 200 return redirect(url('summary_home', repo_name=new_repo.repo_name))
201 201
202 202 @HasPermissionAllDecorator('hg.admin')
203 203 def new(self, format='html'):
204 204 """GET /repos/new: Form to create a new item"""
205 205 new_repo = request.GET.get('repo', '')
206 206 c.new_repo = repo_name_slug(new_repo)
207 207 self.__load_defaults()
208 208 ## apply the defaults from defaults page
209 209 defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
210 210 return htmlfill.render(
211 211 render('admin/repos/repo_add.html'),
212 212 defaults=defaults,
213 213 errors={},
214 214 prefix_error=False,
215 215 encoding="UTF-8"
216 216 )
217 217
218 218 @HasPermissionAllDecorator('hg.admin')
219 219 def update(self, repo_name):
220 220 """
221 221 PUT /repos/repo_name: Update an existing item"""
222 222 # Forms posted to this method should contain a hidden field:
223 223 # <input type="hidden" name="_method" value="PUT" />
224 224 # Or using helpers:
225 225 # h.form(url('repo', repo_name=ID),
226 226 # method='put')
227 227 # url('repo', repo_name=ID)
228 228 self.__load_defaults()
229 229 repo_model = RepoModel()
230 230 changed_name = repo_name
231 231 #override the choices with extracted revisions !
232 232 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
233 233 c.landing_revs_choices = choices
234
235 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
234 repo = Repository.get_by_repo_name(repo_name)
235 _form = RepoForm(edit=True, old_data={'repo_name': repo_name,
236 'repo_group': repo.group.get_dict() \
237 if repo.group else {}},
236 238 repo_groups=c.repo_groups_choices,
237 239 landing_revs=c.landing_revs_choices)()
238 240 try:
239 241 form_result = _form.to_python(dict(request.POST))
240 242 repo = repo_model.update(repo_name, **form_result)
241 243 invalidate_cache('get_repo_cached_%s' % repo_name)
242 244 h.flash(_('Repository %s updated successfully') % repo_name,
243 245 category='success')
244 246 changed_name = repo.repo_name
245 247 action_logger(self.rhodecode_user, 'admin_updated_repo',
246 248 changed_name, self.ip_addr, self.sa)
247 249 Session().commit()
248 250 except formencode.Invalid, errors:
249 251 defaults = self.__load_data(repo_name)
250 252 defaults.update(errors.value)
251 253 return htmlfill.render(
252 254 render('admin/repos/repo_edit.html'),
253 255 defaults=defaults,
254 256 errors=errors.error_dict or {},
255 257 prefix_error=False,
256 258 encoding="UTF-8")
257 259
258 260 except Exception:
259 261 log.error(traceback.format_exc())
260 262 h.flash(_('error occurred during update of repository %s') \
261 263 % repo_name, category='error')
262 264 return redirect(url('edit_repo', repo_name=changed_name))
263 265
264 266 @HasPermissionAllDecorator('hg.admin')
265 267 def delete(self, repo_name):
266 268 """
267 269 DELETE /repos/repo_name: Delete an existing item"""
268 270 # Forms posted to this method should contain a hidden field:
269 271 # <input type="hidden" name="_method" value="DELETE" />
270 272 # Or using helpers:
271 273 # h.form(url('repo', repo_name=ID),
272 274 # method='delete')
273 275 # url('repo', repo_name=ID)
274 276
275 277 repo_model = RepoModel()
276 278 repo = repo_model.get_by_repo_name(repo_name)
277 279 if not repo:
278 280 h.not_mapped_error(repo_name)
279 281 return redirect(url('repos'))
280 282 try:
281 283 action_logger(self.rhodecode_user, 'admin_deleted_repo',
282 284 repo_name, self.ip_addr, self.sa)
283 285 repo_model.delete(repo)
284 286 invalidate_cache('get_repo_cached_%s' % repo_name)
285 287 h.flash(_('deleted repository %s') % repo_name, category='success')
286 288 Session().commit()
287 289 except IntegrityError, e:
288 290 if e.message.find('repositories_fork_id_fkey') != -1:
289 291 log.error(traceback.format_exc())
290 292 h.flash(_('Cannot delete %s it still contains attached '
291 293 'forks') % repo_name,
292 294 category='warning')
293 295 else:
294 296 log.error(traceback.format_exc())
295 297 h.flash(_('An error occurred during '
296 298 'deletion of %s') % repo_name,
297 299 category='error')
298 300
299 301 except Exception, e:
300 302 log.error(traceback.format_exc())
301 303 h.flash(_('An error occurred during deletion of %s') % repo_name,
302 304 category='error')
303 305
304 306 return redirect(url('repos'))
305 307
306 308 @HasRepoPermissionAllDecorator('repository.admin')
307 309 def delete_perm_user(self, repo_name):
308 310 """
309 311 DELETE an existing repository permission user
310 312
311 313 :param repo_name:
312 314 """
313 315 try:
314 316 RepoModel().revoke_user_permission(repo=repo_name,
315 317 user=request.POST['user_id'])
316 318 Session().commit()
317 319 except Exception:
318 320 log.error(traceback.format_exc())
319 321 h.flash(_('An error occurred during deletion of repository user'),
320 322 category='error')
321 323 raise HTTPInternalServerError()
322 324
323 325 @HasRepoPermissionAllDecorator('repository.admin')
324 326 def delete_perm_users_group(self, repo_name):
325 327 """
326 328 DELETE an existing repository permission users group
327 329
328 330 :param repo_name:
329 331 """
330 332
331 333 try:
332 334 RepoModel().revoke_users_group_permission(
333 335 repo=repo_name, group_name=request.POST['users_group_id']
334 336 )
335 337 Session().commit()
336 338 except Exception:
337 339 log.error(traceback.format_exc())
338 340 h.flash(_('An error occurred during deletion of repository'
339 341 ' users groups'),
340 342 category='error')
341 343 raise HTTPInternalServerError()
342 344
343 345 @HasPermissionAllDecorator('hg.admin')
344 346 def repo_stats(self, repo_name):
345 347 """
346 348 DELETE an existing repository statistics
347 349
348 350 :param repo_name:
349 351 """
350 352
351 353 try:
352 354 RepoModel().delete_stats(repo_name)
353 355 Session().commit()
354 356 except Exception, e:
355 357 log.error(traceback.format_exc())
356 358 h.flash(_('An error occurred during deletion of repository stats'),
357 359 category='error')
358 360 return redirect(url('edit_repo', repo_name=repo_name))
359 361
360 362 @HasPermissionAllDecorator('hg.admin')
361 363 def repo_cache(self, repo_name):
362 364 """
363 365 INVALIDATE existing repository cache
364 366
365 367 :param repo_name:
366 368 """
367 369
368 370 try:
369 371 ScmModel().mark_for_invalidation(repo_name)
370 372 Session().commit()
371 373 except Exception, e:
372 374 log.error(traceback.format_exc())
373 375 h.flash(_('An error occurred during cache invalidation'),
374 376 category='error')
375 377 return redirect(url('edit_repo', repo_name=repo_name))
376 378
377 379 @HasPermissionAllDecorator('hg.admin')
378 380 def repo_locking(self, repo_name):
379 381 """
380 382 Unlock repository when it is locked !
381 383
382 384 :param repo_name:
383 385 """
384 386
385 387 try:
386 388 repo = Repository.get_by_repo_name(repo_name)
387 389 if request.POST.get('set_lock'):
388 390 Repository.lock(repo, c.rhodecode_user.user_id)
389 391 elif request.POST.get('set_unlock'):
390 392 Repository.unlock(repo)
391 393 except Exception, e:
392 394 log.error(traceback.format_exc())
393 395 h.flash(_('An error occurred during unlocking'),
394 396 category='error')
395 397 return redirect(url('edit_repo', repo_name=repo_name))
396 398
397 399 @HasPermissionAllDecorator('hg.admin')
398 400 def repo_public_journal(self, repo_name):
399 401 """
400 402 Set's this repository to be visible in public journal,
401 403 in other words assing default user to follow this repo
402 404
403 405 :param repo_name:
404 406 """
405 407
406 408 cur_token = request.POST.get('auth_token')
407 409 token = get_token()
408 410 if cur_token == token:
409 411 try:
410 412 repo_id = Repository.get_by_repo_name(repo_name).repo_id
411 413 user_id = User.get_by_username('default').user_id
412 414 self.scm_model.toggle_following_repo(repo_id, user_id)
413 415 h.flash(_('Updated repository visibility in public journal'),
414 416 category='success')
415 417 Session().commit()
416 418 except:
417 419 h.flash(_('An error occurred during setting this'
418 420 ' repository in public journal'),
419 421 category='error')
420 422
421 423 else:
422 424 h.flash(_('Token mismatch'), category='error')
423 425 return redirect(url('edit_repo', repo_name=repo_name))
424 426
425 427 @HasPermissionAllDecorator('hg.admin')
426 428 def repo_pull(self, repo_name):
427 429 """
428 430 Runs task to update given repository with remote changes,
429 431 ie. make pull on remote location
430 432
431 433 :param repo_name:
432 434 """
433 435 try:
434 436 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
435 437 h.flash(_('Pulled from remote location'), category='success')
436 438 except Exception, e:
437 439 h.flash(_('An error occurred during pull from remote location'),
438 440 category='error')
439 441
440 442 return redirect(url('edit_repo', repo_name=repo_name))
441 443
442 444 @HasPermissionAllDecorator('hg.admin')
443 445 def repo_as_fork(self, repo_name):
444 446 """
445 447 Mark given repository as a fork of another
446 448
447 449 :param repo_name:
448 450 """
449 451 try:
450 452 fork_id = request.POST.get('id_fork_of')
451 453 repo = ScmModel().mark_as_fork(repo_name, fork_id,
452 454 self.rhodecode_user.username)
453 455 fork = repo.fork.repo_name if repo.fork else _('Nothing')
454 456 Session().commit()
455 457 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
456 458 category='success')
457 459 except Exception, e:
458 460 log.error(traceback.format_exc())
459 461 h.flash(_('An error occurred during this operation'),
460 462 category='error')
461 463
462 464 return redirect(url('edit_repo', repo_name=repo_name))
463 465
464 466 @HasPermissionAllDecorator('hg.admin')
465 467 def show(self, repo_name, format='html'):
466 468 """GET /repos/repo_name: Show a specific item"""
467 469 # url('repo', repo_name=ID)
468 470
469 471 @HasPermissionAllDecorator('hg.admin')
470 472 def edit(self, repo_name, format='html'):
471 473 """GET /repos/repo_name/edit: Form to edit an existing item"""
472 474 # url('edit_repo', repo_name=ID)
473 475 defaults = self.__load_data(repo_name)
474 476
475 477 return htmlfill.render(
476 478 render('admin/repos/repo_edit.html'),
477 479 defaults=defaults,
478 480 encoding="UTF-8",
479 481 force_defaults=False
480 482 )
@@ -1,196 +1,198 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.settings
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Settings controller for rhodecode
7 7
8 8 :created_on: Jun 30, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28 import formencode
29 29
30 30 from formencode import htmlfill
31 31
32 32 from pylons import tmpl_context as c, request, url
33 33 from pylons.controllers.util import redirect
34 34 from pylons.i18n.translation import _
35 35
36 36 import rhodecode.lib.helpers as h
37 37
38 38 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator,\
39 39 HasRepoPermissionAnyDecorator
40 40 from rhodecode.lib.base import BaseRepoController, render
41 41 from rhodecode.lib.utils import invalidate_cache, action_logger
42 42
43 43 from rhodecode.model.forms import RepoSettingsForm
44 44 from rhodecode.model.repo import RepoModel
45 45 from rhodecode.model.db import RepoGroup, Repository
46 46 from rhodecode.model.meta import Session
47 47 from rhodecode.model.scm import ScmModel
48 48
49 49 log = logging.getLogger(__name__)
50 50
51 51
52 52 class SettingsController(BaseRepoController):
53 53
54 54 @LoginRequired()
55 55 def __before__(self):
56 56 super(SettingsController, self).__before__()
57 57
58 58 def __load_defaults(self):
59 59 c.repo_groups = RepoGroup.groups_choices(check_perms=True)
60 60 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
61 61
62 62 repo_model = RepoModel()
63 63 c.users_array = repo_model.get_users_js()
64 64 c.users_groups_array = repo_model.get_users_groups_js()
65 65 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
66 66 c.landing_revs_choices = choices
67 67
68 68 def __load_data(self, repo_name=None):
69 69 """
70 70 Load defaults settings for edit, and update
71 71
72 72 :param repo_name:
73 73 """
74 74 self.__load_defaults()
75 75
76 76 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
77 77 repo = db_repo.scm_instance
78 78
79 79 if c.repo_info is None:
80 80 h.not_mapped_error(repo_name)
81 81 return redirect(url('home'))
82 82
83 83 ##override defaults for exact repo info here git/hg etc
84 84 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
85 85 c.landing_revs_choices = choices
86 86
87 87 defaults = RepoModel()._get_defaults(repo_name)
88 88
89 89 return defaults
90 90
91 91 @HasRepoPermissionAllDecorator('repository.admin')
92 92 def index(self, repo_name):
93 93 defaults = self.__load_data(repo_name)
94 94
95 95 return htmlfill.render(
96 96 render('settings/repo_settings.html'),
97 97 defaults=defaults,
98 98 encoding="UTF-8",
99 99 force_defaults=False
100 100 )
101 101
102 102 @HasRepoPermissionAllDecorator('repository.admin')
103 103 def update(self, repo_name):
104 104 self.__load_defaults()
105 105 repo_model = RepoModel()
106 106 changed_name = repo_name
107 107 #override the choices with extracted revisions !
108 108 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
109 109 c.landing_revs_choices = choices
110
110 repo = Repository.get_by_repo_name(repo_name)
111 111 _form = RepoSettingsForm(edit=True,
112 old_data={'repo_name': repo_name},
112 old_data={'repo_name': repo_name,
113 'repo_group': repo.group.get_dict() \
114 if repo.group else {}},
113 115 repo_groups=c.repo_groups_choices,
114 116 landing_revs=c.landing_revs_choices)()
115 117 try:
116 118 form_result = _form.to_python(dict(request.POST))
117 119 repo_model.update(repo_name, **form_result)
118 120 invalidate_cache('get_repo_cached_%s' % repo_name)
119 121 h.flash(_('Repository %s updated successfully') % repo_name,
120 122 category='success')
121 123 changed_name = form_result['repo_name_full']
122 124 action_logger(self.rhodecode_user, 'user_updated_repo',
123 125 changed_name, self.ip_addr, self.sa)
124 126 Session().commit()
125 127 except formencode.Invalid, errors:
126 128 defaults = self.__load_data(repo_name)
127 129 defaults.update(errors.value)
128 130 return htmlfill.render(
129 131 render('settings/repo_settings.html'),
130 132 defaults=errors.value,
131 133 errors=errors.error_dict or {},
132 134 prefix_error=False,
133 135 encoding="UTF-8")
134 136
135 137 except Exception:
136 138 log.error(traceback.format_exc())
137 139 h.flash(_('error occurred during update of repository %s') \
138 140 % repo_name, category='error')
139 141
140 142 return redirect(url('repo_settings_home', repo_name=changed_name))
141 143
142 144 @HasRepoPermissionAllDecorator('repository.admin')
143 145 def delete(self, repo_name):
144 146 """DELETE /repos/repo_name: Delete an existing item"""
145 147 # Forms posted to this method should contain a hidden field:
146 148 # <input type="hidden" name="_method" value="DELETE" />
147 149 # Or using helpers:
148 150 # h.form(url('repo_settings_delete', repo_name=ID),
149 151 # method='delete')
150 152 # url('repo_settings_delete', repo_name=ID)
151 153
152 154 repo_model = RepoModel()
153 155 repo = repo_model.get_by_repo_name(repo_name)
154 156 if not repo:
155 157 h.not_mapped_error(repo_name)
156 158 return redirect(url('home'))
157 159 try:
158 160 action_logger(self.rhodecode_user, 'user_deleted_repo',
159 161 repo_name, self.ip_addr, self.sa)
160 162 repo_model.delete(repo)
161 163 invalidate_cache('get_repo_cached_%s' % repo_name)
162 164 h.flash(_('deleted repository %s') % repo_name, category='success')
163 165 Session().commit()
164 166 except Exception:
165 167 log.error(traceback.format_exc())
166 168 h.flash(_('An error occurred during deletion of %s') % repo_name,
167 169 category='error')
168 170
169 171 return redirect(url('admin_settings_my_account', anchor='my'))
170 172
171 173 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
172 174 def toggle_locking(self, repo_name):
173 175 """
174 176 Toggle locking of repository by simple GET call to url
175 177
176 178 :param repo_name:
177 179 """
178 180
179 181 try:
180 182 repo = Repository.get_by_repo_name(repo_name)
181 183
182 184 if repo.enable_locking:
183 185 if repo.locked[0]:
184 186 Repository.unlock(repo)
185 187 action = _('unlocked')
186 188 else:
187 189 Repository.lock(repo, c.rhodecode_user.user_id)
188 190 action = _('locked')
189 191
190 192 h.flash(_('Repository has been %s') % action,
191 193 category='success')
192 194 except Exception, e:
193 195 log.error(traceback.format_exc())
194 196 h.flash(_('An error occurred during unlocking'),
195 197 category='error')
196 198 return redirect(url('summary_home', repo_name=repo_name))
@@ -1,375 +1,375 b''
1 1 """ this is forms validation classes
2 2 http://formencode.org/module-formencode.validators.html
3 3 for list off all availible validators
4 4
5 5 we can create our own validators
6 6
7 7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 8 pre_validators [] These validators will be applied before the schema
9 9 chained_validators [] These validators will be applied after the schema
10 10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14 14
15 15
16 16 <name> = formencode.validators.<name of validator>
17 17 <name> must equal form name
18 18 list=[1,2,3,4,5]
19 19 for SELECT use formencode.All(OneOf(list), Int())
20 20
21 21 """
22 22 import logging
23 23
24 24 import formencode
25 25 from formencode import All
26 26
27 27 from pylons.i18n.translation import _
28 28
29 29 from rhodecode.model import validators as v
30 30 from rhodecode import BACKENDS
31 31
32 32 log = logging.getLogger(__name__)
33 33
34 34
35 35 class LoginForm(formencode.Schema):
36 36 allow_extra_fields = True
37 37 filter_extra_fields = True
38 38 username = v.UnicodeString(
39 39 strip=True,
40 40 min=1,
41 41 not_empty=True,
42 42 messages={
43 43 'empty': _(u'Please enter a login'),
44 44 'tooShort': _(u'Enter a value %(min)i characters long or more')}
45 45 )
46 46
47 47 password = v.UnicodeString(
48 48 strip=False,
49 49 min=3,
50 50 not_empty=True,
51 51 messages={
52 52 'empty': _(u'Please enter a password'),
53 53 'tooShort': _(u'Enter %(min)i characters or more')}
54 54 )
55 55
56 56 remember = v.StringBoolean(if_missing=False)
57 57
58 58 chained_validators = [v.ValidAuth()]
59 59
60 60
61 61 def UserForm(edit=False, old_data={}):
62 62 class _UserForm(formencode.Schema):
63 63 allow_extra_fields = True
64 64 filter_extra_fields = True
65 65 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
66 66 v.ValidUsername(edit, old_data))
67 67 if edit:
68 68 new_password = All(
69 69 v.ValidPassword(),
70 70 v.UnicodeString(strip=False, min=6, not_empty=False)
71 71 )
72 72 password_confirmation = All(
73 73 v.ValidPassword(),
74 74 v.UnicodeString(strip=False, min=6, not_empty=False),
75 75 )
76 76 admin = v.StringBoolean(if_missing=False)
77 77 else:
78 78 password = All(
79 79 v.ValidPassword(),
80 80 v.UnicodeString(strip=False, min=6, not_empty=True)
81 81 )
82 82 password_confirmation = All(
83 83 v.ValidPassword(),
84 84 v.UnicodeString(strip=False, min=6, not_empty=False)
85 85 )
86 86
87 87 active = v.StringBoolean(if_missing=False)
88 88 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
89 89 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
90 90 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
91 91
92 92 chained_validators = [v.ValidPasswordsMatch()]
93 93
94 94 return _UserForm
95 95
96 96
97 97 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
98 98 class _UsersGroupForm(formencode.Schema):
99 99 allow_extra_fields = True
100 100 filter_extra_fields = True
101 101
102 102 users_group_name = All(
103 103 v.UnicodeString(strip=True, min=1, not_empty=True),
104 104 v.ValidUsersGroup(edit, old_data)
105 105 )
106 106
107 107 users_group_active = v.StringBoolean(if_missing=False)
108 108
109 109 if edit:
110 110 users_group_members = v.OneOf(
111 111 available_members, hideList=False, testValueList=True,
112 112 if_missing=None, not_empty=False
113 113 )
114 114
115 115 return _UsersGroupForm
116 116
117 117
118 118 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
119 119 class _ReposGroupForm(formencode.Schema):
120 120 allow_extra_fields = True
121 121 filter_extra_fields = False
122 122
123 123 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
124 124 v.SlugifyName())
125 125 group_description = v.UnicodeString(strip=True, min=1,
126 126 not_empty=True)
127 127 group_parent_id = v.OneOf(available_groups, hideList=False,
128 128 testValueList=True,
129 129 if_missing=None, not_empty=False)
130 130 enable_locking = v.StringBoolean(if_missing=False)
131 131 recursive = v.StringBoolean(if_missing=False)
132 132 chained_validators = [v.ValidReposGroup(edit, old_data),
133 133 v.ValidPerms('group')]
134 134
135 135 return _ReposGroupForm
136 136
137 137
138 138 def RegisterForm(edit=False, old_data={}):
139 139 class _RegisterForm(formencode.Schema):
140 140 allow_extra_fields = True
141 141 filter_extra_fields = True
142 142 username = All(
143 143 v.ValidUsername(edit, old_data),
144 144 v.UnicodeString(strip=True, min=1, not_empty=True)
145 145 )
146 146 password = All(
147 147 v.ValidPassword(),
148 148 v.UnicodeString(strip=False, min=6, not_empty=True)
149 149 )
150 150 password_confirmation = All(
151 151 v.ValidPassword(),
152 152 v.UnicodeString(strip=False, min=6, not_empty=True)
153 153 )
154 154 active = v.StringBoolean(if_missing=False)
155 155 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
156 156 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
157 157 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
158 158
159 159 chained_validators = [v.ValidPasswordsMatch()]
160 160
161 161 return _RegisterForm
162 162
163 163
164 164 def PasswordResetForm():
165 165 class _PasswordResetForm(formencode.Schema):
166 166 allow_extra_fields = True
167 167 filter_extra_fields = True
168 168 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
169 169 return _PasswordResetForm
170 170
171 171
172 172 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
173 173 repo_groups=[], landing_revs=[]):
174 174 class _RepoForm(formencode.Schema):
175 175 allow_extra_fields = True
176 176 filter_extra_fields = False
177 177 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
178 178 v.SlugifyName())
179 repo_group = All(v.CanWriteGroup(),
179 repo_group = All(v.CanWriteGroup(old_data),
180 180 v.OneOf(repo_groups, hideList=True))
181 181 repo_type = v.OneOf(supported_backends)
182 182 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
183 183 repo_private = v.StringBoolean(if_missing=False)
184 184 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
185 185 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
186 186
187 187 repo_enable_statistics = v.StringBoolean(if_missing=False)
188 188 repo_enable_downloads = v.StringBoolean(if_missing=False)
189 189 repo_enable_locking = v.StringBoolean(if_missing=False)
190 190
191 191 if edit:
192 192 #this is repo owner
193 193 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
194 194
195 195 chained_validators = [v.ValidCloneUri(),
196 196 v.ValidRepoName(edit, old_data),
197 197 v.ValidPerms()]
198 198 return _RepoForm
199 199
200 200
201 201 def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
202 202 repo_groups=[], landing_revs=[]):
203 203 class _RepoForm(formencode.Schema):
204 204 allow_extra_fields = True
205 205 filter_extra_fields = False
206 206 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
207 207 v.SlugifyName())
208 repo_group = All(v.CanWriteGroup(),
208 repo_group = All(v.CanWriteGroup(old_data),
209 209 v.OneOf(repo_groups, hideList=True))
210 210 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
211 211 repo_private = v.StringBoolean(if_missing=False)
212 212 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
213 213 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
214 214
215 215 chained_validators = [v.ValidCloneUri(),
216 216 v.ValidRepoName(edit, old_data),
217 217 v.ValidPerms(),
218 218 v.ValidSettings()]
219 219 return _RepoForm
220 220
221 221
222 222 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
223 223 repo_groups=[], landing_revs=[]):
224 224 class _RepoForkForm(formencode.Schema):
225 225 allow_extra_fields = True
226 226 filter_extra_fields = False
227 227 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
228 228 v.SlugifyName())
229 229 repo_group = All(v.CanWriteGroup(),
230 230 v.OneOf(repo_groups, hideList=True))
231 231 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
232 232 description = v.UnicodeString(strip=True, min=1, not_empty=True)
233 233 private = v.StringBoolean(if_missing=False)
234 234 copy_permissions = v.StringBoolean(if_missing=False)
235 235 update_after_clone = v.StringBoolean(if_missing=False)
236 236 fork_parent_id = v.UnicodeString()
237 237 chained_validators = [v.ValidForkName(edit, old_data)]
238 238 landing_rev = v.OneOf(landing_revs, hideList=True)
239 239
240 240 return _RepoForkForm
241 241
242 242
243 243 def ApplicationSettingsForm():
244 244 class _ApplicationSettingsForm(formencode.Schema):
245 245 allow_extra_fields = True
246 246 filter_extra_fields = False
247 247 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
248 248 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
249 249 rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
250 250
251 251 return _ApplicationSettingsForm
252 252
253 253
254 254 def ApplicationVisualisationForm():
255 255 class _ApplicationVisualisationForm(formencode.Schema):
256 256 allow_extra_fields = True
257 257 filter_extra_fields = False
258 258 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
259 259 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
260 260 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
261 261
262 262 rhodecode_lightweight_dashboard = v.StringBoolean(if_missing=False)
263 263 rhodecode_lightweight_journal = v.StringBoolean(if_missing=False)
264 264
265 265 return _ApplicationVisualisationForm
266 266
267 267
268 268 def ApplicationUiSettingsForm():
269 269 class _ApplicationUiSettingsForm(formencode.Schema):
270 270 allow_extra_fields = True
271 271 filter_extra_fields = False
272 272 web_push_ssl = v.StringBoolean(if_missing=False)
273 273 paths_root_path = All(
274 274 v.ValidPath(),
275 275 v.UnicodeString(strip=True, min=1, not_empty=True)
276 276 )
277 277 hooks_changegroup_update = v.StringBoolean(if_missing=False)
278 278 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
279 279 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
280 280 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
281 281
282 282 extensions_largefiles = v.StringBoolean(if_missing=False)
283 283 extensions_hgsubversion = v.StringBoolean(if_missing=False)
284 284 extensions_hggit = v.StringBoolean(if_missing=False)
285 285
286 286 return _ApplicationUiSettingsForm
287 287
288 288
289 289 def DefaultPermissionsForm(repo_perms_choices, group_perms_choices,
290 290 register_choices, create_choices, fork_choices):
291 291 class _DefaultPermissionsForm(formencode.Schema):
292 292 allow_extra_fields = True
293 293 filter_extra_fields = True
294 294 overwrite_default_repo = v.StringBoolean(if_missing=False)
295 295 overwrite_default_group = v.StringBoolean(if_missing=False)
296 296 anonymous = v.StringBoolean(if_missing=False)
297 297 default_repo_perm = v.OneOf(repo_perms_choices)
298 298 default_group_perm = v.OneOf(group_perms_choices)
299 299 default_register = v.OneOf(register_choices)
300 300 default_create = v.OneOf(create_choices)
301 301 default_fork = v.OneOf(fork_choices)
302 302
303 303 return _DefaultPermissionsForm
304 304
305 305
306 306 def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
307 307 class _DefaultsForm(formencode.Schema):
308 308 allow_extra_fields = True
309 309 filter_extra_fields = True
310 310 default_repo_type = v.OneOf(supported_backends)
311 311 default_repo_private = v.StringBoolean(if_missing=False)
312 312 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
313 313 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
314 314 default_repo_enable_locking = v.StringBoolean(if_missing=False)
315 315
316 316 return _DefaultsForm
317 317
318 318
319 319 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
320 320 tls_kind_choices):
321 321 class _LdapSettingsForm(formencode.Schema):
322 322 allow_extra_fields = True
323 323 filter_extra_fields = True
324 324 #pre_validators = [LdapLibValidator]
325 325 ldap_active = v.StringBoolean(if_missing=False)
326 326 ldap_host = v.UnicodeString(strip=True,)
327 327 ldap_port = v.Number(strip=True,)
328 328 ldap_tls_kind = v.OneOf(tls_kind_choices)
329 329 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
330 330 ldap_dn_user = v.UnicodeString(strip=True,)
331 331 ldap_dn_pass = v.UnicodeString(strip=True,)
332 332 ldap_base_dn = v.UnicodeString(strip=True,)
333 333 ldap_filter = v.UnicodeString(strip=True,)
334 334 ldap_search_scope = v.OneOf(search_scope_choices)
335 335 ldap_attr_login = All(
336 336 v.AttrLoginValidator(),
337 337 v.UnicodeString(strip=True,)
338 338 )
339 339 ldap_attr_firstname = v.UnicodeString(strip=True,)
340 340 ldap_attr_lastname = v.UnicodeString(strip=True,)
341 341 ldap_attr_email = v.UnicodeString(strip=True,)
342 342
343 343 return _LdapSettingsForm
344 344
345 345
346 346 def UserExtraEmailForm():
347 347 class _UserExtraEmailForm(formencode.Schema):
348 348 email = All(v.UniqSystemEmail(), v.Email(not_empty=True))
349 349 return _UserExtraEmailForm
350 350
351 351
352 352 def UserExtraIpForm():
353 353 class _UserExtraIpForm(formencode.Schema):
354 354 ip = v.ValidIp()(not_empty=True)
355 355 return _UserExtraIpForm
356 356
357 357
358 358 def PullRequestForm(repo_id):
359 359 class _PullRequestForm(formencode.Schema):
360 360 allow_extra_fields = True
361 361 filter_extra_fields = True
362 362
363 363 user = v.UnicodeString(strip=True, required=True)
364 364 org_repo = v.UnicodeString(strip=True, required=True)
365 365 org_ref = v.UnicodeString(strip=True, required=True)
366 366 other_repo = v.UnicodeString(strip=True, required=True)
367 367 other_ref = v.UnicodeString(strip=True, required=True)
368 368 revisions = All(#v.NotReviewedRevisions(repo_id)(),
369 369 v.UniqueList(not_empty=True))
370 370 review_members = v.UniqueList(not_empty=True)
371 371
372 372 pullrequest_title = v.UnicodeString(strip=True, required=True, min=3)
373 373 pullrequest_desc = v.UnicodeString(strip=True, required=False)
374 374
375 375 return _PullRequestForm
@@ -1,746 +1,792 b''
1 1 """
2 2 Set of generic validators
3 3 """
4 4 import os
5 5 import re
6 6 import formencode
7 7 import logging
8 8 from collections import defaultdict
9 9 from pylons.i18n.translation import _
10 10 from webhelpers.pylonslib.secure_form import authentication_token
11 11
12 12 from formencode.validators import (
13 13 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set,
14 14 NotEmpty, IPAddress, CIDR
15 15 )
16 16 from rhodecode.lib.compat import OrderedSet
17 17 from rhodecode.lib import ipaddr
18 18 from rhodecode.lib.utils import repo_name_slug
19 from rhodecode.lib.utils2 import safe_int
19 20 from rhodecode.model.db import RepoGroup, Repository, UsersGroup, User,\
20 21 ChangesetStatus
21 22 from rhodecode.lib.exceptions import LdapImportError
22 23 from rhodecode.config.routing import ADMIN_PREFIX
23 from rhodecode.lib.auth import HasReposGroupPermissionAny
24 from rhodecode.lib.auth import HasReposGroupPermissionAny, HasPermissionAny
24 25
25 26 # silence warnings and pylint
26 27 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \
27 28 NotEmpty, IPAddress, CIDR
28 29
29 30 log = logging.getLogger(__name__)
30 31
31 32
32 33 class UniqueList(formencode.FancyValidator):
33 34 """
34 35 Unique List !
35 36 """
36 37 messages = dict(
37 38 empty=_('Value cannot be an empty list'),
38 39 missing_value=_('Value cannot be an empty list'),
39 40 )
40 41
41 42 def _to_python(self, value, state):
42 43 if isinstance(value, list):
43 44 return value
44 45 elif isinstance(value, set):
45 46 return list(value)
46 47 elif isinstance(value, tuple):
47 48 return list(value)
48 49 elif value is None:
49 50 return []
50 51 else:
51 52 return [value]
52 53
53 54 def empty_value(self, value):
54 55 return []
55 56
56 57
57 58 class StateObj(object):
58 59 """
59 60 this is needed to translate the messages using _() in validators
60 61 """
61 62 _ = staticmethod(_)
62 63
63 64
64 65 def M(self, key, state=None, **kwargs):
65 66 """
66 67 returns string from self.message based on given key,
67 68 passed kw params are used to substitute %(named)s params inside
68 69 translated strings
69 70
70 71 :param msg:
71 72 :param state:
72 73 """
73 74 if state is None:
74 75 state = StateObj()
75 76 else:
76 77 state._ = staticmethod(_)
77 78 #inject validator into state object
78 79 return self.message(key, state, **kwargs)
79 80
80 81
81 82 def ValidUsername(edit=False, old_data={}):
82 83 class _validator(formencode.validators.FancyValidator):
83 84 messages = {
84 85 'username_exists': _(u'Username "%(username)s" already exists'),
85 86 'system_invalid_username':
86 87 _(u'Username "%(username)s" is forbidden'),
87 88 'invalid_username':
88 89 _(u'Username may only contain alphanumeric characters '
89 90 'underscores, periods or dashes and must begin with '
90 91 'alphanumeric character')
91 92 }
92 93
93 94 def validate_python(self, value, state):
94 95 if value in ['default', 'new_user']:
95 96 msg = M(self, 'system_invalid_username', state, username=value)
96 97 raise formencode.Invalid(msg, value, state)
97 98 #check if user is unique
98 99 old_un = None
99 100 if edit:
100 101 old_un = User.get(old_data.get('user_id')).username
101 102
102 103 if old_un != value or not edit:
103 104 if User.get_by_username(value, case_insensitive=True):
104 105 msg = M(self, 'username_exists', state, username=value)
105 106 raise formencode.Invalid(msg, value, state)
106 107
107 108 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
108 109 msg = M(self, 'invalid_username', state)
109 110 raise formencode.Invalid(msg, value, state)
110 111 return _validator
111 112
112 113
113 114 def ValidRepoUser():
114 115 class _validator(formencode.validators.FancyValidator):
115 116 messages = {
116 117 'invalid_username': _(u'Username %(username)s is not valid')
117 118 }
118 119
119 120 def validate_python(self, value, state):
120 121 try:
121 122 User.query().filter(User.active == True)\
122 123 .filter(User.username == value).one()
123 124 except Exception:
124 125 msg = M(self, 'invalid_username', state, username=value)
125 126 raise formencode.Invalid(msg, value, state,
126 127 error_dict=dict(username=msg)
127 128 )
128 129
129 130 return _validator
130 131
131 132
132 133 def ValidUsersGroup(edit=False, old_data={}):
133 134 class _validator(formencode.validators.FancyValidator):
134 135 messages = {
135 136 'invalid_group': _(u'Invalid users group name'),
136 137 'group_exist': _(u'Users group "%(usersgroup)s" already exists'),
137 138 'invalid_usersgroup_name':
138 139 _(u'users group name may only contain alphanumeric '
139 140 'characters underscores, periods or dashes and must begin '
140 141 'with alphanumeric character')
141 142 }
142 143
143 144 def validate_python(self, value, state):
144 145 if value in ['default']:
145 146 msg = M(self, 'invalid_group', state)
146 147 raise formencode.Invalid(msg, value, state,
147 148 error_dict=dict(users_group_name=msg)
148 149 )
149 150 #check if group is unique
150 151 old_ugname = None
151 152 if edit:
152 153 old_id = old_data.get('users_group_id')
153 154 old_ugname = UsersGroup.get(old_id).users_group_name
154 155
155 156 if old_ugname != value or not edit:
156 157 is_existing_group = UsersGroup.get_by_group_name(value,
157 158 case_insensitive=True)
158 159 if is_existing_group:
159 160 msg = M(self, 'group_exist', state, usersgroup=value)
160 161 raise formencode.Invalid(msg, value, state,
161 162 error_dict=dict(users_group_name=msg)
162 163 )
163 164
164 165 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
165 166 msg = M(self, 'invalid_usersgroup_name', state)
166 167 raise formencode.Invalid(msg, value, state,
167 168 error_dict=dict(users_group_name=msg)
168 169 )
169 170
170 171 return _validator
171 172
172 173
173 174 def ValidReposGroup(edit=False, old_data={}):
174 175 class _validator(formencode.validators.FancyValidator):
175 176 messages = {
176 177 'group_parent_id': _(u'Cannot assign this group as parent'),
177 178 'group_exists': _(u'Group "%(group_name)s" already exists'),
178 179 'repo_exists':
179 180 _(u'Repository with name "%(group_name)s" already exists')
180 181 }
181 182
182 183 def validate_python(self, value, state):
183 184 # TODO WRITE VALIDATIONS
184 185 group_name = value.get('group_name')
185 186 group_parent_id = value.get('group_parent_id')
186 187
187 188 # slugify repo group just in case :)
188 189 slug = repo_name_slug(group_name)
189 190
190 191 # check for parent of self
191 192 parent_of_self = lambda: (
192 193 old_data['group_id'] == int(group_parent_id)
193 194 if group_parent_id else False
194 195 )
195 196 if edit and parent_of_self():
196 197 msg = M(self, 'group_parent_id', state)
197 198 raise formencode.Invalid(msg, value, state,
198 199 error_dict=dict(group_parent_id=msg)
199 200 )
200 201
201 202 old_gname = None
202 203 if edit:
203 204 old_gname = RepoGroup.get(old_data.get('group_id')).group_name
204 205
205 206 if old_gname != group_name or not edit:
206 207
207 208 # check group
208 209 gr = RepoGroup.query()\
209 210 .filter(RepoGroup.group_name == slug)\
210 211 .filter(RepoGroup.group_parent_id == group_parent_id)\
211 212 .scalar()
212 213
213 214 if gr:
214 215 msg = M(self, 'group_exists', state, group_name=slug)
215 216 raise formencode.Invalid(msg, value, state,
216 217 error_dict=dict(group_name=msg)
217 218 )
218 219
219 220 # check for same repo
220 221 repo = Repository.query()\
221 222 .filter(Repository.repo_name == slug)\
222 223 .scalar()
223 224
224 225 if repo:
225 226 msg = M(self, 'repo_exists', state, group_name=slug)
226 227 raise formencode.Invalid(msg, value, state,
227 228 error_dict=dict(group_name=msg)
228 229 )
229 230
230 231 return _validator
231 232
232 233
233 234 def ValidPassword():
234 235 class _validator(formencode.validators.FancyValidator):
235 236 messages = {
236 237 'invalid_password':
237 238 _(u'Invalid characters (non-ascii) in password')
238 239 }
239 240
240 241 def validate_python(self, value, state):
241 242 try:
242 243 (value or '').decode('ascii')
243 244 except UnicodeError:
244 245 msg = M(self, 'invalid_password', state)
245 246 raise formencode.Invalid(msg, value, state,)
246 247 return _validator
247 248
248 249
249 250 def ValidPasswordsMatch():
250 251 class _validator(formencode.validators.FancyValidator):
251 252 messages = {
252 253 'password_mismatch': _(u'Passwords do not match'),
253 254 }
254 255
255 256 def validate_python(self, value, state):
256 257
257 258 pass_val = value.get('password') or value.get('new_password')
258 259 if pass_val != value['password_confirmation']:
259 260 msg = M(self, 'password_mismatch', state)
260 261 raise formencode.Invalid(msg, value, state,
261 262 error_dict=dict(password_confirmation=msg)
262 263 )
263 264 return _validator
264 265
265 266
266 267 def ValidAuth():
267 268 class _validator(formencode.validators.FancyValidator):
268 269 messages = {
269 270 'invalid_password': _(u'invalid password'),
270 271 'invalid_username': _(u'invalid user name'),
271 272 'disabled_account': _(u'Your account is disabled')
272 273 }
273 274
274 275 def validate_python(self, value, state):
275 276 from rhodecode.lib.auth import authenticate
276 277
277 278 password = value['password']
278 279 username = value['username']
279 280
280 281 if not authenticate(username, password):
281 282 user = User.get_by_username(username)
282 283 if user and user.active is False:
283 284 log.warning('user %s is disabled' % username)
284 285 msg = M(self, 'disabled_account', state)
285 286 raise formencode.Invalid(msg, value, state,
286 287 error_dict=dict(username=msg)
287 288 )
288 289 else:
289 290 log.warning('user %s failed to authenticate' % username)
290 291 msg = M(self, 'invalid_username', state)
291 292 msg2 = M(self, 'invalid_password', state)
292 293 raise formencode.Invalid(msg, value, state,
293 294 error_dict=dict(username=msg, password=msg2)
294 295 )
295 296 return _validator
296 297
297 298
298 299 def ValidAuthToken():
299 300 class _validator(formencode.validators.FancyValidator):
300 301 messages = {
301 302 'invalid_token': _(u'Token mismatch')
302 303 }
303 304
304 305 def validate_python(self, value, state):
305 306 if value != authentication_token():
306 307 msg = M(self, 'invalid_token', state)
307 308 raise formencode.Invalid(msg, value, state)
308 309 return _validator
309 310
310 311
311 312 def ValidRepoName(edit=False, old_data={}):
312 313 class _validator(formencode.validators.FancyValidator):
313 314 messages = {
314 315 'invalid_repo_name':
315 316 _(u'Repository name %(repo)s is disallowed'),
316 317 'repository_exists':
317 318 _(u'Repository named %(repo)s already exists'),
318 319 'repository_in_group_exists': _(u'Repository "%(repo)s" already '
319 320 'exists in group "%(group)s"'),
320 321 'same_group_exists': _(u'Repositories group with name "%(repo)s" '
321 322 'already exists')
322 323 }
323 324
324 325 def _to_python(self, value, state):
325 326 repo_name = repo_name_slug(value.get('repo_name', ''))
326 327 repo_group = value.get('repo_group')
327 328 if repo_group:
328 329 gr = RepoGroup.get(repo_group)
329 330 group_path = gr.full_path
330 331 group_name = gr.group_name
331 332 # value needs to be aware of group name in order to check
332 333 # db key This is an actual just the name to store in the
333 334 # database
334 335 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
335 336 else:
336 337 group_name = group_path = ''
337 338 repo_name_full = repo_name
338 339
339 340 value['repo_name'] = repo_name
340 341 value['repo_name_full'] = repo_name_full
341 342 value['group_path'] = group_path
342 343 value['group_name'] = group_name
343 344 return value
344 345
345 346 def validate_python(self, value, state):
346 347
347 348 repo_name = value.get('repo_name')
348 349 repo_name_full = value.get('repo_name_full')
349 350 group_path = value.get('group_path')
350 351 group_name = value.get('group_name')
351 352
352 353 if repo_name in [ADMIN_PREFIX, '']:
353 354 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
354 355 raise formencode.Invalid(msg, value, state,
355 356 error_dict=dict(repo_name=msg)
356 357 )
357 358
358 359 rename = old_data.get('repo_name') != repo_name_full
359 360 create = not edit
360 361 if rename or create:
361 362
362 363 if group_path != '':
363 364 if Repository.get_by_repo_name(repo_name_full):
364 365 msg = M(self, 'repository_in_group_exists', state,
365 366 repo=repo_name, group=group_name)
366 367 raise formencode.Invalid(msg, value, state,
367 368 error_dict=dict(repo_name=msg)
368 369 )
369 370 elif RepoGroup.get_by_group_name(repo_name_full):
370 371 msg = M(self, 'same_group_exists', state,
371 372 repo=repo_name)
372 373 raise formencode.Invalid(msg, value, state,
373 374 error_dict=dict(repo_name=msg)
374 375 )
375 376
376 377 elif Repository.get_by_repo_name(repo_name_full):
377 378 msg = M(self, 'repository_exists', state,
378 379 repo=repo_name)
379 380 raise formencode.Invalid(msg, value, state,
380 381 error_dict=dict(repo_name=msg)
381 382 )
382 383 return value
383 384 return _validator
384 385
385 386
386 387 def ValidForkName(*args, **kwargs):
387 388 return ValidRepoName(*args, **kwargs)
388 389
389 390
390 391 def SlugifyName():
391 392 class _validator(formencode.validators.FancyValidator):
392 393
393 394 def _to_python(self, value, state):
394 395 return repo_name_slug(value)
395 396
396 397 def validate_python(self, value, state):
397 398 pass
398 399
399 400 return _validator
400 401
401 402
402 403 def ValidCloneUri():
403 404 from rhodecode.lib.utils import make_ui
404 405
405 406 def url_handler(repo_type, url, ui=None):
406 407 if repo_type == 'hg':
407 408 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
408 409 from mercurial.httppeer import httppeer
409 410 if url.startswith('http'):
410 411 ## initially check if it's at least the proper URL
411 412 ## or does it pass basic auth
412 413 MercurialRepository._check_url(url)
413 414 httppeer(ui, url)._capabilities()
414 415 elif url.startswith('svn+http'):
415 416 from hgsubversion.svnrepo import svnremoterepo
416 417 svnremoterepo(ui, url).capabilities
417 418 elif url.startswith('git+http'):
418 419 raise NotImplementedError()
419 420 else:
420 421 raise Exception('clone from URI %s not allowed' % (url))
421 422
422 423 elif repo_type == 'git':
423 424 from rhodecode.lib.vcs.backends.git.repository import GitRepository
424 425 if url.startswith('http'):
425 426 ## initially check if it's at least the proper URL
426 427 ## or does it pass basic auth
427 428 GitRepository._check_url(url)
428 429 elif url.startswith('svn+http'):
429 430 raise NotImplementedError()
430 431 elif url.startswith('hg+http'):
431 432 raise NotImplementedError()
432 433 else:
433 434 raise Exception('clone from URI %s not allowed' % (url))
434 435
435 436 class _validator(formencode.validators.FancyValidator):
436 437 messages = {
437 438 'clone_uri': _(u'invalid clone url'),
438 439 'invalid_clone_uri': _(u'Invalid clone url, provide a '
439 440 'valid clone http(s)/svn+http(s) url')
440 441 }
441 442
442 443 def validate_python(self, value, state):
443 444 repo_type = value.get('repo_type')
444 445 url = value.get('clone_uri')
445 446
446 447 if not url:
447 448 pass
448 449 else:
449 450 try:
450 451 url_handler(repo_type, url, make_ui('db', clear_session=False))
451 452 except Exception:
452 453 log.exception('Url validation failed')
453 454 msg = M(self, 'clone_uri')
454 455 raise formencode.Invalid(msg, value, state,
455 456 error_dict=dict(clone_uri=msg)
456 457 )
457 458 return _validator
458 459
459 460
460 461 def ValidForkType(old_data={}):
461 462 class _validator(formencode.validators.FancyValidator):
462 463 messages = {
463 464 'invalid_fork_type': _(u'Fork have to be the same type as parent')
464 465 }
465 466
466 467 def validate_python(self, value, state):
467 468 if old_data['repo_type'] != value:
468 469 msg = M(self, 'invalid_fork_type', state)
469 470 raise formencode.Invalid(msg, value, state,
470 471 error_dict=dict(repo_type=msg)
471 472 )
472 473 return _validator
473 474
474 475
475 def CanWriteGroup():
476 def CanWriteGroup(old_data=None):
476 477 class _validator(formencode.validators.FancyValidator):
477 478 messages = {
478 479 'permission_denied': _(u"You don't have permissions "
479 480 "to create repository in this group")
480 481 }
481 482
482 483 def validate_python(self, value, state):
483 484 gr = RepoGroup.get(value)
484 if not HasReposGroupPermissionAny(
485 'group.write', 'group.admin'
486 )(gr.group_name, 'get group of repo form'):
485 gr_name = gr.group_name if gr else None # None means ROOT location
486 val = HasReposGroupPermissionAny('group.write', 'group.admin')
487 can_create_repos = HasPermissionAny('hg.admin', 'hg.create.repository')
488 forbidden = not val(gr_name, 'can write into group validator')
489 value_changed = old_data['repo_group'].get('group_id') != safe_int(value)
490 if value_changed: # do check if we changed the value
491 #parent group need to be existing
492 if gr and forbidden:
493 msg = M(self, 'permission_denied', state)
494 raise formencode.Invalid(msg, value, state,
495 error_dict=dict(repo_type=msg)
496 )
497 ## check if we can write to root location !
498 elif gr is None and can_create_repos() is False:
499 msg = M(self, 'permission_denied_root', state)
500 raise formencode.Invalid(msg, value, state,
501 error_dict=dict(repo_type=msg)
502 )
503
504 return _validator
505
506
507 def CanCreateGroup(can_create_in_root=False):
508 class _validator(formencode.validators.FancyValidator):
509 messages = {
510 'permission_denied': _(u"You don't have permissions "
511 "to create a group in this location")
512 }
513
514 def to_python(self, value, state):
515 #root location
516 if value in [-1, "-1"]:
517 return None
518 return value
519
520 def validate_python(self, value, state):
521 gr = RepoGroup.get(value)
522 gr_name = gr.group_name if gr else None # None means ROOT location
523
524 if can_create_in_root and gr is None:
525 #we can create in root, we're fine no validations required
526 return
527
528 forbidden_in_root = gr is None and can_create_in_root is False
529 val = HasReposGroupPermissionAny('group.admin')
530 forbidden = not val(gr_name, 'can create group validator')
531 if forbidden_in_root or forbidden:
487 532 msg = M(self, 'permission_denied', state)
488 533 raise formencode.Invalid(msg, value, state,
489 error_dict=dict(repo_type=msg)
534 error_dict=dict(group_parent_id=msg)
490 535 )
536
491 537 return _validator
492 538
493 539
494 540 def ValidPerms(type_='repo'):
495 541 if type_ == 'group':
496 542 EMPTY_PERM = 'group.none'
497 543 elif type_ == 'repo':
498 544 EMPTY_PERM = 'repository.none'
499 545
500 546 class _validator(formencode.validators.FancyValidator):
501 547 messages = {
502 548 'perm_new_member_name':
503 549 _(u'This username or users group name is not valid')
504 550 }
505 551
506 552 def to_python(self, value, state):
507 553 perms_update = OrderedSet()
508 554 perms_new = OrderedSet()
509 555 # build a list of permission to update and new permission to create
510 556
511 557 #CLEAN OUT ORG VALUE FROM NEW MEMBERS, and group them using
512 558 new_perms_group = defaultdict(dict)
513 559 for k, v in value.copy().iteritems():
514 560 if k.startswith('perm_new_member'):
515 561 del value[k]
516 562 _type, part = k.split('perm_new_member_')
517 563 args = part.split('_')
518 564 if len(args) == 1:
519 565 new_perms_group[args[0]]['perm'] = v
520 566 elif len(args) == 2:
521 567 _key, pos = args
522 568 new_perms_group[pos][_key] = v
523 569
524 570 # fill new permissions in order of how they were added
525 571 for k in sorted(map(int, new_perms_group.keys())):
526 572 perm_dict = new_perms_group[str(k)]
527 573 new_member = perm_dict.get('name')
528 574 new_perm = perm_dict.get('perm')
529 575 new_type = perm_dict.get('type')
530 576 if new_member and new_perm and new_type:
531 577 perms_new.add((new_member, new_perm, new_type))
532 578
533 579 for k, v in value.iteritems():
534 580 if k.startswith('u_perm_') or k.startswith('g_perm_'):
535 581 member = k[7:]
536 582 t = {'u': 'user',
537 583 'g': 'users_group'
538 584 }[k[0]]
539 585 if member == 'default':
540 586 if value.get('repo_private'):
541 587 # set none for default when updating to
542 588 # private repo
543 589 v = EMPTY_PERM
544 590 perms_update.add((member, v, t))
545 591
546 592 value['perms_updates'] = list(perms_update)
547 593 value['perms_new'] = list(perms_new)
548 594
549 595 # update permissions
550 596 for k, v, t in perms_new:
551 597 try:
552 598 if t is 'user':
553 599 self.user_db = User.query()\
554 600 .filter(User.active == True)\
555 601 .filter(User.username == k).one()
556 602 if t is 'users_group':
557 603 self.user_db = UsersGroup.query()\
558 604 .filter(UsersGroup.users_group_active == True)\
559 605 .filter(UsersGroup.users_group_name == k).one()
560 606
561 607 except Exception:
562 608 log.exception('Updated permission failed')
563 609 msg = M(self, 'perm_new_member_type', state)
564 610 raise formencode.Invalid(msg, value, state,
565 611 error_dict=dict(perm_new_member_name=msg)
566 612 )
567 613 return value
568 614 return _validator
569 615
570 616
571 617 def ValidSettings():
572 618 class _validator(formencode.validators.FancyValidator):
573 619 def _to_python(self, value, state):
574 620 # settings form for users that are not admin
575 621 # can't edit certain parameters, it's extra backup if they mangle
576 622 # with forms
577 623
578 624 forbidden_params = [
579 625 'user', 'repo_type', 'repo_enable_locking',
580 626 'repo_enable_downloads', 'repo_enable_statistics'
581 627 ]
582 628
583 629 for param in forbidden_params:
584 630 if param in value:
585 631 del value[param]
586 632 return value
587 633
588 634 def validate_python(self, value, state):
589 635 pass
590 636 return _validator
591 637
592 638
593 639 def ValidPath():
594 640 class _validator(formencode.validators.FancyValidator):
595 641 messages = {
596 642 'invalid_path': _(u'This is not a valid path')
597 643 }
598 644
599 645 def validate_python(self, value, state):
600 646 if not os.path.isdir(value):
601 647 msg = M(self, 'invalid_path', state)
602 648 raise formencode.Invalid(msg, value, state,
603 649 error_dict=dict(paths_root_path=msg)
604 650 )
605 651 return _validator
606 652
607 653
608 654 def UniqSystemEmail(old_data={}):
609 655 class _validator(formencode.validators.FancyValidator):
610 656 messages = {
611 657 'email_taken': _(u'This e-mail address is already taken')
612 658 }
613 659
614 660 def _to_python(self, value, state):
615 661 return value.lower()
616 662
617 663 def validate_python(self, value, state):
618 664 if (old_data.get('email') or '').lower() != value:
619 665 user = User.get_by_email(value, case_insensitive=True)
620 666 if user:
621 667 msg = M(self, 'email_taken', state)
622 668 raise formencode.Invalid(msg, value, state,
623 669 error_dict=dict(email=msg)
624 670 )
625 671 return _validator
626 672
627 673
628 674 def ValidSystemEmail():
629 675 class _validator(formencode.validators.FancyValidator):
630 676 messages = {
631 677 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
632 678 }
633 679
634 680 def _to_python(self, value, state):
635 681 return value.lower()
636 682
637 683 def validate_python(self, value, state):
638 684 user = User.get_by_email(value, case_insensitive=True)
639 685 if user is None:
640 686 msg = M(self, 'non_existing_email', state, email=value)
641 687 raise formencode.Invalid(msg, value, state,
642 688 error_dict=dict(email=msg)
643 689 )
644 690
645 691 return _validator
646 692
647 693
648 694 def LdapLibValidator():
649 695 class _validator(formencode.validators.FancyValidator):
650 696 messages = {
651 697
652 698 }
653 699
654 700 def validate_python(self, value, state):
655 701 try:
656 702 import ldap
657 703 ldap # pyflakes silence !
658 704 except ImportError:
659 705 raise LdapImportError()
660 706
661 707 return _validator
662 708
663 709
664 710 def AttrLoginValidator():
665 711 class _validator(formencode.validators.FancyValidator):
666 712 messages = {
667 713 'invalid_cn':
668 714 _(u'The LDAP Login attribute of the CN must be specified - '
669 715 'this is the name of the attribute that is equivalent '
670 716 'to "username"')
671 717 }
672 718
673 719 def validate_python(self, value, state):
674 720 if not value or not isinstance(value, (str, unicode)):
675 721 msg = M(self, 'invalid_cn', state)
676 722 raise formencode.Invalid(msg, value, state,
677 723 error_dict=dict(ldap_attr_login=msg)
678 724 )
679 725
680 726 return _validator
681 727
682 728
683 729 def NotReviewedRevisions(repo_id):
684 730 class _validator(formencode.validators.FancyValidator):
685 731 messages = {
686 732 'rev_already_reviewed':
687 733 _(u'Revisions %(revs)s are already part of pull request '
688 734 'or have set status')
689 735 }
690 736
691 737 def validate_python(self, value, state):
692 738 # check revisions if they are not reviewed, or a part of another
693 739 # pull request
694 740 statuses = ChangesetStatus.query()\
695 741 .filter(ChangesetStatus.revision.in_(value))\
696 742 .filter(ChangesetStatus.repo_id == repo_id)\
697 743 .all()
698 744
699 745 errors = []
700 746 for cs in statuses:
701 747 if cs.pull_request_id:
702 748 errors.append(['pull_req', cs.revision[:12]])
703 749 elif cs.status:
704 750 errors.append(['status', cs.revision[:12]])
705 751
706 752 if errors:
707 753 revs = ','.join([x[1] for x in errors])
708 754 msg = M(self, 'rev_already_reviewed', state, revs=revs)
709 755 raise formencode.Invalid(msg, value, state,
710 756 error_dict=dict(revisions=revs)
711 757 )
712 758
713 759 return _validator
714 760
715 761
716 762 def ValidIp():
717 763 class _validator(CIDR):
718 764 messages = dict(
719 765 badFormat=_('Please enter a valid IPv4 or IpV6 address'),
720 766 illegalBits=_('The network size (bits) must be within the range'
721 767 ' of 0-32 (not %(bits)r)'))
722 768
723 769 def to_python(self, value, state):
724 770 v = super(_validator, self).to_python(value, state)
725 771 v = v.strip()
726 772 net = ipaddr.IPNetwork(address=v)
727 773 if isinstance(net, ipaddr.IPv4Network):
728 774 #if IPv4 doesn't end with a mask, add /32
729 775 if '/' not in value:
730 776 v += '/32'
731 777 if isinstance(net, ipaddr.IPv6Network):
732 778 #if IPv6 doesn't end with a mask, add /128
733 779 if '/' not in value:
734 780 v += '/128'
735 781 return v
736 782
737 783 def validate_python(self, value, state):
738 784 try:
739 785 addr = value.strip()
740 786 #this raises an ValueError if address is not IpV4 or IpV6
741 787 ipaddr.IPNetwork(address=addr)
742 788 except ValueError:
743 789 raise formencode.Invalid(self.message('badFormat', state),
744 790 value, state)
745 791
746 792 return _validator
General Comments 0
You need to be logged in to leave comments. Login now