##// END OF EJS Templates
repos, repo groups, user groups: allow to use disabled users in owner field....
marcink -
r224:c6a3436d default
parent child Browse files
Show More
@@ -1,407 +1,406 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 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 """
22 """
23 Repository groups controller for RhodeCode
23 Repository groups controller for RhodeCode
24 """
24 """
25
25
26 import logging
26 import logging
27 import formencode
27 import formencode
28
28
29 from formencode import htmlfill
29 from formencode import htmlfill
30
30
31 from pylons import request, tmpl_context as c, url
31 from pylons import request, tmpl_context as c, url
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33 from pylons.i18n.translation import _, ungettext
33 from pylons.i18n.translation import _, ungettext
34
34
35 from rhodecode.lib import auth
35 from rhodecode.lib import auth
36 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
37 from rhodecode.lib.ext_json import json
37 from rhodecode.lib.ext_json import json
38 from rhodecode.lib.auth import (
38 from rhodecode.lib.auth import (
39 LoginRequired, NotAnonymous, HasPermissionAll,
39 LoginRequired, NotAnonymous, HasPermissionAll,
40 HasRepoGroupPermissionAll, HasRepoGroupPermissionAnyDecorator)
40 HasRepoGroupPermissionAll, HasRepoGroupPermissionAnyDecorator)
41 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.base import BaseController, render
42 from rhodecode.model.db import RepoGroup, User
42 from rhodecode.model.db import RepoGroup, User
43 from rhodecode.model.scm import RepoGroupList
43 from rhodecode.model.scm import RepoGroupList
44 from rhodecode.model.repo_group import RepoGroupModel
44 from rhodecode.model.repo_group import RepoGroupModel
45 from rhodecode.model.forms import RepoGroupForm, RepoGroupPermsForm
45 from rhodecode.model.forms import RepoGroupForm, RepoGroupPermsForm
46 from rhodecode.model.meta import Session
46 from rhodecode.model.meta import Session
47 from rhodecode.lib.utils2 import safe_int
47 from rhodecode.lib.utils2 import safe_int
48
48
49
49
50 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
51
51
52
52
53 class RepoGroupsController(BaseController):
53 class RepoGroupsController(BaseController):
54 """REST Controller styled on the Atom Publishing Protocol"""
54 """REST Controller styled on the Atom Publishing Protocol"""
55
55
56 @LoginRequired()
56 @LoginRequired()
57 def __before__(self):
57 def __before__(self):
58 super(RepoGroupsController, self).__before__()
58 super(RepoGroupsController, self).__before__()
59
59
60 def __load_defaults(self, allow_empty_group=False, repo_group=None):
60 def __load_defaults(self, allow_empty_group=False, repo_group=None):
61 if self._can_create_repo_group():
61 if self._can_create_repo_group():
62 # we're global admin, we're ok and we can create TOP level groups
62 # we're global admin, we're ok and we can create TOP level groups
63 allow_empty_group = True
63 allow_empty_group = True
64
64
65 # override the choices for this form, we need to filter choices
65 # override the choices for this form, we need to filter choices
66 # and display only those we have ADMIN right
66 # and display only those we have ADMIN right
67 groups_with_admin_rights = RepoGroupList(
67 groups_with_admin_rights = RepoGroupList(
68 RepoGroup.query().all(),
68 RepoGroup.query().all(),
69 perm_set=['group.admin'])
69 perm_set=['group.admin'])
70 c.repo_groups = RepoGroup.groups_choices(
70 c.repo_groups = RepoGroup.groups_choices(
71 groups=groups_with_admin_rights,
71 groups=groups_with_admin_rights,
72 show_empty_group=allow_empty_group)
72 show_empty_group=allow_empty_group)
73
73
74 if repo_group:
74 if repo_group:
75 # exclude filtered ids
75 # exclude filtered ids
76 exclude_group_ids = [repo_group.group_id]
76 exclude_group_ids = [repo_group.group_id]
77 c.repo_groups = filter(lambda x: x[0] not in exclude_group_ids,
77 c.repo_groups = filter(lambda x: x[0] not in exclude_group_ids,
78 c.repo_groups)
78 c.repo_groups)
79 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
79 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
80 parent_group = repo_group.parent_group
80 parent_group = repo_group.parent_group
81
81
82 add_parent_group = (parent_group and (
82 add_parent_group = (parent_group and (
83 unicode(parent_group.group_id) not in c.repo_groups_choices))
83 unicode(parent_group.group_id) not in c.repo_groups_choices))
84 if add_parent_group:
84 if add_parent_group:
85 c.repo_groups_choices.append(unicode(parent_group.group_id))
85 c.repo_groups_choices.append(unicode(parent_group.group_id))
86 c.repo_groups.append(RepoGroup._generate_choice(parent_group))
86 c.repo_groups.append(RepoGroup._generate_choice(parent_group))
87
87
88 def __load_data(self, group_id):
88 def __load_data(self, group_id):
89 """
89 """
90 Load defaults settings for edit, and update
90 Load defaults settings for edit, and update
91
91
92 :param group_id:
92 :param group_id:
93 """
93 """
94 repo_group = RepoGroup.get_or_404(group_id)
94 repo_group = RepoGroup.get_or_404(group_id)
95 data = repo_group.get_dict()
95 data = repo_group.get_dict()
96 data['group_name'] = repo_group.name
96 data['group_name'] = repo_group.name
97
97
98 # fill owner
98 # fill owner
99 if repo_group.user:
99 if repo_group.user:
100 data.update({'user': repo_group.user.username})
100 data.update({'user': repo_group.user.username})
101 else:
101 else:
102 replacement_user = User.get_first_admin().username
102 replacement_user = User.get_first_admin().username
103 data.update({'user': replacement_user})
103 data.update({'user': replacement_user})
104
104
105 # fill repository group users
105 # fill repository group users
106 for p in repo_group.repo_group_to_perm:
106 for p in repo_group.repo_group_to_perm:
107 data.update({
107 data.update({
108 'u_perm_%s' % p.user.user_id: p.permission.permission_name})
108 'u_perm_%s' % p.user.user_id: p.permission.permission_name})
109
109
110 # fill repository group user groups
110 # fill repository group user groups
111 for p in repo_group.users_group_to_perm:
111 for p in repo_group.users_group_to_perm:
112 data.update({
112 data.update({
113 'g_perm_%s' % p.users_group.users_group_id:
113 'g_perm_%s' % p.users_group.users_group_id:
114 p.permission.permission_name})
114 p.permission.permission_name})
115 # html and form expects -1 as empty parent group
115 # html and form expects -1 as empty parent group
116 data['group_parent_id'] = data['group_parent_id'] or -1
116 data['group_parent_id'] = data['group_parent_id'] or -1
117 return data
117 return data
118
118
119 def _revoke_perms_on_yourself(self, form_result):
119 def _revoke_perms_on_yourself(self, form_result):
120 _updates = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
120 _updates = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
121 form_result['perm_updates'])
121 form_result['perm_updates'])
122 _additions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
122 _additions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
123 form_result['perm_additions'])
123 form_result['perm_additions'])
124 _deletions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
124 _deletions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
125 form_result['perm_deletions'])
125 form_result['perm_deletions'])
126 admin_perm = 'group.admin'
126 admin_perm = 'group.admin'
127 if _updates and _updates[0][1] != admin_perm or \
127 if _updates and _updates[0][1] != admin_perm or \
128 _additions and _additions[0][1] != admin_perm or \
128 _additions and _additions[0][1] != admin_perm or \
129 _deletions and _deletions[0][1] != admin_perm:
129 _deletions and _deletions[0][1] != admin_perm:
130 return True
130 return True
131 return False
131 return False
132
132
133 def _can_create_repo_group(self, parent_group_id=None):
133 def _can_create_repo_group(self, parent_group_id=None):
134 is_admin = HasPermissionAll('hg.admin')('group create controller')
134 is_admin = HasPermissionAll('hg.admin')('group create controller')
135 create_repo_group = HasPermissionAll(
135 create_repo_group = HasPermissionAll(
136 'hg.repogroup.create.true')('group create controller')
136 'hg.repogroup.create.true')('group create controller')
137 if is_admin or (create_repo_group and not parent_group_id):
137 if is_admin or (create_repo_group and not parent_group_id):
138 # we're global admin, or we have global repo group create
138 # we're global admin, or we have global repo group create
139 # permission
139 # permission
140 # we're ok and we can create TOP level groups
140 # we're ok and we can create TOP level groups
141 return True
141 return True
142 elif parent_group_id:
142 elif parent_group_id:
143 # we check the permission if we can write to parent group
143 # we check the permission if we can write to parent group
144 group = RepoGroup.get(parent_group_id)
144 group = RepoGroup.get(parent_group_id)
145 group_name = group.group_name if group else None
145 group_name = group.group_name if group else None
146 if HasRepoGroupPermissionAll('group.admin')(
146 if HasRepoGroupPermissionAll('group.admin')(
147 group_name, 'check if user is an admin of group'):
147 group_name, 'check if user is an admin of group'):
148 # we're an admin of passed in group, we're ok.
148 # we're an admin of passed in group, we're ok.
149 return True
149 return True
150 else:
150 else:
151 return False
151 return False
152 return False
152 return False
153
153
154 @NotAnonymous()
154 @NotAnonymous()
155 def index(self):
155 def index(self):
156 """GET /repo_groups: All items in the collection"""
156 """GET /repo_groups: All items in the collection"""
157 # url('repo_groups')
157 # url('repo_groups')
158
158
159 repo_group_list = RepoGroup.get_all_repo_groups()
159 repo_group_list = RepoGroup.get_all_repo_groups()
160 _perms = ['group.admin']
160 _perms = ['group.admin']
161 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
161 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
162 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
162 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
163 repo_group_list=repo_group_list_acl, admin=True)
163 repo_group_list=repo_group_list_acl, admin=True)
164 c.data = json.dumps(repo_group_data)
164 c.data = json.dumps(repo_group_data)
165 return render('admin/repo_groups/repo_groups.html')
165 return render('admin/repo_groups/repo_groups.html')
166
166
167 # perm checks inside
167 # perm checks inside
168 @NotAnonymous()
168 @NotAnonymous()
169 @auth.CSRFRequired()
169 @auth.CSRFRequired()
170 def create(self):
170 def create(self):
171 """POST /repo_groups: Create a new item"""
171 """POST /repo_groups: Create a new item"""
172 # url('repo_groups')
172 # url('repo_groups')
173
173
174 parent_group_id = safe_int(request.POST.get('group_parent_id'))
174 parent_group_id = safe_int(request.POST.get('group_parent_id'))
175 can_create = self._can_create_repo_group(parent_group_id)
175 can_create = self._can_create_repo_group(parent_group_id)
176
176
177 self.__load_defaults()
177 self.__load_defaults()
178 # permissions for can create group based on parent_id are checked
178 # permissions for can create group based on parent_id are checked
179 # here in the Form
179 # here in the Form
180 available_groups = map(lambda k: unicode(k[0]), c.repo_groups)
180 available_groups = map(lambda k: unicode(k[0]), c.repo_groups)
181 repo_group_form = RepoGroupForm(available_groups=available_groups,
181 repo_group_form = RepoGroupForm(available_groups=available_groups,
182 can_create_in_root=can_create)()
182 can_create_in_root=can_create)()
183 try:
183 try:
184 owner = c.rhodecode_user
184 owner = c.rhodecode_user
185 form_result = repo_group_form.to_python(dict(request.POST))
185 form_result = repo_group_form.to_python(dict(request.POST))
186 RepoGroupModel().create(
186 RepoGroupModel().create(
187 group_name=form_result['group_name_full'],
187 group_name=form_result['group_name_full'],
188 group_description=form_result['group_description'],
188 group_description=form_result['group_description'],
189 owner=owner.user_id,
189 owner=owner.user_id,
190 copy_permissions=form_result['group_copy_permissions']
190 copy_permissions=form_result['group_copy_permissions']
191 )
191 )
192 Session().commit()
192 Session().commit()
193 _new_group_name = form_result['group_name_full']
193 _new_group_name = form_result['group_name_full']
194 repo_group_url = h.link_to(
194 repo_group_url = h.link_to(
195 _new_group_name,
195 _new_group_name,
196 h.url('repo_group_home', group_name=_new_group_name))
196 h.url('repo_group_home', group_name=_new_group_name))
197 h.flash(h.literal(_('Created repository group %s')
197 h.flash(h.literal(_('Created repository group %s')
198 % repo_group_url), category='success')
198 % repo_group_url), category='success')
199 # TODO: in futureaction_logger(, '', '', '', self.sa)
199 # TODO: in futureaction_logger(, '', '', '', self.sa)
200 except formencode.Invalid as errors:
200 except formencode.Invalid as errors:
201 return htmlfill.render(
201 return htmlfill.render(
202 render('admin/repo_groups/repo_group_add.html'),
202 render('admin/repo_groups/repo_group_add.html'),
203 defaults=errors.value,
203 defaults=errors.value,
204 errors=errors.error_dict or {},
204 errors=errors.error_dict or {},
205 prefix_error=False,
205 prefix_error=False,
206 encoding="UTF-8",
206 encoding="UTF-8",
207 force_defaults=False)
207 force_defaults=False)
208 except Exception:
208 except Exception:
209 log.exception("Exception during creation of repository group")
209 log.exception("Exception during creation of repository group")
210 h.flash(_('Error occurred during creation of repository group %s')
210 h.flash(_('Error occurred during creation of repository group %s')
211 % request.POST.get('group_name'), category='error')
211 % request.POST.get('group_name'), category='error')
212
212
213 # TODO: maybe we should get back to the main view, not the admin one
213 # TODO: maybe we should get back to the main view, not the admin one
214 return redirect(url('repo_groups', parent_group=parent_group_id))
214 return redirect(url('repo_groups', parent_group=parent_group_id))
215
215
216 # perm checks inside
216 # perm checks inside
217 @NotAnonymous()
217 @NotAnonymous()
218 def new(self):
218 def new(self):
219 """GET /repo_groups/new: Form to create a new item"""
219 """GET /repo_groups/new: Form to create a new item"""
220 # url('new_repo_group')
220 # url('new_repo_group')
221 # perm check for admin, create_group perm or admin of parent_group
221 # perm check for admin, create_group perm or admin of parent_group
222 parent_group_id = safe_int(request.GET.get('parent_group'))
222 parent_group_id = safe_int(request.GET.get('parent_group'))
223 if not self._can_create_repo_group(parent_group_id):
223 if not self._can_create_repo_group(parent_group_id):
224 return abort(403)
224 return abort(403)
225
225
226 self.__load_defaults()
226 self.__load_defaults()
227 return render('admin/repo_groups/repo_group_add.html')
227 return render('admin/repo_groups/repo_group_add.html')
228
228
229 @HasRepoGroupPermissionAnyDecorator('group.admin')
229 @HasRepoGroupPermissionAnyDecorator('group.admin')
230 @auth.CSRFRequired()
230 @auth.CSRFRequired()
231 def update(self, group_name):
231 def update(self, group_name):
232 """PUT /repo_groups/group_name: Update an existing item"""
232 """PUT /repo_groups/group_name: Update an existing item"""
233 # Forms posted to this method should contain a hidden field:
233 # Forms posted to this method should contain a hidden field:
234 # <input type="hidden" name="_method" value="PUT" />
234 # <input type="hidden" name="_method" value="PUT" />
235 # Or using helpers:
235 # Or using helpers:
236 # h.form(url('repos_group', group_name=GROUP_NAME), method='put')
236 # h.form(url('repos_group', group_name=GROUP_NAME), method='put')
237 # url('repo_group_home', group_name=GROUP_NAME)
237 # url('repo_group_home', group_name=GROUP_NAME)
238
238
239 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
239 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
240 can_create_in_root = self._can_create_repo_group()
240 can_create_in_root = self._can_create_repo_group()
241 show_root_location = can_create_in_root
241 show_root_location = can_create_in_root
242 if not c.repo_group.parent_group:
242 if not c.repo_group.parent_group:
243 # this group don't have a parrent so we should show empty value
243 # this group don't have a parrent so we should show empty value
244 show_root_location = True
244 show_root_location = True
245 self.__load_defaults(allow_empty_group=show_root_location,
245 self.__load_defaults(allow_empty_group=show_root_location,
246 repo_group=c.repo_group)
246 repo_group=c.repo_group)
247
247
248 repo_group_form = RepoGroupForm(
248 repo_group_form = RepoGroupForm(
249 edit=True,
249 edit=True, old_data=c.repo_group.get_dict(),
250 old_data=c.repo_group.get_dict(),
251 available_groups=c.repo_groups_choices,
250 available_groups=c.repo_groups_choices,
252 can_create_in_root=can_create_in_root,
251 can_create_in_root=can_create_in_root, allow_disabled=True)()
253 )()
252
254 try:
253 try:
255 form_result = repo_group_form.to_python(dict(request.POST))
254 form_result = repo_group_form.to_python(dict(request.POST))
256 gr_name = form_result['group_name']
255 gr_name = form_result['group_name']
257 new_gr = RepoGroupModel().update(group_name, form_result)
256 new_gr = RepoGroupModel().update(group_name, form_result)
258 Session().commit()
257 Session().commit()
259 h.flash(_('Updated repository group %s') % (gr_name,),
258 h.flash(_('Updated repository group %s') % (gr_name,),
260 category='success')
259 category='success')
261 # we now have new name !
260 # we now have new name !
262 group_name = new_gr.group_name
261 group_name = new_gr.group_name
263 # TODO: in future action_logger(, '', '', '', self.sa)
262 # TODO: in future action_logger(, '', '', '', self.sa)
264 except formencode.Invalid as errors:
263 except formencode.Invalid as errors:
265 c.active = 'settings'
264 c.active = 'settings'
266 return htmlfill.render(
265 return htmlfill.render(
267 render('admin/repo_groups/repo_group_edit.html'),
266 render('admin/repo_groups/repo_group_edit.html'),
268 defaults=errors.value,
267 defaults=errors.value,
269 errors=errors.error_dict or {},
268 errors=errors.error_dict or {},
270 prefix_error=False,
269 prefix_error=False,
271 encoding="UTF-8",
270 encoding="UTF-8",
272 force_defaults=False)
271 force_defaults=False)
273 except Exception:
272 except Exception:
274 log.exception("Exception during update or repository group")
273 log.exception("Exception during update or repository group")
275 h.flash(_('Error occurred during update of repository group %s')
274 h.flash(_('Error occurred during update of repository group %s')
276 % request.POST.get('group_name'), category='error')
275 % request.POST.get('group_name'), category='error')
277
276
278 return redirect(url('edit_repo_group', group_name=group_name))
277 return redirect(url('edit_repo_group', group_name=group_name))
279
278
280 @HasRepoGroupPermissionAnyDecorator('group.admin')
279 @HasRepoGroupPermissionAnyDecorator('group.admin')
281 @auth.CSRFRequired()
280 @auth.CSRFRequired()
282 def delete(self, group_name):
281 def delete(self, group_name):
283 """DELETE /repo_groups/group_name: Delete an existing item"""
282 """DELETE /repo_groups/group_name: Delete an existing item"""
284 # Forms posted to this method should contain a hidden field:
283 # Forms posted to this method should contain a hidden field:
285 # <input type="hidden" name="_method" value="DELETE" />
284 # <input type="hidden" name="_method" value="DELETE" />
286 # Or using helpers:
285 # Or using helpers:
287 # h.form(url('repos_group', group_name=GROUP_NAME), method='delete')
286 # h.form(url('repos_group', group_name=GROUP_NAME), method='delete')
288 # url('repo_group_home', group_name=GROUP_NAME)
287 # url('repo_group_home', group_name=GROUP_NAME)
289
288
290 gr = c.repo_group = RepoGroupModel()._get_repo_group(group_name)
289 gr = c.repo_group = RepoGroupModel()._get_repo_group(group_name)
291 repos = gr.repositories.all()
290 repos = gr.repositories.all()
292 if repos:
291 if repos:
293 msg = ungettext(
292 msg = ungettext(
294 'This group contains %(num)d repository and cannot be deleted',
293 'This group contains %(num)d repository and cannot be deleted',
295 'This group contains %(num)d repositories and cannot be'
294 'This group contains %(num)d repositories and cannot be'
296 ' deleted',
295 ' deleted',
297 len(repos)) % {'num': len(repos)}
296 len(repos)) % {'num': len(repos)}
298 h.flash(msg, category='warning')
297 h.flash(msg, category='warning')
299 return redirect(url('repo_groups'))
298 return redirect(url('repo_groups'))
300
299
301 children = gr.children.all()
300 children = gr.children.all()
302 if children:
301 if children:
303 msg = ungettext(
302 msg = ungettext(
304 'This group contains %(num)d subgroup and cannot be deleted',
303 'This group contains %(num)d subgroup and cannot be deleted',
305 'This group contains %(num)d subgroups and cannot be deleted',
304 'This group contains %(num)d subgroups and cannot be deleted',
306 len(children)) % {'num': len(children)}
305 len(children)) % {'num': len(children)}
307 h.flash(msg, category='warning')
306 h.flash(msg, category='warning')
308 return redirect(url('repo_groups'))
307 return redirect(url('repo_groups'))
309
308
310 try:
309 try:
311 RepoGroupModel().delete(group_name)
310 RepoGroupModel().delete(group_name)
312 Session().commit()
311 Session().commit()
313 h.flash(_('Removed repository group %s') % group_name,
312 h.flash(_('Removed repository group %s') % group_name,
314 category='success')
313 category='success')
315 # TODO: in future action_logger(, '', '', '', self.sa)
314 # TODO: in future action_logger(, '', '', '', self.sa)
316 except Exception:
315 except Exception:
317 log.exception("Exception during deletion of repository group")
316 log.exception("Exception during deletion of repository group")
318 h.flash(_('Error occurred during deletion of repository group %s')
317 h.flash(_('Error occurred during deletion of repository group %s')
319 % group_name, category='error')
318 % group_name, category='error')
320
319
321 return redirect(url('repo_groups'))
320 return redirect(url('repo_groups'))
322
321
323 @HasRepoGroupPermissionAnyDecorator('group.admin')
322 @HasRepoGroupPermissionAnyDecorator('group.admin')
324 def edit(self, group_name):
323 def edit(self, group_name):
325 """GET /repo_groups/group_name/edit: Form to edit an existing item"""
324 """GET /repo_groups/group_name/edit: Form to edit an existing item"""
326 # url('edit_repo_group', group_name=GROUP_NAME)
325 # url('edit_repo_group', group_name=GROUP_NAME)
327 c.active = 'settings'
326 c.active = 'settings'
328
327
329 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
328 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
330 # we can only allow moving empty group if it's already a top-level
329 # we can only allow moving empty group if it's already a top-level
331 # group, ie has no parents, or we're admin
330 # group, ie has no parents, or we're admin
332 can_create_in_root = self._can_create_repo_group()
331 can_create_in_root = self._can_create_repo_group()
333 show_root_location = can_create_in_root
332 show_root_location = can_create_in_root
334 if not c.repo_group.parent_group:
333 if not c.repo_group.parent_group:
335 # this group don't have a parrent so we should show empty value
334 # this group don't have a parrent so we should show empty value
336 show_root_location = True
335 show_root_location = True
337 self.__load_defaults(allow_empty_group=show_root_location,
336 self.__load_defaults(allow_empty_group=show_root_location,
338 repo_group=c.repo_group)
337 repo_group=c.repo_group)
339 defaults = self.__load_data(c.repo_group.group_id)
338 defaults = self.__load_data(c.repo_group.group_id)
340
339
341 return htmlfill.render(
340 return htmlfill.render(
342 render('admin/repo_groups/repo_group_edit.html'),
341 render('admin/repo_groups/repo_group_edit.html'),
343 defaults=defaults,
342 defaults=defaults,
344 encoding="UTF-8",
343 encoding="UTF-8",
345 force_defaults=False
344 force_defaults=False
346 )
345 )
347
346
348 @HasRepoGroupPermissionAnyDecorator('group.admin')
347 @HasRepoGroupPermissionAnyDecorator('group.admin')
349 def edit_repo_group_advanced(self, group_name):
348 def edit_repo_group_advanced(self, group_name):
350 """GET /repo_groups/group_name/edit: Form to edit an existing item"""
349 """GET /repo_groups/group_name/edit: Form to edit an existing item"""
351 # url('edit_repo_group', group_name=GROUP_NAME)
350 # url('edit_repo_group', group_name=GROUP_NAME)
352 c.active = 'advanced'
351 c.active = 'advanced'
353 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
352 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
354
353
355 return render('admin/repo_groups/repo_group_edit.html')
354 return render('admin/repo_groups/repo_group_edit.html')
356
355
357 @HasRepoGroupPermissionAnyDecorator('group.admin')
356 @HasRepoGroupPermissionAnyDecorator('group.admin')
358 def edit_repo_group_perms(self, group_name):
357 def edit_repo_group_perms(self, group_name):
359 """GET /repo_groups/group_name/edit: Form to edit an existing item"""
358 """GET /repo_groups/group_name/edit: Form to edit an existing item"""
360 # url('edit_repo_group', group_name=GROUP_NAME)
359 # url('edit_repo_group', group_name=GROUP_NAME)
361 c.active = 'perms'
360 c.active = 'perms'
362 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
361 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
363 self.__load_defaults()
362 self.__load_defaults()
364 defaults = self.__load_data(c.repo_group.group_id)
363 defaults = self.__load_data(c.repo_group.group_id)
365
364
366 return htmlfill.render(
365 return htmlfill.render(
367 render('admin/repo_groups/repo_group_edit.html'),
366 render('admin/repo_groups/repo_group_edit.html'),
368 defaults=defaults,
367 defaults=defaults,
369 encoding="UTF-8",
368 encoding="UTF-8",
370 force_defaults=False
369 force_defaults=False
371 )
370 )
372
371
373 @HasRepoGroupPermissionAnyDecorator('group.admin')
372 @HasRepoGroupPermissionAnyDecorator('group.admin')
374 @auth.CSRFRequired()
373 @auth.CSRFRequired()
375 def update_perms(self, group_name):
374 def update_perms(self, group_name):
376 """
375 """
377 Update permissions for given repository group
376 Update permissions for given repository group
378
377
379 :param group_name:
378 :param group_name:
380 """
379 """
381
380
382 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
381 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
383 valid_recursive_choices = ['none', 'repos', 'groups', 'all']
382 valid_recursive_choices = ['none', 'repos', 'groups', 'all']
384 form = RepoGroupPermsForm(valid_recursive_choices)().to_python(
383 form = RepoGroupPermsForm(valid_recursive_choices)().to_python(
385 request.POST)
384 request.POST)
386
385
387 if not c.rhodecode_user.is_admin:
386 if not c.rhodecode_user.is_admin:
388 if self._revoke_perms_on_yourself(form):
387 if self._revoke_perms_on_yourself(form):
389 msg = _('Cannot change permission for yourself as admin')
388 msg = _('Cannot change permission for yourself as admin')
390 h.flash(msg, category='warning')
389 h.flash(msg, category='warning')
391 return redirect(
390 return redirect(
392 url('edit_repo_group_perms', group_name=group_name))
391 url('edit_repo_group_perms', group_name=group_name))
393
392
394 # iterate over all members(if in recursive mode) of this groups and
393 # iterate over all members(if in recursive mode) of this groups and
395 # set the permissions !
394 # set the permissions !
396 # this can be potentially heavy operation
395 # this can be potentially heavy operation
397 RepoGroupModel().update_permissions(
396 RepoGroupModel().update_permissions(
398 c.repo_group,
397 c.repo_group,
399 form['perm_additions'], form['perm_updates'],
398 form['perm_additions'], form['perm_updates'],
400 form['perm_deletions'], form['recursive'])
399 form['perm_deletions'], form['recursive'])
401
400
402 # TODO: implement this
401 # TODO: implement this
403 # action_logger(c.rhodecode_user, 'admin_changed_repo_permissions',
402 # action_logger(c.rhodecode_user, 'admin_changed_repo_permissions',
404 # repo_name, self.ip_addr, self.sa)
403 # repo_name, self.ip_addr, self.sa)
405 Session().commit()
404 Session().commit()
406 h.flash(_('Repository Group permissions updated'), category='success')
405 h.flash(_('Repository Group permissions updated'), category='success')
407 return redirect(url('edit_repo_group_perms', group_name=group_name))
406 return redirect(url('edit_repo_group_perms', group_name=group_name))
@@ -1,878 +1,878 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2013-2016 RhodeCode GmbH
3 # Copyright (C) 2013-2016 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 """
22 """
23 Repositories controller for RhodeCode
23 Repositories controller for RhodeCode
24 """
24 """
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28
28
29 import formencode
29 import formencode
30 from formencode import htmlfill
30 from formencode import htmlfill
31 from pylons import request, tmpl_context as c, url
31 from pylons import request, tmpl_context as c, url
32 from pylons.controllers.util import redirect
32 from pylons.controllers.util import redirect
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34 from webob.exc import HTTPForbidden, HTTPNotFound, HTTPBadRequest
34 from webob.exc import HTTPForbidden, HTTPNotFound, HTTPBadRequest
35
35
36 from rhodecode.lib import auth, helpers as h
36 from rhodecode.lib import auth, helpers as h
37 from rhodecode.lib.auth import (
37 from rhodecode.lib.auth import (
38 LoginRequired, HasPermissionAllDecorator,
38 LoginRequired, HasPermissionAllDecorator,
39 HasRepoPermissionAllDecorator, NotAnonymous, HasPermissionAny,
39 HasRepoPermissionAllDecorator, NotAnonymous, HasPermissionAny,
40 HasRepoGroupPermissionAny, HasRepoPermissionAnyDecorator)
40 HasRepoGroupPermissionAny, HasRepoPermissionAnyDecorator)
41 from rhodecode.lib.base import BaseRepoController, render
41 from rhodecode.lib.base import BaseRepoController, render
42 from rhodecode.lib.ext_json import json
42 from rhodecode.lib.ext_json import json
43 from rhodecode.lib.exceptions import AttachedForksError
43 from rhodecode.lib.exceptions import AttachedForksError
44 from rhodecode.lib.utils import action_logger, repo_name_slug, jsonify
44 from rhodecode.lib.utils import action_logger, repo_name_slug, jsonify
45 from rhodecode.lib.utils2 import safe_int
45 from rhodecode.lib.utils2 import safe_int
46 from rhodecode.lib.vcs import RepositoryError
46 from rhodecode.lib.vcs import RepositoryError
47 from rhodecode.model.db import (
47 from rhodecode.model.db import (
48 User, Repository, UserFollowing, RepoGroup, RepositoryField)
48 User, Repository, UserFollowing, RepoGroup, RepositoryField)
49 from rhodecode.model.forms import (
49 from rhodecode.model.forms import (
50 RepoForm, RepoFieldForm, RepoPermsForm, RepoVcsSettingsForm,
50 RepoForm, RepoFieldForm, RepoPermsForm, RepoVcsSettingsForm,
51 IssueTrackerPatternsForm)
51 IssueTrackerPatternsForm)
52 from rhodecode.model.meta import Session
52 from rhodecode.model.meta import Session
53 from rhodecode.model.repo import RepoModel
53 from rhodecode.model.repo import RepoModel
54 from rhodecode.model.scm import ScmModel, RepoGroupList, RepoList
54 from rhodecode.model.scm import ScmModel, RepoGroupList, RepoList
55 from rhodecode.model.settings import (
55 from rhodecode.model.settings import (
56 SettingsModel, IssueTrackerSettingsModel, VcsSettingsModel,
56 SettingsModel, IssueTrackerSettingsModel, VcsSettingsModel,
57 SettingNotFound)
57 SettingNotFound)
58
58
59 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
60
60
61
61
62 class ReposController(BaseRepoController):
62 class ReposController(BaseRepoController):
63 """
63 """
64 REST Controller styled on the Atom Publishing Protocol"""
64 REST Controller styled on the Atom Publishing Protocol"""
65 # To properly map this controller, ensure your config/routing.py
65 # To properly map this controller, ensure your config/routing.py
66 # file has a resource setup:
66 # file has a resource setup:
67 # map.resource('repo', 'repos')
67 # map.resource('repo', 'repos')
68
68
69 @LoginRequired()
69 @LoginRequired()
70 def __before__(self):
70 def __before__(self):
71 super(ReposController, self).__before__()
71 super(ReposController, self).__before__()
72
72
73 def _load_repo(self, repo_name):
73 def _load_repo(self, repo_name):
74 repo_obj = Repository.get_by_repo_name(repo_name)
74 repo_obj = Repository.get_by_repo_name(repo_name)
75
75
76 if repo_obj is None:
76 if repo_obj is None:
77 h.not_mapped_error(repo_name)
77 h.not_mapped_error(repo_name)
78 return redirect(url('repos'))
78 return redirect(url('repos'))
79
79
80 return repo_obj
80 return repo_obj
81
81
82 def __load_defaults(self, repo=None):
82 def __load_defaults(self, repo=None):
83 acl_groups = RepoGroupList(RepoGroup.query().all(),
83 acl_groups = RepoGroupList(RepoGroup.query().all(),
84 perm_set=['group.write', 'group.admin'])
84 perm_set=['group.write', 'group.admin'])
85 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
85 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
86 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
86 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
87
87
88 # in case someone no longer have a group.write access to a repository
88 # in case someone no longer have a group.write access to a repository
89 # pre fill the list with this entry, we don't care if this is the same
89 # pre fill the list with this entry, we don't care if this is the same
90 # but it will allow saving repo data properly.
90 # but it will allow saving repo data properly.
91
91
92 repo_group = None
92 repo_group = None
93 if repo:
93 if repo:
94 repo_group = repo.group
94 repo_group = repo.group
95 if repo_group and unicode(repo_group.group_id) not in c.repo_groups_choices:
95 if repo_group and unicode(repo_group.group_id) not in c.repo_groups_choices:
96 c.repo_groups_choices.append(unicode(repo_group.group_id))
96 c.repo_groups_choices.append(unicode(repo_group.group_id))
97 c.repo_groups.append(RepoGroup._generate_choice(repo_group))
97 c.repo_groups.append(RepoGroup._generate_choice(repo_group))
98
98
99 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
99 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
100 c.landing_revs_choices = choices
100 c.landing_revs_choices = choices
101
101
102 def __load_data(self, repo_name=None):
102 def __load_data(self, repo_name=None):
103 """
103 """
104 Load defaults settings for edit, and update
104 Load defaults settings for edit, and update
105
105
106 :param repo_name:
106 :param repo_name:
107 """
107 """
108 c.repo_info = self._load_repo(repo_name)
108 c.repo_info = self._load_repo(repo_name)
109 self.__load_defaults(c.repo_info)
109 self.__load_defaults(c.repo_info)
110
110
111 # override defaults for exact repo info here git/hg etc
111 # override defaults for exact repo info here git/hg etc
112 if not c.repository_requirements_missing:
112 if not c.repository_requirements_missing:
113 choices, c.landing_revs = ScmModel().get_repo_landing_revs(
113 choices, c.landing_revs = ScmModel().get_repo_landing_revs(
114 c.repo_info)
114 c.repo_info)
115 c.landing_revs_choices = choices
115 c.landing_revs_choices = choices
116 defaults = RepoModel()._get_defaults(repo_name)
116 defaults = RepoModel()._get_defaults(repo_name)
117
117
118 return defaults
118 return defaults
119
119
120 def _log_creation_exception(self, e, repo_name):
120 def _log_creation_exception(self, e, repo_name):
121 reason = None
121 reason = None
122 if len(e.args) == 2:
122 if len(e.args) == 2:
123 reason = e.args[1]
123 reason = e.args[1]
124
124
125 if reason == 'INVALID_CERTIFICATE':
125 if reason == 'INVALID_CERTIFICATE':
126 log.exception(
126 log.exception(
127 'Exception creating a repository: invalid certificate')
127 'Exception creating a repository: invalid certificate')
128 msg = (_('Error creating repository %s: invalid certificate')
128 msg = (_('Error creating repository %s: invalid certificate')
129 % repo_name)
129 % repo_name)
130 else:
130 else:
131 log.exception("Exception creating a repository")
131 log.exception("Exception creating a repository")
132 msg = (_('Error creating repository %s')
132 msg = (_('Error creating repository %s')
133 % repo_name)
133 % repo_name)
134
134
135 return msg
135 return msg
136
136
137 @NotAnonymous()
137 @NotAnonymous()
138 def index(self, format='html'):
138 def index(self, format='html'):
139 """GET /repos: All items in the collection"""
139 """GET /repos: All items in the collection"""
140 # url('repos')
140 # url('repos')
141
141
142 repo_list = Repository.get_all_repos()
142 repo_list = Repository.get_all_repos()
143 c.repo_list = RepoList(repo_list, perm_set=['repository.admin'])
143 c.repo_list = RepoList(repo_list, perm_set=['repository.admin'])
144 repos_data = RepoModel().get_repos_as_dict(
144 repos_data = RepoModel().get_repos_as_dict(
145 repo_list=c.repo_list, admin=True, super_user_actions=True)
145 repo_list=c.repo_list, admin=True, super_user_actions=True)
146 # json used to render the grid
146 # json used to render the grid
147 c.data = json.dumps(repos_data)
147 c.data = json.dumps(repos_data)
148
148
149 return render('admin/repos/repos.html')
149 return render('admin/repos/repos.html')
150
150
151 # perms check inside
151 # perms check inside
152 @NotAnonymous()
152 @NotAnonymous()
153 @auth.CSRFRequired()
153 @auth.CSRFRequired()
154 def create(self):
154 def create(self):
155 """
155 """
156 POST /repos: Create a new item"""
156 POST /repos: Create a new item"""
157 # url('repos')
157 # url('repos')
158
158
159 self.__load_defaults()
159 self.__load_defaults()
160 form_result = {}
160 form_result = {}
161 task_id = None
161 task_id = None
162 try:
162 try:
163 # CanWriteToGroup validators checks permissions of this POST
163 # CanWriteToGroup validators checks permissions of this POST
164 form_result = RepoForm(repo_groups=c.repo_groups_choices,
164 form_result = RepoForm(repo_groups=c.repo_groups_choices,
165 landing_revs=c.landing_revs_choices)()\
165 landing_revs=c.landing_revs_choices)()\
166 .to_python(dict(request.POST))
166 .to_python(dict(request.POST))
167
167
168 # create is done sometimes async on celery, db transaction
168 # create is done sometimes async on celery, db transaction
169 # management is handled there.
169 # management is handled there.
170 task = RepoModel().create(form_result, c.rhodecode_user.user_id)
170 task = RepoModel().create(form_result, c.rhodecode_user.user_id)
171 from celery.result import BaseAsyncResult
171 from celery.result import BaseAsyncResult
172 if isinstance(task, BaseAsyncResult):
172 if isinstance(task, BaseAsyncResult):
173 task_id = task.task_id
173 task_id = task.task_id
174 except formencode.Invalid as errors:
174 except formencode.Invalid as errors:
175 c.personal_repo_group = RepoGroup.get_by_group_name(
175 c.personal_repo_group = RepoGroup.get_by_group_name(
176 c.rhodecode_user.username)
176 c.rhodecode_user.username)
177 return htmlfill.render(
177 return htmlfill.render(
178 render('admin/repos/repo_add.html'),
178 render('admin/repos/repo_add.html'),
179 defaults=errors.value,
179 defaults=errors.value,
180 errors=errors.error_dict or {},
180 errors=errors.error_dict or {},
181 prefix_error=False,
181 prefix_error=False,
182 encoding="UTF-8",
182 encoding="UTF-8",
183 force_defaults=False)
183 force_defaults=False)
184
184
185 except Exception as e:
185 except Exception as e:
186 msg = self._log_creation_exception(e, form_result.get('repo_name'))
186 msg = self._log_creation_exception(e, form_result.get('repo_name'))
187 h.flash(msg, category='error')
187 h.flash(msg, category='error')
188 return redirect(url('home'))
188 return redirect(url('home'))
189
189
190 return redirect(h.url('repo_creating_home',
190 return redirect(h.url('repo_creating_home',
191 repo_name=form_result['repo_name_full'],
191 repo_name=form_result['repo_name_full'],
192 task_id=task_id))
192 task_id=task_id))
193
193
194 # perms check inside
194 # perms check inside
195 @NotAnonymous()
195 @NotAnonymous()
196 def create_repository(self):
196 def create_repository(self):
197 """GET /_admin/create_repository: Form to create a new item"""
197 """GET /_admin/create_repository: Form to create a new item"""
198 new_repo = request.GET.get('repo', '')
198 new_repo = request.GET.get('repo', '')
199 parent_group = request.GET.get('parent_group')
199 parent_group = request.GET.get('parent_group')
200 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
200 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
201 # you're not super admin nor have global create permissions,
201 # you're not super admin nor have global create permissions,
202 # but maybe you have at least write permission to a parent group ?
202 # but maybe you have at least write permission to a parent group ?
203 _gr = RepoGroup.get(parent_group)
203 _gr = RepoGroup.get(parent_group)
204 gr_name = _gr.group_name if _gr else None
204 gr_name = _gr.group_name if _gr else None
205 # create repositories with write permission on group is set to true
205 # create repositories with write permission on group is set to true
206 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
206 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
207 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
207 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
208 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
208 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
209 if not (group_admin or (group_write and create_on_write)):
209 if not (group_admin or (group_write and create_on_write)):
210 raise HTTPForbidden
210 raise HTTPForbidden
211
211
212 acl_groups = RepoGroupList(RepoGroup.query().all(),
212 acl_groups = RepoGroupList(RepoGroup.query().all(),
213 perm_set=['group.write', 'group.admin'])
213 perm_set=['group.write', 'group.admin'])
214 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
214 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
215 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
215 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
216 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
216 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
217 c.personal_repo_group = RepoGroup.get_by_group_name(c.rhodecode_user.username)
217 c.personal_repo_group = RepoGroup.get_by_group_name(c.rhodecode_user.username)
218 c.new_repo = repo_name_slug(new_repo)
218 c.new_repo = repo_name_slug(new_repo)
219
219
220 ## apply the defaults from defaults page
220 ## apply the defaults from defaults page
221 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
221 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
222 # set checkbox to autochecked
222 # set checkbox to autochecked
223 defaults['repo_copy_permissions'] = True
223 defaults['repo_copy_permissions'] = True
224 if parent_group:
224 if parent_group:
225 defaults.update({'repo_group': parent_group})
225 defaults.update({'repo_group': parent_group})
226
226
227 return htmlfill.render(
227 return htmlfill.render(
228 render('admin/repos/repo_add.html'),
228 render('admin/repos/repo_add.html'),
229 defaults=defaults,
229 defaults=defaults,
230 errors={},
230 errors={},
231 prefix_error=False,
231 prefix_error=False,
232 encoding="UTF-8",
232 encoding="UTF-8",
233 force_defaults=False
233 force_defaults=False
234 )
234 )
235
235
236 @NotAnonymous()
236 @NotAnonymous()
237 def repo_creating(self, repo_name):
237 def repo_creating(self, repo_name):
238 c.repo = repo_name
238 c.repo = repo_name
239 c.task_id = request.GET.get('task_id')
239 c.task_id = request.GET.get('task_id')
240 if not c.repo:
240 if not c.repo:
241 raise HTTPNotFound()
241 raise HTTPNotFound()
242 return render('admin/repos/repo_creating.html')
242 return render('admin/repos/repo_creating.html')
243
243
244 @NotAnonymous()
244 @NotAnonymous()
245 @jsonify
245 @jsonify
246 def repo_check(self, repo_name):
246 def repo_check(self, repo_name):
247 c.repo = repo_name
247 c.repo = repo_name
248 task_id = request.GET.get('task_id')
248 task_id = request.GET.get('task_id')
249
249
250 if task_id and task_id not in ['None']:
250 if task_id and task_id not in ['None']:
251 from rhodecode import CELERY_ENABLED
251 from rhodecode import CELERY_ENABLED
252 from celery.result import AsyncResult
252 from celery.result import AsyncResult
253 if CELERY_ENABLED:
253 if CELERY_ENABLED:
254 task = AsyncResult(task_id)
254 task = AsyncResult(task_id)
255 if task.failed():
255 if task.failed():
256 msg = self._log_creation_exception(task.result, c.repo)
256 msg = self._log_creation_exception(task.result, c.repo)
257 h.flash(msg, category='error')
257 h.flash(msg, category='error')
258 return redirect(url('home'), code=501)
258 return redirect(url('home'), code=501)
259
259
260 repo = Repository.get_by_repo_name(repo_name)
260 repo = Repository.get_by_repo_name(repo_name)
261 if repo and repo.repo_state == Repository.STATE_CREATED:
261 if repo and repo.repo_state == Repository.STATE_CREATED:
262 if repo.clone_uri:
262 if repo.clone_uri:
263 clone_uri = repo.clone_uri_hidden
263 clone_uri = repo.clone_uri_hidden
264 h.flash(_('Created repository %s from %s')
264 h.flash(_('Created repository %s from %s')
265 % (repo.repo_name, clone_uri), category='success')
265 % (repo.repo_name, clone_uri), category='success')
266 else:
266 else:
267 repo_url = h.link_to(repo.repo_name,
267 repo_url = h.link_to(repo.repo_name,
268 h.url('summary_home',
268 h.url('summary_home',
269 repo_name=repo.repo_name))
269 repo_name=repo.repo_name))
270 fork = repo.fork
270 fork = repo.fork
271 if fork:
271 if fork:
272 fork_name = fork.repo_name
272 fork_name = fork.repo_name
273 h.flash(h.literal(_('Forked repository %s as %s')
273 h.flash(h.literal(_('Forked repository %s as %s')
274 % (fork_name, repo_url)), category='success')
274 % (fork_name, repo_url)), category='success')
275 else:
275 else:
276 h.flash(h.literal(_('Created repository %s') % repo_url),
276 h.flash(h.literal(_('Created repository %s') % repo_url),
277 category='success')
277 category='success')
278 return {'result': True}
278 return {'result': True}
279 return {'result': False}
279 return {'result': False}
280
280
281 @HasRepoPermissionAllDecorator('repository.admin')
281 @HasRepoPermissionAllDecorator('repository.admin')
282 @auth.CSRFRequired()
282 @auth.CSRFRequired()
283 def update(self, repo_name):
283 def update(self, repo_name):
284 """
284 """
285 PUT /repos/repo_name: Update an existing item"""
285 PUT /repos/repo_name: Update an existing item"""
286 # Forms posted to this method should contain a hidden field:
286 # Forms posted to this method should contain a hidden field:
287 # <input type="hidden" name="_method" value="PUT" />
287 # <input type="hidden" name="_method" value="PUT" />
288 # Or using helpers:
288 # Or using helpers:
289 # h.form(url('repo', repo_name=ID),
289 # h.form(url('repo', repo_name=ID),
290 # method='put')
290 # method='put')
291 # url('repo', repo_name=ID)
291 # url('repo', repo_name=ID)
292
292
293 self.__load_data(repo_name)
293 self.__load_data(repo_name)
294 c.active = 'settings'
294 c.active = 'settings'
295 c.repo_fields = RepositoryField.query()\
295 c.repo_fields = RepositoryField.query()\
296 .filter(RepositoryField.repository == c.repo_info).all()
296 .filter(RepositoryField.repository == c.repo_info).all()
297
297
298 repo_model = RepoModel()
298 repo_model = RepoModel()
299 changed_name = repo_name
299 changed_name = repo_name
300
300
301 # override the choices with extracted revisions !
301 # override the choices with extracted revisions !
302 c.personal_repo_group = RepoGroup.get_by_group_name(
302 c.personal_repo_group = RepoGroup.get_by_group_name(
303 c.rhodecode_user.username)
303 c.rhodecode_user.username)
304 repo = Repository.get_by_repo_name(repo_name)
304 repo = Repository.get_by_repo_name(repo_name)
305 old_data = {
305 old_data = {
306 'repo_name': repo_name,
306 'repo_name': repo_name,
307 'repo_group': repo.group.get_dict() if repo.group else {},
307 'repo_group': repo.group.get_dict() if repo.group else {},
308 'repo_type': repo.repo_type,
308 'repo_type': repo.repo_type,
309 }
309 }
310 _form = RepoForm(edit=True, old_data=old_data,
310 _form = RepoForm(
311 repo_groups=c.repo_groups_choices,
311 edit=True, old_data=old_data, repo_groups=c.repo_groups_choices,
312 landing_revs=c.landing_revs_choices)()
312 landing_revs=c.landing_revs_choices, allow_disabled=True)()
313
313
314 try:
314 try:
315 form_result = _form.to_python(dict(request.POST))
315 form_result = _form.to_python(dict(request.POST))
316 repo = repo_model.update(repo_name, **form_result)
316 repo = repo_model.update(repo_name, **form_result)
317 ScmModel().mark_for_invalidation(repo_name)
317 ScmModel().mark_for_invalidation(repo_name)
318 h.flash(_('Repository %s updated successfully') % repo_name,
318 h.flash(_('Repository %s updated successfully') % repo_name,
319 category='success')
319 category='success')
320 changed_name = repo.repo_name
320 changed_name = repo.repo_name
321 action_logger(c.rhodecode_user, 'admin_updated_repo',
321 action_logger(c.rhodecode_user, 'admin_updated_repo',
322 changed_name, self.ip_addr, self.sa)
322 changed_name, self.ip_addr, self.sa)
323 Session().commit()
323 Session().commit()
324 except formencode.Invalid as errors:
324 except formencode.Invalid as errors:
325 defaults = self.__load_data(repo_name)
325 defaults = self.__load_data(repo_name)
326 defaults.update(errors.value)
326 defaults.update(errors.value)
327 return htmlfill.render(
327 return htmlfill.render(
328 render('admin/repos/repo_edit.html'),
328 render('admin/repos/repo_edit.html'),
329 defaults=defaults,
329 defaults=defaults,
330 errors=errors.error_dict or {},
330 errors=errors.error_dict or {},
331 prefix_error=False,
331 prefix_error=False,
332 encoding="UTF-8",
332 encoding="UTF-8",
333 force_defaults=False)
333 force_defaults=False)
334
334
335 except Exception:
335 except Exception:
336 log.exception("Exception during update of repository")
336 log.exception("Exception during update of repository")
337 h.flash(_('Error occurred during update of repository %s') \
337 h.flash(_('Error occurred during update of repository %s') \
338 % repo_name, category='error')
338 % repo_name, category='error')
339 return redirect(url('edit_repo', repo_name=changed_name))
339 return redirect(url('edit_repo', repo_name=changed_name))
340
340
341 @HasRepoPermissionAllDecorator('repository.admin')
341 @HasRepoPermissionAllDecorator('repository.admin')
342 @auth.CSRFRequired()
342 @auth.CSRFRequired()
343 def delete(self, repo_name):
343 def delete(self, repo_name):
344 """
344 """
345 DELETE /repos/repo_name: Delete an existing item"""
345 DELETE /repos/repo_name: Delete an existing item"""
346 # Forms posted to this method should contain a hidden field:
346 # Forms posted to this method should contain a hidden field:
347 # <input type="hidden" name="_method" value="DELETE" />
347 # <input type="hidden" name="_method" value="DELETE" />
348 # Or using helpers:
348 # Or using helpers:
349 # h.form(url('repo', repo_name=ID),
349 # h.form(url('repo', repo_name=ID),
350 # method='delete')
350 # method='delete')
351 # url('repo', repo_name=ID)
351 # url('repo', repo_name=ID)
352
352
353 repo_model = RepoModel()
353 repo_model = RepoModel()
354 repo = repo_model.get_by_repo_name(repo_name)
354 repo = repo_model.get_by_repo_name(repo_name)
355 if not repo:
355 if not repo:
356 h.not_mapped_error(repo_name)
356 h.not_mapped_error(repo_name)
357 return redirect(url('repos'))
357 return redirect(url('repos'))
358 try:
358 try:
359 _forks = repo.forks.count()
359 _forks = repo.forks.count()
360 handle_forks = None
360 handle_forks = None
361 if _forks and request.POST.get('forks'):
361 if _forks and request.POST.get('forks'):
362 do = request.POST['forks']
362 do = request.POST['forks']
363 if do == 'detach_forks':
363 if do == 'detach_forks':
364 handle_forks = 'detach'
364 handle_forks = 'detach'
365 h.flash(_('Detached %s forks') % _forks, category='success')
365 h.flash(_('Detached %s forks') % _forks, category='success')
366 elif do == 'delete_forks':
366 elif do == 'delete_forks':
367 handle_forks = 'delete'
367 handle_forks = 'delete'
368 h.flash(_('Deleted %s forks') % _forks, category='success')
368 h.flash(_('Deleted %s forks') % _forks, category='success')
369 repo_model.delete(repo, forks=handle_forks)
369 repo_model.delete(repo, forks=handle_forks)
370 action_logger(c.rhodecode_user, 'admin_deleted_repo',
370 action_logger(c.rhodecode_user, 'admin_deleted_repo',
371 repo_name, self.ip_addr, self.sa)
371 repo_name, self.ip_addr, self.sa)
372 ScmModel().mark_for_invalidation(repo_name)
372 ScmModel().mark_for_invalidation(repo_name)
373 h.flash(_('Deleted repository %s') % repo_name, category='success')
373 h.flash(_('Deleted repository %s') % repo_name, category='success')
374 Session().commit()
374 Session().commit()
375 except AttachedForksError:
375 except AttachedForksError:
376 h.flash(_('Cannot delete %s it still contains attached forks')
376 h.flash(_('Cannot delete %s it still contains attached forks')
377 % repo_name, category='warning')
377 % repo_name, category='warning')
378
378
379 except Exception:
379 except Exception:
380 log.exception("Exception during deletion of repository")
380 log.exception("Exception during deletion of repository")
381 h.flash(_('An error occurred during deletion of %s') % repo_name,
381 h.flash(_('An error occurred during deletion of %s') % repo_name,
382 category='error')
382 category='error')
383
383
384 return redirect(url('repos'))
384 return redirect(url('repos'))
385
385
386 @HasPermissionAllDecorator('hg.admin')
386 @HasPermissionAllDecorator('hg.admin')
387 def show(self, repo_name, format='html'):
387 def show(self, repo_name, format='html'):
388 """GET /repos/repo_name: Show a specific item"""
388 """GET /repos/repo_name: Show a specific item"""
389 # url('repo', repo_name=ID)
389 # url('repo', repo_name=ID)
390
390
391 @HasRepoPermissionAllDecorator('repository.admin')
391 @HasRepoPermissionAllDecorator('repository.admin')
392 def edit(self, repo_name):
392 def edit(self, repo_name):
393 """GET /repo_name/settings: Form to edit an existing item"""
393 """GET /repo_name/settings: Form to edit an existing item"""
394 # url('edit_repo', repo_name=ID)
394 # url('edit_repo', repo_name=ID)
395 defaults = self.__load_data(repo_name)
395 defaults = self.__load_data(repo_name)
396 if 'clone_uri' in defaults:
396 if 'clone_uri' in defaults:
397 del defaults['clone_uri']
397 del defaults['clone_uri']
398
398
399 c.repo_fields = RepositoryField.query()\
399 c.repo_fields = RepositoryField.query()\
400 .filter(RepositoryField.repository == c.repo_info).all()
400 .filter(RepositoryField.repository == c.repo_info).all()
401 c.personal_repo_group = RepoGroup.get_by_group_name(
401 c.personal_repo_group = RepoGroup.get_by_group_name(
402 c.rhodecode_user.username)
402 c.rhodecode_user.username)
403 c.active = 'settings'
403 c.active = 'settings'
404 return htmlfill.render(
404 return htmlfill.render(
405 render('admin/repos/repo_edit.html'),
405 render('admin/repos/repo_edit.html'),
406 defaults=defaults,
406 defaults=defaults,
407 encoding="UTF-8",
407 encoding="UTF-8",
408 force_defaults=False)
408 force_defaults=False)
409
409
410 @HasRepoPermissionAllDecorator('repository.admin')
410 @HasRepoPermissionAllDecorator('repository.admin')
411 def edit_permissions(self, repo_name):
411 def edit_permissions(self, repo_name):
412 """GET /repo_name/settings: Form to edit an existing item"""
412 """GET /repo_name/settings: Form to edit an existing item"""
413 # url('edit_repo', repo_name=ID)
413 # url('edit_repo', repo_name=ID)
414 c.repo_info = self._load_repo(repo_name)
414 c.repo_info = self._load_repo(repo_name)
415 c.active = 'permissions'
415 c.active = 'permissions'
416 defaults = RepoModel()._get_defaults(repo_name)
416 defaults = RepoModel()._get_defaults(repo_name)
417
417
418 return htmlfill.render(
418 return htmlfill.render(
419 render('admin/repos/repo_edit.html'),
419 render('admin/repos/repo_edit.html'),
420 defaults=defaults,
420 defaults=defaults,
421 encoding="UTF-8",
421 encoding="UTF-8",
422 force_defaults=False)
422 force_defaults=False)
423
423
424 @HasRepoPermissionAllDecorator('repository.admin')
424 @HasRepoPermissionAllDecorator('repository.admin')
425 @auth.CSRFRequired()
425 @auth.CSRFRequired()
426 def edit_permissions_update(self, repo_name):
426 def edit_permissions_update(self, repo_name):
427 form = RepoPermsForm()().to_python(request.POST)
427 form = RepoPermsForm()().to_python(request.POST)
428 RepoModel().update_permissions(repo_name,
428 RepoModel().update_permissions(repo_name,
429 form['perm_additions'], form['perm_updates'], form['perm_deletions'])
429 form['perm_additions'], form['perm_updates'], form['perm_deletions'])
430
430
431 #TODO: implement this
431 #TODO: implement this
432 #action_logger(c.rhodecode_user, 'admin_changed_repo_permissions',
432 #action_logger(c.rhodecode_user, 'admin_changed_repo_permissions',
433 # repo_name, self.ip_addr, self.sa)
433 # repo_name, self.ip_addr, self.sa)
434 Session().commit()
434 Session().commit()
435 h.flash(_('Repository permissions updated'), category='success')
435 h.flash(_('Repository permissions updated'), category='success')
436 return redirect(url('edit_repo_perms', repo_name=repo_name))
436 return redirect(url('edit_repo_perms', repo_name=repo_name))
437
437
438 @HasRepoPermissionAllDecorator('repository.admin')
438 @HasRepoPermissionAllDecorator('repository.admin')
439 def edit_fields(self, repo_name):
439 def edit_fields(self, repo_name):
440 """GET /repo_name/settings: Form to edit an existing item"""
440 """GET /repo_name/settings: Form to edit an existing item"""
441 # url('edit_repo', repo_name=ID)
441 # url('edit_repo', repo_name=ID)
442 c.repo_info = self._load_repo(repo_name)
442 c.repo_info = self._load_repo(repo_name)
443 c.repo_fields = RepositoryField.query()\
443 c.repo_fields = RepositoryField.query()\
444 .filter(RepositoryField.repository == c.repo_info).all()
444 .filter(RepositoryField.repository == c.repo_info).all()
445 c.active = 'fields'
445 c.active = 'fields'
446 if request.POST:
446 if request.POST:
447
447
448 return redirect(url('repo_edit_fields'))
448 return redirect(url('repo_edit_fields'))
449 return render('admin/repos/repo_edit.html')
449 return render('admin/repos/repo_edit.html')
450
450
451 @HasRepoPermissionAllDecorator('repository.admin')
451 @HasRepoPermissionAllDecorator('repository.admin')
452 @auth.CSRFRequired()
452 @auth.CSRFRequired()
453 def create_repo_field(self, repo_name):
453 def create_repo_field(self, repo_name):
454 try:
454 try:
455 form_result = RepoFieldForm()().to_python(dict(request.POST))
455 form_result = RepoFieldForm()().to_python(dict(request.POST))
456 RepoModel().add_repo_field(
456 RepoModel().add_repo_field(
457 repo_name, form_result['new_field_key'],
457 repo_name, form_result['new_field_key'],
458 field_type=form_result['new_field_type'],
458 field_type=form_result['new_field_type'],
459 field_value=form_result['new_field_value'],
459 field_value=form_result['new_field_value'],
460 field_label=form_result['new_field_label'],
460 field_label=form_result['new_field_label'],
461 field_desc=form_result['new_field_desc'])
461 field_desc=form_result['new_field_desc'])
462
462
463 Session().commit()
463 Session().commit()
464 except Exception as e:
464 except Exception as e:
465 log.exception("Exception creating field")
465 log.exception("Exception creating field")
466 msg = _('An error occurred during creation of field')
466 msg = _('An error occurred during creation of field')
467 if isinstance(e, formencode.Invalid):
467 if isinstance(e, formencode.Invalid):
468 msg += ". " + e.msg
468 msg += ". " + e.msg
469 h.flash(msg, category='error')
469 h.flash(msg, category='error')
470 return redirect(url('edit_repo_fields', repo_name=repo_name))
470 return redirect(url('edit_repo_fields', repo_name=repo_name))
471
471
472 @HasRepoPermissionAllDecorator('repository.admin')
472 @HasRepoPermissionAllDecorator('repository.admin')
473 @auth.CSRFRequired()
473 @auth.CSRFRequired()
474 def delete_repo_field(self, repo_name, field_id):
474 def delete_repo_field(self, repo_name, field_id):
475 field = RepositoryField.get_or_404(field_id)
475 field = RepositoryField.get_or_404(field_id)
476 try:
476 try:
477 RepoModel().delete_repo_field(repo_name, field.field_key)
477 RepoModel().delete_repo_field(repo_name, field.field_key)
478 Session().commit()
478 Session().commit()
479 except Exception as e:
479 except Exception as e:
480 log.exception("Exception during removal of field")
480 log.exception("Exception during removal of field")
481 msg = _('An error occurred during removal of field')
481 msg = _('An error occurred during removal of field')
482 h.flash(msg, category='error')
482 h.flash(msg, category='error')
483 return redirect(url('edit_repo_fields', repo_name=repo_name))
483 return redirect(url('edit_repo_fields', repo_name=repo_name))
484
484
485 @HasRepoPermissionAllDecorator('repository.admin')
485 @HasRepoPermissionAllDecorator('repository.admin')
486 def edit_advanced(self, repo_name):
486 def edit_advanced(self, repo_name):
487 """GET /repo_name/settings: Form to edit an existing item"""
487 """GET /repo_name/settings: Form to edit an existing item"""
488 # url('edit_repo', repo_name=ID)
488 # url('edit_repo', repo_name=ID)
489 c.repo_info = self._load_repo(repo_name)
489 c.repo_info = self._load_repo(repo_name)
490 c.default_user_id = User.get_default_user().user_id
490 c.default_user_id = User.get_default_user().user_id
491 c.in_public_journal = UserFollowing.query()\
491 c.in_public_journal = UserFollowing.query()\
492 .filter(UserFollowing.user_id == c.default_user_id)\
492 .filter(UserFollowing.user_id == c.default_user_id)\
493 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
493 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
494
494
495 c.active = 'advanced'
495 c.active = 'advanced'
496 c.has_origin_repo_read_perm = False
496 c.has_origin_repo_read_perm = False
497 if c.repo_info.fork:
497 if c.repo_info.fork:
498 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
498 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
499 'repository.write', 'repository.read', 'repository.admin')(
499 'repository.write', 'repository.read', 'repository.admin')(
500 c.repo_info.fork.repo_name, 'repo set as fork page')
500 c.repo_info.fork.repo_name, 'repo set as fork page')
501
501
502 if request.POST:
502 if request.POST:
503 return redirect(url('repo_edit_advanced'))
503 return redirect(url('repo_edit_advanced'))
504 return render('admin/repos/repo_edit.html')
504 return render('admin/repos/repo_edit.html')
505
505
506 @HasRepoPermissionAllDecorator('repository.admin')
506 @HasRepoPermissionAllDecorator('repository.admin')
507 @auth.CSRFRequired()
507 @auth.CSRFRequired()
508 def edit_advanced_journal(self, repo_name):
508 def edit_advanced_journal(self, repo_name):
509 """
509 """
510 Set's this repository to be visible in public journal,
510 Set's this repository to be visible in public journal,
511 in other words assing default user to follow this repo
511 in other words assing default user to follow this repo
512
512
513 :param repo_name:
513 :param repo_name:
514 """
514 """
515
515
516 try:
516 try:
517 repo_id = Repository.get_by_repo_name(repo_name).repo_id
517 repo_id = Repository.get_by_repo_name(repo_name).repo_id
518 user_id = User.get_default_user().user_id
518 user_id = User.get_default_user().user_id
519 self.scm_model.toggle_following_repo(repo_id, user_id)
519 self.scm_model.toggle_following_repo(repo_id, user_id)
520 h.flash(_('Updated repository visibility in public journal'),
520 h.flash(_('Updated repository visibility in public journal'),
521 category='success')
521 category='success')
522 Session().commit()
522 Session().commit()
523 except Exception:
523 except Exception:
524 h.flash(_('An error occurred during setting this'
524 h.flash(_('An error occurred during setting this'
525 ' repository in public journal'),
525 ' repository in public journal'),
526 category='error')
526 category='error')
527
527
528 return redirect(url('edit_repo_advanced', repo_name=repo_name))
528 return redirect(url('edit_repo_advanced', repo_name=repo_name))
529
529
530 @HasRepoPermissionAllDecorator('repository.admin')
530 @HasRepoPermissionAllDecorator('repository.admin')
531 @auth.CSRFRequired()
531 @auth.CSRFRequired()
532 def edit_advanced_fork(self, repo_name):
532 def edit_advanced_fork(self, repo_name):
533 """
533 """
534 Mark given repository as a fork of another
534 Mark given repository as a fork of another
535
535
536 :param repo_name:
536 :param repo_name:
537 """
537 """
538
538
539 new_fork_id = request.POST.get('id_fork_of')
539 new_fork_id = request.POST.get('id_fork_of')
540 try:
540 try:
541
541
542 if new_fork_id and not new_fork_id.isdigit():
542 if new_fork_id and not new_fork_id.isdigit():
543 log.error('Given fork id %s is not an INT', new_fork_id)
543 log.error('Given fork id %s is not an INT', new_fork_id)
544
544
545 fork_id = safe_int(new_fork_id)
545 fork_id = safe_int(new_fork_id)
546 repo = ScmModel().mark_as_fork(repo_name, fork_id,
546 repo = ScmModel().mark_as_fork(repo_name, fork_id,
547 c.rhodecode_user.username)
547 c.rhodecode_user.username)
548 fork = repo.fork.repo_name if repo.fork else _('Nothing')
548 fork = repo.fork.repo_name if repo.fork else _('Nothing')
549 Session().commit()
549 Session().commit()
550 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
550 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
551 category='success')
551 category='success')
552 except RepositoryError as e:
552 except RepositoryError as e:
553 log.exception("Repository Error occurred")
553 log.exception("Repository Error occurred")
554 h.flash(str(e), category='error')
554 h.flash(str(e), category='error')
555 except Exception as e:
555 except Exception as e:
556 log.exception("Exception while editing fork")
556 log.exception("Exception while editing fork")
557 h.flash(_('An error occurred during this operation'),
557 h.flash(_('An error occurred during this operation'),
558 category='error')
558 category='error')
559
559
560 return redirect(url('edit_repo_advanced', repo_name=repo_name))
560 return redirect(url('edit_repo_advanced', repo_name=repo_name))
561
561
562 @HasRepoPermissionAllDecorator('repository.admin')
562 @HasRepoPermissionAllDecorator('repository.admin')
563 @auth.CSRFRequired()
563 @auth.CSRFRequired()
564 def edit_advanced_locking(self, repo_name):
564 def edit_advanced_locking(self, repo_name):
565 """
565 """
566 Unlock repository when it is locked !
566 Unlock repository when it is locked !
567
567
568 :param repo_name:
568 :param repo_name:
569 """
569 """
570 try:
570 try:
571 repo = Repository.get_by_repo_name(repo_name)
571 repo = Repository.get_by_repo_name(repo_name)
572 if request.POST.get('set_lock'):
572 if request.POST.get('set_lock'):
573 Repository.lock(repo, c.rhodecode_user.user_id,
573 Repository.lock(repo, c.rhodecode_user.user_id,
574 lock_reason=Repository.LOCK_WEB)
574 lock_reason=Repository.LOCK_WEB)
575 h.flash(_('Locked repository'), category='success')
575 h.flash(_('Locked repository'), category='success')
576 elif request.POST.get('set_unlock'):
576 elif request.POST.get('set_unlock'):
577 Repository.unlock(repo)
577 Repository.unlock(repo)
578 h.flash(_('Unlocked repository'), category='success')
578 h.flash(_('Unlocked repository'), category='success')
579 except Exception as e:
579 except Exception as e:
580 log.exception("Exception during unlocking")
580 log.exception("Exception during unlocking")
581 h.flash(_('An error occurred during unlocking'),
581 h.flash(_('An error occurred during unlocking'),
582 category='error')
582 category='error')
583 return redirect(url('edit_repo_advanced', repo_name=repo_name))
583 return redirect(url('edit_repo_advanced', repo_name=repo_name))
584
584
585 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
585 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
586 @auth.CSRFRequired()
586 @auth.CSRFRequired()
587 def toggle_locking(self, repo_name):
587 def toggle_locking(self, repo_name):
588 """
588 """
589 Toggle locking of repository by simple GET call to url
589 Toggle locking of repository by simple GET call to url
590
590
591 :param repo_name:
591 :param repo_name:
592 """
592 """
593
593
594 try:
594 try:
595 repo = Repository.get_by_repo_name(repo_name)
595 repo = Repository.get_by_repo_name(repo_name)
596
596
597 if repo.enable_locking:
597 if repo.enable_locking:
598 if repo.locked[0]:
598 if repo.locked[0]:
599 Repository.unlock(repo)
599 Repository.unlock(repo)
600 action = _('Unlocked')
600 action = _('Unlocked')
601 else:
601 else:
602 Repository.lock(repo, c.rhodecode_user.user_id,
602 Repository.lock(repo, c.rhodecode_user.user_id,
603 lock_reason=Repository.LOCK_WEB)
603 lock_reason=Repository.LOCK_WEB)
604 action = _('Locked')
604 action = _('Locked')
605
605
606 h.flash(_('Repository has been %s') % action,
606 h.flash(_('Repository has been %s') % action,
607 category='success')
607 category='success')
608 except Exception:
608 except Exception:
609 log.exception("Exception during unlocking")
609 log.exception("Exception during unlocking")
610 h.flash(_('An error occurred during unlocking'),
610 h.flash(_('An error occurred during unlocking'),
611 category='error')
611 category='error')
612 return redirect(url('summary_home', repo_name=repo_name))
612 return redirect(url('summary_home', repo_name=repo_name))
613
613
614 @HasRepoPermissionAllDecorator('repository.admin')
614 @HasRepoPermissionAllDecorator('repository.admin')
615 @auth.CSRFRequired()
615 @auth.CSRFRequired()
616 def edit_caches(self, repo_name):
616 def edit_caches(self, repo_name):
617 """PUT /{repo_name}/settings/caches: invalidate the repo caches."""
617 """PUT /{repo_name}/settings/caches: invalidate the repo caches."""
618 try:
618 try:
619 ScmModel().mark_for_invalidation(repo_name, delete=True)
619 ScmModel().mark_for_invalidation(repo_name, delete=True)
620 Session().commit()
620 Session().commit()
621 h.flash(_('Cache invalidation successful'),
621 h.flash(_('Cache invalidation successful'),
622 category='success')
622 category='success')
623 except Exception:
623 except Exception:
624 log.exception("Exception during cache invalidation")
624 log.exception("Exception during cache invalidation")
625 h.flash(_('An error occurred during cache invalidation'),
625 h.flash(_('An error occurred during cache invalidation'),
626 category='error')
626 category='error')
627
627
628 return redirect(url('edit_repo_caches', repo_name=c.repo_name))
628 return redirect(url('edit_repo_caches', repo_name=c.repo_name))
629
629
630 @HasRepoPermissionAllDecorator('repository.admin')
630 @HasRepoPermissionAllDecorator('repository.admin')
631 def edit_caches_form(self, repo_name):
631 def edit_caches_form(self, repo_name):
632 """GET /repo_name/settings: Form to edit an existing item"""
632 """GET /repo_name/settings: Form to edit an existing item"""
633 # url('edit_repo', repo_name=ID)
633 # url('edit_repo', repo_name=ID)
634 c.repo_info = self._load_repo(repo_name)
634 c.repo_info = self._load_repo(repo_name)
635 c.active = 'caches'
635 c.active = 'caches'
636
636
637 return render('admin/repos/repo_edit.html')
637 return render('admin/repos/repo_edit.html')
638
638
639 @HasRepoPermissionAllDecorator('repository.admin')
639 @HasRepoPermissionAllDecorator('repository.admin')
640 @auth.CSRFRequired()
640 @auth.CSRFRequired()
641 def edit_remote(self, repo_name):
641 def edit_remote(self, repo_name):
642 """PUT /{repo_name}/settings/remote: edit the repo remote."""
642 """PUT /{repo_name}/settings/remote: edit the repo remote."""
643 try:
643 try:
644 ScmModel().pull_changes(repo_name, c.rhodecode_user.username)
644 ScmModel().pull_changes(repo_name, c.rhodecode_user.username)
645 h.flash(_('Pulled from remote location'), category='success')
645 h.flash(_('Pulled from remote location'), category='success')
646 except Exception:
646 except Exception:
647 log.exception("Exception during pull from remote")
647 log.exception("Exception during pull from remote")
648 h.flash(_('An error occurred during pull from remote location'),
648 h.flash(_('An error occurred during pull from remote location'),
649 category='error')
649 category='error')
650 return redirect(url('edit_repo_remote', repo_name=c.repo_name))
650 return redirect(url('edit_repo_remote', repo_name=c.repo_name))
651
651
652 @HasRepoPermissionAllDecorator('repository.admin')
652 @HasRepoPermissionAllDecorator('repository.admin')
653 def edit_remote_form(self, repo_name):
653 def edit_remote_form(self, repo_name):
654 """GET /repo_name/settings: Form to edit an existing item"""
654 """GET /repo_name/settings: Form to edit an existing item"""
655 # url('edit_repo', repo_name=ID)
655 # url('edit_repo', repo_name=ID)
656 c.repo_info = self._load_repo(repo_name)
656 c.repo_info = self._load_repo(repo_name)
657 c.active = 'remote'
657 c.active = 'remote'
658
658
659 return render('admin/repos/repo_edit.html')
659 return render('admin/repos/repo_edit.html')
660
660
661 @HasRepoPermissionAllDecorator('repository.admin')
661 @HasRepoPermissionAllDecorator('repository.admin')
662 @auth.CSRFRequired()
662 @auth.CSRFRequired()
663 def edit_statistics(self, repo_name):
663 def edit_statistics(self, repo_name):
664 """PUT /{repo_name}/settings/statistics: reset the repo statistics."""
664 """PUT /{repo_name}/settings/statistics: reset the repo statistics."""
665 try:
665 try:
666 RepoModel().delete_stats(repo_name)
666 RepoModel().delete_stats(repo_name)
667 Session().commit()
667 Session().commit()
668 except Exception as e:
668 except Exception as e:
669 log.error(traceback.format_exc())
669 log.error(traceback.format_exc())
670 h.flash(_('An error occurred during deletion of repository stats'),
670 h.flash(_('An error occurred during deletion of repository stats'),
671 category='error')
671 category='error')
672 return redirect(url('edit_repo_statistics', repo_name=c.repo_name))
672 return redirect(url('edit_repo_statistics', repo_name=c.repo_name))
673
673
674 @HasRepoPermissionAllDecorator('repository.admin')
674 @HasRepoPermissionAllDecorator('repository.admin')
675 def edit_statistics_form(self, repo_name):
675 def edit_statistics_form(self, repo_name):
676 """GET /repo_name/settings: Form to edit an existing item"""
676 """GET /repo_name/settings: Form to edit an existing item"""
677 # url('edit_repo', repo_name=ID)
677 # url('edit_repo', repo_name=ID)
678 c.repo_info = self._load_repo(repo_name)
678 c.repo_info = self._load_repo(repo_name)
679 repo = c.repo_info.scm_instance()
679 repo = c.repo_info.scm_instance()
680
680
681 if c.repo_info.stats:
681 if c.repo_info.stats:
682 # this is on what revision we ended up so we add +1 for count
682 # this is on what revision we ended up so we add +1 for count
683 last_rev = c.repo_info.stats.stat_on_revision + 1
683 last_rev = c.repo_info.stats.stat_on_revision + 1
684 else:
684 else:
685 last_rev = 0
685 last_rev = 0
686 c.stats_revision = last_rev
686 c.stats_revision = last_rev
687
687
688 c.repo_last_rev = repo.count()
688 c.repo_last_rev = repo.count()
689
689
690 if last_rev == 0 or c.repo_last_rev == 0:
690 if last_rev == 0 or c.repo_last_rev == 0:
691 c.stats_percentage = 0
691 c.stats_percentage = 0
692 else:
692 else:
693 c.stats_percentage = '%.2f' % ((float((last_rev)) / c.repo_last_rev) * 100)
693 c.stats_percentage = '%.2f' % ((float((last_rev)) / c.repo_last_rev) * 100)
694
694
695 c.active = 'statistics'
695 c.active = 'statistics'
696
696
697 return render('admin/repos/repo_edit.html')
697 return render('admin/repos/repo_edit.html')
698
698
699 @HasRepoPermissionAllDecorator('repository.admin')
699 @HasRepoPermissionAllDecorator('repository.admin')
700 @auth.CSRFRequired()
700 @auth.CSRFRequired()
701 def repo_issuetracker_test(self, repo_name):
701 def repo_issuetracker_test(self, repo_name):
702 if request.is_xhr:
702 if request.is_xhr:
703 return h.urlify_commit_message(
703 return h.urlify_commit_message(
704 request.POST.get('test_text', ''),
704 request.POST.get('test_text', ''),
705 repo_name)
705 repo_name)
706 else:
706 else:
707 raise HTTPBadRequest()
707 raise HTTPBadRequest()
708
708
709 @HasRepoPermissionAllDecorator('repository.admin')
709 @HasRepoPermissionAllDecorator('repository.admin')
710 @auth.CSRFRequired()
710 @auth.CSRFRequired()
711 def repo_issuetracker_delete(self, repo_name):
711 def repo_issuetracker_delete(self, repo_name):
712 uid = request.POST.get('uid')
712 uid = request.POST.get('uid')
713 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
713 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
714 try:
714 try:
715 repo_settings.delete_entries(uid)
715 repo_settings.delete_entries(uid)
716 except Exception:
716 except Exception:
717 h.flash(_('Error occurred during deleting issue tracker entry'),
717 h.flash(_('Error occurred during deleting issue tracker entry'),
718 category='error')
718 category='error')
719 else:
719 else:
720 h.flash(_('Removed issue tracker entry'), category='success')
720 h.flash(_('Removed issue tracker entry'), category='success')
721 return redirect(url('repo_settings_issuetracker',
721 return redirect(url('repo_settings_issuetracker',
722 repo_name=repo_name))
722 repo_name=repo_name))
723
723
724 def _update_patterns(self, form, repo_settings):
724 def _update_patterns(self, form, repo_settings):
725 for uid in form['delete_patterns']:
725 for uid in form['delete_patterns']:
726 repo_settings.delete_entries(uid)
726 repo_settings.delete_entries(uid)
727
727
728 for pattern in form['patterns']:
728 for pattern in form['patterns']:
729 for setting, value, type_ in pattern:
729 for setting, value, type_ in pattern:
730 sett = repo_settings.create_or_update_setting(
730 sett = repo_settings.create_or_update_setting(
731 setting, value, type_)
731 setting, value, type_)
732 Session().add(sett)
732 Session().add(sett)
733
733
734 Session().commit()
734 Session().commit()
735
735
736 @HasRepoPermissionAllDecorator('repository.admin')
736 @HasRepoPermissionAllDecorator('repository.admin')
737 @auth.CSRFRequired()
737 @auth.CSRFRequired()
738 def repo_issuetracker_save(self, repo_name):
738 def repo_issuetracker_save(self, repo_name):
739 # Save inheritance
739 # Save inheritance
740 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
740 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
741 inherited = (request.POST.get('inherit_global_issuetracker')
741 inherited = (request.POST.get('inherit_global_issuetracker')
742 == "inherited")
742 == "inherited")
743 repo_settings.inherit_global_settings = inherited
743 repo_settings.inherit_global_settings = inherited
744 Session().commit()
744 Session().commit()
745
745
746 form = IssueTrackerPatternsForm()().to_python(request.POST)
746 form = IssueTrackerPatternsForm()().to_python(request.POST)
747 if form:
747 if form:
748 self._update_patterns(form, repo_settings)
748 self._update_patterns(form, repo_settings)
749
749
750 h.flash(_('Updated issue tracker entries'), category='success')
750 h.flash(_('Updated issue tracker entries'), category='success')
751 return redirect(url('repo_settings_issuetracker',
751 return redirect(url('repo_settings_issuetracker',
752 repo_name=repo_name))
752 repo_name=repo_name))
753
753
754 @HasRepoPermissionAllDecorator('repository.admin')
754 @HasRepoPermissionAllDecorator('repository.admin')
755 def repo_issuetracker(self, repo_name):
755 def repo_issuetracker(self, repo_name):
756 """GET /admin/settings/issue-tracker: All items in the collection"""
756 """GET /admin/settings/issue-tracker: All items in the collection"""
757 c.active = 'issuetracker'
757 c.active = 'issuetracker'
758 c.data = 'data'
758 c.data = 'data'
759 c.repo_info = self._load_repo(repo_name)
759 c.repo_info = self._load_repo(repo_name)
760
760
761 repo = Repository.get_by_repo_name(repo_name)
761 repo = Repository.get_by_repo_name(repo_name)
762 c.settings_model = IssueTrackerSettingsModel(repo=repo)
762 c.settings_model = IssueTrackerSettingsModel(repo=repo)
763 c.global_patterns = c.settings_model.get_global_settings()
763 c.global_patterns = c.settings_model.get_global_settings()
764 c.repo_patterns = c.settings_model.get_repo_settings()
764 c.repo_patterns = c.settings_model.get_repo_settings()
765
765
766 return render('admin/repos/repo_edit.html')
766 return render('admin/repos/repo_edit.html')
767
767
768 @HasRepoPermissionAllDecorator('repository.admin')
768 @HasRepoPermissionAllDecorator('repository.admin')
769 def repo_settings_vcs(self, repo_name):
769 def repo_settings_vcs(self, repo_name):
770 """GET /{repo_name}/settings/vcs/: All items in the collection"""
770 """GET /{repo_name}/settings/vcs/: All items in the collection"""
771
771
772 model = VcsSettingsModel(repo=repo_name)
772 model = VcsSettingsModel(repo=repo_name)
773
773
774 c.active = 'vcs'
774 c.active = 'vcs'
775 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
775 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
776 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
776 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
777 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
777 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
778 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
778 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
779 c.repo_info = self._load_repo(repo_name)
779 c.repo_info = self._load_repo(repo_name)
780 defaults = self._vcs_form_defaults(repo_name)
780 defaults = self._vcs_form_defaults(repo_name)
781 c.inherit_global_settings = defaults['inherit_global_settings']
781 c.inherit_global_settings = defaults['inherit_global_settings']
782
782
783 return htmlfill.render(
783 return htmlfill.render(
784 render('admin/repos/repo_edit.html'),
784 render('admin/repos/repo_edit.html'),
785 defaults=defaults,
785 defaults=defaults,
786 encoding="UTF-8",
786 encoding="UTF-8",
787 force_defaults=False)
787 force_defaults=False)
788
788
789 @HasRepoPermissionAllDecorator('repository.admin')
789 @HasRepoPermissionAllDecorator('repository.admin')
790 @auth.CSRFRequired()
790 @auth.CSRFRequired()
791 def repo_settings_vcs_update(self, repo_name):
791 def repo_settings_vcs_update(self, repo_name):
792 """POST /{repo_name}/settings/vcs/: All items in the collection"""
792 """POST /{repo_name}/settings/vcs/: All items in the collection"""
793 c.active = 'vcs'
793 c.active = 'vcs'
794
794
795 model = VcsSettingsModel(repo=repo_name)
795 model = VcsSettingsModel(repo=repo_name)
796 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
796 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
797 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
797 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
798 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
798 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
799 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
799 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
800 c.repo_info = self._load_repo(repo_name)
800 c.repo_info = self._load_repo(repo_name)
801 defaults = self._vcs_form_defaults(repo_name)
801 defaults = self._vcs_form_defaults(repo_name)
802 c.inherit_global_settings = defaults['inherit_global_settings']
802 c.inherit_global_settings = defaults['inherit_global_settings']
803
803
804 application_form = RepoVcsSettingsForm(repo_name)()
804 application_form = RepoVcsSettingsForm(repo_name)()
805 try:
805 try:
806 form_result = application_form.to_python(dict(request.POST))
806 form_result = application_form.to_python(dict(request.POST))
807 except formencode.Invalid as errors:
807 except formencode.Invalid as errors:
808 h.flash(
808 h.flash(
809 _("Some form inputs contain invalid data."),
809 _("Some form inputs contain invalid data."),
810 category='error')
810 category='error')
811 return htmlfill.render(
811 return htmlfill.render(
812 render('admin/repos/repo_edit.html'),
812 render('admin/repos/repo_edit.html'),
813 defaults=errors.value,
813 defaults=errors.value,
814 errors=errors.error_dict or {},
814 errors=errors.error_dict or {},
815 prefix_error=False,
815 prefix_error=False,
816 encoding="UTF-8",
816 encoding="UTF-8",
817 force_defaults=False
817 force_defaults=False
818 )
818 )
819
819
820 try:
820 try:
821 inherit_global_settings = form_result['inherit_global_settings']
821 inherit_global_settings = form_result['inherit_global_settings']
822 model.create_or_update_repo_settings(
822 model.create_or_update_repo_settings(
823 form_result, inherit_global_settings=inherit_global_settings)
823 form_result, inherit_global_settings=inherit_global_settings)
824 except Exception:
824 except Exception:
825 log.exception("Exception while updating settings")
825 log.exception("Exception while updating settings")
826 h.flash(
826 h.flash(
827 _('Error occurred during updating repository VCS settings'),
827 _('Error occurred during updating repository VCS settings'),
828 category='error')
828 category='error')
829 else:
829 else:
830 Session().commit()
830 Session().commit()
831 h.flash(_('Updated VCS settings'), category='success')
831 h.flash(_('Updated VCS settings'), category='success')
832 return redirect(url('repo_vcs_settings', repo_name=repo_name))
832 return redirect(url('repo_vcs_settings', repo_name=repo_name))
833
833
834 return htmlfill.render(
834 return htmlfill.render(
835 render('admin/repos/repo_edit.html'),
835 render('admin/repos/repo_edit.html'),
836 defaults=self._vcs_form_defaults(repo_name),
836 defaults=self._vcs_form_defaults(repo_name),
837 encoding="UTF-8",
837 encoding="UTF-8",
838 force_defaults=False)
838 force_defaults=False)
839
839
840 @HasRepoPermissionAllDecorator('repository.admin')
840 @HasRepoPermissionAllDecorator('repository.admin')
841 @auth.CSRFRequired()
841 @auth.CSRFRequired()
842 @jsonify
842 @jsonify
843 def repo_delete_svn_pattern(self, repo_name):
843 def repo_delete_svn_pattern(self, repo_name):
844 if not request.is_xhr:
844 if not request.is_xhr:
845 return False
845 return False
846
846
847 delete_pattern_id = request.POST.get('delete_svn_pattern')
847 delete_pattern_id = request.POST.get('delete_svn_pattern')
848 model = VcsSettingsModel(repo=repo_name)
848 model = VcsSettingsModel(repo=repo_name)
849 try:
849 try:
850 model.delete_repo_svn_pattern(delete_pattern_id)
850 model.delete_repo_svn_pattern(delete_pattern_id)
851 except SettingNotFound:
851 except SettingNotFound:
852 raise HTTPBadRequest()
852 raise HTTPBadRequest()
853
853
854 Session().commit()
854 Session().commit()
855 return True
855 return True
856
856
857 def _vcs_form_defaults(self, repo_name):
857 def _vcs_form_defaults(self, repo_name):
858 model = VcsSettingsModel(repo=repo_name)
858 model = VcsSettingsModel(repo=repo_name)
859 global_defaults = model.get_global_settings()
859 global_defaults = model.get_global_settings()
860
860
861 repo_defaults = {}
861 repo_defaults = {}
862 repo_defaults.update(global_defaults)
862 repo_defaults.update(global_defaults)
863 repo_defaults.update(model.get_repo_settings())
863 repo_defaults.update(model.get_repo_settings())
864
864
865 global_defaults = {
865 global_defaults = {
866 '{}_inherited'.format(k): global_defaults[k]
866 '{}_inherited'.format(k): global_defaults[k]
867 for k in global_defaults}
867 for k in global_defaults}
868
868
869 defaults = {
869 defaults = {
870 'inherit_global_settings': model.inherit_global_settings
870 'inherit_global_settings': model.inherit_global_settings
871 }
871 }
872 defaults.update(global_defaults)
872 defaults.update(global_defaults)
873 defaults.update(repo_defaults)
873 defaults.update(repo_defaults)
874 defaults.update({
874 defaults.update({
875 'new_svn_branch': '',
875 'new_svn_branch': '',
876 'new_svn_tag': '',
876 'new_svn_tag': '',
877 })
877 })
878 return defaults
878 return defaults
@@ -1,480 +1,480 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2016 RhodeCode GmbH
3 # Copyright (C) 2011-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 User Groups crud controller for pylons
22 User Groups crud controller for pylons
23 """
23 """
24
24
25 import logging
25 import logging
26 import formencode
26 import formencode
27
27
28 from formencode import htmlfill
28 from formencode import htmlfill
29 from pylons import request, tmpl_context as c, url, config
29 from pylons import request, tmpl_context as c, url, config
30 from pylons.controllers.util import redirect
30 from pylons.controllers.util import redirect
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32
32
33 from sqlalchemy.orm import joinedload
33 from sqlalchemy.orm import joinedload
34
34
35 from rhodecode.lib import auth
35 from rhodecode.lib import auth
36 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
37 from rhodecode.lib.exceptions import UserGroupAssignedException,\
37 from rhodecode.lib.exceptions import UserGroupAssignedException,\
38 RepoGroupAssignmentError
38 RepoGroupAssignmentError
39 from rhodecode.lib.utils import jsonify, action_logger
39 from rhodecode.lib.utils import jsonify, action_logger
40 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
40 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
41 from rhodecode.lib.auth import (
41 from rhodecode.lib.auth import (
42 LoginRequired, NotAnonymous, HasUserGroupPermissionAnyDecorator,
42 LoginRequired, NotAnonymous, HasUserGroupPermissionAnyDecorator,
43 HasPermissionAnyDecorator)
43 HasPermissionAnyDecorator)
44 from rhodecode.lib.base import BaseController, render
44 from rhodecode.lib.base import BaseController, render
45 from rhodecode.model.permission import PermissionModel
45 from rhodecode.model.permission import PermissionModel
46 from rhodecode.model.scm import UserGroupList
46 from rhodecode.model.scm import UserGroupList
47 from rhodecode.model.user_group import UserGroupModel
47 from rhodecode.model.user_group import UserGroupModel
48 from rhodecode.model.db import (
48 from rhodecode.model.db import (
49 User, UserGroup, UserGroupRepoToPerm, UserGroupRepoGroupToPerm)
49 User, UserGroup, UserGroupRepoToPerm, UserGroupRepoGroupToPerm)
50 from rhodecode.model.forms import (
50 from rhodecode.model.forms import (
51 UserGroupForm, UserGroupPermsForm, UserIndividualPermissionsForm,
51 UserGroupForm, UserGroupPermsForm, UserIndividualPermissionsForm,
52 UserPermissionsForm)
52 UserPermissionsForm)
53 from rhodecode.model.meta import Session
53 from rhodecode.model.meta import Session
54 from rhodecode.lib.utils import action_logger
54 from rhodecode.lib.utils import action_logger
55 from rhodecode.lib.ext_json import json
55 from rhodecode.lib.ext_json import json
56
56
57 log = logging.getLogger(__name__)
57 log = logging.getLogger(__name__)
58
58
59
59
60 class UserGroupsController(BaseController):
60 class UserGroupsController(BaseController):
61 """REST Controller styled on the Atom Publishing Protocol"""
61 """REST Controller styled on the Atom Publishing Protocol"""
62
62
63 @LoginRequired()
63 @LoginRequired()
64 def __before__(self):
64 def __before__(self):
65 super(UserGroupsController, self).__before__()
65 super(UserGroupsController, self).__before__()
66 c.available_permissions = config['available_permissions']
66 c.available_permissions = config['available_permissions']
67 PermissionModel().set_global_permission_choices(c, translator=_)
67 PermissionModel().set_global_permission_choices(c, translator=_)
68
68
69 def __load_data(self, user_group_id):
69 def __load_data(self, user_group_id):
70 c.group_members_obj = [x.user for x in c.user_group.members]
70 c.group_members_obj = [x.user for x in c.user_group.members]
71 c.group_members_obj.sort(key=lambda u: u.username.lower())
71 c.group_members_obj.sort(key=lambda u: u.username.lower())
72
72
73 c.group_members = [(x.user_id, x.username) for x in c.group_members_obj]
73 c.group_members = [(x.user_id, x.username) for x in c.group_members_obj]
74
74
75 c.available_members = [(x.user_id, x.username)
75 c.available_members = [(x.user_id, x.username)
76 for x in User.query().all()]
76 for x in User.query().all()]
77 c.available_members.sort(key=lambda u: u[1].lower())
77 c.available_members.sort(key=lambda u: u[1].lower())
78
78
79 def __load_defaults(self, user_group_id):
79 def __load_defaults(self, user_group_id):
80 """
80 """
81 Load defaults settings for edit, and update
81 Load defaults settings for edit, and update
82
82
83 :param user_group_id:
83 :param user_group_id:
84 """
84 """
85 user_group = UserGroup.get_or_404(user_group_id)
85 user_group = UserGroup.get_or_404(user_group_id)
86 data = user_group.get_dict()
86 data = user_group.get_dict()
87 # fill owner
87 # fill owner
88 if user_group.user:
88 if user_group.user:
89 data.update({'user': user_group.user.username})
89 data.update({'user': user_group.user.username})
90 else:
90 else:
91 replacement_user = User.get_first_admin().username
91 replacement_user = User.get_first_admin().username
92 data.update({'user': replacement_user})
92 data.update({'user': replacement_user})
93 return data
93 return data
94
94
95 def _revoke_perms_on_yourself(self, form_result):
95 def _revoke_perms_on_yourself(self, form_result):
96 _updates = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
96 _updates = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
97 form_result['perm_updates'])
97 form_result['perm_updates'])
98 _additions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
98 _additions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
99 form_result['perm_additions'])
99 form_result['perm_additions'])
100 _deletions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
100 _deletions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
101 form_result['perm_deletions'])
101 form_result['perm_deletions'])
102 admin_perm = 'usergroup.admin'
102 admin_perm = 'usergroup.admin'
103 if _updates and _updates[0][1] != admin_perm or \
103 if _updates and _updates[0][1] != admin_perm or \
104 _additions and _additions[0][1] != admin_perm or \
104 _additions and _additions[0][1] != admin_perm or \
105 _deletions and _deletions[0][1] != admin_perm:
105 _deletions and _deletions[0][1] != admin_perm:
106 return True
106 return True
107 return False
107 return False
108
108
109 # permission check inside
109 # permission check inside
110 @NotAnonymous()
110 @NotAnonymous()
111 def index(self):
111 def index(self):
112 """GET /users_groups: All items in the collection"""
112 """GET /users_groups: All items in the collection"""
113 # url('users_groups')
113 # url('users_groups')
114
114
115 from rhodecode.lib.utils import PartialRenderer
115 from rhodecode.lib.utils import PartialRenderer
116 _render = PartialRenderer('data_table/_dt_elements.html')
116 _render = PartialRenderer('data_table/_dt_elements.html')
117
117
118 def user_group_name(user_group_id, user_group_name):
118 def user_group_name(user_group_id, user_group_name):
119 return _render("user_group_name", user_group_id, user_group_name)
119 return _render("user_group_name", user_group_id, user_group_name)
120
120
121 def user_group_actions(user_group_id, user_group_name):
121 def user_group_actions(user_group_id, user_group_name):
122 return _render("user_group_actions", user_group_id, user_group_name)
122 return _render("user_group_actions", user_group_id, user_group_name)
123
123
124 ## json generate
124 ## json generate
125 group_iter = UserGroupList(UserGroup.query().all(),
125 group_iter = UserGroupList(UserGroup.query().all(),
126 perm_set=['usergroup.admin'])
126 perm_set=['usergroup.admin'])
127
127
128 user_groups_data = []
128 user_groups_data = []
129 for user_gr in group_iter:
129 for user_gr in group_iter:
130 user_groups_data.append({
130 user_groups_data.append({
131 "group_name": user_group_name(
131 "group_name": user_group_name(
132 user_gr.users_group_id, h.escape(user_gr.users_group_name)),
132 user_gr.users_group_id, h.escape(user_gr.users_group_name)),
133 "group_name_raw": user_gr.users_group_name,
133 "group_name_raw": user_gr.users_group_name,
134 "desc": h.escape(user_gr.user_group_description),
134 "desc": h.escape(user_gr.user_group_description),
135 "members": len(user_gr.members),
135 "members": len(user_gr.members),
136 "active": h.bool2icon(user_gr.users_group_active),
136 "active": h.bool2icon(user_gr.users_group_active),
137 "owner": h.escape(h.link_to_user(user_gr.user.username)),
137 "owner": h.escape(h.link_to_user(user_gr.user.username)),
138 "action": user_group_actions(
138 "action": user_group_actions(
139 user_gr.users_group_id, user_gr.users_group_name)
139 user_gr.users_group_id, user_gr.users_group_name)
140 })
140 })
141
141
142 c.data = json.dumps(user_groups_data)
142 c.data = json.dumps(user_groups_data)
143 return render('admin/user_groups/user_groups.html')
143 return render('admin/user_groups/user_groups.html')
144
144
145 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
145 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
146 @auth.CSRFRequired()
146 @auth.CSRFRequired()
147 def create(self):
147 def create(self):
148 """POST /users_groups: Create a new item"""
148 """POST /users_groups: Create a new item"""
149 # url('users_groups')
149 # url('users_groups')
150
150
151 users_group_form = UserGroupForm()()
151 users_group_form = UserGroupForm()()
152 try:
152 try:
153 form_result = users_group_form.to_python(dict(request.POST))
153 form_result = users_group_form.to_python(dict(request.POST))
154 user_group = UserGroupModel().create(
154 user_group = UserGroupModel().create(
155 name=form_result['users_group_name'],
155 name=form_result['users_group_name'],
156 description=form_result['user_group_description'],
156 description=form_result['user_group_description'],
157 owner=c.rhodecode_user.user_id,
157 owner=c.rhodecode_user.user_id,
158 active=form_result['users_group_active'])
158 active=form_result['users_group_active'])
159 Session().flush()
159 Session().flush()
160
160
161 user_group_name = form_result['users_group_name']
161 user_group_name = form_result['users_group_name']
162 action_logger(c.rhodecode_user,
162 action_logger(c.rhodecode_user,
163 'admin_created_users_group:%s' % user_group_name,
163 'admin_created_users_group:%s' % user_group_name,
164 None, self.ip_addr, self.sa)
164 None, self.ip_addr, self.sa)
165 user_group_link = h.link_to(h.escape(user_group_name),
165 user_group_link = h.link_to(h.escape(user_group_name),
166 url('edit_users_group',
166 url('edit_users_group',
167 user_group_id=user_group.users_group_id))
167 user_group_id=user_group.users_group_id))
168 h.flash(h.literal(_('Created user group %(user_group_link)s')
168 h.flash(h.literal(_('Created user group %(user_group_link)s')
169 % {'user_group_link': user_group_link}),
169 % {'user_group_link': user_group_link}),
170 category='success')
170 category='success')
171 Session().commit()
171 Session().commit()
172 except formencode.Invalid as errors:
172 except formencode.Invalid as errors:
173 return htmlfill.render(
173 return htmlfill.render(
174 render('admin/user_groups/user_group_add.html'),
174 render('admin/user_groups/user_group_add.html'),
175 defaults=errors.value,
175 defaults=errors.value,
176 errors=errors.error_dict or {},
176 errors=errors.error_dict or {},
177 prefix_error=False,
177 prefix_error=False,
178 encoding="UTF-8",
178 encoding="UTF-8",
179 force_defaults=False)
179 force_defaults=False)
180 except Exception:
180 except Exception:
181 log.exception("Exception creating user group")
181 log.exception("Exception creating user group")
182 h.flash(_('Error occurred during creation of user group %s') \
182 h.flash(_('Error occurred during creation of user group %s') \
183 % request.POST.get('users_group_name'), category='error')
183 % request.POST.get('users_group_name'), category='error')
184
184
185 return redirect(
185 return redirect(
186 url('edit_users_group', user_group_id=user_group.users_group_id))
186 url('edit_users_group', user_group_id=user_group.users_group_id))
187
187
188 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
188 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
189 def new(self):
189 def new(self):
190 """GET /user_groups/new: Form to create a new item"""
190 """GET /user_groups/new: Form to create a new item"""
191 # url('new_users_group')
191 # url('new_users_group')
192 return render('admin/user_groups/user_group_add.html')
192 return render('admin/user_groups/user_group_add.html')
193
193
194 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
194 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
195 @auth.CSRFRequired()
195 @auth.CSRFRequired()
196 def update(self, user_group_id):
196 def update(self, user_group_id):
197 """PUT /user_groups/user_group_id: Update an existing item"""
197 """PUT /user_groups/user_group_id: Update an existing item"""
198 # Forms posted to this method should contain a hidden field:
198 # Forms posted to this method should contain a hidden field:
199 # <input type="hidden" name="_method" value="PUT" />
199 # <input type="hidden" name="_method" value="PUT" />
200 # Or using helpers:
200 # Or using helpers:
201 # h.form(url('users_group', user_group_id=ID),
201 # h.form(url('users_group', user_group_id=ID),
202 # method='put')
202 # method='put')
203 # url('users_group', user_group_id=ID)
203 # url('users_group', user_group_id=ID)
204
204
205 user_group_id = safe_int(user_group_id)
205 user_group_id = safe_int(user_group_id)
206 c.user_group = UserGroup.get_or_404(user_group_id)
206 c.user_group = UserGroup.get_or_404(user_group_id)
207 c.active = 'settings'
207 c.active = 'settings'
208 self.__load_data(user_group_id)
208 self.__load_data(user_group_id)
209
209
210 available_members = [safe_unicode(x[0]) for x in c.available_members]
210 available_members = [safe_unicode(x[0]) for x in c.available_members]
211
211
212 users_group_form = UserGroupForm(edit=True,
212 users_group_form = UserGroupForm(
213 old_data=c.user_group.get_dict(),
213 edit=True, old_data=c.user_group.get_dict(),
214 available_members=available_members)()
214 available_members=available_members, allow_disabled=True)()
215
215
216 try:
216 try:
217 form_result = users_group_form.to_python(request.POST)
217 form_result = users_group_form.to_python(request.POST)
218 UserGroupModel().update(c.user_group, form_result)
218 UserGroupModel().update(c.user_group, form_result)
219 gr = form_result['users_group_name']
219 gr = form_result['users_group_name']
220 action_logger(c.rhodecode_user,
220 action_logger(c.rhodecode_user,
221 'admin_updated_users_group:%s' % gr,
221 'admin_updated_users_group:%s' % gr,
222 None, self.ip_addr, self.sa)
222 None, self.ip_addr, self.sa)
223 h.flash(_('Updated user group %s') % gr, category='success')
223 h.flash(_('Updated user group %s') % gr, category='success')
224 Session().commit()
224 Session().commit()
225 except formencode.Invalid as errors:
225 except formencode.Invalid as errors:
226 defaults = errors.value
226 defaults = errors.value
227 e = errors.error_dict or {}
227 e = errors.error_dict or {}
228
228
229 return htmlfill.render(
229 return htmlfill.render(
230 render('admin/user_groups/user_group_edit.html'),
230 render('admin/user_groups/user_group_edit.html'),
231 defaults=defaults,
231 defaults=defaults,
232 errors=e,
232 errors=e,
233 prefix_error=False,
233 prefix_error=False,
234 encoding="UTF-8",
234 encoding="UTF-8",
235 force_defaults=False)
235 force_defaults=False)
236 except Exception:
236 except Exception:
237 log.exception("Exception during update of user group")
237 log.exception("Exception during update of user group")
238 h.flash(_('Error occurred during update of user group %s')
238 h.flash(_('Error occurred during update of user group %s')
239 % request.POST.get('users_group_name'), category='error')
239 % request.POST.get('users_group_name'), category='error')
240
240
241 return redirect(url('edit_users_group', user_group_id=user_group_id))
241 return redirect(url('edit_users_group', user_group_id=user_group_id))
242
242
243 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
243 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
244 @auth.CSRFRequired()
244 @auth.CSRFRequired()
245 def delete(self, user_group_id):
245 def delete(self, user_group_id):
246 """DELETE /user_groups/user_group_id: Delete an existing item"""
246 """DELETE /user_groups/user_group_id: Delete an existing item"""
247 # Forms posted to this method should contain a hidden field:
247 # Forms posted to this method should contain a hidden field:
248 # <input type="hidden" name="_method" value="DELETE" />
248 # <input type="hidden" name="_method" value="DELETE" />
249 # Or using helpers:
249 # Or using helpers:
250 # h.form(url('users_group', user_group_id=ID),
250 # h.form(url('users_group', user_group_id=ID),
251 # method='delete')
251 # method='delete')
252 # url('users_group', user_group_id=ID)
252 # url('users_group', user_group_id=ID)
253 user_group_id = safe_int(user_group_id)
253 user_group_id = safe_int(user_group_id)
254 c.user_group = UserGroup.get_or_404(user_group_id)
254 c.user_group = UserGroup.get_or_404(user_group_id)
255 force = str2bool(request.POST.get('force'))
255 force = str2bool(request.POST.get('force'))
256
256
257 try:
257 try:
258 UserGroupModel().delete(c.user_group, force=force)
258 UserGroupModel().delete(c.user_group, force=force)
259 Session().commit()
259 Session().commit()
260 h.flash(_('Successfully deleted user group'), category='success')
260 h.flash(_('Successfully deleted user group'), category='success')
261 except UserGroupAssignedException as e:
261 except UserGroupAssignedException as e:
262 h.flash(str(e), category='error')
262 h.flash(str(e), category='error')
263 except Exception:
263 except Exception:
264 log.exception("Exception during deletion of user group")
264 log.exception("Exception during deletion of user group")
265 h.flash(_('An error occurred during deletion of user group'),
265 h.flash(_('An error occurred during deletion of user group'),
266 category='error')
266 category='error')
267 return redirect(url('users_groups'))
267 return redirect(url('users_groups'))
268
268
269 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
269 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
270 def edit(self, user_group_id):
270 def edit(self, user_group_id):
271 """GET /user_groups/user_group_id/edit: Form to edit an existing item"""
271 """GET /user_groups/user_group_id/edit: Form to edit an existing item"""
272 # url('edit_users_group', user_group_id=ID)
272 # url('edit_users_group', user_group_id=ID)
273
273
274 user_group_id = safe_int(user_group_id)
274 user_group_id = safe_int(user_group_id)
275 c.user_group = UserGroup.get_or_404(user_group_id)
275 c.user_group = UserGroup.get_or_404(user_group_id)
276 c.active = 'settings'
276 c.active = 'settings'
277 self.__load_data(user_group_id)
277 self.__load_data(user_group_id)
278
278
279 defaults = self.__load_defaults(user_group_id)
279 defaults = self.__load_defaults(user_group_id)
280
280
281 return htmlfill.render(
281 return htmlfill.render(
282 render('admin/user_groups/user_group_edit.html'),
282 render('admin/user_groups/user_group_edit.html'),
283 defaults=defaults,
283 defaults=defaults,
284 encoding="UTF-8",
284 encoding="UTF-8",
285 force_defaults=False
285 force_defaults=False
286 )
286 )
287
287
288 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
288 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
289 def edit_perms(self, user_group_id):
289 def edit_perms(self, user_group_id):
290 user_group_id = safe_int(user_group_id)
290 user_group_id = safe_int(user_group_id)
291 c.user_group = UserGroup.get_or_404(user_group_id)
291 c.user_group = UserGroup.get_or_404(user_group_id)
292 c.active = 'perms'
292 c.active = 'perms'
293
293
294 defaults = {}
294 defaults = {}
295 # fill user group users
295 # fill user group users
296 for p in c.user_group.user_user_group_to_perm:
296 for p in c.user_group.user_user_group_to_perm:
297 defaults.update({'u_perm_%s' % p.user.user_id:
297 defaults.update({'u_perm_%s' % p.user.user_id:
298 p.permission.permission_name})
298 p.permission.permission_name})
299
299
300 for p in c.user_group.user_group_user_group_to_perm:
300 for p in c.user_group.user_group_user_group_to_perm:
301 defaults.update({'g_perm_%s' % p.user_group.users_group_id:
301 defaults.update({'g_perm_%s' % p.user_group.users_group_id:
302 p.permission.permission_name})
302 p.permission.permission_name})
303
303
304 return htmlfill.render(
304 return htmlfill.render(
305 render('admin/user_groups/user_group_edit.html'),
305 render('admin/user_groups/user_group_edit.html'),
306 defaults=defaults,
306 defaults=defaults,
307 encoding="UTF-8",
307 encoding="UTF-8",
308 force_defaults=False
308 force_defaults=False
309 )
309 )
310
310
311 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
311 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
312 @auth.CSRFRequired()
312 @auth.CSRFRequired()
313 def update_perms(self, user_group_id):
313 def update_perms(self, user_group_id):
314 """
314 """
315 grant permission for given usergroup
315 grant permission for given usergroup
316
316
317 :param user_group_id:
317 :param user_group_id:
318 """
318 """
319 user_group_id = safe_int(user_group_id)
319 user_group_id = safe_int(user_group_id)
320 c.user_group = UserGroup.get_or_404(user_group_id)
320 c.user_group = UserGroup.get_or_404(user_group_id)
321 form = UserGroupPermsForm()().to_python(request.POST)
321 form = UserGroupPermsForm()().to_python(request.POST)
322
322
323 if not c.rhodecode_user.is_admin:
323 if not c.rhodecode_user.is_admin:
324 if self._revoke_perms_on_yourself(form):
324 if self._revoke_perms_on_yourself(form):
325 msg = _('Cannot change permission for yourself as admin')
325 msg = _('Cannot change permission for yourself as admin')
326 h.flash(msg, category='warning')
326 h.flash(msg, category='warning')
327 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
327 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
328
328
329 try:
329 try:
330 UserGroupModel().update_permissions(user_group_id,
330 UserGroupModel().update_permissions(user_group_id,
331 form['perm_additions'], form['perm_updates'], form['perm_deletions'])
331 form['perm_additions'], form['perm_updates'], form['perm_deletions'])
332 except RepoGroupAssignmentError:
332 except RepoGroupAssignmentError:
333 h.flash(_('Target group cannot be the same'), category='error')
333 h.flash(_('Target group cannot be the same'), category='error')
334 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
334 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
335 #TODO: implement this
335 #TODO: implement this
336 #action_logger(c.rhodecode_user, 'admin_changed_repo_permissions',
336 #action_logger(c.rhodecode_user, 'admin_changed_repo_permissions',
337 # repo_name, self.ip_addr, self.sa)
337 # repo_name, self.ip_addr, self.sa)
338 Session().commit()
338 Session().commit()
339 h.flash(_('User Group permissions updated'), category='success')
339 h.flash(_('User Group permissions updated'), category='success')
340 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
340 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
341
341
342 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
342 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
343 def edit_perms_summary(self, user_group_id):
343 def edit_perms_summary(self, user_group_id):
344 user_group_id = safe_int(user_group_id)
344 user_group_id = safe_int(user_group_id)
345 c.user_group = UserGroup.get_or_404(user_group_id)
345 c.user_group = UserGroup.get_or_404(user_group_id)
346 c.active = 'perms_summary'
346 c.active = 'perms_summary'
347 permissions = {
347 permissions = {
348 'repositories': {},
348 'repositories': {},
349 'repositories_groups': {},
349 'repositories_groups': {},
350 }
350 }
351 ugroup_repo_perms = UserGroupRepoToPerm.query()\
351 ugroup_repo_perms = UserGroupRepoToPerm.query()\
352 .options(joinedload(UserGroupRepoToPerm.permission))\
352 .options(joinedload(UserGroupRepoToPerm.permission))\
353 .options(joinedload(UserGroupRepoToPerm.repository))\
353 .options(joinedload(UserGroupRepoToPerm.repository))\
354 .filter(UserGroupRepoToPerm.users_group_id == user_group_id)\
354 .filter(UserGroupRepoToPerm.users_group_id == user_group_id)\
355 .all()
355 .all()
356
356
357 for gr in ugroup_repo_perms:
357 for gr in ugroup_repo_perms:
358 permissions['repositories'][gr.repository.repo_name] \
358 permissions['repositories'][gr.repository.repo_name] \
359 = gr.permission.permission_name
359 = gr.permission.permission_name
360
360
361 ugroup_group_perms = UserGroupRepoGroupToPerm.query()\
361 ugroup_group_perms = UserGroupRepoGroupToPerm.query()\
362 .options(joinedload(UserGroupRepoGroupToPerm.permission))\
362 .options(joinedload(UserGroupRepoGroupToPerm.permission))\
363 .options(joinedload(UserGroupRepoGroupToPerm.group))\
363 .options(joinedload(UserGroupRepoGroupToPerm.group))\
364 .filter(UserGroupRepoGroupToPerm.users_group_id == user_group_id)\
364 .filter(UserGroupRepoGroupToPerm.users_group_id == user_group_id)\
365 .all()
365 .all()
366
366
367 for gr in ugroup_group_perms:
367 for gr in ugroup_group_perms:
368 permissions['repositories_groups'][gr.group.group_name] \
368 permissions['repositories_groups'][gr.group.group_name] \
369 = gr.permission.permission_name
369 = gr.permission.permission_name
370 c.permissions = permissions
370 c.permissions = permissions
371 return render('admin/user_groups/user_group_edit.html')
371 return render('admin/user_groups/user_group_edit.html')
372
372
373 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
373 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
374 def edit_global_perms(self, user_group_id):
374 def edit_global_perms(self, user_group_id):
375 user_group_id = safe_int(user_group_id)
375 user_group_id = safe_int(user_group_id)
376 c.user_group = UserGroup.get_or_404(user_group_id)
376 c.user_group = UserGroup.get_or_404(user_group_id)
377 c.active = 'global_perms'
377 c.active = 'global_perms'
378
378
379 c.default_user = User.get_default_user()
379 c.default_user = User.get_default_user()
380 defaults = c.user_group.get_dict()
380 defaults = c.user_group.get_dict()
381 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
381 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
382 defaults.update(c.user_group.get_default_perms())
382 defaults.update(c.user_group.get_default_perms())
383
383
384 return htmlfill.render(
384 return htmlfill.render(
385 render('admin/user_groups/user_group_edit.html'),
385 render('admin/user_groups/user_group_edit.html'),
386 defaults=defaults,
386 defaults=defaults,
387 encoding="UTF-8",
387 encoding="UTF-8",
388 force_defaults=False
388 force_defaults=False
389 )
389 )
390
390
391 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
391 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
392 @auth.CSRFRequired()
392 @auth.CSRFRequired()
393 def update_global_perms(self, user_group_id):
393 def update_global_perms(self, user_group_id):
394 """PUT /users_perm/user_group_id: Update an existing item"""
394 """PUT /users_perm/user_group_id: Update an existing item"""
395 # url('users_group_perm', user_group_id=ID, method='put')
395 # url('users_group_perm', user_group_id=ID, method='put')
396 user_group_id = safe_int(user_group_id)
396 user_group_id = safe_int(user_group_id)
397 user_group = UserGroup.get_or_404(user_group_id)
397 user_group = UserGroup.get_or_404(user_group_id)
398 c.active = 'global_perms'
398 c.active = 'global_perms'
399
399
400 try:
400 try:
401 # first stage that verifies the checkbox
401 # first stage that verifies the checkbox
402 _form = UserIndividualPermissionsForm()
402 _form = UserIndividualPermissionsForm()
403 form_result = _form.to_python(dict(request.POST))
403 form_result = _form.to_python(dict(request.POST))
404 inherit_perms = form_result['inherit_default_permissions']
404 inherit_perms = form_result['inherit_default_permissions']
405 user_group.inherit_default_permissions = inherit_perms
405 user_group.inherit_default_permissions = inherit_perms
406 Session().add(user_group)
406 Session().add(user_group)
407
407
408 if not inherit_perms:
408 if not inherit_perms:
409 # only update the individual ones if we un check the flag
409 # only update the individual ones if we un check the flag
410 _form = UserPermissionsForm(
410 _form = UserPermissionsForm(
411 [x[0] for x in c.repo_create_choices],
411 [x[0] for x in c.repo_create_choices],
412 [x[0] for x in c.repo_create_on_write_choices],
412 [x[0] for x in c.repo_create_on_write_choices],
413 [x[0] for x in c.repo_group_create_choices],
413 [x[0] for x in c.repo_group_create_choices],
414 [x[0] for x in c.user_group_create_choices],
414 [x[0] for x in c.user_group_create_choices],
415 [x[0] for x in c.fork_choices],
415 [x[0] for x in c.fork_choices],
416 [x[0] for x in c.inherit_default_permission_choices])()
416 [x[0] for x in c.inherit_default_permission_choices])()
417
417
418 form_result = _form.to_python(dict(request.POST))
418 form_result = _form.to_python(dict(request.POST))
419 form_result.update({'perm_user_group_id': user_group.users_group_id})
419 form_result.update({'perm_user_group_id': user_group.users_group_id})
420
420
421 PermissionModel().update_user_group_permissions(form_result)
421 PermissionModel().update_user_group_permissions(form_result)
422
422
423 Session().commit()
423 Session().commit()
424 h.flash(_('User Group global permissions updated successfully'),
424 h.flash(_('User Group global permissions updated successfully'),
425 category='success')
425 category='success')
426
426
427 except formencode.Invalid as errors:
427 except formencode.Invalid as errors:
428 defaults = errors.value
428 defaults = errors.value
429 c.user_group = user_group
429 c.user_group = user_group
430 return htmlfill.render(
430 return htmlfill.render(
431 render('admin/user_groups/user_group_edit.html'),
431 render('admin/user_groups/user_group_edit.html'),
432 defaults=defaults,
432 defaults=defaults,
433 errors=errors.error_dict or {},
433 errors=errors.error_dict or {},
434 prefix_error=False,
434 prefix_error=False,
435 encoding="UTF-8",
435 encoding="UTF-8",
436 force_defaults=False)
436 force_defaults=False)
437
437
438 except Exception:
438 except Exception:
439 log.exception("Exception during permissions saving")
439 log.exception("Exception during permissions saving")
440 h.flash(_('An error occurred during permissions saving'),
440 h.flash(_('An error occurred during permissions saving'),
441 category='error')
441 category='error')
442
442
443 return redirect(url('edit_user_group_global_perms', user_group_id=user_group_id))
443 return redirect(url('edit_user_group_global_perms', user_group_id=user_group_id))
444
444
445 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
445 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
446 def edit_advanced(self, user_group_id):
446 def edit_advanced(self, user_group_id):
447 user_group_id = safe_int(user_group_id)
447 user_group_id = safe_int(user_group_id)
448 c.user_group = UserGroup.get_or_404(user_group_id)
448 c.user_group = UserGroup.get_or_404(user_group_id)
449 c.active = 'advanced'
449 c.active = 'advanced'
450 c.group_members_obj = sorted(
450 c.group_members_obj = sorted(
451 (x.user for x in c.user_group.members),
451 (x.user for x in c.user_group.members),
452 key=lambda u: u.username.lower())
452 key=lambda u: u.username.lower())
453
453
454 c.group_to_repos = sorted(
454 c.group_to_repos = sorted(
455 (x.repository for x in c.user_group.users_group_repo_to_perm),
455 (x.repository for x in c.user_group.users_group_repo_to_perm),
456 key=lambda u: u.repo_name.lower())
456 key=lambda u: u.repo_name.lower())
457
457
458 c.group_to_repo_groups = sorted(
458 c.group_to_repo_groups = sorted(
459 (x.group for x in c.user_group.users_group_repo_group_to_perm),
459 (x.group for x in c.user_group.users_group_repo_group_to_perm),
460 key=lambda u: u.group_name.lower())
460 key=lambda u: u.group_name.lower())
461
461
462 return render('admin/user_groups/user_group_edit.html')
462 return render('admin/user_groups/user_group_edit.html')
463
463
464 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
464 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
465 def edit_members(self, user_group_id):
465 def edit_members(self, user_group_id):
466 user_group_id = safe_int(user_group_id)
466 user_group_id = safe_int(user_group_id)
467 c.user_group = UserGroup.get_or_404(user_group_id)
467 c.user_group = UserGroup.get_or_404(user_group_id)
468 c.active = 'members'
468 c.active = 'members'
469 c.group_members_obj = sorted((x.user for x in c.user_group.members),
469 c.group_members_obj = sorted((x.user for x in c.user_group.members),
470 key=lambda u: u.username.lower())
470 key=lambda u: u.username.lower())
471
471
472 group_members = [(x.user_id, x.username) for x in c.group_members_obj]
472 group_members = [(x.user_id, x.username) for x in c.group_members_obj]
473
473
474 if request.is_xhr:
474 if request.is_xhr:
475 return jsonify(lambda *a, **k: {
475 return jsonify(lambda *a, **k: {
476 'members': group_members
476 'members': group_members
477 })
477 })
478
478
479 c.group_members = group_members
479 c.group_members = group_members
480 return render('admin/user_groups/user_group_edit.html')
480 return render('admin/user_groups/user_group_edit.html')
@@ -1,547 +1,561 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 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 this is forms validation classes
22 this is forms validation classes
23 http://formencode.org/module-formencode.validators.html
23 http://formencode.org/module-formencode.validators.html
24 for list off all availible validators
24 for list off all availible validators
25
25
26 we can create our own validators
26 we can create our own validators
27
27
28 The table below outlines the options which can be used in a schema in addition to the validators themselves
28 The table below outlines the options which can be used in a schema in addition to the validators themselves
29 pre_validators [] These validators will be applied before the schema
29 pre_validators [] These validators will be applied before the schema
30 chained_validators [] These validators will be applied after the schema
30 chained_validators [] These validators will be applied after the schema
31 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
31 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
32 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
32 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
33 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.
33 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.
34 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
34 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
35
35
36
36
37 <name> = formencode.validators.<name of validator>
37 <name> = formencode.validators.<name of validator>
38 <name> must equal form name
38 <name> must equal form name
39 list=[1,2,3,4,5]
39 list=[1,2,3,4,5]
40 for SELECT use formencode.All(OneOf(list), Int())
40 for SELECT use formencode.All(OneOf(list), Int())
41
41
42 """
42 """
43
43
44 import logging
44 import logging
45
45
46 import formencode
46 import formencode
47 from formencode import All, Pipe
47 from formencode import All, Pipe
48
48
49 from pylons.i18n.translation import _
49 from pylons.i18n.translation import _
50
50
51 from rhodecode import BACKENDS
51 from rhodecode import BACKENDS
52 from rhodecode.model import validators as v
52 from rhodecode.model import validators as v
53
53
54 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
55
55
56
56
57 def LoginForm():
57 def LoginForm():
58 class _LoginForm(formencode.Schema):
58 class _LoginForm(formencode.Schema):
59 allow_extra_fields = True
59 allow_extra_fields = True
60 filter_extra_fields = True
60 filter_extra_fields = True
61 username = v.UnicodeString(
61 username = v.UnicodeString(
62 strip=True,
62 strip=True,
63 min=1,
63 min=1,
64 not_empty=True,
64 not_empty=True,
65 messages={
65 messages={
66 'empty': _(u'Please enter a login'),
66 'empty': _(u'Please enter a login'),
67 'tooShort': _(u'Enter a value %(min)i characters long or more')
67 'tooShort': _(u'Enter a value %(min)i characters long or more')
68 }
68 }
69 )
69 )
70
70
71 password = v.UnicodeString(
71 password = v.UnicodeString(
72 strip=False,
72 strip=False,
73 min=3,
73 min=3,
74 not_empty=True,
74 not_empty=True,
75 messages={
75 messages={
76 'empty': _(u'Please enter a password'),
76 'empty': _(u'Please enter a password'),
77 'tooShort': _(u'Enter %(min)i characters or more')}
77 'tooShort': _(u'Enter %(min)i characters or more')}
78 )
78 )
79
79
80 remember = v.StringBoolean(if_missing=False)
80 remember = v.StringBoolean(if_missing=False)
81
81
82 chained_validators = [v.ValidAuth()]
82 chained_validators = [v.ValidAuth()]
83 return _LoginForm
83 return _LoginForm
84
84
85
85
86 def PasswordChangeForm(username):
86 def PasswordChangeForm(username):
87 class _PasswordChangeForm(formencode.Schema):
87 class _PasswordChangeForm(formencode.Schema):
88 allow_extra_fields = True
88 allow_extra_fields = True
89 filter_extra_fields = True
89 filter_extra_fields = True
90
90
91 current_password = v.ValidOldPassword(username)(not_empty=True)
91 current_password = v.ValidOldPassword(username)(not_empty=True)
92 new_password = All(v.ValidPassword(), v.UnicodeString(strip=False, min=6))
92 new_password = All(v.ValidPassword(), v.UnicodeString(strip=False, min=6))
93 new_password_confirmation = All(v.ValidPassword(), v.UnicodeString(strip=False, min=6))
93 new_password_confirmation = All(v.ValidPassword(), v.UnicodeString(strip=False, min=6))
94
94
95 chained_validators = [v.ValidPasswordsMatch('new_password',
95 chained_validators = [v.ValidPasswordsMatch('new_password',
96 'new_password_confirmation')]
96 'new_password_confirmation')]
97 return _PasswordChangeForm
97 return _PasswordChangeForm
98
98
99
99
100 def UserForm(edit=False, available_languages=[], old_data={}):
100 def UserForm(edit=False, available_languages=[], old_data={}):
101 class _UserForm(formencode.Schema):
101 class _UserForm(formencode.Schema):
102 allow_extra_fields = True
102 allow_extra_fields = True
103 filter_extra_fields = True
103 filter_extra_fields = True
104 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
104 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
105 v.ValidUsername(edit, old_data))
105 v.ValidUsername(edit, old_data))
106 if edit:
106 if edit:
107 new_password = All(
107 new_password = All(
108 v.ValidPassword(),
108 v.ValidPassword(),
109 v.UnicodeString(strip=False, min=6, not_empty=False)
109 v.UnicodeString(strip=False, min=6, not_empty=False)
110 )
110 )
111 password_confirmation = All(
111 password_confirmation = All(
112 v.ValidPassword(),
112 v.ValidPassword(),
113 v.UnicodeString(strip=False, min=6, not_empty=False),
113 v.UnicodeString(strip=False, min=6, not_empty=False),
114 )
114 )
115 admin = v.StringBoolean(if_missing=False)
115 admin = v.StringBoolean(if_missing=False)
116 else:
116 else:
117 password = All(
117 password = All(
118 v.ValidPassword(),
118 v.ValidPassword(),
119 v.UnicodeString(strip=False, min=6, not_empty=True)
119 v.UnicodeString(strip=False, min=6, not_empty=True)
120 )
120 )
121 password_confirmation = All(
121 password_confirmation = All(
122 v.ValidPassword(),
122 v.ValidPassword(),
123 v.UnicodeString(strip=False, min=6, not_empty=False)
123 v.UnicodeString(strip=False, min=6, not_empty=False)
124 )
124 )
125
125
126 password_change = v.StringBoolean(if_missing=False)
126 password_change = v.StringBoolean(if_missing=False)
127 create_repo_group = v.StringBoolean(if_missing=False)
127 create_repo_group = v.StringBoolean(if_missing=False)
128
128
129 active = v.StringBoolean(if_missing=False)
129 active = v.StringBoolean(if_missing=False)
130 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
130 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
131 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
131 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
132 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
132 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
133 extern_name = v.UnicodeString(strip=True)
133 extern_name = v.UnicodeString(strip=True)
134 extern_type = v.UnicodeString(strip=True)
134 extern_type = v.UnicodeString(strip=True)
135 language = v.OneOf(available_languages, hideList=False,
135 language = v.OneOf(available_languages, hideList=False,
136 testValueList=True, if_missing=None)
136 testValueList=True, if_missing=None)
137 chained_validators = [v.ValidPasswordsMatch()]
137 chained_validators = [v.ValidPasswordsMatch()]
138 return _UserForm
138 return _UserForm
139
139
140
140
141 def UserGroupForm(edit=False, old_data={}, available_members=[]):
141 def UserGroupForm(edit=False, old_data=None, available_members=None,
142 allow_disabled=False):
143 old_data = old_data or {}
144 available_members = available_members or []
145
142 class _UserGroupForm(formencode.Schema):
146 class _UserGroupForm(formencode.Schema):
143 allow_extra_fields = True
147 allow_extra_fields = True
144 filter_extra_fields = True
148 filter_extra_fields = True
145
149
146 users_group_name = All(
150 users_group_name = All(
147 v.UnicodeString(strip=True, min=1, not_empty=True),
151 v.UnicodeString(strip=True, min=1, not_empty=True),
148 v.ValidUserGroup(edit, old_data)
152 v.ValidUserGroup(edit, old_data)
149 )
153 )
150 user_group_description = v.UnicodeString(strip=True, min=1,
154 user_group_description = v.UnicodeString(strip=True, min=1,
151 not_empty=False)
155 not_empty=False)
152
156
153 users_group_active = v.StringBoolean(if_missing=False)
157 users_group_active = v.StringBoolean(if_missing=False)
154
158
155 if edit:
159 if edit:
156 users_group_members = v.OneOf(
160 users_group_members = v.OneOf(
157 available_members, hideList=False, testValueList=True,
161 available_members, hideList=False, testValueList=True,
158 if_missing=None, not_empty=False
162 if_missing=None, not_empty=False
159 )
163 )
160 #this is user group owner
164 # this is user group owner
161 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
165 user = All(
162
166 v.UnicodeString(not_empty=True),
167 v.ValidRepoUser(allow_disabled))
163 return _UserGroupForm
168 return _UserGroupForm
164
169
165
170
166 def RepoGroupForm(edit=False, old_data={}, available_groups=[],
171 def RepoGroupForm(edit=False, old_data=None, available_groups=None,
167 can_create_in_root=False):
172 can_create_in_root=False, allow_disabled=False):
173 old_data = old_data or {}
174 available_groups = available_groups or []
175
168 class _RepoGroupForm(formencode.Schema):
176 class _RepoGroupForm(formencode.Schema):
169 allow_extra_fields = True
177 allow_extra_fields = True
170 filter_extra_fields = False
178 filter_extra_fields = False
171
179
172 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
180 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
173 v.SlugifyName(),)
181 v.SlugifyName(),)
174 group_description = v.UnicodeString(strip=True, min=1,
182 group_description = v.UnicodeString(strip=True, min=1,
175 not_empty=False)
183 not_empty=False)
176 group_copy_permissions = v.StringBoolean(if_missing=False)
184 group_copy_permissions = v.StringBoolean(if_missing=False)
177
185
178 group_parent_id = v.OneOf(available_groups, hideList=False,
186 group_parent_id = v.OneOf(available_groups, hideList=False,
179 testValueList=True, not_empty=True)
187 testValueList=True, not_empty=True)
180 enable_locking = v.StringBoolean(if_missing=False)
188 enable_locking = v.StringBoolean(if_missing=False)
181 chained_validators = [v.ValidRepoGroup(edit, old_data, can_create_in_root)]
189 chained_validators = [
190 v.ValidRepoGroup(edit, old_data, can_create_in_root)]
182
191
183 if edit:
192 if edit:
184 #this is repo group owner
193 # this is repo group owner
185 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
194 user = All(
195 v.UnicodeString(not_empty=True),
196 v.ValidRepoUser(allow_disabled))
186
197
187 return _RepoGroupForm
198 return _RepoGroupForm
188
199
189
200
190 def RegisterForm(edit=False, old_data={}):
201 def RegisterForm(edit=False, old_data={}):
191 class _RegisterForm(formencode.Schema):
202 class _RegisterForm(formencode.Schema):
192 allow_extra_fields = True
203 allow_extra_fields = True
193 filter_extra_fields = True
204 filter_extra_fields = True
194 username = All(
205 username = All(
195 v.ValidUsername(edit, old_data),
206 v.ValidUsername(edit, old_data),
196 v.UnicodeString(strip=True, min=1, not_empty=True)
207 v.UnicodeString(strip=True, min=1, not_empty=True)
197 )
208 )
198 password = All(
209 password = All(
199 v.ValidPassword(),
210 v.ValidPassword(),
200 v.UnicodeString(strip=False, min=6, not_empty=True)
211 v.UnicodeString(strip=False, min=6, not_empty=True)
201 )
212 )
202 password_confirmation = All(
213 password_confirmation = All(
203 v.ValidPassword(),
214 v.ValidPassword(),
204 v.UnicodeString(strip=False, min=6, not_empty=True)
215 v.UnicodeString(strip=False, min=6, not_empty=True)
205 )
216 )
206 active = v.StringBoolean(if_missing=False)
217 active = v.StringBoolean(if_missing=False)
207 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
218 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
208 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
219 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
209 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
220 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
210
221
211 chained_validators = [v.ValidPasswordsMatch()]
222 chained_validators = [v.ValidPasswordsMatch()]
212
223
213 return _RegisterForm
224 return _RegisterForm
214
225
215
226
216 def PasswordResetForm():
227 def PasswordResetForm():
217 class _PasswordResetForm(formencode.Schema):
228 class _PasswordResetForm(formencode.Schema):
218 allow_extra_fields = True
229 allow_extra_fields = True
219 filter_extra_fields = True
230 filter_extra_fields = True
220 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
231 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
221 return _PasswordResetForm
232 return _PasswordResetForm
222
233
223
234
224 def RepoForm(edit=False, old_data=None, repo_groups=None, landing_revs=None):
235 def RepoForm(edit=False, old_data=None, repo_groups=None, landing_revs=None,
236 allow_disabled=False):
225 old_data = old_data or {}
237 old_data = old_data or {}
226 repo_groups = repo_groups or []
238 repo_groups = repo_groups or []
227 landing_revs = landing_revs or []
239 landing_revs = landing_revs or []
228 supported_backends = BACKENDS.keys()
240 supported_backends = BACKENDS.keys()
229
241
230 class _RepoForm(formencode.Schema):
242 class _RepoForm(formencode.Schema):
231 allow_extra_fields = True
243 allow_extra_fields = True
232 filter_extra_fields = False
244 filter_extra_fields = False
233 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
245 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
234 v.SlugifyName())
246 v.SlugifyName())
235 repo_group = All(v.CanWriteGroup(old_data),
247 repo_group = All(v.CanWriteGroup(old_data),
236 v.OneOf(repo_groups, hideList=True))
248 v.OneOf(repo_groups, hideList=True))
237 repo_type = v.OneOf(supported_backends, required=False,
249 repo_type = v.OneOf(supported_backends, required=False,
238 if_missing=old_data.get('repo_type'))
250 if_missing=old_data.get('repo_type'))
239 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
251 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
240 repo_private = v.StringBoolean(if_missing=False)
252 repo_private = v.StringBoolean(if_missing=False)
241 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
253 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
242 repo_copy_permissions = v.StringBoolean(if_missing=False)
254 repo_copy_permissions = v.StringBoolean(if_missing=False)
243 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
255 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
244
256
245 repo_enable_statistics = v.StringBoolean(if_missing=False)
257 repo_enable_statistics = v.StringBoolean(if_missing=False)
246 repo_enable_downloads = v.StringBoolean(if_missing=False)
258 repo_enable_downloads = v.StringBoolean(if_missing=False)
247 repo_enable_locking = v.StringBoolean(if_missing=False)
259 repo_enable_locking = v.StringBoolean(if_missing=False)
248
260
249 if edit:
261 if edit:
250 # this is repo owner
262 # this is repo owner
251 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
263 user = All(
264 v.UnicodeString(not_empty=True),
265 v.ValidRepoUser(allow_disabled))
252 clone_uri_change = v.UnicodeString(
266 clone_uri_change = v.UnicodeString(
253 not_empty=False, if_missing=v.Missing)
267 not_empty=False, if_missing=v.Missing)
254
268
255 chained_validators = [v.ValidCloneUri(),
269 chained_validators = [v.ValidCloneUri(),
256 v.ValidRepoName(edit, old_data)]
270 v.ValidRepoName(edit, old_data)]
257 return _RepoForm
271 return _RepoForm
258
272
259
273
260 def RepoPermsForm():
274 def RepoPermsForm():
261 class _RepoPermsForm(formencode.Schema):
275 class _RepoPermsForm(formencode.Schema):
262 allow_extra_fields = True
276 allow_extra_fields = True
263 filter_extra_fields = False
277 filter_extra_fields = False
264 chained_validators = [v.ValidPerms(type_='repo')]
278 chained_validators = [v.ValidPerms(type_='repo')]
265 return _RepoPermsForm
279 return _RepoPermsForm
266
280
267
281
268 def RepoGroupPermsForm(valid_recursive_choices):
282 def RepoGroupPermsForm(valid_recursive_choices):
269 class _RepoGroupPermsForm(formencode.Schema):
283 class _RepoGroupPermsForm(formencode.Schema):
270 allow_extra_fields = True
284 allow_extra_fields = True
271 filter_extra_fields = False
285 filter_extra_fields = False
272 recursive = v.OneOf(valid_recursive_choices)
286 recursive = v.OneOf(valid_recursive_choices)
273 chained_validators = [v.ValidPerms(type_='repo_group')]
287 chained_validators = [v.ValidPerms(type_='repo_group')]
274 return _RepoGroupPermsForm
288 return _RepoGroupPermsForm
275
289
276
290
277 def UserGroupPermsForm():
291 def UserGroupPermsForm():
278 class _UserPermsForm(formencode.Schema):
292 class _UserPermsForm(formencode.Schema):
279 allow_extra_fields = True
293 allow_extra_fields = True
280 filter_extra_fields = False
294 filter_extra_fields = False
281 chained_validators = [v.ValidPerms(type_='user_group')]
295 chained_validators = [v.ValidPerms(type_='user_group')]
282 return _UserPermsForm
296 return _UserPermsForm
283
297
284
298
285 def RepoFieldForm():
299 def RepoFieldForm():
286 class _RepoFieldForm(formencode.Schema):
300 class _RepoFieldForm(formencode.Schema):
287 filter_extra_fields = True
301 filter_extra_fields = True
288 allow_extra_fields = True
302 allow_extra_fields = True
289
303
290 new_field_key = All(v.FieldKey(),
304 new_field_key = All(v.FieldKey(),
291 v.UnicodeString(strip=True, min=3, not_empty=True))
305 v.UnicodeString(strip=True, min=3, not_empty=True))
292 new_field_value = v.UnicodeString(not_empty=False, if_missing=u'')
306 new_field_value = v.UnicodeString(not_empty=False, if_missing=u'')
293 new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'],
307 new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'],
294 if_missing='str')
308 if_missing='str')
295 new_field_label = v.UnicodeString(not_empty=False)
309 new_field_label = v.UnicodeString(not_empty=False)
296 new_field_desc = v.UnicodeString(not_empty=False)
310 new_field_desc = v.UnicodeString(not_empty=False)
297
311
298 return _RepoFieldForm
312 return _RepoFieldForm
299
313
300
314
301 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
315 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
302 repo_groups=[], landing_revs=[]):
316 repo_groups=[], landing_revs=[]):
303 class _RepoForkForm(formencode.Schema):
317 class _RepoForkForm(formencode.Schema):
304 allow_extra_fields = True
318 allow_extra_fields = True
305 filter_extra_fields = False
319 filter_extra_fields = False
306 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
320 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
307 v.SlugifyName())
321 v.SlugifyName())
308 repo_group = All(v.CanWriteGroup(),
322 repo_group = All(v.CanWriteGroup(),
309 v.OneOf(repo_groups, hideList=True))
323 v.OneOf(repo_groups, hideList=True))
310 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
324 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
311 description = v.UnicodeString(strip=True, min=1, not_empty=True)
325 description = v.UnicodeString(strip=True, min=1, not_empty=True)
312 private = v.StringBoolean(if_missing=False)
326 private = v.StringBoolean(if_missing=False)
313 copy_permissions = v.StringBoolean(if_missing=False)
327 copy_permissions = v.StringBoolean(if_missing=False)
314 fork_parent_id = v.UnicodeString()
328 fork_parent_id = v.UnicodeString()
315 chained_validators = [v.ValidForkName(edit, old_data)]
329 chained_validators = [v.ValidForkName(edit, old_data)]
316 landing_rev = v.OneOf(landing_revs, hideList=True)
330 landing_rev = v.OneOf(landing_revs, hideList=True)
317
331
318 return _RepoForkForm
332 return _RepoForkForm
319
333
320
334
321 def ApplicationSettingsForm():
335 def ApplicationSettingsForm():
322 class _ApplicationSettingsForm(formencode.Schema):
336 class _ApplicationSettingsForm(formencode.Schema):
323 allow_extra_fields = True
337 allow_extra_fields = True
324 filter_extra_fields = False
338 filter_extra_fields = False
325 rhodecode_title = v.UnicodeString(strip=True, max=40, not_empty=False)
339 rhodecode_title = v.UnicodeString(strip=True, max=40, not_empty=False)
326 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
340 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
327 rhodecode_pre_code = v.UnicodeString(strip=True, min=1, not_empty=False)
341 rhodecode_pre_code = v.UnicodeString(strip=True, min=1, not_empty=False)
328 rhodecode_post_code = v.UnicodeString(strip=True, min=1, not_empty=False)
342 rhodecode_post_code = v.UnicodeString(strip=True, min=1, not_empty=False)
329 rhodecode_captcha_public_key = v.UnicodeString(strip=True, min=1, not_empty=False)
343 rhodecode_captcha_public_key = v.UnicodeString(strip=True, min=1, not_empty=False)
330 rhodecode_captcha_private_key = v.UnicodeString(strip=True, min=1, not_empty=False)
344 rhodecode_captcha_private_key = v.UnicodeString(strip=True, min=1, not_empty=False)
331
345
332 return _ApplicationSettingsForm
346 return _ApplicationSettingsForm
333
347
334
348
335 def ApplicationVisualisationForm():
349 def ApplicationVisualisationForm():
336 class _ApplicationVisualisationForm(formencode.Schema):
350 class _ApplicationVisualisationForm(formencode.Schema):
337 allow_extra_fields = True
351 allow_extra_fields = True
338 filter_extra_fields = False
352 filter_extra_fields = False
339 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
353 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
340 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
354 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
341 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
355 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
342
356
343 rhodecode_repository_fields = v.StringBoolean(if_missing=False)
357 rhodecode_repository_fields = v.StringBoolean(if_missing=False)
344 rhodecode_lightweight_journal = v.StringBoolean(if_missing=False)
358 rhodecode_lightweight_journal = v.StringBoolean(if_missing=False)
345 rhodecode_dashboard_items = v.Int(min=5, not_empty=True)
359 rhodecode_dashboard_items = v.Int(min=5, not_empty=True)
346 rhodecode_admin_grid_items = v.Int(min=5, not_empty=True)
360 rhodecode_admin_grid_items = v.Int(min=5, not_empty=True)
347 rhodecode_show_version = v.StringBoolean(if_missing=False)
361 rhodecode_show_version = v.StringBoolean(if_missing=False)
348 rhodecode_use_gravatar = v.StringBoolean(if_missing=False)
362 rhodecode_use_gravatar = v.StringBoolean(if_missing=False)
349 rhodecode_markup_renderer = v.OneOf(['markdown', 'rst'])
363 rhodecode_markup_renderer = v.OneOf(['markdown', 'rst'])
350 rhodecode_gravatar_url = v.UnicodeString(min=3)
364 rhodecode_gravatar_url = v.UnicodeString(min=3)
351 rhodecode_clone_uri_tmpl = v.UnicodeString(min=3)
365 rhodecode_clone_uri_tmpl = v.UnicodeString(min=3)
352 rhodecode_support_url = v.UnicodeString()
366 rhodecode_support_url = v.UnicodeString()
353 rhodecode_show_revision_number = v.StringBoolean(if_missing=False)
367 rhodecode_show_revision_number = v.StringBoolean(if_missing=False)
354 rhodecode_show_sha_length = v.Int(min=4, not_empty=True)
368 rhodecode_show_sha_length = v.Int(min=4, not_empty=True)
355
369
356 return _ApplicationVisualisationForm
370 return _ApplicationVisualisationForm
357
371
358
372
359 class _BaseVcsSettingsForm(formencode.Schema):
373 class _BaseVcsSettingsForm(formencode.Schema):
360 allow_extra_fields = True
374 allow_extra_fields = True
361 filter_extra_fields = False
375 filter_extra_fields = False
362 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
376 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
363 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
377 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
364 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
378 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
365
379
366 extensions_largefiles = v.StringBoolean(if_missing=False)
380 extensions_largefiles = v.StringBoolean(if_missing=False)
367 phases_publish = v.StringBoolean(if_missing=False)
381 phases_publish = v.StringBoolean(if_missing=False)
368
382
369 rhodecode_pr_merge_enabled = v.StringBoolean(if_missing=False)
383 rhodecode_pr_merge_enabled = v.StringBoolean(if_missing=False)
370 rhodecode_use_outdated_comments = v.StringBoolean(if_missing=False)
384 rhodecode_use_outdated_comments = v.StringBoolean(if_missing=False)
371
385
372
386
373 def ApplicationUiSettingsForm():
387 def ApplicationUiSettingsForm():
374 class _ApplicationUiSettingsForm(_BaseVcsSettingsForm):
388 class _ApplicationUiSettingsForm(_BaseVcsSettingsForm):
375 web_push_ssl = v.StringBoolean(if_missing=False)
389 web_push_ssl = v.StringBoolean(if_missing=False)
376 paths_root_path = All(
390 paths_root_path = All(
377 v.ValidPath(),
391 v.ValidPath(),
378 v.UnicodeString(strip=True, min=1, not_empty=True)
392 v.UnicodeString(strip=True, min=1, not_empty=True)
379 )
393 )
380 extensions_hgsubversion = v.StringBoolean(if_missing=False)
394 extensions_hgsubversion = v.StringBoolean(if_missing=False)
381 extensions_hggit = v.StringBoolean(if_missing=False)
395 extensions_hggit = v.StringBoolean(if_missing=False)
382 new_svn_branch = v.ValidSvnPattern(section='vcs_svn_branch')
396 new_svn_branch = v.ValidSvnPattern(section='vcs_svn_branch')
383 new_svn_tag = v.ValidSvnPattern(section='vcs_svn_tag')
397 new_svn_tag = v.ValidSvnPattern(section='vcs_svn_tag')
384
398
385 return _ApplicationUiSettingsForm
399 return _ApplicationUiSettingsForm
386
400
387
401
388 def RepoVcsSettingsForm(repo_name):
402 def RepoVcsSettingsForm(repo_name):
389 class _RepoVcsSettingsForm(_BaseVcsSettingsForm):
403 class _RepoVcsSettingsForm(_BaseVcsSettingsForm):
390 inherit_global_settings = v.StringBoolean(if_missing=False)
404 inherit_global_settings = v.StringBoolean(if_missing=False)
391 new_svn_branch = v.ValidSvnPattern(
405 new_svn_branch = v.ValidSvnPattern(
392 section='vcs_svn_branch', repo_name=repo_name)
406 section='vcs_svn_branch', repo_name=repo_name)
393 new_svn_tag = v.ValidSvnPattern(
407 new_svn_tag = v.ValidSvnPattern(
394 section='vcs_svn_tag', repo_name=repo_name)
408 section='vcs_svn_tag', repo_name=repo_name)
395
409
396 return _RepoVcsSettingsForm
410 return _RepoVcsSettingsForm
397
411
398
412
399 def LabsSettingsForm():
413 def LabsSettingsForm():
400 class _LabSettingsForm(formencode.Schema):
414 class _LabSettingsForm(formencode.Schema):
401 allow_extra_fields = True
415 allow_extra_fields = True
402 filter_extra_fields = False
416 filter_extra_fields = False
403
417
404 rhodecode_hg_use_rebase_for_merging = v.StringBoolean(if_missing=False)
418 rhodecode_hg_use_rebase_for_merging = v.StringBoolean(if_missing=False)
405 rhodecode_proxy_subversion_http_requests = v.StringBoolean(
419 rhodecode_proxy_subversion_http_requests = v.StringBoolean(
406 if_missing=False)
420 if_missing=False)
407 rhodecode_subversion_http_server_url = v.UnicodeString(
421 rhodecode_subversion_http_server_url = v.UnicodeString(
408 strip=True, if_missing=None)
422 strip=True, if_missing=None)
409
423
410 return _LabSettingsForm
424 return _LabSettingsForm
411
425
412
426
413 def ApplicationPermissionsForm(register_choices, extern_activate_choices):
427 def ApplicationPermissionsForm(register_choices, extern_activate_choices):
414 class _DefaultPermissionsForm(formencode.Schema):
428 class _DefaultPermissionsForm(formencode.Schema):
415 allow_extra_fields = True
429 allow_extra_fields = True
416 filter_extra_fields = True
430 filter_extra_fields = True
417
431
418 anonymous = v.StringBoolean(if_missing=False)
432 anonymous = v.StringBoolean(if_missing=False)
419 default_register = v.OneOf(register_choices)
433 default_register = v.OneOf(register_choices)
420 default_register_message = v.UnicodeString()
434 default_register_message = v.UnicodeString()
421 default_extern_activate = v.OneOf(extern_activate_choices)
435 default_extern_activate = v.OneOf(extern_activate_choices)
422
436
423 return _DefaultPermissionsForm
437 return _DefaultPermissionsForm
424
438
425
439
426 def ObjectPermissionsForm(repo_perms_choices, group_perms_choices,
440 def ObjectPermissionsForm(repo_perms_choices, group_perms_choices,
427 user_group_perms_choices):
441 user_group_perms_choices):
428 class _ObjectPermissionsForm(formencode.Schema):
442 class _ObjectPermissionsForm(formencode.Schema):
429 allow_extra_fields = True
443 allow_extra_fields = True
430 filter_extra_fields = True
444 filter_extra_fields = True
431 overwrite_default_repo = v.StringBoolean(if_missing=False)
445 overwrite_default_repo = v.StringBoolean(if_missing=False)
432 overwrite_default_group = v.StringBoolean(if_missing=False)
446 overwrite_default_group = v.StringBoolean(if_missing=False)
433 overwrite_default_user_group = v.StringBoolean(if_missing=False)
447 overwrite_default_user_group = v.StringBoolean(if_missing=False)
434 default_repo_perm = v.OneOf(repo_perms_choices)
448 default_repo_perm = v.OneOf(repo_perms_choices)
435 default_group_perm = v.OneOf(group_perms_choices)
449 default_group_perm = v.OneOf(group_perms_choices)
436 default_user_group_perm = v.OneOf(user_group_perms_choices)
450 default_user_group_perm = v.OneOf(user_group_perms_choices)
437
451
438 return _ObjectPermissionsForm
452 return _ObjectPermissionsForm
439
453
440
454
441 def UserPermissionsForm(create_choices, create_on_write_choices,
455 def UserPermissionsForm(create_choices, create_on_write_choices,
442 repo_group_create_choices, user_group_create_choices,
456 repo_group_create_choices, user_group_create_choices,
443 fork_choices, inherit_default_permissions_choices):
457 fork_choices, inherit_default_permissions_choices):
444 class _DefaultPermissionsForm(formencode.Schema):
458 class _DefaultPermissionsForm(formencode.Schema):
445 allow_extra_fields = True
459 allow_extra_fields = True
446 filter_extra_fields = True
460 filter_extra_fields = True
447
461
448 anonymous = v.StringBoolean(if_missing=False)
462 anonymous = v.StringBoolean(if_missing=False)
449
463
450 default_repo_create = v.OneOf(create_choices)
464 default_repo_create = v.OneOf(create_choices)
451 default_repo_create_on_write = v.OneOf(create_on_write_choices)
465 default_repo_create_on_write = v.OneOf(create_on_write_choices)
452 default_user_group_create = v.OneOf(user_group_create_choices)
466 default_user_group_create = v.OneOf(user_group_create_choices)
453 default_repo_group_create = v.OneOf(repo_group_create_choices)
467 default_repo_group_create = v.OneOf(repo_group_create_choices)
454 default_fork_create = v.OneOf(fork_choices)
468 default_fork_create = v.OneOf(fork_choices)
455 default_inherit_default_permissions = v.OneOf(inherit_default_permissions_choices)
469 default_inherit_default_permissions = v.OneOf(inherit_default_permissions_choices)
456
470
457 return _DefaultPermissionsForm
471 return _DefaultPermissionsForm
458
472
459
473
460 def UserIndividualPermissionsForm():
474 def UserIndividualPermissionsForm():
461 class _DefaultPermissionsForm(formencode.Schema):
475 class _DefaultPermissionsForm(formencode.Schema):
462 allow_extra_fields = True
476 allow_extra_fields = True
463 filter_extra_fields = True
477 filter_extra_fields = True
464
478
465 inherit_default_permissions = v.StringBoolean(if_missing=False)
479 inherit_default_permissions = v.StringBoolean(if_missing=False)
466
480
467 return _DefaultPermissionsForm
481 return _DefaultPermissionsForm
468
482
469
483
470 def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
484 def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
471 class _DefaultsForm(formencode.Schema):
485 class _DefaultsForm(formencode.Schema):
472 allow_extra_fields = True
486 allow_extra_fields = True
473 filter_extra_fields = True
487 filter_extra_fields = True
474 default_repo_type = v.OneOf(supported_backends)
488 default_repo_type = v.OneOf(supported_backends)
475 default_repo_private = v.StringBoolean(if_missing=False)
489 default_repo_private = v.StringBoolean(if_missing=False)
476 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
490 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
477 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
491 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
478 default_repo_enable_locking = v.StringBoolean(if_missing=False)
492 default_repo_enable_locking = v.StringBoolean(if_missing=False)
479
493
480 return _DefaultsForm
494 return _DefaultsForm
481
495
482
496
483 def AuthSettingsForm():
497 def AuthSettingsForm():
484 class _AuthSettingsForm(formencode.Schema):
498 class _AuthSettingsForm(formencode.Schema):
485 allow_extra_fields = True
499 allow_extra_fields = True
486 filter_extra_fields = True
500 filter_extra_fields = True
487 auth_plugins = All(v.ValidAuthPlugins(),
501 auth_plugins = All(v.ValidAuthPlugins(),
488 v.UniqueListFromString()(not_empty=True))
502 v.UniqueListFromString()(not_empty=True))
489
503
490 return _AuthSettingsForm
504 return _AuthSettingsForm
491
505
492
506
493 def UserExtraEmailForm():
507 def UserExtraEmailForm():
494 class _UserExtraEmailForm(formencode.Schema):
508 class _UserExtraEmailForm(formencode.Schema):
495 email = All(v.UniqSystemEmail(), v.Email(not_empty=True))
509 email = All(v.UniqSystemEmail(), v.Email(not_empty=True))
496 return _UserExtraEmailForm
510 return _UserExtraEmailForm
497
511
498
512
499 def UserExtraIpForm():
513 def UserExtraIpForm():
500 class _UserExtraIpForm(formencode.Schema):
514 class _UserExtraIpForm(formencode.Schema):
501 ip = v.ValidIp()(not_empty=True)
515 ip = v.ValidIp()(not_empty=True)
502 return _UserExtraIpForm
516 return _UserExtraIpForm
503
517
504
518
505 def PullRequestForm(repo_id):
519 def PullRequestForm(repo_id):
506 class _PullRequestForm(formencode.Schema):
520 class _PullRequestForm(formencode.Schema):
507 allow_extra_fields = True
521 allow_extra_fields = True
508 filter_extra_fields = True
522 filter_extra_fields = True
509
523
510 user = v.UnicodeString(strip=True, required=True)
524 user = v.UnicodeString(strip=True, required=True)
511 source_repo = v.UnicodeString(strip=True, required=True)
525 source_repo = v.UnicodeString(strip=True, required=True)
512 source_ref = v.UnicodeString(strip=True, required=True)
526 source_ref = v.UnicodeString(strip=True, required=True)
513 target_repo = v.UnicodeString(strip=True, required=True)
527 target_repo = v.UnicodeString(strip=True, required=True)
514 target_ref = v.UnicodeString(strip=True, required=True)
528 target_ref = v.UnicodeString(strip=True, required=True)
515 revisions = All(#v.NotReviewedRevisions(repo_id)(),
529 revisions = All(#v.NotReviewedRevisions(repo_id)(),
516 v.UniqueList()(not_empty=True))
530 v.UniqueList()(not_empty=True))
517 review_members = v.UniqueList(convert=int)(not_empty=True)
531 review_members = v.UniqueList(convert=int)(not_empty=True)
518
532
519 pullrequest_title = v.UnicodeString(strip=True, required=True)
533 pullrequest_title = v.UnicodeString(strip=True, required=True)
520 pullrequest_desc = v.UnicodeString(strip=True, required=False)
534 pullrequest_desc = v.UnicodeString(strip=True, required=False)
521
535
522 return _PullRequestForm
536 return _PullRequestForm
523
537
524
538
525 def GistForm(lifetime_options, acl_level_options):
539 def GistForm(lifetime_options, acl_level_options):
526 class _GistForm(formencode.Schema):
540 class _GistForm(formencode.Schema):
527
541
528 gistid = All(v.UniqGistId(), v.UnicodeString(strip=True, min=3, not_empty=False, if_missing=None))
542 gistid = All(v.UniqGistId(), v.UnicodeString(strip=True, min=3, not_empty=False, if_missing=None))
529 filename = All(v.BasePath()(),
543 filename = All(v.BasePath()(),
530 v.UnicodeString(strip=True, required=False))
544 v.UnicodeString(strip=True, required=False))
531 description = v.UnicodeString(required=False, if_missing=u'')
545 description = v.UnicodeString(required=False, if_missing=u'')
532 lifetime = v.OneOf(lifetime_options)
546 lifetime = v.OneOf(lifetime_options)
533 mimetype = v.UnicodeString(required=False, if_missing=None)
547 mimetype = v.UnicodeString(required=False, if_missing=None)
534 content = v.UnicodeString(required=True, not_empty=True)
548 content = v.UnicodeString(required=True, not_empty=True)
535 public = v.UnicodeString(required=False, if_missing=u'')
549 public = v.UnicodeString(required=False, if_missing=u'')
536 private = v.UnicodeString(required=False, if_missing=u'')
550 private = v.UnicodeString(required=False, if_missing=u'')
537 acl_level = v.OneOf(acl_level_options)
551 acl_level = v.OneOf(acl_level_options)
538
552
539 return _GistForm
553 return _GistForm
540
554
541
555
542 def IssueTrackerPatternsForm():
556 def IssueTrackerPatternsForm():
543 class _IssueTrackerPatternsForm(formencode.Schema):
557 class _IssueTrackerPatternsForm(formencode.Schema):
544 allow_extra_fields = True
558 allow_extra_fields = True
545 filter_extra_fields = False
559 filter_extra_fields = False
546 chained_validators = [v.ValidPattern()]
560 chained_validators = [v.ValidPattern()]
547 return _IssueTrackerPatternsForm
561 return _IssueTrackerPatternsForm
@@ -1,1120 +1,1125 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 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.config.routing 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
47 from rhodecode.lib.utils2 import safe_int, str2bool, aslist, md5
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():
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 }
201 }
201
202
202 def validate_python(self, value, state):
203 def validate_python(self, value, state):
203 try:
204 try:
204 User.query().filter(User.active == true())\
205 user = User.query().filter(User.username == value).one()
205 .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):
212 msg = M(self, 'disabled_username', state, username=value)
213 raise formencode.Invalid(
214 msg, value, state, error_dict={'username': msg}
215 )
211
216
212 return _validator
217 return _validator
213
218
214
219
215 def ValidUserGroup(edit=False, old_data={}):
220 def ValidUserGroup(edit=False, old_data={}):
216 class _validator(formencode.validators.FancyValidator):
221 class _validator(formencode.validators.FancyValidator):
217 messages = {
222 messages = {
218 'invalid_group': _(u'Invalid user group name'),
223 'invalid_group': _(u'Invalid user group name'),
219 'group_exist': _(u'User group "%(usergroup)s" already exists'),
224 'group_exist': _(u'User group "%(usergroup)s" already exists'),
220 'invalid_usergroup_name':
225 'invalid_usergroup_name':
221 _(u'user group name may only contain alphanumeric '
226 _(u'user group name may only contain alphanumeric '
222 u'characters underscores, periods or dashes and must begin '
227 u'characters underscores, periods or dashes and must begin '
223 u'with alphanumeric character')
228 u'with alphanumeric character')
224 }
229 }
225
230
226 def validate_python(self, value, state):
231 def validate_python(self, value, state):
227 if value in ['default']:
232 if value in ['default']:
228 msg = M(self, 'invalid_group', state)
233 msg = M(self, 'invalid_group', state)
229 raise formencode.Invalid(
234 raise formencode.Invalid(
230 msg, value, state, error_dict={'users_group_name': msg}
235 msg, value, state, error_dict={'users_group_name': msg}
231 )
236 )
232 # check if group is unique
237 # check if group is unique
233 old_ugname = None
238 old_ugname = None
234 if edit:
239 if edit:
235 old_id = old_data.get('users_group_id')
240 old_id = old_data.get('users_group_id')
236 old_ugname = UserGroup.get(old_id).users_group_name
241 old_ugname = UserGroup.get(old_id).users_group_name
237
242
238 if old_ugname != value or not edit:
243 if old_ugname != value or not edit:
239 is_existing_group = UserGroup.get_by_group_name(
244 is_existing_group = UserGroup.get_by_group_name(
240 value, case_insensitive=True)
245 value, case_insensitive=True)
241 if is_existing_group:
246 if is_existing_group:
242 msg = M(self, 'group_exist', state, usergroup=value)
247 msg = M(self, 'group_exist', state, usergroup=value)
243 raise formencode.Invalid(
248 raise formencode.Invalid(
244 msg, value, state, error_dict={'users_group_name': msg}
249 msg, value, state, error_dict={'users_group_name': msg}
245 )
250 )
246
251
247 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:
248 msg = M(self, 'invalid_usergroup_name', state)
253 msg = M(self, 'invalid_usergroup_name', state)
249 raise formencode.Invalid(
254 raise formencode.Invalid(
250 msg, value, state, error_dict={'users_group_name': msg}
255 msg, value, state, error_dict={'users_group_name': msg}
251 )
256 )
252
257
253 return _validator
258 return _validator
254
259
255
260
256 def ValidRepoGroup(edit=False, old_data={}, can_create_in_root=False):
261 def ValidRepoGroup(edit=False, old_data={}, can_create_in_root=False):
257 class _validator(formencode.validators.FancyValidator):
262 class _validator(formencode.validators.FancyValidator):
258 messages = {
263 messages = {
259 'group_parent_id': _(u'Cannot assign this group as parent'),
264 'group_parent_id': _(u'Cannot assign this group as parent'),
260 'group_exists': _(u'Group "%(group_name)s" already exists'),
265 'group_exists': _(u'Group "%(group_name)s" already exists'),
261 'repo_exists': _(u'Repository with name "%(group_name)s" '
266 'repo_exists': _(u'Repository with name "%(group_name)s" '
262 u'already exists'),
267 u'already exists'),
263 'permission_denied': _(u"no permission to store repository group"
268 'permission_denied': _(u"no permission to store repository group"
264 u"in this location"),
269 u"in this location"),
265 'permission_denied_root': _(
270 'permission_denied_root': _(
266 u"no permission to store repository group "
271 u"no permission to store repository group "
267 u"in root location")
272 u"in root location")
268 }
273 }
269
274
270 def _to_python(self, value, state):
275 def _to_python(self, value, state):
271 group_name = repo_name_slug(value.get('group_name', ''))
276 group_name = repo_name_slug(value.get('group_name', ''))
272 group_parent_id = safe_int(value.get('group_parent_id'))
277 group_parent_id = safe_int(value.get('group_parent_id'))
273 gr = RepoGroup.get(group_parent_id)
278 gr = RepoGroup.get(group_parent_id)
274 if gr:
279 if gr:
275 parent_group_path = gr.full_path
280 parent_group_path = gr.full_path
276 # 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
277 # 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
278 # database
283 # database
279 group_name_full = (
284 group_name_full = (
280 parent_group_path + RepoGroup.url_sep() + group_name)
285 parent_group_path + RepoGroup.url_sep() + group_name)
281 else:
286 else:
282 group_name_full = group_name
287 group_name_full = group_name
283
288
284 value['group_name'] = group_name
289 value['group_name'] = group_name
285 value['group_name_full'] = group_name_full
290 value['group_name_full'] = group_name_full
286 value['group_parent_id'] = group_parent_id
291 value['group_parent_id'] = group_parent_id
287 return value
292 return value
288
293
289 def validate_python(self, value, state):
294 def validate_python(self, value, state):
290
295
291 old_group_name = None
296 old_group_name = None
292 group_name = value.get('group_name')
297 group_name = value.get('group_name')
293 group_name_full = value.get('group_name_full')
298 group_name_full = value.get('group_name_full')
294 group_parent_id = safe_int(value.get('group_parent_id'))
299 group_parent_id = safe_int(value.get('group_parent_id'))
295 if group_parent_id == -1:
300 if group_parent_id == -1:
296 group_parent_id = None
301 group_parent_id = None
297
302
298 group_obj = RepoGroup.get(old_data.get('group_id'))
303 group_obj = RepoGroup.get(old_data.get('group_id'))
299 parent_group_changed = False
304 parent_group_changed = False
300 if edit:
305 if edit:
301 old_group_name = group_obj.group_name
306 old_group_name = group_obj.group_name
302 old_group_parent_id = group_obj.group_parent_id
307 old_group_parent_id = group_obj.group_parent_id
303
308
304 if group_parent_id != old_group_parent_id:
309 if group_parent_id != old_group_parent_id:
305 parent_group_changed = True
310 parent_group_changed = True
306
311
307 # TODO: mikhail: the following if statement is not reached
312 # TODO: mikhail: the following if statement is not reached
308 # since group_parent_id's OneOf validation fails before.
313 # since group_parent_id's OneOf validation fails before.
309 # Can be removed.
314 # Can be removed.
310
315
311 # check against setting a parent of self
316 # check against setting a parent of self
312 parent_of_self = (
317 parent_of_self = (
313 old_data['group_id'] == group_parent_id
318 old_data['group_id'] == group_parent_id
314 if group_parent_id else False
319 if group_parent_id else False
315 )
320 )
316 if parent_of_self:
321 if parent_of_self:
317 msg = M(self, 'group_parent_id', state)
322 msg = M(self, 'group_parent_id', state)
318 raise formencode.Invalid(
323 raise formencode.Invalid(
319 msg, value, state, error_dict={'group_parent_id': msg}
324 msg, value, state, error_dict={'group_parent_id': msg}
320 )
325 )
321
326
322 # group we're moving current group inside
327 # group we're moving current group inside
323 child_group = None
328 child_group = None
324 if group_parent_id:
329 if group_parent_id:
325 child_group = RepoGroup.query().filter(
330 child_group = RepoGroup.query().filter(
326 RepoGroup.group_id == group_parent_id).scalar()
331 RepoGroup.group_id == group_parent_id).scalar()
327
332
328 # 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
329 # it's children
334 # it's children
330 if edit and child_group:
335 if edit and child_group:
331 parents = [x.group_id for x in child_group.parents]
336 parents = [x.group_id for x in child_group.parents]
332 move_to_children = old_data['group_id'] in parents
337 move_to_children = old_data['group_id'] in parents
333 if move_to_children:
338 if move_to_children:
334 msg = M(self, 'group_parent_id', state)
339 msg = M(self, 'group_parent_id', state)
335 raise formencode.Invalid(
340 raise formencode.Invalid(
336 msg, value, state, error_dict={'group_parent_id': msg})
341 msg, value, state, error_dict={'group_parent_id': msg})
337
342
338 # Check if we have permission to store in the parent.
343 # Check if we have permission to store in the parent.
339 # Only check if the parent group changed.
344 # Only check if the parent group changed.
340 if parent_group_changed:
345 if parent_group_changed:
341 if child_group is None:
346 if child_group is None:
342 if not can_create_in_root:
347 if not can_create_in_root:
343 msg = M(self, 'permission_denied_root', state)
348 msg = M(self, 'permission_denied_root', state)
344 raise formencode.Invalid(
349 raise formencode.Invalid(
345 msg, value, state,
350 msg, value, state,
346 error_dict={'group_parent_id': msg})
351 error_dict={'group_parent_id': msg})
347 else:
352 else:
348 valid = HasRepoGroupPermissionAny('group.admin')
353 valid = HasRepoGroupPermissionAny('group.admin')
349 forbidden = not valid(
354 forbidden = not valid(
350 child_group.group_name, 'can create group validator')
355 child_group.group_name, 'can create group validator')
351 if forbidden:
356 if forbidden:
352 msg = M(self, 'permission_denied', state)
357 msg = M(self, 'permission_denied', state)
353 raise formencode.Invalid(
358 raise formencode.Invalid(
354 msg, value, state,
359 msg, value, state,
355 error_dict={'group_parent_id': msg})
360 error_dict={'group_parent_id': msg})
356
361
357 # 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
358 # or repositories with the same name
363 # or repositories with the same name
359 if old_group_name != group_name_full or not edit:
364 if old_group_name != group_name_full or not edit:
360 # check group
365 # check group
361 gr = RepoGroup.get_by_group_name(group_name_full)
366 gr = RepoGroup.get_by_group_name(group_name_full)
362 if gr:
367 if gr:
363 msg = M(self, 'group_exists', state, group_name=group_name)
368 msg = M(self, 'group_exists', state, group_name=group_name)
364 raise formencode.Invalid(
369 raise formencode.Invalid(
365 msg, value, state, error_dict={'group_name': msg})
370 msg, value, state, error_dict={'group_name': msg})
366
371
367 # check for same repo
372 # check for same repo
368 repo = Repository.get_by_repo_name(group_name_full)
373 repo = Repository.get_by_repo_name(group_name_full)
369 if repo:
374 if repo:
370 msg = M(self, 'repo_exists', state, group_name=group_name)
375 msg = M(self, 'repo_exists', state, group_name=group_name)
371 raise formencode.Invalid(
376 raise formencode.Invalid(
372 msg, value, state, error_dict={'group_name': msg})
377 msg, value, state, error_dict={'group_name': msg})
373
378
374 return _validator
379 return _validator
375
380
376
381
377 def ValidPassword():
382 def ValidPassword():
378 class _validator(formencode.validators.FancyValidator):
383 class _validator(formencode.validators.FancyValidator):
379 messages = {
384 messages = {
380 'invalid_password':
385 'invalid_password':
381 _(u'Invalid characters (non-ascii) in password')
386 _(u'Invalid characters (non-ascii) in password')
382 }
387 }
383
388
384 def validate_python(self, value, state):
389 def validate_python(self, value, state):
385 try:
390 try:
386 (value or '').decode('ascii')
391 (value or '').decode('ascii')
387 except UnicodeError:
392 except UnicodeError:
388 msg = M(self, 'invalid_password', state)
393 msg = M(self, 'invalid_password', state)
389 raise formencode.Invalid(msg, value, state,)
394 raise formencode.Invalid(msg, value, state,)
390 return _validator
395 return _validator
391
396
392
397
393 def ValidOldPassword(username):
398 def ValidOldPassword(username):
394 class _validator(formencode.validators.FancyValidator):
399 class _validator(formencode.validators.FancyValidator):
395 messages = {
400 messages = {
396 'invalid_password': _(u'Invalid old password')
401 'invalid_password': _(u'Invalid old password')
397 }
402 }
398
403
399 def validate_python(self, value, state):
404 def validate_python(self, value, state):
400 from rhodecode.authentication.base import authenticate, HTTP_TYPE
405 from rhodecode.authentication.base import authenticate, HTTP_TYPE
401 if not authenticate(username, value, '', HTTP_TYPE):
406 if not authenticate(username, value, '', HTTP_TYPE):
402 msg = M(self, 'invalid_password', state)
407 msg = M(self, 'invalid_password', state)
403 raise formencode.Invalid(
408 raise formencode.Invalid(
404 msg, value, state, error_dict={'current_password': msg}
409 msg, value, state, error_dict={'current_password': msg}
405 )
410 )
406 return _validator
411 return _validator
407
412
408
413
409 def ValidPasswordsMatch(
414 def ValidPasswordsMatch(
410 passwd='new_password', passwd_confirmation='password_confirmation'):
415 passwd='new_password', passwd_confirmation='password_confirmation'):
411 class _validator(formencode.validators.FancyValidator):
416 class _validator(formencode.validators.FancyValidator):
412 messages = {
417 messages = {
413 'password_mismatch': _(u'Passwords do not match'),
418 'password_mismatch': _(u'Passwords do not match'),
414 }
419 }
415
420
416 def validate_python(self, value, state):
421 def validate_python(self, value, state):
417
422
418 pass_val = value.get('password') or value.get(passwd)
423 pass_val = value.get('password') or value.get(passwd)
419 if pass_val != value[passwd_confirmation]:
424 if pass_val != value[passwd_confirmation]:
420 msg = M(self, 'password_mismatch', state)
425 msg = M(self, 'password_mismatch', state)
421 raise formencode.Invalid(
426 raise formencode.Invalid(
422 msg, value, state,
427 msg, value, state,
423 error_dict={passwd: msg, passwd_confirmation: msg}
428 error_dict={passwd: msg, passwd_confirmation: msg}
424 )
429 )
425 return _validator
430 return _validator
426
431
427
432
428 def ValidAuth():
433 def ValidAuth():
429 class _validator(formencode.validators.FancyValidator):
434 class _validator(formencode.validators.FancyValidator):
430 messages = {
435 messages = {
431 'invalid_password': _(u'invalid password'),
436 'invalid_password': _(u'invalid password'),
432 'invalid_username': _(u'invalid user name'),
437 'invalid_username': _(u'invalid user name'),
433 'disabled_account': _(u'Your account is disabled')
438 'disabled_account': _(u'Your account is disabled')
434 }
439 }
435
440
436 def validate_python(self, value, state):
441 def validate_python(self, value, state):
437 from rhodecode.authentication.base import authenticate, HTTP_TYPE
442 from rhodecode.authentication.base import authenticate, HTTP_TYPE
438
443
439 password = value['password']
444 password = value['password']
440 username = value['username']
445 username = value['username']
441
446
442 if not authenticate(username, password, '', HTTP_TYPE,
447 if not authenticate(username, password, '', HTTP_TYPE,
443 skip_missing=True):
448 skip_missing=True):
444 user = User.get_by_username(username)
449 user = User.get_by_username(username)
445 if user and not user.active:
450 if user and not user.active:
446 log.warning('user %s is disabled', username)
451 log.warning('user %s is disabled', username)
447 msg = M(self, 'disabled_account', state)
452 msg = M(self, 'disabled_account', state)
448 raise formencode.Invalid(
453 raise formencode.Invalid(
449 msg, value, state, error_dict={'username': msg}
454 msg, value, state, error_dict={'username': msg}
450 )
455 )
451 else:
456 else:
452 log.warning('user `%s` failed to authenticate', username)
457 log.warning('user `%s` failed to authenticate', username)
453 msg = M(self, 'invalid_username', state)
458 msg = M(self, 'invalid_username', state)
454 msg2 = M(self, 'invalid_password', state)
459 msg2 = M(self, 'invalid_password', state)
455 raise formencode.Invalid(
460 raise formencode.Invalid(
456 msg, value, state,
461 msg, value, state,
457 error_dict={'username': msg, 'password': msg2}
462 error_dict={'username': msg, 'password': msg2}
458 )
463 )
459 return _validator
464 return _validator
460
465
461
466
462 def ValidAuthToken():
467 def ValidAuthToken():
463 class _validator(formencode.validators.FancyValidator):
468 class _validator(formencode.validators.FancyValidator):
464 messages = {
469 messages = {
465 'invalid_token': _(u'Token mismatch')
470 'invalid_token': _(u'Token mismatch')
466 }
471 }
467
472
468 def validate_python(self, value, state):
473 def validate_python(self, value, state):
469 if value != authentication_token():
474 if value != authentication_token():
470 msg = M(self, 'invalid_token', state)
475 msg = M(self, 'invalid_token', state)
471 raise formencode.Invalid(msg, value, state)
476 raise formencode.Invalid(msg, value, state)
472 return _validator
477 return _validator
473
478
474
479
475 def ValidRepoName(edit=False, old_data={}):
480 def ValidRepoName(edit=False, old_data={}):
476 class _validator(formencode.validators.FancyValidator):
481 class _validator(formencode.validators.FancyValidator):
477 messages = {
482 messages = {
478 'invalid_repo_name':
483 'invalid_repo_name':
479 _(u'Repository name %(repo)s is disallowed'),
484 _(u'Repository name %(repo)s is disallowed'),
480 # top level
485 # top level
481 'repository_exists': _(u'Repository with name %(repo)s '
486 'repository_exists': _(u'Repository with name %(repo)s '
482 u'already exists'),
487 u'already exists'),
483 'group_exists': _(u'Repository group with name "%(repo)s" '
488 'group_exists': _(u'Repository group with name "%(repo)s" '
484 u'already exists'),
489 u'already exists'),
485 # inside a group
490 # inside a group
486 'repository_in_group_exists': _(u'Repository with name %(repo)s '
491 'repository_in_group_exists': _(u'Repository with name %(repo)s '
487 u'exists in group "%(group)s"'),
492 u'exists in group "%(group)s"'),
488 'group_in_group_exists': _(
493 'group_in_group_exists': _(
489 u'Repository group with name "%(repo)s" '
494 u'Repository group with name "%(repo)s" '
490 u'exists in group "%(group)s"'),
495 u'exists in group "%(group)s"'),
491 }
496 }
492
497
493 def _to_python(self, value, state):
498 def _to_python(self, value, state):
494 repo_name = repo_name_slug(value.get('repo_name', ''))
499 repo_name = repo_name_slug(value.get('repo_name', ''))
495 repo_group = value.get('repo_group')
500 repo_group = value.get('repo_group')
496 if repo_group:
501 if repo_group:
497 gr = RepoGroup.get(repo_group)
502 gr = RepoGroup.get(repo_group)
498 group_path = gr.full_path
503 group_path = gr.full_path
499 group_name = gr.group_name
504 group_name = gr.group_name
500 # 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
501 # 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
502 # database
507 # database
503 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
508 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
504 else:
509 else:
505 group_name = group_path = ''
510 group_name = group_path = ''
506 repo_name_full = repo_name
511 repo_name_full = repo_name
507
512
508 value['repo_name'] = repo_name
513 value['repo_name'] = repo_name
509 value['repo_name_full'] = repo_name_full
514 value['repo_name_full'] = repo_name_full
510 value['group_path'] = group_path
515 value['group_path'] = group_path
511 value['group_name'] = group_name
516 value['group_name'] = group_name
512 return value
517 return value
513
518
514 def validate_python(self, value, state):
519 def validate_python(self, value, state):
515
520
516 repo_name = value.get('repo_name')
521 repo_name = value.get('repo_name')
517 repo_name_full = value.get('repo_name_full')
522 repo_name_full = value.get('repo_name_full')
518 group_path = value.get('group_path')
523 group_path = value.get('group_path')
519 group_name = value.get('group_name')
524 group_name = value.get('group_name')
520
525
521 if repo_name in [ADMIN_PREFIX, '']:
526 if repo_name in [ADMIN_PREFIX, '']:
522 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
527 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
523 raise formencode.Invalid(
528 raise formencode.Invalid(
524 msg, value, state, error_dict={'repo_name': msg})
529 msg, value, state, error_dict={'repo_name': msg})
525
530
526 rename = old_data.get('repo_name') != repo_name_full
531 rename = old_data.get('repo_name') != repo_name_full
527 create = not edit
532 create = not edit
528 if rename or create:
533 if rename or create:
529
534
530 if group_path:
535 if group_path:
531 if Repository.get_by_repo_name(repo_name_full):
536 if Repository.get_by_repo_name(repo_name_full):
532 msg = M(self, 'repository_in_group_exists', state,
537 msg = M(self, 'repository_in_group_exists', state,
533 repo=repo_name, group=group_name)
538 repo=repo_name, group=group_name)
534 raise formencode.Invalid(
539 raise formencode.Invalid(
535 msg, value, state, error_dict={'repo_name': msg})
540 msg, value, state, error_dict={'repo_name': msg})
536 if RepoGroup.get_by_group_name(repo_name_full):
541 if RepoGroup.get_by_group_name(repo_name_full):
537 msg = M(self, 'group_in_group_exists', state,
542 msg = M(self, 'group_in_group_exists', state,
538 repo=repo_name, group=group_name)
543 repo=repo_name, group=group_name)
539 raise formencode.Invalid(
544 raise formencode.Invalid(
540 msg, value, state, error_dict={'repo_name': msg})
545 msg, value, state, error_dict={'repo_name': msg})
541 else:
546 else:
542 if RepoGroup.get_by_group_name(repo_name_full):
547 if RepoGroup.get_by_group_name(repo_name_full):
543 msg = M(self, 'group_exists', state, repo=repo_name)
548 msg = M(self, 'group_exists', state, repo=repo_name)
544 raise formencode.Invalid(
549 raise formencode.Invalid(
545 msg, value, state, error_dict={'repo_name': msg})
550 msg, value, state, error_dict={'repo_name': msg})
546
551
547 if Repository.get_by_repo_name(repo_name_full):
552 if Repository.get_by_repo_name(repo_name_full):
548 msg = M(
553 msg = M(
549 self, 'repository_exists', state, repo=repo_name)
554 self, 'repository_exists', state, repo=repo_name)
550 raise formencode.Invalid(
555 raise formencode.Invalid(
551 msg, value, state, error_dict={'repo_name': msg})
556 msg, value, state, error_dict={'repo_name': msg})
552 return value
557 return value
553 return _validator
558 return _validator
554
559
555
560
556 def ValidForkName(*args, **kwargs):
561 def ValidForkName(*args, **kwargs):
557 return ValidRepoName(*args, **kwargs)
562 return ValidRepoName(*args, **kwargs)
558
563
559
564
560 def SlugifyName():
565 def SlugifyName():
561 class _validator(formencode.validators.FancyValidator):
566 class _validator(formencode.validators.FancyValidator):
562
567
563 def _to_python(self, value, state):
568 def _to_python(self, value, state):
564 return repo_name_slug(value)
569 return repo_name_slug(value)
565
570
566 def validate_python(self, value, state):
571 def validate_python(self, value, state):
567 pass
572 pass
568
573
569 return _validator
574 return _validator
570
575
571
576
572 def ValidCloneUri():
577 def ValidCloneUri():
573 class InvalidCloneUrl(Exception):
578 class InvalidCloneUrl(Exception):
574 allowed_prefixes = ()
579 allowed_prefixes = ()
575
580
576 def url_handler(repo_type, url):
581 def url_handler(repo_type, url):
577 config = make_db_config(clear_session=False)
582 config = make_db_config(clear_session=False)
578 if repo_type == 'hg':
583 if repo_type == 'hg':
579 allowed_prefixes = ('http', 'svn+http', 'git+http')
584 allowed_prefixes = ('http', 'svn+http', 'git+http')
580
585
581 if 'http' in url[:4]:
586 if 'http' in url[:4]:
582 # initially check if it's at least the proper URL
587 # initially check if it's at least the proper URL
583 # or does it pass basic auth
588 # or does it pass basic auth
584 MercurialRepository.check_url(url, config)
589 MercurialRepository.check_url(url, config)
585 elif 'svn+http' in url[:8]: # svn->hg import
590 elif 'svn+http' in url[:8]: # svn->hg import
586 SubversionRepository.check_url(url, config)
591 SubversionRepository.check_url(url, config)
587 elif 'git+http' in url[:8]: # git->hg import
592 elif 'git+http' in url[:8]: # git->hg import
588 raise NotImplementedError()
593 raise NotImplementedError()
589 else:
594 else:
590 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
595 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
591 'Allowed url must start with one of %s'
596 'Allowed url must start with one of %s'
592 % (url, ','.join(allowed_prefixes)))
597 % (url, ','.join(allowed_prefixes)))
593 exc.allowed_prefixes = allowed_prefixes
598 exc.allowed_prefixes = allowed_prefixes
594 raise exc
599 raise exc
595
600
596 elif repo_type == 'git':
601 elif repo_type == 'git':
597 allowed_prefixes = ('http', 'svn+http', 'hg+http')
602 allowed_prefixes = ('http', 'svn+http', 'hg+http')
598 if 'http' in url[:4]:
603 if 'http' in url[:4]:
599 # initially check if it's at least the proper URL
604 # initially check if it's at least the proper URL
600 # or does it pass basic auth
605 # or does it pass basic auth
601 GitRepository.check_url(url, config)
606 GitRepository.check_url(url, config)
602 elif 'svn+http' in url[:8]: # svn->git import
607 elif 'svn+http' in url[:8]: # svn->git import
603 raise NotImplementedError()
608 raise NotImplementedError()
604 elif 'hg+http' in url[:8]: # hg->git import
609 elif 'hg+http' in url[:8]: # hg->git import
605 raise NotImplementedError()
610 raise NotImplementedError()
606 else:
611 else:
607 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
612 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
608 'Allowed url must start with one of %s'
613 'Allowed url must start with one of %s'
609 % (url, ','.join(allowed_prefixes)))
614 % (url, ','.join(allowed_prefixes)))
610 exc.allowed_prefixes = allowed_prefixes
615 exc.allowed_prefixes = allowed_prefixes
611 raise exc
616 raise exc
612
617
613 class _validator(formencode.validators.FancyValidator):
618 class _validator(formencode.validators.FancyValidator):
614 messages = {
619 messages = {
615 'clone_uri': _(u'invalid clone url for %(rtype)s repository'),
620 'clone_uri': _(u'invalid clone url for %(rtype)s repository'),
616 'invalid_clone_uri': _(
621 'invalid_clone_uri': _(
617 u'Invalid clone url, provide a valid clone '
622 u'Invalid clone url, provide a valid clone '
618 u'url starting with one of %(allowed_prefixes)s')
623 u'url starting with one of %(allowed_prefixes)s')
619 }
624 }
620
625
621 def validate_python(self, value, state):
626 def validate_python(self, value, state):
622 repo_type = value.get('repo_type')
627 repo_type = value.get('repo_type')
623 url = value.get('clone_uri')
628 url = value.get('clone_uri')
624
629
625 if url:
630 if url:
626 try:
631 try:
627 url_handler(repo_type, url)
632 url_handler(repo_type, url)
628 except InvalidCloneUrl as e:
633 except InvalidCloneUrl as e:
629 log.warning(e)
634 log.warning(e)
630 msg = M(self, 'invalid_clone_uri', rtype=repo_type,
635 msg = M(self, 'invalid_clone_uri', rtype=repo_type,
631 allowed_prefixes=','.join(e.allowed_prefixes))
636 allowed_prefixes=','.join(e.allowed_prefixes))
632 raise formencode.Invalid(msg, value, state,
637 raise formencode.Invalid(msg, value, state,
633 error_dict={'clone_uri': msg})
638 error_dict={'clone_uri': msg})
634 except Exception:
639 except Exception:
635 log.exception('Url validation failed')
640 log.exception('Url validation failed')
636 msg = M(self, 'clone_uri', rtype=repo_type)
641 msg = M(self, 'clone_uri', rtype=repo_type)
637 raise formencode.Invalid(msg, value, state,
642 raise formencode.Invalid(msg, value, state,
638 error_dict={'clone_uri': msg})
643 error_dict={'clone_uri': msg})
639 return _validator
644 return _validator
640
645
641
646
642 def ValidForkType(old_data={}):
647 def ValidForkType(old_data={}):
643 class _validator(formencode.validators.FancyValidator):
648 class _validator(formencode.validators.FancyValidator):
644 messages = {
649 messages = {
645 'invalid_fork_type': _(u'Fork have to be the same type as parent')
650 'invalid_fork_type': _(u'Fork have to be the same type as parent')
646 }
651 }
647
652
648 def validate_python(self, value, state):
653 def validate_python(self, value, state):
649 if old_data['repo_type'] != value:
654 if old_data['repo_type'] != value:
650 msg = M(self, 'invalid_fork_type', state)
655 msg = M(self, 'invalid_fork_type', state)
651 raise formencode.Invalid(
656 raise formencode.Invalid(
652 msg, value, state, error_dict={'repo_type': msg}
657 msg, value, state, error_dict={'repo_type': msg}
653 )
658 )
654 return _validator
659 return _validator
655
660
656
661
657 def CanWriteGroup(old_data=None):
662 def CanWriteGroup(old_data=None):
658 class _validator(formencode.validators.FancyValidator):
663 class _validator(formencode.validators.FancyValidator):
659 messages = {
664 messages = {
660 'permission_denied': _(
665 'permission_denied': _(
661 u"You do not have the permission "
666 u"You do not have the permission "
662 u"to create repositories in this group."),
667 u"to create repositories in this group."),
663 'permission_denied_root': _(
668 'permission_denied_root': _(
664 u"You do not have the permission to store repositories in "
669 u"You do not have the permission to store repositories in "
665 u"the root location.")
670 u"the root location.")
666 }
671 }
667
672
668 def _to_python(self, value, state):
673 def _to_python(self, value, state):
669 # root location
674 # root location
670 if value in [-1, "-1"]:
675 if value in [-1, "-1"]:
671 return None
676 return None
672 return value
677 return value
673
678
674 def validate_python(self, value, state):
679 def validate_python(self, value, state):
675 gr = RepoGroup.get(value)
680 gr = RepoGroup.get(value)
676 gr_name = gr.group_name if gr else None # None means ROOT location
681 gr_name = gr.group_name if gr else None # None means ROOT location
677 # create repositories with write permission on group is set to true
682 # create repositories with write permission on group is set to true
678 create_on_write = HasPermissionAny(
683 create_on_write = HasPermissionAny(
679 'hg.create.write_on_repogroup.true')()
684 'hg.create.write_on_repogroup.true')()
680 group_admin = HasRepoGroupPermissionAny('group.admin')(
685 group_admin = HasRepoGroupPermissionAny('group.admin')(
681 gr_name, 'can write into group validator')
686 gr_name, 'can write into group validator')
682 group_write = HasRepoGroupPermissionAny('group.write')(
687 group_write = HasRepoGroupPermissionAny('group.write')(
683 gr_name, 'can write into group validator')
688 gr_name, 'can write into group validator')
684 forbidden = not (group_admin or (group_write and create_on_write))
689 forbidden = not (group_admin or (group_write and create_on_write))
685 can_create_repos = HasPermissionAny(
690 can_create_repos = HasPermissionAny(
686 'hg.admin', 'hg.create.repository')
691 'hg.admin', 'hg.create.repository')
687 gid = (old_data['repo_group'].get('group_id')
692 gid = (old_data['repo_group'].get('group_id')
688 if (old_data and 'repo_group' in old_data) else None)
693 if (old_data and 'repo_group' in old_data) else None)
689 value_changed = gid != safe_int(value)
694 value_changed = gid != safe_int(value)
690 new = not old_data
695 new = not old_data
691 # do check if we changed the value, there's a case that someone got
696 # do check if we changed the value, there's a case that someone got
692 # revoked write permissions to a repository, he still created, we
697 # revoked write permissions to a repository, he still created, we
693 # don't need to check permission if he didn't change the value of
698 # don't need to check permission if he didn't change the value of
694 # groups in form box
699 # groups in form box
695 if value_changed or new:
700 if value_changed or new:
696 # parent group need to be existing
701 # parent group need to be existing
697 if gr and forbidden:
702 if gr and forbidden:
698 msg = M(self, 'permission_denied', state)
703 msg = M(self, 'permission_denied', state)
699 raise formencode.Invalid(
704 raise formencode.Invalid(
700 msg, value, state, error_dict={'repo_type': msg}
705 msg, value, state, error_dict={'repo_type': msg}
701 )
706 )
702 # check if we can write to root location !
707 # check if we can write to root location !
703 elif gr is None and not can_create_repos():
708 elif gr is None and not can_create_repos():
704 msg = M(self, 'permission_denied_root', state)
709 msg = M(self, 'permission_denied_root', state)
705 raise formencode.Invalid(
710 raise formencode.Invalid(
706 msg, value, state, error_dict={'repo_type': msg}
711 msg, value, state, error_dict={'repo_type': msg}
707 )
712 )
708
713
709 return _validator
714 return _validator
710
715
711
716
712 def ValidPerms(type_='repo'):
717 def ValidPerms(type_='repo'):
713 if type_ == 'repo_group':
718 if type_ == 'repo_group':
714 EMPTY_PERM = 'group.none'
719 EMPTY_PERM = 'group.none'
715 elif type_ == 'repo':
720 elif type_ == 'repo':
716 EMPTY_PERM = 'repository.none'
721 EMPTY_PERM = 'repository.none'
717 elif type_ == 'user_group':
722 elif type_ == 'user_group':
718 EMPTY_PERM = 'usergroup.none'
723 EMPTY_PERM = 'usergroup.none'
719
724
720 class _validator(formencode.validators.FancyValidator):
725 class _validator(formencode.validators.FancyValidator):
721 messages = {
726 messages = {
722 'perm_new_member_name':
727 'perm_new_member_name':
723 _(u'This username or user group name is not valid')
728 _(u'This username or user group name is not valid')
724 }
729 }
725
730
726 def _to_python(self, value, state):
731 def _to_python(self, value, state):
727 perm_updates = OrderedSet()
732 perm_updates = OrderedSet()
728 perm_additions = OrderedSet()
733 perm_additions = OrderedSet()
729 perm_deletions = OrderedSet()
734 perm_deletions = OrderedSet()
730 # build a list of permission to update/delete and new permission
735 # build a list of permission to update/delete and new permission
731
736
732 # Read the perm_new_member/perm_del_member attributes and group
737 # Read the perm_new_member/perm_del_member attributes and group
733 # them by they IDs
738 # them by they IDs
734 new_perms_group = defaultdict(dict)
739 new_perms_group = defaultdict(dict)
735 del_perms_group = defaultdict(dict)
740 del_perms_group = defaultdict(dict)
736 for k, v in value.copy().iteritems():
741 for k, v in value.copy().iteritems():
737 if k.startswith('perm_del_member'):
742 if k.startswith('perm_del_member'):
738 # delete from org storage so we don't process that later
743 # delete from org storage so we don't process that later
739 del value[k]
744 del value[k]
740 # part is `id`, `type`
745 # part is `id`, `type`
741 _type, part = k.split('perm_del_member_')
746 _type, part = k.split('perm_del_member_')
742 args = part.split('_')
747 args = part.split('_')
743 if len(args) == 2:
748 if len(args) == 2:
744 _key, pos = args
749 _key, pos = args
745 del_perms_group[pos][_key] = v
750 del_perms_group[pos][_key] = v
746 if k.startswith('perm_new_member'):
751 if k.startswith('perm_new_member'):
747 # delete from org storage so we don't process that later
752 # delete from org storage so we don't process that later
748 del value[k]
753 del value[k]
749 # part is `id`, `type`, `perm`
754 # part is `id`, `type`, `perm`
750 _type, part = k.split('perm_new_member_')
755 _type, part = k.split('perm_new_member_')
751 args = part.split('_')
756 args = part.split('_')
752 if len(args) == 2:
757 if len(args) == 2:
753 _key, pos = args
758 _key, pos = args
754 new_perms_group[pos][_key] = v
759 new_perms_group[pos][_key] = v
755
760
756 # store the deletes
761 # store the deletes
757 for k in sorted(del_perms_group.keys()):
762 for k in sorted(del_perms_group.keys()):
758 perm_dict = del_perms_group[k]
763 perm_dict = del_perms_group[k]
759 del_member = perm_dict.get('id')
764 del_member = perm_dict.get('id')
760 del_type = perm_dict.get('type')
765 del_type = perm_dict.get('type')
761 if del_member and del_type:
766 if del_member and del_type:
762 perm_deletions.add((del_member, None, del_type))
767 perm_deletions.add((del_member, None, del_type))
763
768
764 # store additions in order of how they were added in web form
769 # store additions in order of how they were added in web form
765 for k in sorted(new_perms_group.keys()):
770 for k in sorted(new_perms_group.keys()):
766 perm_dict = new_perms_group[k]
771 perm_dict = new_perms_group[k]
767 new_member = perm_dict.get('id')
772 new_member = perm_dict.get('id')
768 new_type = perm_dict.get('type')
773 new_type = perm_dict.get('type')
769 new_perm = perm_dict.get('perm')
774 new_perm = perm_dict.get('perm')
770 if new_member and new_perm and new_type:
775 if new_member and new_perm and new_type:
771 perm_additions.add((new_member, new_perm, new_type))
776 perm_additions.add((new_member, new_perm, new_type))
772
777
773 # get updates of permissions
778 # get updates of permissions
774 # (read the existing radio button states)
779 # (read the existing radio button states)
775 for k, update_value in value.iteritems():
780 for k, update_value in value.iteritems():
776 if k.startswith('u_perm_') or k.startswith('g_perm_'):
781 if k.startswith('u_perm_') or k.startswith('g_perm_'):
777 member = k[7:]
782 member = k[7:]
778 update_type = {'u': 'user',
783 update_type = {'u': 'user',
779 'g': 'users_group'}[k[0]]
784 'g': 'users_group'}[k[0]]
780 if member == User.DEFAULT_USER:
785 if member == User.DEFAULT_USER:
781 if str2bool(value.get('repo_private')):
786 if str2bool(value.get('repo_private')):
782 # set none for default when updating to
787 # set none for default when updating to
783 # private repo protects agains form manipulation
788 # private repo protects agains form manipulation
784 update_value = EMPTY_PERM
789 update_value = EMPTY_PERM
785 perm_updates.add((member, update_value, update_type))
790 perm_updates.add((member, update_value, update_type))
786 # check the deletes
791 # check the deletes
787
792
788 value['perm_additions'] = list(perm_additions)
793 value['perm_additions'] = list(perm_additions)
789 value['perm_updates'] = list(perm_updates)
794 value['perm_updates'] = list(perm_updates)
790 value['perm_deletions'] = list(perm_deletions)
795 value['perm_deletions'] = list(perm_deletions)
791
796
792 # validate users they exist and they are active !
797 # validate users they exist and they are active !
793 for member_id, _perm, member_type in perm_additions:
798 for member_id, _perm, member_type in perm_additions:
794 try:
799 try:
795 if member_type == 'user':
800 if member_type == 'user':
796 self.user_db = User.query()\
801 self.user_db = User.query()\
797 .filter(User.active == true())\
802 .filter(User.active == true())\
798 .filter(User.user_id == member_id).one()
803 .filter(User.user_id == member_id).one()
799 if member_type == 'users_group':
804 if member_type == 'users_group':
800 self.user_db = UserGroup.query()\
805 self.user_db = UserGroup.query()\
801 .filter(UserGroup.users_group_active == true())\
806 .filter(UserGroup.users_group_active == true())\
802 .filter(UserGroup.users_group_id == member_id)\
807 .filter(UserGroup.users_group_id == member_id)\
803 .one()
808 .one()
804
809
805 except Exception:
810 except Exception:
806 log.exception('Updated permission failed: org_exc:')
811 log.exception('Updated permission failed: org_exc:')
807 msg = M(self, 'perm_new_member_type', state)
812 msg = M(self, 'perm_new_member_type', state)
808 raise formencode.Invalid(
813 raise formencode.Invalid(
809 msg, value, state, error_dict={
814 msg, value, state, error_dict={
810 'perm_new_member_name': msg}
815 'perm_new_member_name': msg}
811 )
816 )
812 return value
817 return value
813 return _validator
818 return _validator
814
819
815
820
816 def ValidSettings():
821 def ValidSettings():
817 class _validator(formencode.validators.FancyValidator):
822 class _validator(formencode.validators.FancyValidator):
818 def _to_python(self, value, state):
823 def _to_python(self, value, state):
819 # settings form for users that are not admin
824 # settings form for users that are not admin
820 # can't edit certain parameters, it's extra backup if they mangle
825 # can't edit certain parameters, it's extra backup if they mangle
821 # with forms
826 # with forms
822
827
823 forbidden_params = [
828 forbidden_params = [
824 'user', 'repo_type', 'repo_enable_locking',
829 'user', 'repo_type', 'repo_enable_locking',
825 'repo_enable_downloads', 'repo_enable_statistics'
830 'repo_enable_downloads', 'repo_enable_statistics'
826 ]
831 ]
827
832
828 for param in forbidden_params:
833 for param in forbidden_params:
829 if param in value:
834 if param in value:
830 del value[param]
835 del value[param]
831 return value
836 return value
832
837
833 def validate_python(self, value, state):
838 def validate_python(self, value, state):
834 pass
839 pass
835 return _validator
840 return _validator
836
841
837
842
838 def ValidPath():
843 def ValidPath():
839 class _validator(formencode.validators.FancyValidator):
844 class _validator(formencode.validators.FancyValidator):
840 messages = {
845 messages = {
841 'invalid_path': _(u'This is not a valid path')
846 'invalid_path': _(u'This is not a valid path')
842 }
847 }
843
848
844 def validate_python(self, value, state):
849 def validate_python(self, value, state):
845 if not os.path.isdir(value):
850 if not os.path.isdir(value):
846 msg = M(self, 'invalid_path', state)
851 msg = M(self, 'invalid_path', state)
847 raise formencode.Invalid(
852 raise formencode.Invalid(
848 msg, value, state, error_dict={'paths_root_path': msg}
853 msg, value, state, error_dict={'paths_root_path': msg}
849 )
854 )
850 return _validator
855 return _validator
851
856
852
857
853 def UniqSystemEmail(old_data={}):
858 def UniqSystemEmail(old_data={}):
854 class _validator(formencode.validators.FancyValidator):
859 class _validator(formencode.validators.FancyValidator):
855 messages = {
860 messages = {
856 'email_taken': _(u'This e-mail address is already taken')
861 'email_taken': _(u'This e-mail address is already taken')
857 }
862 }
858
863
859 def _to_python(self, value, state):
864 def _to_python(self, value, state):
860 return value.lower()
865 return value.lower()
861
866
862 def validate_python(self, value, state):
867 def validate_python(self, value, state):
863 if (old_data.get('email') or '').lower() != value:
868 if (old_data.get('email') or '').lower() != value:
864 user = User.get_by_email(value, case_insensitive=True)
869 user = User.get_by_email(value, case_insensitive=True)
865 if user:
870 if user:
866 msg = M(self, 'email_taken', state)
871 msg = M(self, 'email_taken', state)
867 raise formencode.Invalid(
872 raise formencode.Invalid(
868 msg, value, state, error_dict={'email': msg}
873 msg, value, state, error_dict={'email': msg}
869 )
874 )
870 return _validator
875 return _validator
871
876
872
877
873 def ValidSystemEmail():
878 def ValidSystemEmail():
874 class _validator(formencode.validators.FancyValidator):
879 class _validator(formencode.validators.FancyValidator):
875 messages = {
880 messages = {
876 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
881 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
877 }
882 }
878
883
879 def _to_python(self, value, state):
884 def _to_python(self, value, state):
880 return value.lower()
885 return value.lower()
881
886
882 def validate_python(self, value, state):
887 def validate_python(self, value, state):
883 user = User.get_by_email(value, case_insensitive=True)
888 user = User.get_by_email(value, case_insensitive=True)
884 if user is None:
889 if user is None:
885 msg = M(self, 'non_existing_email', state, email=value)
890 msg = M(self, 'non_existing_email', state, email=value)
886 raise formencode.Invalid(
891 raise formencode.Invalid(
887 msg, value, state, error_dict={'email': msg}
892 msg, value, state, error_dict={'email': msg}
888 )
893 )
889
894
890 return _validator
895 return _validator
891
896
892
897
893 def NotReviewedRevisions(repo_id):
898 def NotReviewedRevisions(repo_id):
894 class _validator(formencode.validators.FancyValidator):
899 class _validator(formencode.validators.FancyValidator):
895 messages = {
900 messages = {
896 'rev_already_reviewed':
901 'rev_already_reviewed':
897 _(u'Revisions %(revs)s are already part of pull request '
902 _(u'Revisions %(revs)s are already part of pull request '
898 u'or have set status'),
903 u'or have set status'),
899 }
904 }
900
905
901 def validate_python(self, value, state):
906 def validate_python(self, value, state):
902 # check revisions if they are not reviewed, or a part of another
907 # check revisions if they are not reviewed, or a part of another
903 # pull request
908 # pull request
904 statuses = ChangesetStatus.query()\
909 statuses = ChangesetStatus.query()\
905 .filter(ChangesetStatus.revision.in_(value))\
910 .filter(ChangesetStatus.revision.in_(value))\
906 .filter(ChangesetStatus.repo_id == repo_id)\
911 .filter(ChangesetStatus.repo_id == repo_id)\
907 .all()
912 .all()
908
913
909 errors = []
914 errors = []
910 for status in statuses:
915 for status in statuses:
911 if status.pull_request_id:
916 if status.pull_request_id:
912 errors.append(['pull_req', status.revision[:12]])
917 errors.append(['pull_req', status.revision[:12]])
913 elif status.status:
918 elif status.status:
914 errors.append(['status', status.revision[:12]])
919 errors.append(['status', status.revision[:12]])
915
920
916 if errors:
921 if errors:
917 revs = ','.join([x[1] for x in errors])
922 revs = ','.join([x[1] for x in errors])
918 msg = M(self, 'rev_already_reviewed', state, revs=revs)
923 msg = M(self, 'rev_already_reviewed', state, revs=revs)
919 raise formencode.Invalid(
924 raise formencode.Invalid(
920 msg, value, state, error_dict={'revisions': revs})
925 msg, value, state, error_dict={'revisions': revs})
921
926
922 return _validator
927 return _validator
923
928
924
929
925 def ValidIp():
930 def ValidIp():
926 class _validator(CIDR):
931 class _validator(CIDR):
927 messages = {
932 messages = {
928 'badFormat': _(u'Please enter a valid IPv4 or IpV6 address'),
933 'badFormat': _(u'Please enter a valid IPv4 or IpV6 address'),
929 'illegalBits': _(
934 'illegalBits': _(
930 u'The network size (bits) must be within the range '
935 u'The network size (bits) must be within the range '
931 u'of 0-32 (not %(bits)r)'),
936 u'of 0-32 (not %(bits)r)'),
932 }
937 }
933
938
934 # we ovveride the default to_python() call
939 # we ovveride the default to_python() call
935 def to_python(self, value, state):
940 def to_python(self, value, state):
936 v = super(_validator, self).to_python(value, state)
941 v = super(_validator, self).to_python(value, state)
937 v = v.strip()
942 v = v.strip()
938 net = ipaddress.ip_network(address=v, strict=False)
943 net = ipaddress.ip_network(address=v, strict=False)
939 return str(net)
944 return str(net)
940
945
941 def validate_python(self, value, state):
946 def validate_python(self, value, state):
942 try:
947 try:
943 addr = value.strip()
948 addr = value.strip()
944 # this raises an ValueError if address is not IpV4 or IpV6
949 # this raises an ValueError if address is not IpV4 or IpV6
945 ipaddress.ip_network(addr, strict=False)
950 ipaddress.ip_network(addr, strict=False)
946 except ValueError:
951 except ValueError:
947 raise formencode.Invalid(self.message('badFormat', state),
952 raise formencode.Invalid(self.message('badFormat', state),
948 value, state)
953 value, state)
949
954
950 return _validator
955 return _validator
951
956
952
957
953 def FieldKey():
958 def FieldKey():
954 class _validator(formencode.validators.FancyValidator):
959 class _validator(formencode.validators.FancyValidator):
955 messages = {
960 messages = {
956 'badFormat': _(
961 'badFormat': _(
957 u'Key name can only consist of letters, '
962 u'Key name can only consist of letters, '
958 u'underscore, dash or numbers'),
963 u'underscore, dash or numbers'),
959 }
964 }
960
965
961 def validate_python(self, value, state):
966 def validate_python(self, value, state):
962 if not re.match('[a-zA-Z0-9_-]+$', value):
967 if not re.match('[a-zA-Z0-9_-]+$', value):
963 raise formencode.Invalid(self.message('badFormat', state),
968 raise formencode.Invalid(self.message('badFormat', state),
964 value, state)
969 value, state)
965 return _validator
970 return _validator
966
971
967
972
968 def BasePath():
973 def BasePath():
969 class _validator(formencode.validators.FancyValidator):
974 class _validator(formencode.validators.FancyValidator):
970 messages = {
975 messages = {
971 'badPath': _(u'Filename cannot be inside a directory'),
976 'badPath': _(u'Filename cannot be inside a directory'),
972 }
977 }
973
978
974 def _to_python(self, value, state):
979 def _to_python(self, value, state):
975 return value
980 return value
976
981
977 def validate_python(self, value, state):
982 def validate_python(self, value, state):
978 if value != os.path.basename(value):
983 if value != os.path.basename(value):
979 raise formencode.Invalid(self.message('badPath', state),
984 raise formencode.Invalid(self.message('badPath', state),
980 value, state)
985 value, state)
981 return _validator
986 return _validator
982
987
983
988
984 def ValidAuthPlugins():
989 def ValidAuthPlugins():
985 class _validator(formencode.validators.FancyValidator):
990 class _validator(formencode.validators.FancyValidator):
986 messages = {
991 messages = {
987 'import_duplicate': _(
992 'import_duplicate': _(
988 u'Plugins %(loaded)s and %(next_to_load)s '
993 u'Plugins %(loaded)s and %(next_to_load)s '
989 u'both export the same name'),
994 u'both export the same name'),
990 'missing_includeme': _(
995 'missing_includeme': _(
991 u'The plugin "%(plugin_id)s" is missing an includeme '
996 u'The plugin "%(plugin_id)s" is missing an includeme '
992 u'function.'),
997 u'function.'),
993 'import_error': _(
998 'import_error': _(
994 u'Can not load plugin "%(plugin_id)s"'),
999 u'Can not load plugin "%(plugin_id)s"'),
995 'no_plugin': _(
1000 'no_plugin': _(
996 u'No plugin available with ID "%(plugin_id)s"'),
1001 u'No plugin available with ID "%(plugin_id)s"'),
997 }
1002 }
998
1003
999 def _to_python(self, value, state):
1004 def _to_python(self, value, state):
1000 # filter empty values
1005 # filter empty values
1001 return filter(lambda s: s not in [None, ''], value)
1006 return filter(lambda s: s not in [None, ''], value)
1002
1007
1003 def _validate_legacy_plugin_id(self, plugin_id, value, state):
1008 def _validate_legacy_plugin_id(self, plugin_id, value, state):
1004 """
1009 """
1005 Validates that the plugin import works. It also checks that the
1010 Validates that the plugin import works. It also checks that the
1006 plugin has an includeme attribute.
1011 plugin has an includeme attribute.
1007 """
1012 """
1008 try:
1013 try:
1009 plugin = _import_legacy_plugin(plugin_id)
1014 plugin = _import_legacy_plugin(plugin_id)
1010 except Exception as e:
1015 except Exception as e:
1011 log.exception(
1016 log.exception(
1012 'Exception during import of auth legacy plugin "{}"'
1017 'Exception during import of auth legacy plugin "{}"'
1013 .format(plugin_id))
1018 .format(plugin_id))
1014 msg = M(self, 'import_error', plugin_id=plugin_id)
1019 msg = M(self, 'import_error', plugin_id=plugin_id)
1015 raise formencode.Invalid(msg, value, state)
1020 raise formencode.Invalid(msg, value, state)
1016
1021
1017 if not hasattr(plugin, 'includeme'):
1022 if not hasattr(plugin, 'includeme'):
1018 msg = M(self, 'missing_includeme', plugin_id=plugin_id)
1023 msg = M(self, 'missing_includeme', plugin_id=plugin_id)
1019 raise formencode.Invalid(msg, value, state)
1024 raise formencode.Invalid(msg, value, state)
1020
1025
1021 return plugin
1026 return plugin
1022
1027
1023 def _validate_plugin_id(self, plugin_id, value, state):
1028 def _validate_plugin_id(self, plugin_id, value, state):
1024 """
1029 """
1025 Plugins are already imported during app start up. Therefore this
1030 Plugins are already imported during app start up. Therefore this
1026 validation only retrieves the plugin from the plugin registry and
1031 validation only retrieves the plugin from the plugin registry and
1027 if it returns something not None everything is OK.
1032 if it returns something not None everything is OK.
1028 """
1033 """
1029 plugin = loadplugin(plugin_id)
1034 plugin = loadplugin(plugin_id)
1030
1035
1031 if plugin is None:
1036 if plugin is None:
1032 msg = M(self, 'no_plugin', plugin_id=plugin_id)
1037 msg = M(self, 'no_plugin', plugin_id=plugin_id)
1033 raise formencode.Invalid(msg, value, state)
1038 raise formencode.Invalid(msg, value, state)
1034
1039
1035 return plugin
1040 return plugin
1036
1041
1037 def validate_python(self, value, state):
1042 def validate_python(self, value, state):
1038 unique_names = {}
1043 unique_names = {}
1039 for plugin_id in value:
1044 for plugin_id in value:
1040
1045
1041 # Validate legacy or normal plugin.
1046 # Validate legacy or normal plugin.
1042 if plugin_id.startswith(legacy_plugin_prefix):
1047 if plugin_id.startswith(legacy_plugin_prefix):
1043 plugin = self._validate_legacy_plugin_id(
1048 plugin = self._validate_legacy_plugin_id(
1044 plugin_id, value, state)
1049 plugin_id, value, state)
1045 else:
1050 else:
1046 plugin = self._validate_plugin_id(plugin_id, value, state)
1051 plugin = self._validate_plugin_id(plugin_id, value, state)
1047
1052
1048 # Only allow unique plugin names.
1053 # Only allow unique plugin names.
1049 if plugin.name in unique_names:
1054 if plugin.name in unique_names:
1050 msg = M(self, 'import_duplicate', state,
1055 msg = M(self, 'import_duplicate', state,
1051 loaded=unique_names[plugin.name],
1056 loaded=unique_names[plugin.name],
1052 next_to_load=plugin)
1057 next_to_load=plugin)
1053 raise formencode.Invalid(msg, value, state)
1058 raise formencode.Invalid(msg, value, state)
1054 unique_names[plugin.name] = plugin
1059 unique_names[plugin.name] = plugin
1055
1060
1056 return _validator
1061 return _validator
1057
1062
1058
1063
1059 def UniqGistId():
1064 def UniqGistId():
1060 class _validator(formencode.validators.FancyValidator):
1065 class _validator(formencode.validators.FancyValidator):
1061 messages = {
1066 messages = {
1062 'gistid_taken': _(u'This gistid is already in use')
1067 'gistid_taken': _(u'This gistid is already in use')
1063 }
1068 }
1064
1069
1065 def _to_python(self, value, state):
1070 def _to_python(self, value, state):
1066 return repo_name_slug(value.lower())
1071 return repo_name_slug(value.lower())
1067
1072
1068 def validate_python(self, value, state):
1073 def validate_python(self, value, state):
1069 existing = Gist.get_by_access_id(value)
1074 existing = Gist.get_by_access_id(value)
1070 if existing:
1075 if existing:
1071 msg = M(self, 'gistid_taken', state)
1076 msg = M(self, 'gistid_taken', state)
1072 raise formencode.Invalid(
1077 raise formencode.Invalid(
1073 msg, value, state, error_dict={'gistid': msg}
1078 msg, value, state, error_dict={'gistid': msg}
1074 )
1079 )
1075
1080
1076 return _validator
1081 return _validator
1077
1082
1078
1083
1079 def ValidPattern():
1084 def ValidPattern():
1080
1085
1081 class _Validator(formencode.validators.FancyValidator):
1086 class _Validator(formencode.validators.FancyValidator):
1082
1087
1083 def _to_python(self, value, state):
1088 def _to_python(self, value, state):
1084 patterns = []
1089 patterns = []
1085
1090
1086 prefix = 'new_pattern'
1091 prefix = 'new_pattern'
1087 for name, v in value.iteritems():
1092 for name, v in value.iteritems():
1088 pattern_name = '_'.join((prefix, 'pattern'))
1093 pattern_name = '_'.join((prefix, 'pattern'))
1089 if name.startswith(pattern_name):
1094 if name.startswith(pattern_name):
1090 new_item_id = name[len(pattern_name)+1:]
1095 new_item_id = name[len(pattern_name)+1:]
1091
1096
1092 def _field(name):
1097 def _field(name):
1093 return '%s_%s_%s' % (prefix, name, new_item_id)
1098 return '%s_%s_%s' % (prefix, name, new_item_id)
1094
1099
1095 values = {
1100 values = {
1096 'issuetracker_pat': value.get(_field('pattern')),
1101 'issuetracker_pat': value.get(_field('pattern')),
1097 'issuetracker_pat': value.get(_field('pattern')),
1102 'issuetracker_pat': value.get(_field('pattern')),
1098 'issuetracker_url': value.get(_field('url')),
1103 'issuetracker_url': value.get(_field('url')),
1099 'issuetracker_pref': value.get(_field('prefix')),
1104 'issuetracker_pref': value.get(_field('prefix')),
1100 'issuetracker_desc': value.get(_field('description'))
1105 'issuetracker_desc': value.get(_field('description'))
1101 }
1106 }
1102 new_uid = md5(values['issuetracker_pat'])
1107 new_uid = md5(values['issuetracker_pat'])
1103
1108
1104 has_required_fields = (
1109 has_required_fields = (
1105 values['issuetracker_pat']
1110 values['issuetracker_pat']
1106 and values['issuetracker_url'])
1111 and values['issuetracker_url'])
1107
1112
1108 if has_required_fields:
1113 if has_required_fields:
1109 settings = [
1114 settings = [
1110 ('_'.join((key, new_uid)), values[key], 'unicode')
1115 ('_'.join((key, new_uid)), values[key], 'unicode')
1111 for key in values]
1116 for key in values]
1112 patterns.append(settings)
1117 patterns.append(settings)
1113
1118
1114 value['patterns'] = patterns
1119 value['patterns'] = patterns
1115 delete_patterns = value.get('uid') or []
1120 delete_patterns = value.get('uid') or []
1116 if not isinstance(delete_patterns, (list, tuple)):
1121 if not isinstance(delete_patterns, (list, tuple)):
1117 delete_patterns = [delete_patterns]
1122 delete_patterns = [delete_patterns]
1118 value['delete_patterns'] = delete_patterns
1123 value['delete_patterns'] = delete_patterns
1119 return value
1124 return value
1120 return _Validator
1125 return _Validator
General Comments 0
You need to be logged in to leave comments. Login now