##// END OF EJS Templates
repo group: introduce editing of owner...
Mads Kiilerich -
r8734:abc29122 stable
parent child Browse files
Show More
@@ -1,402 +1,403 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
5 # (at your option) any later version.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU General Public License
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 """
14 """
15 kallithea.controllers.admin.repo_groups
15 kallithea.controllers.admin.repo_groups
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17
17
18 Repository groups controller for Kallithea
18 Repository groups controller for Kallithea
19
19
20 This file was forked by the Kallithea project in July 2014.
20 This file was forked by the Kallithea project in July 2014.
21 Original author and date, and relevant copyright and licensing information is below:
21 Original author and date, and relevant copyright and licensing information is below:
22 :created_on: Mar 23, 2010
22 :created_on: Mar 23, 2010
23 :author: marcink
23 :author: marcink
24 :copyright: (c) 2013 RhodeCode GmbH, and others.
24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 :license: GPLv3, see LICENSE.md for more details.
25 :license: GPLv3, see LICENSE.md for more details.
26 """
26 """
27
27
28 import logging
28 import logging
29 import traceback
29 import traceback
30
30
31 import formencode
31 import formencode
32 from formencode import htmlfill
32 from formencode import htmlfill
33 from tg import app_globals, request
33 from tg import app_globals, request
34 from tg import tmpl_context as c
34 from tg import tmpl_context as c
35 from tg.i18n import ugettext as _
35 from tg.i18n import ugettext as _
36 from tg.i18n import ungettext
36 from tg.i18n import ungettext
37 from webob.exc import HTTPForbidden, HTTPFound, HTTPInternalServerError, HTTPNotFound
37 from webob.exc import HTTPForbidden, HTTPFound, HTTPInternalServerError, HTTPNotFound
38
38
39 from kallithea.controllers import base
39 from kallithea.controllers import base
40 from kallithea.lib import webutils
40 from kallithea.lib import webutils
41 from kallithea.lib.auth import HasPermissionAny, HasRepoGroupPermissionLevel, HasRepoGroupPermissionLevelDecorator, LoginRequired
41 from kallithea.lib.auth import HasPermissionAny, HasRepoGroupPermissionLevel, HasRepoGroupPermissionLevelDecorator, LoginRequired
42 from kallithea.lib.utils2 import safe_int
42 from kallithea.lib.utils2 import safe_int
43 from kallithea.lib.webutils import url
43 from kallithea.lib.webutils import url
44 from kallithea.model import db, meta
44 from kallithea.model import db, meta
45 from kallithea.model.forms import RepoGroupForm, RepoGroupPermsForm
45 from kallithea.model.forms import RepoGroupForm, RepoGroupPermsForm
46 from kallithea.model.repo import RepoModel
46 from kallithea.model.repo import RepoModel
47 from kallithea.model.repo_group import RepoGroupModel
47 from kallithea.model.repo_group import RepoGroupModel
48 from kallithea.model.scm import AvailableRepoGroupChoices, RepoGroupList
48 from kallithea.model.scm import AvailableRepoGroupChoices, RepoGroupList
49
49
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53
53
54 class RepoGroupsController(base.BaseController):
54 class RepoGroupsController(base.BaseController):
55
55
56 @LoginRequired(allow_default_user=True)
56 @LoginRequired(allow_default_user=True)
57 def _before(self, *args, **kwargs):
57 def _before(self, *args, **kwargs):
58 super(RepoGroupsController, self)._before(*args, **kwargs)
58 super(RepoGroupsController, self)._before(*args, **kwargs)
59
59
60 def __load_defaults(self, extras=(), exclude=()):
60 def __load_defaults(self, extras=(), exclude=()):
61 """extras is used for keeping current parent ignoring permissions
61 """extras is used for keeping current parent ignoring permissions
62 exclude is used for not moving group to itself TODO: also exclude descendants
62 exclude is used for not moving group to itself TODO: also exclude descendants
63 Note: only admin can create top level groups
63 Note: only admin can create top level groups
64 """
64 """
65 repo_groups = AvailableRepoGroupChoices('admin', extras)
65 repo_groups = AvailableRepoGroupChoices('admin', extras)
66 exclude_group_ids = set(rg.group_id for rg in exclude)
66 exclude_group_ids = set(rg.group_id for rg in exclude)
67 c.repo_groups = [rg for rg in repo_groups
67 c.repo_groups = [rg for rg in repo_groups
68 if rg[0] not in exclude_group_ids]
68 if rg[0] not in exclude_group_ids]
69
69
70 def __load_data(self, group_id):
70 def __load_data(self, group_id):
71 """
71 """
72 Load defaults settings for edit, and update
72 Load defaults settings for edit, and update
73
73
74 :param group_id:
74 :param group_id:
75 """
75 """
76 repo_group = db.RepoGroup.get_or_404(group_id)
76 repo_group = db.RepoGroup.get_or_404(group_id)
77 data = repo_group.get_dict()
77 data = repo_group.get_dict()
78 data['group_name'] = repo_group.name
78 data['group_name'] = repo_group.name
79 data['owner'] = repo_group.owner.username
79
80
80 # fill repository group users
81 # fill repository group users
81 for p in repo_group.repo_group_to_perm:
82 for p in repo_group.repo_group_to_perm:
82 data.update({'u_perm_%s' % p.user.username:
83 data.update({'u_perm_%s' % p.user.username:
83 p.permission.permission_name})
84 p.permission.permission_name})
84
85
85 # fill repository group groups
86 # fill repository group groups
86 for p in repo_group.users_group_to_perm:
87 for p in repo_group.users_group_to_perm:
87 data.update({'g_perm_%s' % p.users_group.users_group_name:
88 data.update({'g_perm_%s' % p.users_group.users_group_name:
88 p.permission.permission_name})
89 p.permission.permission_name})
89
90
90 return data
91 return data
91
92
92 def _revoke_perms_on_yourself(self, form_result):
93 def _revoke_perms_on_yourself(self, form_result):
93 _up = [u for u in form_result['perms_updates'] if request.authuser.username == u[0]]
94 _up = [u for u in form_result['perms_updates'] if request.authuser.username == u[0]]
94 _new = [u for u in form_result['perms_new'] if request.authuser.username == u[0]]
95 _new = [u for u in form_result['perms_new'] if request.authuser.username == u[0]]
95 if _new and _new[0][1] != 'group.admin' or _up and _up[0][1] != 'group.admin':
96 if _new and _new[0][1] != 'group.admin' or _up and _up[0][1] != 'group.admin':
96 return True
97 return True
97 return False
98 return False
98
99
99 def index(self, format='html'):
100 def index(self, format='html'):
100 _list = db.RepoGroup.query(sorted=True).all()
101 _list = db.RepoGroup.query(sorted=True).all()
101 group_iter = RepoGroupList(_list, perm_level='admin')
102 group_iter = RepoGroupList(_list, perm_level='admin')
102 repo_groups_data = []
103 repo_groups_data = []
103 _tmpl_lookup = app_globals.mako_lookup
104 _tmpl_lookup = app_globals.mako_lookup
104 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
105 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
105
106
106 def repo_group_name(repo_group_name, children_groups):
107 def repo_group_name(repo_group_name, children_groups):
107 return template.get_def("repo_group_name") \
108 return template.get_def("repo_group_name") \
108 .render_unicode(repo_group_name, children_groups, _=_, webutils=webutils, c=c)
109 .render_unicode(repo_group_name, children_groups, _=_, webutils=webutils, c=c)
109
110
110 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
111 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
111 return template.get_def("repo_group_actions") \
112 return template.get_def("repo_group_actions") \
112 .render_unicode(repo_group_id, repo_group_name, gr_count, _=_, webutils=webutils, c=c,
113 .render_unicode(repo_group_id, repo_group_name, gr_count, _=_, webutils=webutils, c=c,
113 ungettext=ungettext)
114 ungettext=ungettext)
114
115
115 for repo_gr in group_iter:
116 for repo_gr in group_iter:
116 children_groups = [g.name for g in repo_gr.parents] + [repo_gr.name]
117 children_groups = [g.name for g in repo_gr.parents] + [repo_gr.name]
117 repo_count = repo_gr.repositories.count()
118 repo_count = repo_gr.repositories.count()
118 repo_groups_data.append({
119 repo_groups_data.append({
119 "raw_name": webutils.escape(repo_gr.group_name),
120 "raw_name": webutils.escape(repo_gr.group_name),
120 "group_name": repo_group_name(repo_gr.group_name, children_groups),
121 "group_name": repo_group_name(repo_gr.group_name, children_groups),
121 "desc": webutils.escape(repo_gr.group_description),
122 "desc": webutils.escape(repo_gr.group_description),
122 "repos": repo_count,
123 "repos": repo_count,
123 "owner": repo_gr.owner.username,
124 "owner": repo_gr.owner.username,
124 "action": repo_group_actions(repo_gr.group_id, repo_gr.group_name,
125 "action": repo_group_actions(repo_gr.group_id, repo_gr.group_name,
125 repo_count)
126 repo_count)
126 })
127 })
127
128
128 c.data = {
129 c.data = {
129 "sort": None,
130 "sort": None,
130 "dir": "asc",
131 "dir": "asc",
131 "records": repo_groups_data
132 "records": repo_groups_data
132 }
133 }
133
134
134 return base.render('admin/repo_groups/repo_groups.html')
135 return base.render('admin/repo_groups/repo_groups.html')
135
136
136 def create(self):
137 def create(self):
137 self.__load_defaults()
138 self.__load_defaults()
138
139
139 # permissions for can create group based on parent_id are checked
140 # permissions for can create group based on parent_id are checked
140 # here in the Form
141 # here in the Form
141 repo_group_form = RepoGroupForm(repo_groups=c.repo_groups)
142 repo_group_form = RepoGroupForm(repo_groups=c.repo_groups)
142 form_result = None
143 form_result = None
143 try:
144 try:
144 form_result = repo_group_form.to_python(dict(request.POST))
145 form_result = repo_group_form.to_python(dict(request.POST))
145 gr = RepoGroupModel().create(
146 gr = RepoGroupModel().create(
146 group_name=form_result['group_name'],
147 group_name=form_result['group_name'],
147 group_description=form_result['group_description'],
148 group_description=form_result['group_description'],
148 parent=form_result['parent_group_id'],
149 parent=form_result['parent_group_id'],
149 owner=request.authuser.user_id, # TODO: make editable
150 owner=request.authuser.user_id,
150 copy_permissions=form_result['group_copy_permissions']
151 copy_permissions=form_result['group_copy_permissions']
151 )
152 )
152 meta.Session().commit()
153 meta.Session().commit()
153 # TODO: in future action_logger(, '', '', '')
154 # TODO: in future action_logger(, '', '', '')
154 except formencode.Invalid as errors:
155 except formencode.Invalid as errors:
155 return htmlfill.render(
156 return htmlfill.render(
156 base.render('admin/repo_groups/repo_group_add.html'),
157 base.render('admin/repo_groups/repo_group_add.html'),
157 defaults=errors.value,
158 defaults=errors.value,
158 errors=errors.error_dict or {},
159 errors=errors.error_dict or {},
159 prefix_error=False,
160 prefix_error=False,
160 encoding="UTF-8",
161 encoding="UTF-8",
161 force_defaults=False)
162 force_defaults=False)
162 except Exception:
163 except Exception:
163 log.error(traceback.format_exc())
164 log.error(traceback.format_exc())
164 webutils.flash(_('Error occurred during creation of repository group %s')
165 webutils.flash(_('Error occurred during creation of repository group %s')
165 % request.POST.get('group_name'), category='error')
166 % request.POST.get('group_name'), category='error')
166 if form_result is None:
167 if form_result is None:
167 raise
168 raise
168 parent_group_id = form_result['parent_group_id']
169 parent_group_id = form_result['parent_group_id']
169 # TODO: maybe we should get back to the main view, not the admin one
170 # TODO: maybe we should get back to the main view, not the admin one
170 raise HTTPFound(location=url('repos_groups', parent_group=parent_group_id))
171 raise HTTPFound(location=url('repos_groups', parent_group=parent_group_id))
171 webutils.flash(_('Created repository group %s') % gr.group_name,
172 webutils.flash(_('Created repository group %s') % gr.group_name,
172 category='success')
173 category='success')
173 raise HTTPFound(location=url('repos_group_home', group_name=gr.group_name))
174 raise HTTPFound(location=url('repos_group_home', group_name=gr.group_name))
174
175
175 def new(self):
176 def new(self):
176 parent_group_id = safe_int(request.GET.get('parent_group') or '-1')
177 parent_group_id = safe_int(request.GET.get('parent_group') or '-1')
177 if HasPermissionAny('hg.admin')('group create'):
178 if HasPermissionAny('hg.admin')('group create'):
178 # we're global admin, we're ok and we can create TOP level groups
179 # we're global admin, we're ok and we can create TOP level groups
179 pass
180 pass
180 else:
181 else:
181 # we pass in parent group into creation form, thus we know
182 # we pass in parent group into creation form, thus we know
182 # what would be the group, we can check perms here !
183 # what would be the group, we can check perms here !
183 group = db.RepoGroup.get(parent_group_id) if parent_group_id else None
184 group = db.RepoGroup.get(parent_group_id) if parent_group_id else None
184 group_name = group.group_name if group else None
185 group_name = group.group_name if group else None
185 if HasRepoGroupPermissionLevel('admin')(group_name, 'group create'):
186 if HasRepoGroupPermissionLevel('admin')(group_name, 'group create'):
186 pass
187 pass
187 else:
188 else:
188 raise HTTPForbidden()
189 raise HTTPForbidden()
189
190
190 self.__load_defaults()
191 self.__load_defaults()
191 return htmlfill.render(
192 return htmlfill.render(
192 base.render('admin/repo_groups/repo_group_add.html'),
193 base.render('admin/repo_groups/repo_group_add.html'),
193 defaults={'parent_group_id': parent_group_id},
194 defaults={'parent_group_id': parent_group_id},
194 errors={},
195 errors={},
195 prefix_error=False,
196 prefix_error=False,
196 encoding="UTF-8",
197 encoding="UTF-8",
197 force_defaults=False)
198 force_defaults=False)
198
199
199 @HasRepoGroupPermissionLevelDecorator('admin')
200 @HasRepoGroupPermissionLevelDecorator('admin')
200 def update(self, group_name):
201 def update(self, group_name):
201 c.repo_group = db.RepoGroup.guess_instance(group_name)
202 c.repo_group = db.RepoGroup.guess_instance(group_name)
202 self.__load_defaults(extras=[c.repo_group.parent_group],
203 self.__load_defaults(extras=[c.repo_group.parent_group],
203 exclude=[c.repo_group])
204 exclude=[c.repo_group])
204
205
205 # TODO: kill allow_empty_group - it is only used for redundant form validation!
206 # TODO: kill allow_empty_group - it is only used for redundant form validation!
206 if HasPermissionAny('hg.admin')('group edit'):
207 if HasPermissionAny('hg.admin')('group edit'):
207 # we're global admin, we're ok and we can create TOP level groups
208 # we're global admin, we're ok and we can create TOP level groups
208 allow_empty_group = True
209 allow_empty_group = True
209 elif not c.repo_group.parent_group:
210 elif not c.repo_group.parent_group:
210 allow_empty_group = True
211 allow_empty_group = True
211 else:
212 else:
212 allow_empty_group = False
213 allow_empty_group = False
213 repo_group_form = RepoGroupForm(
214 repo_group_form = RepoGroupForm(
214 edit=True,
215 edit=True,
215 old_data=c.repo_group.get_dict(),
216 old_data=c.repo_group.get_dict(),
216 repo_groups=c.repo_groups,
217 repo_groups=c.repo_groups,
217 can_create_in_root=allow_empty_group,
218 can_create_in_root=allow_empty_group,
218 )()
219 )()
219 try:
220 try:
220 form_result = repo_group_form.to_python(dict(request.POST))
221 form_result = repo_group_form.to_python(dict(request.POST))
221
222
222 new_gr = RepoGroupModel().update(group_name, form_result)
223 new_gr = RepoGroupModel().update(group_name, form_result)
223 meta.Session().commit()
224 meta.Session().commit()
224 webutils.flash(_('Updated repository group %s')
225 webutils.flash(_('Updated repository group %s')
225 % form_result['group_name'], category='success')
226 % form_result['group_name'], category='success')
226 # we now have new name !
227 # we now have new name !
227 group_name = new_gr.group_name
228 group_name = new_gr.group_name
228 # TODO: in future action_logger(, '', '', '')
229 # TODO: in future action_logger(, '', '', '')
229 except formencode.Invalid as errors:
230 except formencode.Invalid as errors:
230 c.active = 'settings'
231 c.active = 'settings'
231 return htmlfill.render(
232 return htmlfill.render(
232 base.render('admin/repo_groups/repo_group_edit.html'),
233 base.render('admin/repo_groups/repo_group_edit.html'),
233 defaults=errors.value,
234 defaults=errors.value,
234 errors=errors.error_dict or {},
235 errors=errors.error_dict or {},
235 prefix_error=False,
236 prefix_error=False,
236 encoding="UTF-8",
237 encoding="UTF-8",
237 force_defaults=False)
238 force_defaults=False)
238 except Exception:
239 except Exception:
239 log.error(traceback.format_exc())
240 log.error(traceback.format_exc())
240 webutils.flash(_('Error occurred during update of repository group %s')
241 webutils.flash(_('Error occurred during update of repository group %s')
241 % request.POST.get('group_name'), category='error')
242 % request.POST.get('group_name'), category='error')
242
243
243 raise HTTPFound(location=url('edit_repo_group', group_name=group_name))
244 raise HTTPFound(location=url('edit_repo_group', group_name=group_name))
244
245
245 @HasRepoGroupPermissionLevelDecorator('admin')
246 @HasRepoGroupPermissionLevelDecorator('admin')
246 def delete(self, group_name):
247 def delete(self, group_name):
247 gr = c.repo_group = db.RepoGroup.guess_instance(group_name)
248 gr = c.repo_group = db.RepoGroup.guess_instance(group_name)
248 parent_group = gr.parent_group
249 parent_group = gr.parent_group
249 repos = gr.repositories.all()
250 repos = gr.repositories.all()
250 if repos:
251 if repos:
251 webutils.flash(_('This group contains %s repositories and cannot be '
252 webutils.flash(_('This group contains %s repositories and cannot be '
252 'deleted') % len(repos), category='warning')
253 'deleted') % len(repos), category='warning')
253 raise HTTPFound(location=url('repos_groups'))
254 raise HTTPFound(location=url('repos_groups'))
254
255
255 children = gr.children.all()
256 children = gr.children.all()
256 if children:
257 if children:
257 webutils.flash(_('This group contains %s subgroups and cannot be deleted'
258 webutils.flash(_('This group contains %s subgroups and cannot be deleted'
258 % (len(children))), category='warning')
259 % (len(children))), category='warning')
259 raise HTTPFound(location=url('repos_groups'))
260 raise HTTPFound(location=url('repos_groups'))
260
261
261 try:
262 try:
262 RepoGroupModel().delete(group_name)
263 RepoGroupModel().delete(group_name)
263 meta.Session().commit()
264 meta.Session().commit()
264 webutils.flash(_('Removed repository group %s') % group_name,
265 webutils.flash(_('Removed repository group %s') % group_name,
265 category='success')
266 category='success')
266 # TODO: in future action_logger(, '', '', '')
267 # TODO: in future action_logger(, '', '', '')
267 except Exception:
268 except Exception:
268 log.error(traceback.format_exc())
269 log.error(traceback.format_exc())
269 webutils.flash(_('Error occurred during deletion of repository group %s')
270 webutils.flash(_('Error occurred during deletion of repository group %s')
270 % group_name, category='error')
271 % group_name, category='error')
271
272
272 if parent_group:
273 if parent_group:
273 raise HTTPFound(location=url('repos_group_home', group_name=parent_group.group_name))
274 raise HTTPFound(location=url('repos_group_home', group_name=parent_group.group_name))
274 raise HTTPFound(location=url('repos_groups'))
275 raise HTTPFound(location=url('repos_groups'))
275
276
276 def show_by_name(self, group_name):
277 def show_by_name(self, group_name):
277 """
278 """
278 This is a proxy that does a lookup group_name -> id, and shows
279 This is a proxy that does a lookup group_name -> id, and shows
279 the group by id view instead
280 the group by id view instead
280 """
281 """
281 group_name = group_name.rstrip('/')
282 group_name = group_name.rstrip('/')
282 id_ = db.RepoGroup.get_by_group_name(group_name)
283 id_ = db.RepoGroup.get_by_group_name(group_name)
283 if id_:
284 if id_:
284 return self.show(group_name)
285 return self.show(group_name)
285 raise HTTPNotFound
286 raise HTTPNotFound
286
287
287 @HasRepoGroupPermissionLevelDecorator('read')
288 @HasRepoGroupPermissionLevelDecorator('read')
288 def show(self, group_name):
289 def show(self, group_name):
289 c.active = 'settings'
290 c.active = 'settings'
290
291
291 c.group = c.repo_group = db.RepoGroup.guess_instance(group_name)
292 c.group = c.repo_group = db.RepoGroup.guess_instance(group_name)
292
293
293 groups = db.RepoGroup.query(sorted=True).filter_by(parent_group=c.group).all()
294 groups = db.RepoGroup.query(sorted=True).filter_by(parent_group=c.group).all()
294 repo_groups_list = self.scm_model.get_repo_groups(groups)
295 repo_groups_list = self.scm_model.get_repo_groups(groups)
295
296
296 repos_list = db.Repository.query(sorted=True).filter_by(group=c.group).all()
297 repos_list = db.Repository.query(sorted=True).filter_by(group=c.group).all()
297 c.data = RepoModel().get_repos_as_dict(repos_list,
298 c.data = RepoModel().get_repos_as_dict(repos_list,
298 repo_groups_list=repo_groups_list,
299 repo_groups_list=repo_groups_list,
299 short_name=True)
300 short_name=True)
300
301
301 return base.render('admin/repo_groups/repo_group_show.html')
302 return base.render('admin/repo_groups/repo_group_show.html')
302
303
303 @HasRepoGroupPermissionLevelDecorator('admin')
304 @HasRepoGroupPermissionLevelDecorator('admin')
304 def edit(self, group_name):
305 def edit(self, group_name):
305 c.active = 'settings'
306 c.active = 'settings'
306
307
307 c.repo_group = db.RepoGroup.guess_instance(group_name)
308 c.repo_group = db.RepoGroup.guess_instance(group_name)
308 self.__load_defaults(extras=[c.repo_group.parent_group],
309 self.__load_defaults(extras=[c.repo_group.parent_group],
309 exclude=[c.repo_group])
310 exclude=[c.repo_group])
310 defaults = self.__load_data(c.repo_group.group_id)
311 defaults = self.__load_data(c.repo_group.group_id)
311
312
312 return htmlfill.render(
313 return htmlfill.render(
313 base.render('admin/repo_groups/repo_group_edit.html'),
314 base.render('admin/repo_groups/repo_group_edit.html'),
314 defaults=defaults,
315 defaults=defaults,
315 encoding="UTF-8",
316 encoding="UTF-8",
316 force_defaults=False
317 force_defaults=False
317 )
318 )
318
319
319 @HasRepoGroupPermissionLevelDecorator('admin')
320 @HasRepoGroupPermissionLevelDecorator('admin')
320 def edit_repo_group_advanced(self, group_name):
321 def edit_repo_group_advanced(self, group_name):
321 c.active = 'advanced'
322 c.active = 'advanced'
322 c.repo_group = db.RepoGroup.guess_instance(group_name)
323 c.repo_group = db.RepoGroup.guess_instance(group_name)
323
324
324 return base.render('admin/repo_groups/repo_group_edit.html')
325 return base.render('admin/repo_groups/repo_group_edit.html')
325
326
326 @HasRepoGroupPermissionLevelDecorator('admin')
327 @HasRepoGroupPermissionLevelDecorator('admin')
327 def edit_repo_group_perms(self, group_name):
328 def edit_repo_group_perms(self, group_name):
328 c.active = 'perms'
329 c.active = 'perms'
329 c.repo_group = db.RepoGroup.guess_instance(group_name)
330 c.repo_group = db.RepoGroup.guess_instance(group_name)
330 self.__load_defaults()
331 self.__load_defaults()
331 defaults = self.__load_data(c.repo_group.group_id)
332 defaults = self.__load_data(c.repo_group.group_id)
332
333
333 return htmlfill.render(
334 return htmlfill.render(
334 base.render('admin/repo_groups/repo_group_edit.html'),
335 base.render('admin/repo_groups/repo_group_edit.html'),
335 defaults=defaults,
336 defaults=defaults,
336 encoding="UTF-8",
337 encoding="UTF-8",
337 force_defaults=False
338 force_defaults=False
338 )
339 )
339
340
340 @HasRepoGroupPermissionLevelDecorator('admin')
341 @HasRepoGroupPermissionLevelDecorator('admin')
341 def update_perms(self, group_name):
342 def update_perms(self, group_name):
342 """
343 """
343 Update permissions for given repository group
344 Update permissions for given repository group
344
345
345 :param group_name:
346 :param group_name:
346 """
347 """
347
348
348 c.repo_group = db.RepoGroup.guess_instance(group_name)
349 c.repo_group = db.RepoGroup.guess_instance(group_name)
349 valid_recursive_choices = ['none', 'repos', 'groups', 'all']
350 valid_recursive_choices = ['none', 'repos', 'groups', 'all']
350 form_result = RepoGroupPermsForm(valid_recursive_choices)().to_python(request.POST)
351 form_result = RepoGroupPermsForm(valid_recursive_choices)().to_python(request.POST)
351 if not request.authuser.is_admin:
352 if not request.authuser.is_admin:
352 if self._revoke_perms_on_yourself(form_result):
353 if self._revoke_perms_on_yourself(form_result):
353 msg = _('Cannot revoke permission for yourself as admin')
354 msg = _('Cannot revoke permission for yourself as admin')
354 webutils.flash(msg, category='warning')
355 webutils.flash(msg, category='warning')
355 raise HTTPFound(location=url('edit_repo_group_perms', group_name=group_name))
356 raise HTTPFound(location=url('edit_repo_group_perms', group_name=group_name))
356 recursive = form_result['recursive']
357 recursive = form_result['recursive']
357 # iterate over all members(if in recursive mode) of this groups and
358 # iterate over all members(if in recursive mode) of this groups and
358 # set the permissions !
359 # set the permissions !
359 # this can be potentially heavy operation
360 # this can be potentially heavy operation
360 RepoGroupModel()._update_permissions(c.repo_group,
361 RepoGroupModel()._update_permissions(c.repo_group,
361 form_result['perms_new'],
362 form_result['perms_new'],
362 form_result['perms_updates'],
363 form_result['perms_updates'],
363 recursive)
364 recursive)
364 # TODO: implement this
365 # TODO: implement this
365 #action_logger(request.authuser, 'admin_changed_repo_permissions',
366 #action_logger(request.authuser, 'admin_changed_repo_permissions',
366 # repo_name, request.ip_addr)
367 # repo_name, request.ip_addr)
367 meta.Session().commit()
368 meta.Session().commit()
368 webutils.flash(_('Repository group permissions updated'), category='success')
369 webutils.flash(_('Repository group permissions updated'), category='success')
369 raise HTTPFound(location=url('edit_repo_group_perms', group_name=group_name))
370 raise HTTPFound(location=url('edit_repo_group_perms', group_name=group_name))
370
371
371 @HasRepoGroupPermissionLevelDecorator('admin')
372 @HasRepoGroupPermissionLevelDecorator('admin')
372 def delete_perms(self, group_name):
373 def delete_perms(self, group_name):
373 try:
374 try:
374 obj_type = request.POST.get('obj_type')
375 obj_type = request.POST.get('obj_type')
375 obj_id = None
376 obj_id = None
376 if obj_type == 'user':
377 if obj_type == 'user':
377 obj_id = safe_int(request.POST.get('user_id'))
378 obj_id = safe_int(request.POST.get('user_id'))
378 elif obj_type == 'user_group':
379 elif obj_type == 'user_group':
379 obj_id = safe_int(request.POST.get('user_group_id'))
380 obj_id = safe_int(request.POST.get('user_group_id'))
380
381
381 if not request.authuser.is_admin:
382 if not request.authuser.is_admin:
382 if obj_type == 'user' and request.authuser.user_id == obj_id:
383 if obj_type == 'user' and request.authuser.user_id == obj_id:
383 msg = _('Cannot revoke permission for yourself as admin')
384 msg = _('Cannot revoke permission for yourself as admin')
384 webutils.flash(msg, category='warning')
385 webutils.flash(msg, category='warning')
385 raise Exception('revoke admin permission on self')
386 raise Exception('revoke admin permission on self')
386 recursive = request.POST.get('recursive', 'none')
387 recursive = request.POST.get('recursive', 'none')
387 if obj_type == 'user':
388 if obj_type == 'user':
388 RepoGroupModel().delete_permission(repo_group=group_name,
389 RepoGroupModel().delete_permission(repo_group=group_name,
389 obj=obj_id, obj_type='user',
390 obj=obj_id, obj_type='user',
390 recursive=recursive)
391 recursive=recursive)
391 elif obj_type == 'user_group':
392 elif obj_type == 'user_group':
392 RepoGroupModel().delete_permission(repo_group=group_name,
393 RepoGroupModel().delete_permission(repo_group=group_name,
393 obj=obj_id,
394 obj=obj_id,
394 obj_type='user_group',
395 obj_type='user_group',
395 recursive=recursive)
396 recursive=recursive)
396
397
397 meta.Session().commit()
398 meta.Session().commit()
398 except Exception:
399 except Exception:
399 log.error(traceback.format_exc())
400 log.error(traceback.format_exc())
400 webutils.flash(_('An error occurred during revoking of permission'),
401 webutils.flash(_('An error occurred during revoking of permission'),
401 category='error')
402 category='error')
402 raise HTTPInternalServerError()
403 raise HTTPInternalServerError()
@@ -1,564 +1,565 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
5 # (at your option) any later version.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU General Public License
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 """
14 """
15 these are form validation classes
15 these are form validation classes
16 http://formencode.org/module-formencode.validators.html
16 http://formencode.org/module-formencode.validators.html
17 for list of all available validators
17 for list of all available validators
18
18
19 we can create our own validators
19 we can create our own validators
20
20
21 The table below outlines the options which can be used in a schema in addition to the validators themselves
21 The table below outlines the options which can be used in a schema in addition to the validators themselves
22 pre_validators [] These validators will be applied before the schema
22 pre_validators [] These validators will be applied before the schema
23 chained_validators [] These validators will be applied after the schema
23 chained_validators [] These validators will be applied after the schema
24 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
24 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
25 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
25 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
26 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.
26 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.
27 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
27 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
28
28
29
29
30 <name> = formencode.validators.<name of validator>
30 <name> = formencode.validators.<name of validator>
31 <name> must equal form name
31 <name> must equal form name
32 list=[1,2,3,4,5]
32 list=[1,2,3,4,5]
33 for SELECT use formencode.All(OneOf(list), Int())
33 for SELECT use formencode.All(OneOf(list), Int())
34
34
35 """
35 """
36 import logging
36 import logging
37
37
38 import formencode
38 import formencode
39 from formencode import All
39 from formencode import All
40 from tg.i18n import ugettext as _
40 from tg.i18n import ugettext as _
41
41
42 import kallithea
42 import kallithea
43 from kallithea.model import validators as v
43 from kallithea.model import validators as v
44
44
45
45
46 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
47
47
48
48
49 def LoginForm():
49 def LoginForm():
50 class _LoginForm(formencode.Schema):
50 class _LoginForm(formencode.Schema):
51 allow_extra_fields = True
51 allow_extra_fields = True
52 filter_extra_fields = True
52 filter_extra_fields = True
53 username = v.UnicodeString(
53 username = v.UnicodeString(
54 strip=True,
54 strip=True,
55 min=1,
55 min=1,
56 not_empty=True,
56 not_empty=True,
57 messages={
57 messages={
58 'empty': _('Please enter a login'),
58 'empty': _('Please enter a login'),
59 'tooShort': _('Enter a value %(min)i characters long or more')}
59 'tooShort': _('Enter a value %(min)i characters long or more')}
60 )
60 )
61
61
62 password = v.UnicodeString(
62 password = v.UnicodeString(
63 strip=False,
63 strip=False,
64 min=3,
64 min=3,
65 not_empty=True,
65 not_empty=True,
66 messages={
66 messages={
67 'empty': _('Please enter a password'),
67 'empty': _('Please enter a password'),
68 'tooShort': _('Enter %(min)i characters or more')}
68 'tooShort': _('Enter %(min)i characters or more')}
69 )
69 )
70
70
71 remember = v.StringBoolean(if_missing=False)
71 remember = v.StringBoolean(if_missing=False)
72
72
73 chained_validators = [v.ValidAuth()]
73 chained_validators = [v.ValidAuth()]
74 return _LoginForm
74 return _LoginForm
75
75
76
76
77 def PasswordChangeForm(username):
77 def PasswordChangeForm(username):
78 class _PasswordChangeForm(formencode.Schema):
78 class _PasswordChangeForm(formencode.Schema):
79 allow_extra_fields = True
79 allow_extra_fields = True
80 filter_extra_fields = True
80 filter_extra_fields = True
81
81
82 current_password = v.ValidOldPassword(username)(not_empty=True)
82 current_password = v.ValidOldPassword(username)(not_empty=True)
83 new_password = All(v.ValidPassword(), v.UnicodeString(strip=False, min=6))
83 new_password = All(v.ValidPassword(), v.UnicodeString(strip=False, min=6))
84 new_password_confirmation = All(v.ValidPassword(), v.UnicodeString(strip=False, min=6))
84 new_password_confirmation = All(v.ValidPassword(), v.UnicodeString(strip=False, min=6))
85
85
86 chained_validators = [v.ValidPasswordsMatch('new_password',
86 chained_validators = [v.ValidPasswordsMatch('new_password',
87 'new_password_confirmation')]
87 'new_password_confirmation')]
88 return _PasswordChangeForm
88 return _PasswordChangeForm
89
89
90
90
91 def UserForm(edit=False, old_data=None):
91 def UserForm(edit=False, old_data=None):
92 old_data = old_data or {}
92 old_data = old_data or {}
93
93
94 class _UserForm(formencode.Schema):
94 class _UserForm(formencode.Schema):
95 allow_extra_fields = True
95 allow_extra_fields = True
96 filter_extra_fields = True
96 filter_extra_fields = True
97 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
97 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
98 v.ValidUsername(edit, old_data))
98 v.ValidUsername(edit, old_data))
99 if edit:
99 if edit:
100 new_password = All(
100 new_password = All(
101 v.ValidPassword(),
101 v.ValidPassword(),
102 v.UnicodeString(strip=False, min=6, not_empty=False)
102 v.UnicodeString(strip=False, min=6, not_empty=False)
103 )
103 )
104 password_confirmation = All(
104 password_confirmation = All(
105 v.ValidPassword(),
105 v.ValidPassword(),
106 v.UnicodeString(strip=False, min=6, not_empty=False),
106 v.UnicodeString(strip=False, min=6, not_empty=False),
107 )
107 )
108 admin = v.StringBoolean(if_missing=False)
108 admin = v.StringBoolean(if_missing=False)
109 chained_validators = [v.ValidPasswordsMatch('new_password',
109 chained_validators = [v.ValidPasswordsMatch('new_password',
110 'password_confirmation')]
110 'password_confirmation')]
111 else:
111 else:
112 password = All(
112 password = All(
113 v.ValidPassword(),
113 v.ValidPassword(),
114 v.UnicodeString(strip=False, min=6, not_empty=True)
114 v.UnicodeString(strip=False, min=6, not_empty=True)
115 )
115 )
116 password_confirmation = All(
116 password_confirmation = All(
117 v.ValidPassword(),
117 v.ValidPassword(),
118 v.UnicodeString(strip=False, min=6, not_empty=False)
118 v.UnicodeString(strip=False, min=6, not_empty=False)
119 )
119 )
120 chained_validators = [v.ValidPasswordsMatch('password',
120 chained_validators = [v.ValidPasswordsMatch('password',
121 'password_confirmation')]
121 'password_confirmation')]
122
122
123 active = v.StringBoolean(if_missing=False)
123 active = v.StringBoolean(if_missing=False)
124 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
124 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
125 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
125 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
126 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
126 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
127 extern_name = v.UnicodeString(strip=True, if_missing=None)
127 extern_name = v.UnicodeString(strip=True, if_missing=None)
128 extern_type = v.UnicodeString(strip=True, if_missing=None)
128 extern_type = v.UnicodeString(strip=True, if_missing=None)
129 return _UserForm
129 return _UserForm
130
130
131
131
132 def UserGroupForm(edit=False, old_data=None, available_members=None):
132 def UserGroupForm(edit=False, old_data=None, available_members=None):
133 old_data = old_data or {}
133 old_data = old_data or {}
134 available_members = available_members or []
134 available_members = available_members or []
135
135
136 class _UserGroupForm(formencode.Schema):
136 class _UserGroupForm(formencode.Schema):
137 allow_extra_fields = True
137 allow_extra_fields = True
138 filter_extra_fields = True
138 filter_extra_fields = True
139
139
140 users_group_name = All(
140 users_group_name = All(
141 v.UnicodeString(strip=True, min=1, not_empty=True),
141 v.UnicodeString(strip=True, min=1, not_empty=True),
142 v.ValidUserGroup(edit, old_data)
142 v.ValidUserGroup(edit, old_data)
143 )
143 )
144 user_group_description = v.UnicodeString(strip=True, min=1,
144 user_group_description = v.UnicodeString(strip=True, min=1,
145 not_empty=False)
145 not_empty=False)
146
146
147 users_group_active = v.StringBoolean(if_missing=False)
147 users_group_active = v.StringBoolean(if_missing=False)
148
148
149 if edit:
149 if edit:
150 users_group_members = v.OneOf(
150 users_group_members = v.OneOf(
151 available_members, hideList=False, testValueList=True,
151 available_members, hideList=False, testValueList=True,
152 if_missing=None, not_empty=False
152 if_missing=None, not_empty=False
153 )
153 )
154
154
155 return _UserGroupForm
155 return _UserGroupForm
156
156
157
157
158 def RepoGroupForm(edit=False, old_data=None, repo_groups=None,
158 def RepoGroupForm(edit=False, old_data=None, repo_groups=None,
159 can_create_in_root=False):
159 can_create_in_root=False):
160 old_data = old_data or {}
160 old_data = old_data or {}
161 repo_groups = repo_groups or []
161 repo_groups = repo_groups or []
162 repo_group_ids = [rg[0] for rg in repo_groups]
162 repo_group_ids = [rg[0] for rg in repo_groups]
163
163
164 class _RepoGroupForm(formencode.Schema):
164 class _RepoGroupForm(formencode.Schema):
165 allow_extra_fields = True
165 allow_extra_fields = True
166 filter_extra_fields = False
166 filter_extra_fields = False
167
167
168 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
168 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
169 v.SlugifyName(),
169 v.SlugifyName(),
170 v.ValidRegex(msg=_('Name must not contain only digits'))(r'(?!^\d+$)^.+$'))
170 v.ValidRegex(msg=_('Name must not contain only digits'))(r'(?!^\d+$)^.+$'))
171 group_description = v.UnicodeString(strip=True, min=1,
171 group_description = v.UnicodeString(strip=True, min=1,
172 not_empty=False)
172 not_empty=False)
173 group_copy_permissions = v.StringBoolean(if_missing=False)
173 group_copy_permissions = v.StringBoolean(if_missing=False)
174
174
175 if edit:
175 if edit:
176 owner = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
176 # FIXME: do a special check that we cannot move a group to one of
177 # FIXME: do a special check that we cannot move a group to one of
177 # its children
178 # its children
178 pass
179 pass
179
180
180 parent_group_id = All(v.CanCreateGroup(can_create_in_root),
181 parent_group_id = All(v.CanCreateGroup(can_create_in_root),
181 v.OneOf(repo_group_ids, hideList=False,
182 v.OneOf(repo_group_ids, hideList=False,
182 testValueList=True,
183 testValueList=True,
183 if_missing=None, not_empty=True),
184 if_missing=None, not_empty=True),
184 v.Int(min=-1, not_empty=True))
185 v.Int(min=-1, not_empty=True))
185 chained_validators = [v.ValidRepoGroup(edit, old_data)]
186 chained_validators = [v.ValidRepoGroup(edit, old_data)]
186
187
187 return _RepoGroupForm
188 return _RepoGroupForm
188
189
189
190
190 def RegisterForm(edit=False, old_data=None):
191 def RegisterForm(edit=False, old_data=None):
191 class _RegisterForm(formencode.Schema):
192 class _RegisterForm(formencode.Schema):
192 allow_extra_fields = True
193 allow_extra_fields = True
193 filter_extra_fields = True
194 filter_extra_fields = True
194 username = All(
195 username = All(
195 v.ValidUsername(edit, old_data),
196 v.ValidUsername(edit, old_data),
196 v.UnicodeString(strip=True, min=1, not_empty=True)
197 v.UnicodeString(strip=True, min=1, not_empty=True)
197 )
198 )
198 password = All(
199 password = All(
199 v.ValidPassword(),
200 v.ValidPassword(),
200 v.UnicodeString(strip=False, min=6, not_empty=True)
201 v.UnicodeString(strip=False, min=6, not_empty=True)
201 )
202 )
202 password_confirmation = All(
203 password_confirmation = All(
203 v.ValidPassword(),
204 v.ValidPassword(),
204 v.UnicodeString(strip=False, min=6, not_empty=True)
205 v.UnicodeString(strip=False, min=6, not_empty=True)
205 )
206 )
206 active = v.StringBoolean(if_missing=False)
207 active = v.StringBoolean(if_missing=False)
207 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
208 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
208 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
209 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
209 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
210 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
210
211
211 chained_validators = [v.ValidPasswordsMatch('password',
212 chained_validators = [v.ValidPasswordsMatch('password',
212 'password_confirmation')]
213 'password_confirmation')]
213
214
214 return _RegisterForm
215 return _RegisterForm
215
216
216
217
217 def PasswordResetRequestForm():
218 def PasswordResetRequestForm():
218 class _PasswordResetRequestForm(formencode.Schema):
219 class _PasswordResetRequestForm(formencode.Schema):
219 allow_extra_fields = True
220 allow_extra_fields = True
220 filter_extra_fields = True
221 filter_extra_fields = True
221 email = v.Email(not_empty=True)
222 email = v.Email(not_empty=True)
222 return _PasswordResetRequestForm
223 return _PasswordResetRequestForm
223
224
224
225
225 def PasswordResetConfirmationForm():
226 def PasswordResetConfirmationForm():
226 class _PasswordResetConfirmationForm(formencode.Schema):
227 class _PasswordResetConfirmationForm(formencode.Schema):
227 allow_extra_fields = True
228 allow_extra_fields = True
228 filter_extra_fields = True
229 filter_extra_fields = True
229
230
230 email = v.UnicodeString(strip=True, not_empty=True)
231 email = v.UnicodeString(strip=True, not_empty=True)
231 timestamp = v.Number(strip=True, not_empty=True)
232 timestamp = v.Number(strip=True, not_empty=True)
232 token = v.UnicodeString(strip=True, not_empty=True)
233 token = v.UnicodeString(strip=True, not_empty=True)
233 password = All(v.ValidPassword(), v.UnicodeString(strip=False, min=6))
234 password = All(v.ValidPassword(), v.UnicodeString(strip=False, min=6))
234 password_confirm = All(v.ValidPassword(), v.UnicodeString(strip=False, min=6))
235 password_confirm = All(v.ValidPassword(), v.UnicodeString(strip=False, min=6))
235
236
236 chained_validators = [v.ValidPasswordsMatch('password',
237 chained_validators = [v.ValidPasswordsMatch('password',
237 'password_confirm')]
238 'password_confirm')]
238 return _PasswordResetConfirmationForm
239 return _PasswordResetConfirmationForm
239
240
240
241
241 def RepoForm(edit=False, old_data=None, supported_backends=kallithea.BACKENDS,
242 def RepoForm(edit=False, old_data=None, supported_backends=kallithea.BACKENDS,
242 repo_groups=None, landing_revs=None):
243 repo_groups=None, landing_revs=None):
243 old_data = old_data or {}
244 old_data = old_data or {}
244 repo_groups = repo_groups or []
245 repo_groups = repo_groups or []
245 landing_revs = landing_revs or []
246 landing_revs = landing_revs or []
246 repo_group_ids = [rg[0] for rg in repo_groups]
247 repo_group_ids = [rg[0] for rg in repo_groups]
247
248
248 class _RepoForm(formencode.Schema):
249 class _RepoForm(formencode.Schema):
249 allow_extra_fields = True
250 allow_extra_fields = True
250 filter_extra_fields = False
251 filter_extra_fields = False
251 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
252 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
252 v.SlugifyName())
253 v.SlugifyName())
253 repo_group = All(v.CanWriteGroup(old_data),
254 repo_group = All(v.CanWriteGroup(old_data),
254 v.OneOf(repo_group_ids, hideList=True),
255 v.OneOf(repo_group_ids, hideList=True),
255 v.Int(min=-1, not_empty=True))
256 v.Int(min=-1, not_empty=True))
256 repo_type = v.OneOf(supported_backends, required=False,
257 repo_type = v.OneOf(supported_backends, required=False,
257 if_missing=old_data.get('repo_type'))
258 if_missing=old_data.get('repo_type'))
258 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
259 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
259 repo_private = v.StringBoolean(if_missing=False)
260 repo_private = v.StringBoolean(if_missing=False)
260 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
261 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
261 repo_copy_permissions = v.StringBoolean(if_missing=False)
262 repo_copy_permissions = v.StringBoolean(if_missing=False)
262 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
263 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
263
264
264 repo_enable_statistics = v.StringBoolean(if_missing=False)
265 repo_enable_statistics = v.StringBoolean(if_missing=False)
265 repo_enable_downloads = v.StringBoolean(if_missing=False)
266 repo_enable_downloads = v.StringBoolean(if_missing=False)
266
267
267 if edit:
268 if edit:
268 owner = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
269 owner = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
269 # Not a real field - just for reference for validation:
270 # Not a real field - just for reference for validation:
270 # clone_uri_hidden = v.UnicodeString(if_missing='')
271 # clone_uri_hidden = v.UnicodeString(if_missing='')
271
272
272 chained_validators = [v.ValidCloneUri(),
273 chained_validators = [v.ValidCloneUri(),
273 v.ValidRepoName(edit, old_data)]
274 v.ValidRepoName(edit, old_data)]
274 return _RepoForm
275 return _RepoForm
275
276
276
277
277 def RepoPermsForm():
278 def RepoPermsForm():
278 class _RepoPermsForm(formencode.Schema):
279 class _RepoPermsForm(formencode.Schema):
279 allow_extra_fields = True
280 allow_extra_fields = True
280 filter_extra_fields = False
281 filter_extra_fields = False
281 chained_validators = [v.ValidPerms(type_='repo')]
282 chained_validators = [v.ValidPerms(type_='repo')]
282 return _RepoPermsForm
283 return _RepoPermsForm
283
284
284
285
285 def RepoGroupPermsForm(valid_recursive_choices):
286 def RepoGroupPermsForm(valid_recursive_choices):
286 class _RepoGroupPermsForm(formencode.Schema):
287 class _RepoGroupPermsForm(formencode.Schema):
287 allow_extra_fields = True
288 allow_extra_fields = True
288 filter_extra_fields = False
289 filter_extra_fields = False
289 recursive = v.OneOf(valid_recursive_choices)
290 recursive = v.OneOf(valid_recursive_choices)
290 chained_validators = [v.ValidPerms(type_='repo_group')]
291 chained_validators = [v.ValidPerms(type_='repo_group')]
291 return _RepoGroupPermsForm
292 return _RepoGroupPermsForm
292
293
293
294
294 def UserGroupPermsForm():
295 def UserGroupPermsForm():
295 class _UserPermsForm(formencode.Schema):
296 class _UserPermsForm(formencode.Schema):
296 allow_extra_fields = True
297 allow_extra_fields = True
297 filter_extra_fields = False
298 filter_extra_fields = False
298 chained_validators = [v.ValidPerms(type_='user_group')]
299 chained_validators = [v.ValidPerms(type_='user_group')]
299 return _UserPermsForm
300 return _UserPermsForm
300
301
301
302
302 def RepoFieldForm():
303 def RepoFieldForm():
303 class _RepoFieldForm(formencode.Schema):
304 class _RepoFieldForm(formencode.Schema):
304 filter_extra_fields = True
305 filter_extra_fields = True
305 allow_extra_fields = True
306 allow_extra_fields = True
306
307
307 new_field_key = All(v.FieldKey(),
308 new_field_key = All(v.FieldKey(),
308 v.UnicodeString(strip=True, min=3, not_empty=True))
309 v.UnicodeString(strip=True, min=3, not_empty=True))
309 new_field_value = v.UnicodeString(not_empty=False, if_missing='')
310 new_field_value = v.UnicodeString(not_empty=False, if_missing='')
310 new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'],
311 new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'],
311 if_missing='str')
312 if_missing='str')
312 new_field_label = v.UnicodeString(not_empty=False)
313 new_field_label = v.UnicodeString(not_empty=False)
313 new_field_desc = v.UnicodeString(not_empty=False)
314 new_field_desc = v.UnicodeString(not_empty=False)
314
315
315 return _RepoFieldForm
316 return _RepoFieldForm
316
317
317
318
318 def RepoForkForm(edit=False, old_data=None, supported_backends=kallithea.BACKENDS,
319 def RepoForkForm(edit=False, old_data=None, supported_backends=kallithea.BACKENDS,
319 repo_groups=None, landing_revs=None):
320 repo_groups=None, landing_revs=None):
320 old_data = old_data or {}
321 old_data = old_data or {}
321 repo_groups = repo_groups or []
322 repo_groups = repo_groups or []
322 landing_revs = landing_revs or []
323 landing_revs = landing_revs or []
323 repo_group_ids = [rg[0] for rg in repo_groups]
324 repo_group_ids = [rg[0] for rg in repo_groups]
324
325
325 class _RepoForkForm(formencode.Schema):
326 class _RepoForkForm(formencode.Schema):
326 allow_extra_fields = True
327 allow_extra_fields = True
327 filter_extra_fields = False
328 filter_extra_fields = False
328 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
329 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
329 v.SlugifyName())
330 v.SlugifyName())
330 repo_group = All(v.CanWriteGroup(),
331 repo_group = All(v.CanWriteGroup(),
331 v.OneOf(repo_group_ids, hideList=True),
332 v.OneOf(repo_group_ids, hideList=True),
332 v.Int(min=-1, not_empty=True))
333 v.Int(min=-1, not_empty=True))
333 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
334 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
334 description = v.UnicodeString(strip=True, min=1, not_empty=True)
335 description = v.UnicodeString(strip=True, min=1, not_empty=True)
335 private = v.StringBoolean(if_missing=False)
336 private = v.StringBoolean(if_missing=False)
336 copy_permissions = v.StringBoolean(if_missing=False)
337 copy_permissions = v.StringBoolean(if_missing=False)
337 update_after_clone = v.StringBoolean(if_missing=False)
338 update_after_clone = v.StringBoolean(if_missing=False)
338 fork_parent_id = v.UnicodeString()
339 fork_parent_id = v.UnicodeString()
339 chained_validators = [v.ValidForkName(edit, old_data)]
340 chained_validators = [v.ValidForkName(edit, old_data)]
340 landing_rev = v.OneOf(landing_revs, hideList=True)
341 landing_rev = v.OneOf(landing_revs, hideList=True)
341
342
342 return _RepoForkForm
343 return _RepoForkForm
343
344
344
345
345 def ApplicationSettingsForm():
346 def ApplicationSettingsForm():
346 class _ApplicationSettingsForm(formencode.Schema):
347 class _ApplicationSettingsForm(formencode.Schema):
347 allow_extra_fields = True
348 allow_extra_fields = True
348 filter_extra_fields = False
349 filter_extra_fields = False
349 title = v.UnicodeString(strip=True, not_empty=False)
350 title = v.UnicodeString(strip=True, not_empty=False)
350 realm = v.UnicodeString(strip=True, min=1, not_empty=True)
351 realm = v.UnicodeString(strip=True, min=1, not_empty=True)
351 ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
352 ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
352 captcha_public_key = v.UnicodeString(strip=True, min=1, not_empty=False)
353 captcha_public_key = v.UnicodeString(strip=True, min=1, not_empty=False)
353 captcha_private_key = v.UnicodeString(strip=True, min=1, not_empty=False)
354 captcha_private_key = v.UnicodeString(strip=True, min=1, not_empty=False)
354
355
355 return _ApplicationSettingsForm
356 return _ApplicationSettingsForm
356
357
357
358
358 def ApplicationVisualisationForm():
359 def ApplicationVisualisationForm():
359 class _ApplicationVisualisationForm(formencode.Schema):
360 class _ApplicationVisualisationForm(formencode.Schema):
360 allow_extra_fields = True
361 allow_extra_fields = True
361 filter_extra_fields = False
362 filter_extra_fields = False
362 show_public_icon = v.StringBoolean(if_missing=False)
363 show_public_icon = v.StringBoolean(if_missing=False)
363 show_private_icon = v.StringBoolean(if_missing=False)
364 show_private_icon = v.StringBoolean(if_missing=False)
364 stylify_metalabels = v.StringBoolean(if_missing=False)
365 stylify_metalabels = v.StringBoolean(if_missing=False)
365
366
366 repository_fields = v.StringBoolean(if_missing=False)
367 repository_fields = v.StringBoolean(if_missing=False)
367 lightweight_journal = v.StringBoolean(if_missing=False)
368 lightweight_journal = v.StringBoolean(if_missing=False)
368 dashboard_items = v.Int(min=5, not_empty=True)
369 dashboard_items = v.Int(min=5, not_empty=True)
369 admin_grid_items = v.Int(min=5, not_empty=True)
370 admin_grid_items = v.Int(min=5, not_empty=True)
370 show_version = v.StringBoolean(if_missing=False)
371 show_version = v.StringBoolean(if_missing=False)
371 use_gravatar = v.StringBoolean(if_missing=False)
372 use_gravatar = v.StringBoolean(if_missing=False)
372 gravatar_url = v.UnicodeString(min=3)
373 gravatar_url = v.UnicodeString(min=3)
373 clone_uri_tmpl = v.UnicodeString(min=3)
374 clone_uri_tmpl = v.UnicodeString(min=3)
374 clone_ssh_tmpl = v.UnicodeString()
375 clone_ssh_tmpl = v.UnicodeString()
375
376
376 return _ApplicationVisualisationForm
377 return _ApplicationVisualisationForm
377
378
378
379
379 def ApplicationUiSettingsForm():
380 def ApplicationUiSettingsForm():
380 class _ApplicationUiSettingsForm(formencode.Schema):
381 class _ApplicationUiSettingsForm(formencode.Schema):
381 allow_extra_fields = True
382 allow_extra_fields = True
382 filter_extra_fields = False
383 filter_extra_fields = False
383 paths_root_path = All(
384 paths_root_path = All(
384 v.ValidPath(),
385 v.ValidPath(),
385 v.UnicodeString(strip=True, min=1, not_empty=True)
386 v.UnicodeString(strip=True, min=1, not_empty=True)
386 )
387 )
387 hooks_changegroup_kallithea_update = v.StringBoolean(if_missing=False)
388 hooks_changegroup_kallithea_update = v.StringBoolean(if_missing=False)
388 hooks_changegroup_kallithea_repo_size = v.StringBoolean(if_missing=False)
389 hooks_changegroup_kallithea_repo_size = v.StringBoolean(if_missing=False)
389
390
390 extensions_largefiles = v.StringBoolean(if_missing=False)
391 extensions_largefiles = v.StringBoolean(if_missing=False)
391 extensions_hggit = v.StringBoolean(if_missing=False)
392 extensions_hggit = v.StringBoolean(if_missing=False)
392
393
393 return _ApplicationUiSettingsForm
394 return _ApplicationUiSettingsForm
394
395
395
396
396 def DefaultPermissionsForm(repo_perms_choices, group_perms_choices,
397 def DefaultPermissionsForm(repo_perms_choices, group_perms_choices,
397 user_group_perms_choices, create_choices,
398 user_group_perms_choices, create_choices,
398 user_group_create_choices, fork_choices,
399 user_group_create_choices, fork_choices,
399 register_choices, extern_activate_choices):
400 register_choices, extern_activate_choices):
400 class _DefaultPermissionsForm(formencode.Schema):
401 class _DefaultPermissionsForm(formencode.Schema):
401 allow_extra_fields = True
402 allow_extra_fields = True
402 filter_extra_fields = True
403 filter_extra_fields = True
403 overwrite_default_repo = v.StringBoolean(if_missing=False)
404 overwrite_default_repo = v.StringBoolean(if_missing=False)
404 overwrite_default_group = v.StringBoolean(if_missing=False)
405 overwrite_default_group = v.StringBoolean(if_missing=False)
405 overwrite_default_user_group = v.StringBoolean(if_missing=False)
406 overwrite_default_user_group = v.StringBoolean(if_missing=False)
406 anonymous = v.StringBoolean(if_missing=False)
407 anonymous = v.StringBoolean(if_missing=False)
407 default_repo_perm = v.OneOf(repo_perms_choices)
408 default_repo_perm = v.OneOf(repo_perms_choices)
408 default_group_perm = v.OneOf(group_perms_choices)
409 default_group_perm = v.OneOf(group_perms_choices)
409 default_user_group_perm = v.OneOf(user_group_perms_choices)
410 default_user_group_perm = v.OneOf(user_group_perms_choices)
410
411
411 default_repo_create = v.OneOf(create_choices)
412 default_repo_create = v.OneOf(create_choices)
412 default_user_group_create = v.OneOf(user_group_create_choices)
413 default_user_group_create = v.OneOf(user_group_create_choices)
413 default_fork = v.OneOf(fork_choices)
414 default_fork = v.OneOf(fork_choices)
414
415
415 default_register = v.OneOf(register_choices)
416 default_register = v.OneOf(register_choices)
416 default_extern_activate = v.OneOf(extern_activate_choices)
417 default_extern_activate = v.OneOf(extern_activate_choices)
417 return _DefaultPermissionsForm
418 return _DefaultPermissionsForm
418
419
419
420
420 def CustomDefaultPermissionsForm():
421 def CustomDefaultPermissionsForm():
421 class _CustomDefaultPermissionsForm(formencode.Schema):
422 class _CustomDefaultPermissionsForm(formencode.Schema):
422 filter_extra_fields = True
423 filter_extra_fields = True
423 allow_extra_fields = True
424 allow_extra_fields = True
424
425
425 create_repo_perm = v.StringBoolean(if_missing=False)
426 create_repo_perm = v.StringBoolean(if_missing=False)
426 create_user_group_perm = v.StringBoolean(if_missing=False)
427 create_user_group_perm = v.StringBoolean(if_missing=False)
427 #create_repo_group_perm Impl. later
428 #create_repo_group_perm Impl. later
428
429
429 fork_repo_perm = v.StringBoolean(if_missing=False)
430 fork_repo_perm = v.StringBoolean(if_missing=False)
430
431
431 return _CustomDefaultPermissionsForm
432 return _CustomDefaultPermissionsForm
432
433
433
434
434 def DefaultsForm(edit=False, old_data=None, supported_backends=kallithea.BACKENDS):
435 def DefaultsForm(edit=False, old_data=None, supported_backends=kallithea.BACKENDS):
435 class _DefaultsForm(formencode.Schema):
436 class _DefaultsForm(formencode.Schema):
436 allow_extra_fields = True
437 allow_extra_fields = True
437 filter_extra_fields = True
438 filter_extra_fields = True
438 default_repo_type = v.OneOf(supported_backends)
439 default_repo_type = v.OneOf(supported_backends)
439 default_repo_private = v.StringBoolean(if_missing=False)
440 default_repo_private = v.StringBoolean(if_missing=False)
440 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
441 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
441 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
442 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
442
443
443 return _DefaultsForm
444 return _DefaultsForm
444
445
445
446
446 def AuthSettingsForm(current_active_modules):
447 def AuthSettingsForm(current_active_modules):
447 class _AuthSettingsForm(formencode.Schema):
448 class _AuthSettingsForm(formencode.Schema):
448 allow_extra_fields = True
449 allow_extra_fields = True
449 filter_extra_fields = True
450 filter_extra_fields = True
450 auth_plugins = All(v.ValidAuthPlugins(),
451 auth_plugins = All(v.ValidAuthPlugins(),
451 v.UniqueListFromString()(not_empty=True))
452 v.UniqueListFromString()(not_empty=True))
452
453
453 def __init__(self, *args, **kwargs):
454 def __init__(self, *args, **kwargs):
454 # The auth plugins tell us what form validators they use
455 # The auth plugins tell us what form validators they use
455 if current_active_modules:
456 if current_active_modules:
456 import kallithea.lib.auth_modules
457 import kallithea.lib.auth_modules
457 from kallithea.lib.auth_modules import LazyFormencode
458 from kallithea.lib.auth_modules import LazyFormencode
458 for module in current_active_modules:
459 for module in current_active_modules:
459 plugin = kallithea.lib.auth_modules.loadplugin(module)
460 plugin = kallithea.lib.auth_modules.loadplugin(module)
460 plugin_name = plugin.name
461 plugin_name = plugin.name
461 for sv in plugin.plugin_settings():
462 for sv in plugin.plugin_settings():
462 newk = "auth_%s_%s" % (plugin_name, sv["name"])
463 newk = "auth_%s_%s" % (plugin_name, sv["name"])
463 # can be a LazyFormencode object from plugin settings
464 # can be a LazyFormencode object from plugin settings
464 validator = sv["validator"]
465 validator = sv["validator"]
465 if isinstance(validator, LazyFormencode):
466 if isinstance(validator, LazyFormencode):
466 validator = validator()
467 validator = validator()
467 # init all lazy validators from formencode.All
468 # init all lazy validators from formencode.All
468 if isinstance(validator, All):
469 if isinstance(validator, All):
469 init_validators = []
470 init_validators = []
470 for validator in validator.validators:
471 for validator in validator.validators:
471 if isinstance(validator, LazyFormencode):
472 if isinstance(validator, LazyFormencode):
472 validator = validator()
473 validator = validator()
473 init_validators.append(validator)
474 init_validators.append(validator)
474 validator.validators = init_validators
475 validator.validators = init_validators
475
476
476 self.add_field(newk, validator)
477 self.add_field(newk, validator)
477 formencode.Schema.__init__(self, *args, **kwargs)
478 formencode.Schema.__init__(self, *args, **kwargs)
478
479
479 return _AuthSettingsForm
480 return _AuthSettingsForm
480
481
481
482
482 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
483 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
483 tls_kind_choices):
484 tls_kind_choices):
484 class _LdapSettingsForm(formencode.Schema):
485 class _LdapSettingsForm(formencode.Schema):
485 allow_extra_fields = True
486 allow_extra_fields = True
486 filter_extra_fields = True
487 filter_extra_fields = True
487 #pre_validators = [LdapLibValidator]
488 #pre_validators = [LdapLibValidator]
488 ldap_active = v.StringBoolean(if_missing=False)
489 ldap_active = v.StringBoolean(if_missing=False)
489 ldap_host = v.UnicodeString(strip=True,)
490 ldap_host = v.UnicodeString(strip=True,)
490 ldap_port = v.Number(strip=True,)
491 ldap_port = v.Number(strip=True,)
491 ldap_tls_kind = v.OneOf(tls_kind_choices)
492 ldap_tls_kind = v.OneOf(tls_kind_choices)
492 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
493 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
493 ldap_dn_user = v.UnicodeString(strip=True,)
494 ldap_dn_user = v.UnicodeString(strip=True,)
494 ldap_dn_pass = v.UnicodeString(strip=True,)
495 ldap_dn_pass = v.UnicodeString(strip=True,)
495 ldap_base_dn = v.UnicodeString(strip=True,)
496 ldap_base_dn = v.UnicodeString(strip=True,)
496 ldap_filter = v.UnicodeString(strip=True,)
497 ldap_filter = v.UnicodeString(strip=True,)
497 ldap_search_scope = v.OneOf(search_scope_choices)
498 ldap_search_scope = v.OneOf(search_scope_choices)
498 ldap_attr_login = v.AttrLoginValidator()(not_empty=True)
499 ldap_attr_login = v.AttrLoginValidator()(not_empty=True)
499 ldap_attr_firstname = v.UnicodeString(strip=True,)
500 ldap_attr_firstname = v.UnicodeString(strip=True,)
500 ldap_attr_lastname = v.UnicodeString(strip=True,)
501 ldap_attr_lastname = v.UnicodeString(strip=True,)
501 ldap_attr_email = v.UnicodeString(strip=True,)
502 ldap_attr_email = v.UnicodeString(strip=True,)
502
503
503 return _LdapSettingsForm
504 return _LdapSettingsForm
504
505
505
506
506 def UserExtraEmailForm():
507 def UserExtraEmailForm():
507 class _UserExtraEmailForm(formencode.Schema):
508 class _UserExtraEmailForm(formencode.Schema):
508 email = All(v.UniqSystemEmail(), v.Email(not_empty=True))
509 email = All(v.UniqSystemEmail(), v.Email(not_empty=True))
509 return _UserExtraEmailForm
510 return _UserExtraEmailForm
510
511
511
512
512 def UserExtraIpForm():
513 def UserExtraIpForm():
513 class _UserExtraIpForm(formencode.Schema):
514 class _UserExtraIpForm(formencode.Schema):
514 ip = v.ValidIp()(not_empty=True)
515 ip = v.ValidIp()(not_empty=True)
515 return _UserExtraIpForm
516 return _UserExtraIpForm
516
517
517
518
518 def PullRequestForm(repo_id):
519 def PullRequestForm(repo_id):
519 class _PullRequestForm(formencode.Schema):
520 class _PullRequestForm(formencode.Schema):
520 allow_extra_fields = True
521 allow_extra_fields = True
521 filter_extra_fields = True
522 filter_extra_fields = True
522
523
523 org_repo = v.UnicodeString(strip=True, required=True)
524 org_repo = v.UnicodeString(strip=True, required=True)
524 org_ref = v.UnicodeString(strip=True, required=True)
525 org_ref = v.UnicodeString(strip=True, required=True)
525 other_repo = v.UnicodeString(strip=True, required=True)
526 other_repo = v.UnicodeString(strip=True, required=True)
526 other_ref = v.UnicodeString(strip=True, required=True)
527 other_ref = v.UnicodeString(strip=True, required=True)
527
528
528 pullrequest_title = v.UnicodeString(strip=True, required=True)
529 pullrequest_title = v.UnicodeString(strip=True, required=True)
529 pullrequest_desc = v.UnicodeString(strip=True, required=False)
530 pullrequest_desc = v.UnicodeString(strip=True, required=False)
530
531
531 return _PullRequestForm
532 return _PullRequestForm
532
533
533
534
534 def PullRequestPostForm():
535 def PullRequestPostForm():
535 class _PullRequestPostForm(formencode.Schema):
536 class _PullRequestPostForm(formencode.Schema):
536 allow_extra_fields = True
537 allow_extra_fields = True
537 filter_extra_fields = True
538 filter_extra_fields = True
538
539
539 pullrequest_title = v.UnicodeString(strip=True, required=True)
540 pullrequest_title = v.UnicodeString(strip=True, required=True)
540 pullrequest_desc = v.UnicodeString(strip=True, required=False)
541 pullrequest_desc = v.UnicodeString(strip=True, required=False)
541 org_review_members = v.Set()
542 org_review_members = v.Set()
542 review_members = v.Set()
543 review_members = v.Set()
543 updaterev = v.UnicodeString(strip=True, required=False, if_missing=None)
544 updaterev = v.UnicodeString(strip=True, required=False, if_missing=None)
544 owner = All(v.UnicodeString(strip=True, required=True),
545 owner = All(v.UnicodeString(strip=True, required=True),
545 v.ValidRepoUser())
546 v.ValidRepoUser())
546
547
547 return _PullRequestPostForm
548 return _PullRequestPostForm
548
549
549
550
550 def GistForm(lifetime_options):
551 def GistForm(lifetime_options):
551 class _GistForm(formencode.Schema):
552 class _GistForm(formencode.Schema):
552 allow_extra_fields = True
553 allow_extra_fields = True
553 filter_extra_fields = True
554 filter_extra_fields = True
554
555
555 filename = All(v.BasePath()(),
556 filename = All(v.BasePath()(),
556 v.UnicodeString(strip=True, required=False))
557 v.UnicodeString(strip=True, required=False))
557 description = v.UnicodeString(required=False, if_missing='')
558 description = v.UnicodeString(required=False, if_missing='')
558 lifetime = v.OneOf(lifetime_options)
559 lifetime = v.OneOf(lifetime_options)
559 mimetype = v.UnicodeString(required=False, if_missing=None)
560 mimetype = v.UnicodeString(required=False, if_missing=None)
560 content = v.UnicodeString(required=True, not_empty=True)
561 content = v.UnicodeString(required=True, not_empty=True)
561 public = v.UnicodeString(required=False, if_missing='')
562 public = v.UnicodeString(required=False, if_missing='')
562 private = v.UnicodeString(required=False, if_missing='')
563 private = v.UnicodeString(required=False, if_missing='')
563
564
564 return _GistForm
565 return _GistForm
@@ -1,532 +1,534 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
5 # (at your option) any later version.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU General Public License
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 """
14 """
15 kallithea.model.repo_group
15 kallithea.model.repo_group
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~
17
17
18 repo group model for Kallithea
18 repo group model for Kallithea
19
19
20 This file was forked by the Kallithea project in July 2014.
20 This file was forked by the Kallithea project in July 2014.
21 Original author and date, and relevant copyright and licensing information is below:
21 Original author and date, and relevant copyright and licensing information is below:
22 :created_on: Jan 25, 2011
22 :created_on: Jan 25, 2011
23 :author: marcink
23 :author: marcink
24 :copyright: (c) 2013 RhodeCode GmbH, and others.
24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 :license: GPLv3, see LICENSE.md for more details.
25 :license: GPLv3, see LICENSE.md for more details.
26 """
26 """
27
27
28
28
29 import datetime
29 import datetime
30 import logging
30 import logging
31 import os
31 import os
32 import shutil
32 import shutil
33 import traceback
33 import traceback
34
34
35 import kallithea.lib.utils2
35 import kallithea.lib.utils2
36 from kallithea.lib.utils2 import LazyProperty
36 from kallithea.lib.utils2 import LazyProperty
37 from kallithea.model import db, meta, repo
37 from kallithea.model import db, meta, repo
38
38
39
39
40 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
41
41
42
42
43 class RepoGroupModel(object):
43 class RepoGroupModel(object):
44
44
45 @LazyProperty
45 @LazyProperty
46 def repos_path(self):
46 def repos_path(self):
47 """
47 """
48 Gets the repositories root path from database
48 Gets the repositories root path from database
49 """
49 """
50
50
51 q = db.Ui.get_by_key('paths', '/')
51 q = db.Ui.get_by_key('paths', '/')
52 return q.ui_value
52 return q.ui_value
53
53
54 def _create_default_perms(self, new_group):
54 def _create_default_perms(self, new_group):
55 # create default permission
55 # create default permission
56 default_perm = 'group.read'
56 default_perm = 'group.read'
57 def_user = db.User.get_default_user()
57 def_user = db.User.get_default_user()
58 for p in def_user.user_perms:
58 for p in def_user.user_perms:
59 if p.permission.permission_name.startswith('group.'):
59 if p.permission.permission_name.startswith('group.'):
60 default_perm = p.permission.permission_name
60 default_perm = p.permission.permission_name
61 break
61 break
62
62
63 repo_group_to_perm = db.UserRepoGroupToPerm()
63 repo_group_to_perm = db.UserRepoGroupToPerm()
64 repo_group_to_perm.permission = db.Permission.get_by_key(default_perm)
64 repo_group_to_perm.permission = db.Permission.get_by_key(default_perm)
65
65
66 repo_group_to_perm.group = new_group
66 repo_group_to_perm.group = new_group
67 repo_group_to_perm.user_id = def_user.user_id
67 repo_group_to_perm.user_id = def_user.user_id
68 meta.Session().add(repo_group_to_perm)
68 meta.Session().add(repo_group_to_perm)
69 return repo_group_to_perm
69 return repo_group_to_perm
70
70
71 def _create_group(self, group_name):
71 def _create_group(self, group_name):
72 """
72 """
73 makes repository group on filesystem
73 makes repository group on filesystem
74
74
75 :param repo_name:
75 :param repo_name:
76 :param parent_id:
76 :param parent_id:
77 """
77 """
78
78
79 create_path = os.path.join(self.repos_path, group_name)
79 create_path = os.path.join(self.repos_path, group_name)
80 log.debug('creating new group in %s', create_path)
80 log.debug('creating new group in %s', create_path)
81
81
82 if os.path.isdir(create_path):
82 if os.path.isdir(create_path):
83 raise Exception('That directory already exists !')
83 raise Exception('That directory already exists !')
84
84
85 os.makedirs(create_path)
85 os.makedirs(create_path)
86 log.debug('Created group in %s', create_path)
86 log.debug('Created group in %s', create_path)
87
87
88 def _rename_group(self, old, new):
88 def _rename_group(self, old, new):
89 """
89 """
90 Renames a group on filesystem
90 Renames a group on filesystem
91
91
92 :param group_name:
92 :param group_name:
93 """
93 """
94
94
95 if old == new:
95 if old == new:
96 log.debug('skipping group rename')
96 log.debug('skipping group rename')
97 return
97 return
98
98
99 log.debug('renaming repository group from %s to %s', old, new)
99 log.debug('renaming repository group from %s to %s', old, new)
100
100
101 old_path = os.path.join(self.repos_path, old)
101 old_path = os.path.join(self.repos_path, old)
102 new_path = os.path.join(self.repos_path, new)
102 new_path = os.path.join(self.repos_path, new)
103
103
104 log.debug('renaming repos paths from %s to %s', old_path, new_path)
104 log.debug('renaming repos paths from %s to %s', old_path, new_path)
105
105
106 if os.path.isdir(new_path):
106 if os.path.isdir(new_path):
107 raise Exception('Was trying to rename to already '
107 raise Exception('Was trying to rename to already '
108 'existing dir %s' % new_path)
108 'existing dir %s' % new_path)
109 shutil.move(old_path, new_path)
109 shutil.move(old_path, new_path)
110
110
111 def _delete_group(self, group, force_delete=False):
111 def _delete_group(self, group, force_delete=False):
112 """
112 """
113 Deletes a group from a filesystem
113 Deletes a group from a filesystem
114
114
115 :param group: instance of group from database
115 :param group: instance of group from database
116 :param force_delete: use shutil rmtree to remove all objects
116 :param force_delete: use shutil rmtree to remove all objects
117 """
117 """
118 paths = group.full_path.split(kallithea.URL_SEP)
118 paths = group.full_path.split(kallithea.URL_SEP)
119 paths = os.sep.join(paths)
119 paths = os.sep.join(paths)
120
120
121 rm_path = os.path.join(self.repos_path, paths)
121 rm_path = os.path.join(self.repos_path, paths)
122 log.info("Removing group %s", rm_path)
122 log.info("Removing group %s", rm_path)
123 # delete only if that path really exists
123 # delete only if that path really exists
124 if os.path.isdir(rm_path):
124 if os.path.isdir(rm_path):
125 if force_delete:
125 if force_delete:
126 shutil.rmtree(rm_path)
126 shutil.rmtree(rm_path)
127 else:
127 else:
128 # archive that group
128 # archive that group
129 _now = datetime.datetime.now()
129 _now = datetime.datetime.now()
130 _ms = str(_now.microsecond).rjust(6, '0')
130 _ms = str(_now.microsecond).rjust(6, '0')
131 _d = 'rm__%s_GROUP_%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
131 _d = 'rm__%s_GROUP_%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
132 group.name)
132 group.name)
133 shutil.move(rm_path, os.path.join(self.repos_path, _d))
133 shutil.move(rm_path, os.path.join(self.repos_path, _d))
134
134
135 def create(self, group_name, group_description, owner, parent=None,
135 def create(self, group_name, group_description, owner, parent=None,
136 just_db=False, copy_permissions=False):
136 just_db=False, copy_permissions=False):
137 try:
137 try:
138 if kallithea.lib.utils2.repo_name_slug(group_name) != group_name:
138 if kallithea.lib.utils2.repo_name_slug(group_name) != group_name:
139 raise Exception('invalid repo group name %s' % group_name)
139 raise Exception('invalid repo group name %s' % group_name)
140
140
141 owner = db.User.guess_instance(owner)
141 owner = db.User.guess_instance(owner)
142 parent_group = db.RepoGroup.guess_instance(parent)
142 parent_group = db.RepoGroup.guess_instance(parent)
143 new_repo_group = db.RepoGroup()
143 new_repo_group = db.RepoGroup()
144 new_repo_group.owner = owner
144 new_repo_group.owner = owner
145 new_repo_group.group_description = group_description or group_name
145 new_repo_group.group_description = group_description or group_name
146 new_repo_group.parent_group = parent_group
146 new_repo_group.parent_group = parent_group
147 new_repo_group.group_name = new_repo_group.get_new_name(group_name)
147 new_repo_group.group_name = new_repo_group.get_new_name(group_name)
148
148
149 meta.Session().add(new_repo_group)
149 meta.Session().add(new_repo_group)
150
150
151 # create an ADMIN permission for owner except if we're super admin,
151 # create an ADMIN permission for owner except if we're super admin,
152 # later owner should go into the owner field of groups
152 # later owner should go into the owner field of groups
153 if not owner.is_admin:
153 if not owner.is_admin:
154 self.grant_user_permission(repo_group=new_repo_group,
154 self.grant_user_permission(repo_group=new_repo_group,
155 user=owner, perm='group.admin')
155 user=owner, perm='group.admin')
156
156
157 if parent_group and copy_permissions:
157 if parent_group and copy_permissions:
158 # copy permissions from parent
158 # copy permissions from parent
159 user_perms = db.UserRepoGroupToPerm.query() \
159 user_perms = db.UserRepoGroupToPerm.query() \
160 .filter(db.UserRepoGroupToPerm.group == parent_group).all()
160 .filter(db.UserRepoGroupToPerm.group == parent_group).all()
161
161
162 group_perms = db.UserGroupRepoGroupToPerm.query() \
162 group_perms = db.UserGroupRepoGroupToPerm.query() \
163 .filter(db.UserGroupRepoGroupToPerm.group == parent_group).all()
163 .filter(db.UserGroupRepoGroupToPerm.group == parent_group).all()
164
164
165 for perm in user_perms:
165 for perm in user_perms:
166 # don't copy over the permission for user who is creating
166 # don't copy over the permission for user who is creating
167 # this group, if he is not super admin he get's admin
167 # this group, if he is not super admin he get's admin
168 # permission set above
168 # permission set above
169 if perm.user != owner or owner.is_admin:
169 if perm.user != owner or owner.is_admin:
170 db.UserRepoGroupToPerm.create(perm.user, new_repo_group, perm.permission)
170 db.UserRepoGroupToPerm.create(perm.user, new_repo_group, perm.permission)
171
171
172 for perm in group_perms:
172 for perm in group_perms:
173 db.UserGroupRepoGroupToPerm.create(perm.users_group, new_repo_group, perm.permission)
173 db.UserGroupRepoGroupToPerm.create(perm.users_group, new_repo_group, perm.permission)
174 else:
174 else:
175 self._create_default_perms(new_repo_group)
175 self._create_default_perms(new_repo_group)
176
176
177 if not just_db:
177 if not just_db:
178 # we need to flush here, in order to check if database won't
178 # we need to flush here, in order to check if database won't
179 # throw any exceptions, create filesystem dirs at the very end
179 # throw any exceptions, create filesystem dirs at the very end
180 meta.Session().flush()
180 meta.Session().flush()
181 self._create_group(new_repo_group.group_name)
181 self._create_group(new_repo_group.group_name)
182
182
183 return new_repo_group
183 return new_repo_group
184 except Exception:
184 except Exception:
185 log.error(traceback.format_exc())
185 log.error(traceback.format_exc())
186 raise
186 raise
187
187
188 def _update_permissions(self, repo_group, perms_new=None,
188 def _update_permissions(self, repo_group, perms_new=None,
189 perms_updates=None, recursive=None,
189 perms_updates=None, recursive=None,
190 check_perms=True):
190 check_perms=True):
191 from kallithea.lib.auth import HasUserGroupPermissionLevel
191 from kallithea.lib.auth import HasUserGroupPermissionLevel
192
192
193 if not perms_new:
193 if not perms_new:
194 perms_new = []
194 perms_new = []
195 if not perms_updates:
195 if not perms_updates:
196 perms_updates = []
196 perms_updates = []
197
197
198 def _set_perm_user(obj, user, perm):
198 def _set_perm_user(obj, user, perm):
199 if isinstance(obj, db.RepoGroup):
199 if isinstance(obj, db.RepoGroup):
200 self.grant_user_permission(repo_group=obj, user=user, perm=perm)
200 self.grant_user_permission(repo_group=obj, user=user, perm=perm)
201 elif isinstance(obj, db.Repository):
201 elif isinstance(obj, db.Repository):
202 user = db.User.guess_instance(user)
202 user = db.User.guess_instance(user)
203
203
204 # private repos will not allow to change the default permissions
204 # private repos will not allow to change the default permissions
205 # using recursive mode
205 # using recursive mode
206 if obj.private and user.is_default_user:
206 if obj.private and user.is_default_user:
207 return
207 return
208
208
209 # we set group permission but we have to switch to repo
209 # we set group permission but we have to switch to repo
210 # permission
210 # permission
211 perm = perm.replace('group.', 'repository.')
211 perm = perm.replace('group.', 'repository.')
212 repo.RepoModel().grant_user_permission(
212 repo.RepoModel().grant_user_permission(
213 repo=obj, user=user, perm=perm
213 repo=obj, user=user, perm=perm
214 )
214 )
215
215
216 def _set_perm_group(obj, users_group, perm):
216 def _set_perm_group(obj, users_group, perm):
217 if isinstance(obj, db.RepoGroup):
217 if isinstance(obj, db.RepoGroup):
218 self.grant_user_group_permission(repo_group=obj,
218 self.grant_user_group_permission(repo_group=obj,
219 group_name=users_group,
219 group_name=users_group,
220 perm=perm)
220 perm=perm)
221 elif isinstance(obj, db.Repository):
221 elif isinstance(obj, db.Repository):
222 # we set group permission but we have to switch to repo
222 # we set group permission but we have to switch to repo
223 # permission
223 # permission
224 perm = perm.replace('group.', 'repository.')
224 perm = perm.replace('group.', 'repository.')
225 repo.RepoModel().grant_user_group_permission(
225 repo.RepoModel().grant_user_group_permission(
226 repo=obj, group_name=users_group, perm=perm
226 repo=obj, group_name=users_group, perm=perm
227 )
227 )
228
228
229 # start updates
229 # start updates
230 updates = []
230 updates = []
231 log.debug('Now updating permissions for %s in recursive mode:%s',
231 log.debug('Now updating permissions for %s in recursive mode:%s',
232 repo_group, recursive)
232 repo_group, recursive)
233
233
234 for obj in repo_group.recursive_groups_and_repos():
234 for obj in repo_group.recursive_groups_and_repos():
235 # iterated obj is an instance of a repos group or repository in
235 # iterated obj is an instance of a repos group or repository in
236 # that group, recursive option can be: none, repos, groups, all
236 # that group, recursive option can be: none, repos, groups, all
237 if recursive == 'all':
237 if recursive == 'all':
238 pass
238 pass
239 elif recursive == 'repos':
239 elif recursive == 'repos':
240 # skip groups, other than this one
240 # skip groups, other than this one
241 if isinstance(obj, db.RepoGroup) and not obj == repo_group:
241 if isinstance(obj, db.RepoGroup) and not obj == repo_group:
242 continue
242 continue
243 elif recursive == 'groups':
243 elif recursive == 'groups':
244 # skip repos
244 # skip repos
245 if isinstance(obj, db.Repository):
245 if isinstance(obj, db.Repository):
246 continue
246 continue
247 else: # recursive == 'none': # DEFAULT don't apply to iterated objects
247 else: # recursive == 'none': # DEFAULT don't apply to iterated objects
248 obj = repo_group
248 obj = repo_group
249 # also we do a break at the end of this loop.
249 # also we do a break at the end of this loop.
250
250
251 # update permissions
251 # update permissions
252 for member, perm, member_type in perms_updates:
252 for member, perm, member_type in perms_updates:
253 ## set for user
253 ## set for user
254 if member_type == 'user':
254 if member_type == 'user':
255 # this updates also current one if found
255 # this updates also current one if found
256 _set_perm_user(obj, user=member, perm=perm)
256 _set_perm_user(obj, user=member, perm=perm)
257 ## set for user group
257 ## set for user group
258 else:
258 else:
259 # check if we have permissions to alter this usergroup's access
259 # check if we have permissions to alter this usergroup's access
260 if not check_perms or HasUserGroupPermissionLevel('read')(member):
260 if not check_perms or HasUserGroupPermissionLevel('read')(member):
261 _set_perm_group(obj, users_group=member, perm=perm)
261 _set_perm_group(obj, users_group=member, perm=perm)
262 # set new permissions
262 # set new permissions
263 for member, perm, member_type in perms_new:
263 for member, perm, member_type in perms_new:
264 if member_type == 'user':
264 if member_type == 'user':
265 _set_perm_user(obj, user=member, perm=perm)
265 _set_perm_user(obj, user=member, perm=perm)
266 else:
266 else:
267 # check if we have permissions to alter this usergroup's access
267 # check if we have permissions to alter this usergroup's access
268 if not check_perms or HasUserGroupPermissionLevel('read')(member):
268 if not check_perms or HasUserGroupPermissionLevel('read')(member):
269 _set_perm_group(obj, users_group=member, perm=perm)
269 _set_perm_group(obj, users_group=member, perm=perm)
270 updates.append(obj)
270 updates.append(obj)
271 # if it's not recursive call for all,repos,groups
271 # if it's not recursive call for all,repos,groups
272 # break the loop and don't proceed with other changes
272 # break the loop and don't proceed with other changes
273 if recursive not in ['all', 'repos', 'groups']:
273 if recursive not in ['all', 'repos', 'groups']:
274 break
274 break
275
275
276 return updates
276 return updates
277
277
278 def update(self, repo_group, repo_group_args):
278 def update(self, repo_group, repo_group_args):
279 try:
279 try:
280 repo_group = db.RepoGroup.guess_instance(repo_group)
280 repo_group = db.RepoGroup.guess_instance(repo_group)
281 old_path = repo_group.full_path
281 old_path = repo_group.full_path
282
282
283 # change properties
283 # change properties
284 if 'owner' in repo_group_args:
285 repo_group.owner = db.User.get_by_username(repo_group_args['owner'])
284 if 'group_description' in repo_group_args:
286 if 'group_description' in repo_group_args:
285 repo_group.group_description = repo_group_args['group_description']
287 repo_group.group_description = repo_group_args['group_description']
286 if 'parent_group_id' in repo_group_args:
288 if 'parent_group_id' in repo_group_args:
287 assert repo_group_args['parent_group_id'] != '-1', repo_group_args # RepoGroupForm should have converted to None
289 assert repo_group_args['parent_group_id'] != '-1', repo_group_args # RepoGroupForm should have converted to None
288 repo_group.parent_group = db.RepoGroup.get(repo_group_args['parent_group_id'])
290 repo_group.parent_group = db.RepoGroup.get(repo_group_args['parent_group_id'])
289 repo_group.group_name = repo_group.get_new_name(repo_group.name)
291 repo_group.group_name = repo_group.get_new_name(repo_group.name)
290 if 'group_name' in repo_group_args:
292 if 'group_name' in repo_group_args:
291 group_name = repo_group_args['group_name']
293 group_name = repo_group_args['group_name']
292 if kallithea.lib.utils2.repo_name_slug(group_name) != group_name:
294 if kallithea.lib.utils2.repo_name_slug(group_name) != group_name:
293 raise Exception('invalid repo group name %s' % group_name)
295 raise Exception('invalid repo group name %s' % group_name)
294 repo_group.group_name = repo_group.get_new_name(group_name)
296 repo_group.group_name = repo_group.get_new_name(group_name)
295 new_path = repo_group.full_path
297 new_path = repo_group.full_path
296 meta.Session().add(repo_group)
298 meta.Session().add(repo_group)
297
299
298 # iterate over all members of this groups and do fixes
300 # iterate over all members of this groups and do fixes
299 # if obj is a repoGroup also fix the name of the group according
301 # if obj is a repoGroup also fix the name of the group according
300 # to the parent
302 # to the parent
301 # if obj is a Repo fix it's name
303 # if obj is a Repo fix it's name
302 # this can be potentially heavy operation
304 # this can be potentially heavy operation
303 for obj in repo_group.recursive_groups_and_repos():
305 for obj in repo_group.recursive_groups_and_repos():
304 # set the value from it's parent
306 # set the value from it's parent
305 if isinstance(obj, db.RepoGroup):
307 if isinstance(obj, db.RepoGroup):
306 new_name = obj.get_new_name(obj.name)
308 new_name = obj.get_new_name(obj.name)
307 log.debug('Fixing group %s to new name %s'
309 log.debug('Fixing group %s to new name %s'
308 % (obj.group_name, new_name))
310 % (obj.group_name, new_name))
309 obj.group_name = new_name
311 obj.group_name = new_name
310 elif isinstance(obj, db.Repository):
312 elif isinstance(obj, db.Repository):
311 # we need to get all repositories from this new group and
313 # we need to get all repositories from this new group and
312 # rename them accordingly to new group path
314 # rename them accordingly to new group path
313 new_name = obj.get_new_name(obj.just_name)
315 new_name = obj.get_new_name(obj.just_name)
314 log.debug('Fixing repo %s to new name %s'
316 log.debug('Fixing repo %s to new name %s'
315 % (obj.repo_name, new_name))
317 % (obj.repo_name, new_name))
316 obj.repo_name = new_name
318 obj.repo_name = new_name
317
319
318 self._rename_group(old_path, new_path)
320 self._rename_group(old_path, new_path)
319
321
320 return repo_group
322 return repo_group
321 except Exception:
323 except Exception:
322 log.error(traceback.format_exc())
324 log.error(traceback.format_exc())
323 raise
325 raise
324
326
325 def delete(self, repo_group, force_delete=False):
327 def delete(self, repo_group, force_delete=False):
326 repo_group = db.RepoGroup.guess_instance(repo_group)
328 repo_group = db.RepoGroup.guess_instance(repo_group)
327 try:
329 try:
328 meta.Session().delete(repo_group)
330 meta.Session().delete(repo_group)
329 self._delete_group(repo_group, force_delete)
331 self._delete_group(repo_group, force_delete)
330 except Exception:
332 except Exception:
331 log.error('Error removing repo_group %s', repo_group)
333 log.error('Error removing repo_group %s', repo_group)
332 raise
334 raise
333
335
334 def add_permission(self, repo_group, obj, obj_type, perm, recursive):
336 def add_permission(self, repo_group, obj, obj_type, perm, recursive):
335 repo_group = db.RepoGroup.guess_instance(repo_group)
337 repo_group = db.RepoGroup.guess_instance(repo_group)
336 perm = db.Permission.guess_instance(perm)
338 perm = db.Permission.guess_instance(perm)
337
339
338 for el in repo_group.recursive_groups_and_repos():
340 for el in repo_group.recursive_groups_and_repos():
339 # iterated obj is an instance of a repos group or repository in
341 # iterated obj is an instance of a repos group or repository in
340 # that group, recursive option can be: none, repos, groups, all
342 # that group, recursive option can be: none, repos, groups, all
341 if recursive == 'all':
343 if recursive == 'all':
342 pass
344 pass
343 elif recursive == 'repos':
345 elif recursive == 'repos':
344 # skip groups, other than this one
346 # skip groups, other than this one
345 if isinstance(el, db.RepoGroup) and not el == repo_group:
347 if isinstance(el, db.RepoGroup) and not el == repo_group:
346 continue
348 continue
347 elif recursive == 'groups':
349 elif recursive == 'groups':
348 # skip repos
350 # skip repos
349 if isinstance(el, db.Repository):
351 if isinstance(el, db.Repository):
350 continue
352 continue
351 else: # recursive == 'none': # DEFAULT don't apply to iterated objects
353 else: # recursive == 'none': # DEFAULT don't apply to iterated objects
352 el = repo_group
354 el = repo_group
353 # also we do a break at the end of this loop.
355 # also we do a break at the end of this loop.
354
356
355 if isinstance(el, db.RepoGroup):
357 if isinstance(el, db.RepoGroup):
356 if obj_type == 'user':
358 if obj_type == 'user':
357 RepoGroupModel().grant_user_permission(el, user=obj, perm=perm)
359 RepoGroupModel().grant_user_permission(el, user=obj, perm=perm)
358 elif obj_type == 'user_group':
360 elif obj_type == 'user_group':
359 RepoGroupModel().grant_user_group_permission(el, group_name=obj, perm=perm)
361 RepoGroupModel().grant_user_group_permission(el, group_name=obj, perm=perm)
360 else:
362 else:
361 raise Exception('undefined object type %s' % obj_type)
363 raise Exception('undefined object type %s' % obj_type)
362 elif isinstance(el, db.Repository):
364 elif isinstance(el, db.Repository):
363 # for repos we need to hotfix the name of permission
365 # for repos we need to hotfix the name of permission
364 _perm = perm.permission_name.replace('group.', 'repository.')
366 _perm = perm.permission_name.replace('group.', 'repository.')
365 if obj_type == 'user':
367 if obj_type == 'user':
366 repo.RepoModel().grant_user_permission(el, user=obj, perm=_perm)
368 repo.RepoModel().grant_user_permission(el, user=obj, perm=_perm)
367 elif obj_type == 'user_group':
369 elif obj_type == 'user_group':
368 repo.RepoModel().grant_user_group_permission(el, group_name=obj, perm=_perm)
370 repo.RepoModel().grant_user_group_permission(el, group_name=obj, perm=_perm)
369 else:
371 else:
370 raise Exception('undefined object type %s' % obj_type)
372 raise Exception('undefined object type %s' % obj_type)
371 else:
373 else:
372 raise Exception('el should be instance of Repository or '
374 raise Exception('el should be instance of Repository or '
373 'RepositoryGroup got %s instead' % type(el))
375 'RepositoryGroup got %s instead' % type(el))
374
376
375 # if it's not recursive call for all,repos,groups
377 # if it's not recursive call for all,repos,groups
376 # break the loop and don't proceed with other changes
378 # break the loop and don't proceed with other changes
377 if recursive not in ['all', 'repos', 'groups']:
379 if recursive not in ['all', 'repos', 'groups']:
378 break
380 break
379
381
380 def delete_permission(self, repo_group, obj, obj_type, recursive):
382 def delete_permission(self, repo_group, obj, obj_type, recursive):
381 """
383 """
382 Revokes permission for repo_group for given obj(user or users_group),
384 Revokes permission for repo_group for given obj(user or users_group),
383 obj_type can be user or user group
385 obj_type can be user or user group
384
386
385 :param repo_group:
387 :param repo_group:
386 :param obj: user or user group id
388 :param obj: user or user group id
387 :param obj_type: user or user group type
389 :param obj_type: user or user group type
388 :param recursive: recurse to all children of group
390 :param recursive: recurse to all children of group
389 """
391 """
390 repo_group = db.RepoGroup.guess_instance(repo_group)
392 repo_group = db.RepoGroup.guess_instance(repo_group)
391
393
392 for el in repo_group.recursive_groups_and_repos():
394 for el in repo_group.recursive_groups_and_repos():
393 # iterated obj is an instance of a repos group or repository in
395 # iterated obj is an instance of a repos group or repository in
394 # that group, recursive option can be: none, repos, groups, all
396 # that group, recursive option can be: none, repos, groups, all
395 if recursive == 'all':
397 if recursive == 'all':
396 pass
398 pass
397 elif recursive == 'repos':
399 elif recursive == 'repos':
398 # skip groups, other than this one
400 # skip groups, other than this one
399 if isinstance(el, db.RepoGroup) and not el == repo_group:
401 if isinstance(el, db.RepoGroup) and not el == repo_group:
400 continue
402 continue
401 elif recursive == 'groups':
403 elif recursive == 'groups':
402 # skip repos
404 # skip repos
403 if isinstance(el, db.Repository):
405 if isinstance(el, db.Repository):
404 continue
406 continue
405 else: # recursive == 'none': # DEFAULT don't apply to iterated objects
407 else: # recursive == 'none': # DEFAULT don't apply to iterated objects
406 el = repo_group
408 el = repo_group
407 # also we do a break at the end of this loop.
409 # also we do a break at the end of this loop.
408
410
409 if isinstance(el, db.RepoGroup):
411 if isinstance(el, db.RepoGroup):
410 if obj_type == 'user':
412 if obj_type == 'user':
411 RepoGroupModel().revoke_user_permission(el, user=obj)
413 RepoGroupModel().revoke_user_permission(el, user=obj)
412 elif obj_type == 'user_group':
414 elif obj_type == 'user_group':
413 RepoGroupModel().revoke_user_group_permission(el, group_name=obj)
415 RepoGroupModel().revoke_user_group_permission(el, group_name=obj)
414 else:
416 else:
415 raise Exception('undefined object type %s' % obj_type)
417 raise Exception('undefined object type %s' % obj_type)
416 elif isinstance(el, db.Repository):
418 elif isinstance(el, db.Repository):
417 if obj_type == 'user':
419 if obj_type == 'user':
418 repo.RepoModel().revoke_user_permission(el, user=obj)
420 repo.RepoModel().revoke_user_permission(el, user=obj)
419 elif obj_type == 'user_group':
421 elif obj_type == 'user_group':
420 repo.RepoModel().revoke_user_group_permission(el, group_name=obj)
422 repo.RepoModel().revoke_user_group_permission(el, group_name=obj)
421 else:
423 else:
422 raise Exception('undefined object type %s' % obj_type)
424 raise Exception('undefined object type %s' % obj_type)
423 else:
425 else:
424 raise Exception('el should be instance of Repository or '
426 raise Exception('el should be instance of Repository or '
425 'RepositoryGroup got %s instead' % type(el))
427 'RepositoryGroup got %s instead' % type(el))
426
428
427 # if it's not recursive call for all,repos,groups
429 # if it's not recursive call for all,repos,groups
428 # break the loop and don't proceed with other changes
430 # break the loop and don't proceed with other changes
429 if recursive not in ['all', 'repos', 'groups']:
431 if recursive not in ['all', 'repos', 'groups']:
430 break
432 break
431
433
432 def grant_user_permission(self, repo_group, user, perm):
434 def grant_user_permission(self, repo_group, user, perm):
433 """
435 """
434 Grant permission for user on given repository group, or update
436 Grant permission for user on given repository group, or update
435 existing one if found
437 existing one if found
436
438
437 :param repo_group: Instance of RepoGroup, repositories_group_id,
439 :param repo_group: Instance of RepoGroup, repositories_group_id,
438 or repositories_group name
440 or repositories_group name
439 :param user: Instance of User, user_id or username
441 :param user: Instance of User, user_id or username
440 :param perm: Instance of Permission, or permission_name
442 :param perm: Instance of Permission, or permission_name
441 """
443 """
442
444
443 repo_group = db.RepoGroup.guess_instance(repo_group)
445 repo_group = db.RepoGroup.guess_instance(repo_group)
444 user = db.User.guess_instance(user)
446 user = db.User.guess_instance(user)
445 permission = db.Permission.guess_instance(perm)
447 permission = db.Permission.guess_instance(perm)
446
448
447 # check if we have that permission already
449 # check if we have that permission already
448 obj = db.UserRepoGroupToPerm.query() \
450 obj = db.UserRepoGroupToPerm.query() \
449 .filter(db.UserRepoGroupToPerm.user == user) \
451 .filter(db.UserRepoGroupToPerm.user == user) \
450 .filter(db.UserRepoGroupToPerm.group == repo_group) \
452 .filter(db.UserRepoGroupToPerm.group == repo_group) \
451 .scalar()
453 .scalar()
452 if obj is None:
454 if obj is None:
453 # create new !
455 # create new !
454 obj = db.UserRepoGroupToPerm()
456 obj = db.UserRepoGroupToPerm()
455 meta.Session().add(obj)
457 meta.Session().add(obj)
456 obj.group = repo_group
458 obj.group = repo_group
457 obj.user = user
459 obj.user = user
458 obj.permission = permission
460 obj.permission = permission
459 log.debug('Granted perm %s to %s on %s', perm, user, repo_group)
461 log.debug('Granted perm %s to %s on %s', perm, user, repo_group)
460 return obj
462 return obj
461
463
462 def revoke_user_permission(self, repo_group, user):
464 def revoke_user_permission(self, repo_group, user):
463 """
465 """
464 Revoke permission for user on given repository group
466 Revoke permission for user on given repository group
465
467
466 :param repo_group: Instance of RepoGroup, repositories_group_id,
468 :param repo_group: Instance of RepoGroup, repositories_group_id,
467 or repositories_group name
469 or repositories_group name
468 :param user: Instance of User, user_id or username
470 :param user: Instance of User, user_id or username
469 """
471 """
470
472
471 repo_group = db.RepoGroup.guess_instance(repo_group)
473 repo_group = db.RepoGroup.guess_instance(repo_group)
472 user = db.User.guess_instance(user)
474 user = db.User.guess_instance(user)
473
475
474 obj = db.UserRepoGroupToPerm.query() \
476 obj = db.UserRepoGroupToPerm.query() \
475 .filter(db.UserRepoGroupToPerm.user == user) \
477 .filter(db.UserRepoGroupToPerm.user == user) \
476 .filter(db.UserRepoGroupToPerm.group == repo_group) \
478 .filter(db.UserRepoGroupToPerm.group == repo_group) \
477 .scalar()
479 .scalar()
478 if obj is not None:
480 if obj is not None:
479 meta.Session().delete(obj)
481 meta.Session().delete(obj)
480 log.debug('Revoked perm on %s on %s', repo_group, user)
482 log.debug('Revoked perm on %s on %s', repo_group, user)
481
483
482 def grant_user_group_permission(self, repo_group, group_name, perm):
484 def grant_user_group_permission(self, repo_group, group_name, perm):
483 """
485 """
484 Grant permission for user group on given repository group, or update
486 Grant permission for user group on given repository group, or update
485 existing one if found
487 existing one if found
486
488
487 :param repo_group: Instance of RepoGroup, repositories_group_id,
489 :param repo_group: Instance of RepoGroup, repositories_group_id,
488 or repositories_group name
490 or repositories_group name
489 :param group_name: Instance of UserGroup, users_group_id,
491 :param group_name: Instance of UserGroup, users_group_id,
490 or user group name
492 or user group name
491 :param perm: Instance of Permission, or permission_name
493 :param perm: Instance of Permission, or permission_name
492 """
494 """
493 repo_group = db.RepoGroup.guess_instance(repo_group)
495 repo_group = db.RepoGroup.guess_instance(repo_group)
494 group_name = db.UserGroup.guess_instance(group_name)
496 group_name = db.UserGroup.guess_instance(group_name)
495 permission = db.Permission.guess_instance(perm)
497 permission = db.Permission.guess_instance(perm)
496
498
497 # check if we have that permission already
499 # check if we have that permission already
498 obj = db.UserGroupRepoGroupToPerm.query() \
500 obj = db.UserGroupRepoGroupToPerm.query() \
499 .filter(db.UserGroupRepoGroupToPerm.group == repo_group) \
501 .filter(db.UserGroupRepoGroupToPerm.group == repo_group) \
500 .filter(db.UserGroupRepoGroupToPerm.users_group == group_name) \
502 .filter(db.UserGroupRepoGroupToPerm.users_group == group_name) \
501 .scalar()
503 .scalar()
502
504
503 if obj is None:
505 if obj is None:
504 # create new
506 # create new
505 obj = db.UserGroupRepoGroupToPerm()
507 obj = db.UserGroupRepoGroupToPerm()
506 meta.Session().add(obj)
508 meta.Session().add(obj)
507
509
508 obj.group = repo_group
510 obj.group = repo_group
509 obj.users_group = group_name
511 obj.users_group = group_name
510 obj.permission = permission
512 obj.permission = permission
511 log.debug('Granted perm %s to %s on %s', perm, group_name, repo_group)
513 log.debug('Granted perm %s to %s on %s', perm, group_name, repo_group)
512 return obj
514 return obj
513
515
514 def revoke_user_group_permission(self, repo_group, group_name):
516 def revoke_user_group_permission(self, repo_group, group_name):
515 """
517 """
516 Revoke permission for user group on given repository group
518 Revoke permission for user group on given repository group
517
519
518 :param repo_group: Instance of RepoGroup, repositories_group_id,
520 :param repo_group: Instance of RepoGroup, repositories_group_id,
519 or repositories_group name
521 or repositories_group name
520 :param group_name: Instance of UserGroup, users_group_id,
522 :param group_name: Instance of UserGroup, users_group_id,
521 or user group name
523 or user group name
522 """
524 """
523 repo_group = db.RepoGroup.guess_instance(repo_group)
525 repo_group = db.RepoGroup.guess_instance(repo_group)
524 group_name = db.UserGroup.guess_instance(group_name)
526 group_name = db.UserGroup.guess_instance(group_name)
525
527
526 obj = db.UserGroupRepoGroupToPerm.query() \
528 obj = db.UserGroupRepoGroupToPerm.query() \
527 .filter(db.UserGroupRepoGroupToPerm.group == repo_group) \
529 .filter(db.UserGroupRepoGroupToPerm.group == repo_group) \
528 .filter(db.UserGroupRepoGroupToPerm.users_group == group_name) \
530 .filter(db.UserGroupRepoGroupToPerm.users_group == group_name) \
529 .scalar()
531 .scalar()
530 if obj is not None:
532 if obj is not None:
531 meta.Session().delete(obj)
533 meta.Session().delete(obj)
532 log.debug('Revoked perm to %s on %s', repo_group, group_name)
534 log.debug('Revoked perm to %s on %s', repo_group, group_name)
@@ -1,51 +1,59 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 ${h.form(url('update_repos_group',group_name=c.repo_group.group_name))}
2 ${h.form(url('update_repos_group',group_name=c.repo_group.group_name))}
3 <div class="form">
3 <div class="form">
4 <div class="form-group">
4 <div class="form-group">
5 <label class="control-label" for="group_name">${_('Group name')}:</label>
5 <label class="control-label" for="group_name">${_('Group name')}:</label>
6 <div>
6 <div>
7 ${h.text('group_name',class_='form-control')}
7 ${h.text('group_name',class_='form-control')}
8 </div>
8 </div>
9 </div>
9 </div>
10
10
11 <div class="form-group">
11 <div class="form-group">
12 <label class="control-label" for="owner">${_('Owner')}:</label>
13 <div>
14 ${h.text('owner',class_='form-control', placeholder=_('Type name of user'))}
15 </div>
16 </div>
17
18 <div class="form-group">
12 <label class="control-label" for="group_description">${_('Description')}:</label>
19 <label class="control-label" for="group_description">${_('Description')}:</label>
13 <div>
20 <div>
14 ${h.textarea('group_description',cols=23,rows=5,class_='form-control')}
21 ${h.textarea('group_description',cols=23,rows=5,class_='form-control')}
15 </div>
22 </div>
16 </div>
23 </div>
17
24
18 <div class="form-group">
25 <div class="form-group">
19 <label class="control-label" for="parent_group_id">${_('Group parent')}:</label>
26 <label class="control-label" for="parent_group_id">${_('Group parent')}:</label>
20 <div>
27 <div>
21 ${h.select('parent_group_id','',c.repo_groups,class_='form-control')}
28 ${h.select('parent_group_id','',c.repo_groups,class_='form-control')}
22 </div>
29 </div>
23 </div>
30 </div>
24
31
25 <div class="form-group">
32 <div class="form-group">
26 <div class="buttons">
33 <div class="buttons">
27 ${h.submit('save',_('Save'),class_="btn btn-default")}
34 ${h.submit('save',_('Save'),class_="btn btn-default")}
28 ${h.reset('reset',_('Reset'),class_="btn btn-default")}
35 ${h.reset('reset',_('Reset'),class_="btn btn-default")}
29 </div>
36 </div>
30 </div>
37 </div>
31 </div>
38 </div>
32 ${h.end_form()}
39 ${h.end_form()}
33
40
34 ${h.form(url('delete_repo_group', group_name=c.repo_group.group_name))}
41 ${h.form(url('delete_repo_group', group_name=c.repo_group.group_name))}
35 <div class="form">
42 <div class="form">
36 <div class="form-group">
43 <div class="form-group">
37 <div class="buttons">
44 <div class="buttons">
38 ${h.submit('remove_%s' % c.repo_group.group_name,_('Remove this group'),class_="btn btn-danger",onclick="return confirm('"+_('Confirm to delete this group')+"');")}
45 ${h.submit('remove_%s' % c.repo_group.group_name,_('Remove this group'),class_="btn btn-danger",onclick="return confirm('"+_('Confirm to delete this group')+"');")}
39 </div>
46 </div>
40 </div>
47 </div>
41 </div>
48 </div>
42 ${h.end_form()}
49 ${h.end_form()}
43
50
44 <script>
51 <script>
45 'use strict';
52 'use strict';
46 $(document).ready(function(){
53 $(document).ready(function(){
47 $("#parent_group_id").select2({
54 $("#parent_group_id").select2({
48 'dropdownAutoWidth': true
55 'dropdownAutoWidth': true
49 });
56 });
57 SimpleUserAutoComplete($('#owner'));
50 });
58 });
51 </script>
59 </script>
@@ -1,2921 +1,2921 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
5 # (at your option) any later version.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU General Public License
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14
14
15 """
15 """
16 Tests for the JSON-RPC web api.
16 Tests for the JSON-RPC web api.
17 """
17 """
18
18
19 import datetime
19 import datetime
20 import os
20 import os
21 import random
21 import random
22 import re
22 import re
23 import string
23 import string
24 from typing import Sized
24 from typing import Sized
25
25
26 import mock
26 import mock
27 import pytest
27 import pytest
28 from webtest import TestApp
28 from webtest import TestApp
29
29
30 from kallithea.lib import ext_json
30 from kallithea.lib import ext_json
31 from kallithea.lib.auth import AuthUser
31 from kallithea.lib.auth import AuthUser
32 from kallithea.lib.utils2 import ascii_bytes
32 from kallithea.lib.utils2 import ascii_bytes
33 from kallithea.model import db, meta
33 from kallithea.model import db, meta
34 from kallithea.model.changeset_status import ChangesetStatusModel
34 from kallithea.model.changeset_status import ChangesetStatusModel
35 from kallithea.model.gist import GistModel
35 from kallithea.model.gist import GistModel
36 from kallithea.model.pull_request import PullRequestModel
36 from kallithea.model.pull_request import PullRequestModel
37 from kallithea.model.repo import RepoModel
37 from kallithea.model.repo import RepoModel
38 from kallithea.model.repo_group import RepoGroupModel
38 from kallithea.model.repo_group import RepoGroupModel
39 from kallithea.model.scm import ScmModel
39 from kallithea.model.scm import ScmModel
40 from kallithea.model.user import UserModel
40 from kallithea.model.user import UserModel
41 from kallithea.model.user_group import UserGroupModel
41 from kallithea.model.user_group import UserGroupModel
42 from kallithea.tests import base
42 from kallithea.tests import base
43 from kallithea.tests.fixture import Fixture, raise_exception
43 from kallithea.tests.fixture import Fixture, raise_exception
44
44
45
45
46 API_URL = '/_admin/api'
46 API_URL = '/_admin/api'
47 TEST_USER_GROUP = 'test_user_group'
47 TEST_USER_GROUP = 'test_user_group'
48 TEST_REPO_GROUP = 'test_repo_group'
48 TEST_REPO_GROUP = 'test_repo_group'
49
49
50 fixture = Fixture()
50 fixture = Fixture()
51
51
52
52
53 def _build_data(apikey, method, **kw):
53 def _build_data(apikey, method, **kw):
54 """
54 """
55 Builds API data with given random ID
55 Builds API data with given random ID
56 For convenience, the json is returned as str
56 For convenience, the json is returned as str
57 """
57 """
58 random_id = random.randrange(1, 9999)
58 random_id = random.randrange(1, 9999)
59 return random_id, ext_json.dumps({
59 return random_id, ext_json.dumps({
60 "id": random_id,
60 "id": random_id,
61 "api_key": apikey,
61 "api_key": apikey,
62 "method": method,
62 "method": method,
63 "args": kw
63 "args": kw
64 })
64 })
65
65
66
66
67 jsonify = lambda obj: ext_json.loads(ext_json.dumps(obj))
67 jsonify = lambda obj: ext_json.loads(ext_json.dumps(obj))
68
68
69
69
70 def api_call(test_obj, params):
70 def api_call(test_obj, params):
71 response = test_obj.app.post(API_URL, content_type='application/json',
71 response = test_obj.app.post(API_URL, content_type='application/json',
72 params=params)
72 params=params)
73 return response
73 return response
74
74
75
75
76 ## helpers
76 ## helpers
77 def make_user_group(name=TEST_USER_GROUP):
77 def make_user_group(name=TEST_USER_GROUP):
78 gr = fixture.create_user_group(name, cur_user=base.TEST_USER_ADMIN_LOGIN)
78 gr = fixture.create_user_group(name, cur_user=base.TEST_USER_ADMIN_LOGIN)
79 UserGroupModel().add_user_to_group(user_group=gr,
79 UserGroupModel().add_user_to_group(user_group=gr,
80 user=base.TEST_USER_ADMIN_LOGIN)
80 user=base.TEST_USER_ADMIN_LOGIN)
81 meta.Session().commit()
81 meta.Session().commit()
82 return gr
82 return gr
83
83
84
84
85 def make_repo_group(name=TEST_REPO_GROUP):
85 def make_repo_group(name=TEST_REPO_GROUP):
86 gr = fixture.create_repo_group(name, cur_user=base.TEST_USER_ADMIN_LOGIN)
86 gr = fixture.create_repo_group(name, cur_user=base.TEST_USER_ADMIN_LOGIN)
87 meta.Session().commit()
87 meta.Session().commit()
88 return gr
88 return gr
89
89
90
90
91 class _BaseTestApi(object):
91 class _BaseTestApi(object):
92 app: TestApp # assigned by app_fixture in subclass TestController mixin
92 app: TestApp # assigned by app_fixture in subclass TestController mixin
93 # assigned in subclass:
93 # assigned in subclass:
94 REPO: str
94 REPO: str
95 REPO_TYPE: str
95 REPO_TYPE: str
96 TEST_REVISION: str
96 TEST_REVISION: str
97 TEST_PR_SRC: str
97 TEST_PR_SRC: str
98 TEST_PR_DST: str
98 TEST_PR_DST: str
99 TEST_PR_REVISIONS: Sized
99 TEST_PR_REVISIONS: Sized
100
100
101 @classmethod
101 @classmethod
102 def setup_class(cls):
102 def setup_class(cls):
103 cls.usr = db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN)
103 cls.usr = db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN)
104 cls.apikey = cls.usr.api_key
104 cls.apikey = cls.usr.api_key
105 cls.test_user = UserModel().create_or_update(
105 cls.test_user = UserModel().create_or_update(
106 username='test-api',
106 username='test-api',
107 password='test',
107 password='test',
108 email='test@example.com',
108 email='test@example.com',
109 firstname='first',
109 firstname='first',
110 lastname='last'
110 lastname='last'
111 )
111 )
112 meta.Session().commit()
112 meta.Session().commit()
113 cls.TEST_USER_LOGIN = cls.test_user.username
113 cls.TEST_USER_LOGIN = cls.test_user.username
114 cls.apikey_regular = cls.test_user.api_key
114 cls.apikey_regular = cls.test_user.api_key
115
115
116 @classmethod
116 @classmethod
117 def teardown_class(cls):
117 def teardown_class(cls):
118 pass
118 pass
119
119
120 def setup_method(self, method):
120 def setup_method(self, method):
121 make_user_group()
121 make_user_group()
122 make_repo_group()
122 make_repo_group()
123
123
124 def teardown_method(self, method):
124 def teardown_method(self, method):
125 fixture.destroy_user_group(TEST_USER_GROUP)
125 fixture.destroy_user_group(TEST_USER_GROUP)
126 fixture.destroy_gists()
126 fixture.destroy_gists()
127 fixture.destroy_repo_group(TEST_REPO_GROUP)
127 fixture.destroy_repo_group(TEST_REPO_GROUP)
128
128
129 def _compare_ok(self, id_, expected, given):
129 def _compare_ok(self, id_, expected, given):
130 expected = jsonify({
130 expected = jsonify({
131 'id': id_,
131 'id': id_,
132 'error': None,
132 'error': None,
133 'result': expected
133 'result': expected
134 })
134 })
135 given = ext_json.loads(given)
135 given = ext_json.loads(given)
136 assert expected == given, (expected, given)
136 assert expected == given, (expected, given)
137
137
138 def _compare_error(self, id_, expected, given):
138 def _compare_error(self, id_, expected, given):
139 expected = jsonify({
139 expected = jsonify({
140 'id': id_,
140 'id': id_,
141 'error': expected,
141 'error': expected,
142 'result': None
142 'result': None
143 })
143 })
144 given = ext_json.loads(given)
144 given = ext_json.loads(given)
145 assert expected == given, (expected, given)
145 assert expected == given, (expected, given)
146
146
147 def test_api_wrong_key(self):
147 def test_api_wrong_key(self):
148 id_, params = _build_data('trololo', 'get_user')
148 id_, params = _build_data('trololo', 'get_user')
149 response = api_call(self, params)
149 response = api_call(self, params)
150
150
151 expected = 'Invalid API key'
151 expected = 'Invalid API key'
152 self._compare_error(id_, expected, given=response.body)
152 self._compare_error(id_, expected, given=response.body)
153
153
154 def test_api_missing_non_optional_param(self):
154 def test_api_missing_non_optional_param(self):
155 id_, params = _build_data(self.apikey, 'get_repo')
155 id_, params = _build_data(self.apikey, 'get_repo')
156 response = api_call(self, params)
156 response = api_call(self, params)
157
157
158 expected = 'Missing non optional `repoid` arg in JSON DATA'
158 expected = 'Missing non optional `repoid` arg in JSON DATA'
159 self._compare_error(id_, expected, given=response.body)
159 self._compare_error(id_, expected, given=response.body)
160
160
161 def test_api_missing_non_optional_param_args_null(self):
161 def test_api_missing_non_optional_param_args_null(self):
162 id_, params = _build_data(self.apikey, 'get_repo')
162 id_, params = _build_data(self.apikey, 'get_repo')
163 params = params.replace('"args": {}', '"args": null')
163 params = params.replace('"args": {}', '"args": null')
164 response = api_call(self, params)
164 response = api_call(self, params)
165
165
166 expected = 'Missing non optional `repoid` arg in JSON DATA'
166 expected = 'Missing non optional `repoid` arg in JSON DATA'
167 self._compare_error(id_, expected, given=response.body)
167 self._compare_error(id_, expected, given=response.body)
168
168
169 def test_api_missing_non_optional_param_args_bad(self):
169 def test_api_missing_non_optional_param_args_bad(self):
170 id_, params = _build_data(self.apikey, 'get_repo')
170 id_, params = _build_data(self.apikey, 'get_repo')
171 params = params.replace('"args": {}', '"args": 1')
171 params = params.replace('"args": {}', '"args": 1')
172 response = api_call(self, params)
172 response = api_call(self, params)
173
173
174 expected = 'Missing non optional `repoid` arg in JSON DATA'
174 expected = 'Missing non optional `repoid` arg in JSON DATA'
175 self._compare_error(id_, expected, given=response.body)
175 self._compare_error(id_, expected, given=response.body)
176
176
177 def test_api_args_is_null(self):
177 def test_api_args_is_null(self):
178 id_, params = _build_data(self.apikey, 'get_users', )
178 id_, params = _build_data(self.apikey, 'get_users', )
179 params = params.replace('"args": {}', '"args": null')
179 params = params.replace('"args": {}', '"args": null')
180 response = api_call(self, params)
180 response = api_call(self, params)
181 assert response.status == '200 OK'
181 assert response.status == '200 OK'
182
182
183 def test_api_args_is_bad(self):
183 def test_api_args_is_bad(self):
184 id_, params = _build_data(self.apikey, 'get_users', )
184 id_, params = _build_data(self.apikey, 'get_users', )
185 params = params.replace('"args": {}', '"args": 1')
185 params = params.replace('"args": {}', '"args": 1')
186 response = api_call(self, params)
186 response = api_call(self, params)
187 assert response.status == '200 OK'
187 assert response.status == '200 OK'
188
188
189 def test_api_args_different_args(self):
189 def test_api_args_different_args(self):
190 expected = {
190 expected = {
191 'ascii_letters': string.ascii_letters,
191 'ascii_letters': string.ascii_letters,
192 'ws': string.whitespace,
192 'ws': string.whitespace,
193 'printables': string.printable
193 'printables': string.printable
194 }
194 }
195 id_, params = _build_data(self.apikey, 'test', args=expected)
195 id_, params = _build_data(self.apikey, 'test', args=expected)
196 response = api_call(self, params)
196 response = api_call(self, params)
197 assert response.status == '200 OK'
197 assert response.status == '200 OK'
198 self._compare_ok(id_, expected, response.body)
198 self._compare_ok(id_, expected, response.body)
199
199
200 def test_api_get_users(self):
200 def test_api_get_users(self):
201 id_, params = _build_data(self.apikey, 'get_users', )
201 id_, params = _build_data(self.apikey, 'get_users', )
202 response = api_call(self, params)
202 response = api_call(self, params)
203 ret_all = []
203 ret_all = []
204 _users = db.User.query().filter_by(is_default_user=False) \
204 _users = db.User.query().filter_by(is_default_user=False) \
205 .order_by(db.User.username).all()
205 .order_by(db.User.username).all()
206 for usr in _users:
206 for usr in _users:
207 ret = usr.get_api_data()
207 ret = usr.get_api_data()
208 ret_all.append(jsonify(ret))
208 ret_all.append(jsonify(ret))
209 expected = ret_all
209 expected = ret_all
210 self._compare_ok(id_, expected, given=response.body)
210 self._compare_ok(id_, expected, given=response.body)
211
211
212 def test_api_get_user(self):
212 def test_api_get_user(self):
213 id_, params = _build_data(self.apikey, 'get_user',
213 id_, params = _build_data(self.apikey, 'get_user',
214 userid=base.TEST_USER_ADMIN_LOGIN)
214 userid=base.TEST_USER_ADMIN_LOGIN)
215 response = api_call(self, params)
215 response = api_call(self, params)
216
216
217 usr = db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN)
217 usr = db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN)
218 ret = usr.get_api_data()
218 ret = usr.get_api_data()
219 ret['permissions'] = AuthUser(dbuser=usr).permissions
219 ret['permissions'] = AuthUser(dbuser=usr).permissions
220
220
221 expected = ret
221 expected = ret
222 self._compare_ok(id_, expected, given=response.body)
222 self._compare_ok(id_, expected, given=response.body)
223
223
224 def test_api_get_user_that_does_not_exist(self):
224 def test_api_get_user_that_does_not_exist(self):
225 id_, params = _build_data(self.apikey, 'get_user',
225 id_, params = _build_data(self.apikey, 'get_user',
226 userid='trololo')
226 userid='trololo')
227 response = api_call(self, params)
227 response = api_call(self, params)
228
228
229 expected = "user `%s` does not exist" % 'trololo'
229 expected = "user `%s` does not exist" % 'trololo'
230 self._compare_error(id_, expected, given=response.body)
230 self._compare_error(id_, expected, given=response.body)
231
231
232 def test_api_get_user_without_giving_userid(self):
232 def test_api_get_user_without_giving_userid(self):
233 id_, params = _build_data(self.apikey, 'get_user')
233 id_, params = _build_data(self.apikey, 'get_user')
234 response = api_call(self, params)
234 response = api_call(self, params)
235
235
236 usr = db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN)
236 usr = db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN)
237 ret = usr.get_api_data()
237 ret = usr.get_api_data()
238 ret['permissions'] = AuthUser(dbuser=usr).permissions
238 ret['permissions'] = AuthUser(dbuser=usr).permissions
239
239
240 expected = ret
240 expected = ret
241 self._compare_ok(id_, expected, given=response.body)
241 self._compare_ok(id_, expected, given=response.body)
242
242
243 def test_api_get_user_without_giving_userid_non_admin(self):
243 def test_api_get_user_without_giving_userid_non_admin(self):
244 id_, params = _build_data(self.apikey_regular, 'get_user')
244 id_, params = _build_data(self.apikey_regular, 'get_user')
245 response = api_call(self, params)
245 response = api_call(self, params)
246
246
247 usr = db.User.get_by_username(self.TEST_USER_LOGIN)
247 usr = db.User.get_by_username(self.TEST_USER_LOGIN)
248 ret = usr.get_api_data()
248 ret = usr.get_api_data()
249 ret['permissions'] = AuthUser(dbuser=usr).permissions
249 ret['permissions'] = AuthUser(dbuser=usr).permissions
250
250
251 expected = ret
251 expected = ret
252 self._compare_ok(id_, expected, given=response.body)
252 self._compare_ok(id_, expected, given=response.body)
253
253
254 def test_api_get_user_with_giving_userid_non_admin(self):
254 def test_api_get_user_with_giving_userid_non_admin(self):
255 id_, params = _build_data(self.apikey_regular, 'get_user',
255 id_, params = _build_data(self.apikey_regular, 'get_user',
256 userid=self.TEST_USER_LOGIN)
256 userid=self.TEST_USER_LOGIN)
257 response = api_call(self, params)
257 response = api_call(self, params)
258
258
259 expected = 'userid is not the same as your user'
259 expected = 'userid is not the same as your user'
260 self._compare_error(id_, expected, given=response.body)
260 self._compare_error(id_, expected, given=response.body)
261
261
262 def test_api_pull_remote(self):
262 def test_api_pull_remote(self):
263 # Note: pulling from local repos is a misfeature - it will bypass access control
263 # Note: pulling from local repos is a misfeature - it will bypass access control
264 # ... but ok, if the path already has been set in the database
264 # ... but ok, if the path already has been set in the database
265 repo_name = 'test_pull'
265 repo_name = 'test_pull'
266 r = fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
266 r = fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
267 # hack around that clone_uri can't be set to to a local path
267 # hack around that clone_uri can't be set to to a local path
268 # (as shown by test_api_create_repo_clone_uri_local)
268 # (as shown by test_api_create_repo_clone_uri_local)
269 r.clone_uri = os.path.join(db.Ui.get_by_key('paths', '/').ui_value, self.REPO)
269 r.clone_uri = os.path.join(db.Ui.get_by_key('paths', '/').ui_value, self.REPO)
270 meta.Session().commit()
270 meta.Session().commit()
271
271
272 pre_cached_tip = [repo.get_api_data()['last_changeset']['short_id'] for repo in db.Repository.query().filter(db.Repository.repo_name == repo_name)]
272 pre_cached_tip = [repo.get_api_data()['last_changeset']['short_id'] for repo in db.Repository.query().filter(db.Repository.repo_name == repo_name)]
273
273
274 id_, params = _build_data(self.apikey, 'pull',
274 id_, params = _build_data(self.apikey, 'pull',
275 repoid=repo_name,)
275 repoid=repo_name,)
276 response = api_call(self, params)
276 response = api_call(self, params)
277
277
278 expected = {'msg': 'Pulled from `%s`' % repo_name,
278 expected = {'msg': 'Pulled from `%s`' % repo_name,
279 'repository': repo_name}
279 'repository': repo_name}
280 self._compare_ok(id_, expected, given=response.body)
280 self._compare_ok(id_, expected, given=response.body)
281
281
282 post_cached_tip = [repo.get_api_data()['last_changeset']['short_id'] for repo in db.Repository.query().filter(db.Repository.repo_name == repo_name)]
282 post_cached_tip = [repo.get_api_data()['last_changeset']['short_id'] for repo in db.Repository.query().filter(db.Repository.repo_name == repo_name)]
283
283
284 fixture.destroy_repo(repo_name)
284 fixture.destroy_repo(repo_name)
285
285
286 assert pre_cached_tip != post_cached_tip
286 assert pre_cached_tip != post_cached_tip
287
287
288 def test_api_pull_fork(self):
288 def test_api_pull_fork(self):
289 fork_name = 'fork'
289 fork_name = 'fork'
290 fixture.create_fork(self.REPO, fork_name)
290 fixture.create_fork(self.REPO, fork_name)
291 id_, params = _build_data(self.apikey, 'pull',
291 id_, params = _build_data(self.apikey, 'pull',
292 repoid=fork_name,)
292 repoid=fork_name,)
293 response = api_call(self, params)
293 response = api_call(self, params)
294
294
295 expected = {'msg': 'Pulled from `%s`' % fork_name,
295 expected = {'msg': 'Pulled from `%s`' % fork_name,
296 'repository': fork_name}
296 'repository': fork_name}
297 self._compare_ok(id_, expected, given=response.body)
297 self._compare_ok(id_, expected, given=response.body)
298
298
299 fixture.destroy_repo(fork_name)
299 fixture.destroy_repo(fork_name)
300
300
301 def test_api_pull_error_no_remote_no_fork(self):
301 def test_api_pull_error_no_remote_no_fork(self):
302 # should fail because no clone_uri is set
302 # should fail because no clone_uri is set
303 id_, params = _build_data(self.apikey, 'pull',
303 id_, params = _build_data(self.apikey, 'pull',
304 repoid=self.REPO, )
304 repoid=self.REPO, )
305 response = api_call(self, params)
305 response = api_call(self, params)
306
306
307 expected = 'Unable to pull changes from `%s`' % self.REPO
307 expected = 'Unable to pull changes from `%s`' % self.REPO
308 self._compare_error(id_, expected, given=response.body)
308 self._compare_error(id_, expected, given=response.body)
309
309
310 def test_api_pull_custom_remote(self):
310 def test_api_pull_custom_remote(self):
311 repo_name = 'test_pull_custom_remote'
311 repo_name = 'test_pull_custom_remote'
312 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
312 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
313
313
314 custom_remote_path = os.path.join(db.Ui.get_by_key('paths', '/').ui_value, self.REPO)
314 custom_remote_path = os.path.join(db.Ui.get_by_key('paths', '/').ui_value, self.REPO)
315
315
316 id_, params = _build_data(self.apikey, 'pull',
316 id_, params = _build_data(self.apikey, 'pull',
317 repoid=repo_name,
317 repoid=repo_name,
318 clone_uri=custom_remote_path)
318 clone_uri=custom_remote_path)
319 response = api_call(self, params)
319 response = api_call(self, params)
320
320
321 expected = {'msg': 'Pulled from `%s`' % repo_name,
321 expected = {'msg': 'Pulled from `%s`' % repo_name,
322 'repository': repo_name}
322 'repository': repo_name}
323 self._compare_ok(id_, expected, given=response.body)
323 self._compare_ok(id_, expected, given=response.body)
324
324
325 fixture.destroy_repo(repo_name)
325 fixture.destroy_repo(repo_name)
326
326
327 def test_api_rescan_repos(self):
327 def test_api_rescan_repos(self):
328 id_, params = _build_data(self.apikey, 'rescan_repos')
328 id_, params = _build_data(self.apikey, 'rescan_repos')
329 response = api_call(self, params)
329 response = api_call(self, params)
330
330
331 expected = {'added': [], 'removed': []}
331 expected = {'added': [], 'removed': []}
332 self._compare_ok(id_, expected, given=response.body)
332 self._compare_ok(id_, expected, given=response.body)
333
333
334 @mock.patch.object(ScmModel, 'repo_scan', raise_exception)
334 @mock.patch.object(ScmModel, 'repo_scan', raise_exception)
335 def test_api_rescann_error(self):
335 def test_api_rescann_error(self):
336 id_, params = _build_data(self.apikey, 'rescan_repos', )
336 id_, params = _build_data(self.apikey, 'rescan_repos', )
337 response = api_call(self, params)
337 response = api_call(self, params)
338
338
339 expected = 'Error occurred during rescan repositories action'
339 expected = 'Error occurred during rescan repositories action'
340 self._compare_error(id_, expected, given=response.body)
340 self._compare_error(id_, expected, given=response.body)
341
341
342 def test_api_create_existing_user(self):
342 def test_api_create_existing_user(self):
343 id_, params = _build_data(self.apikey, 'create_user',
343 id_, params = _build_data(self.apikey, 'create_user',
344 username=base.TEST_USER_ADMIN_LOGIN,
344 username=base.TEST_USER_ADMIN_LOGIN,
345 email='test@example.com',
345 email='test@example.com',
346 password='trololo')
346 password='trololo')
347 response = api_call(self, params)
347 response = api_call(self, params)
348
348
349 expected = "user `%s` already exist" % base.TEST_USER_ADMIN_LOGIN
349 expected = "user `%s` already exist" % base.TEST_USER_ADMIN_LOGIN
350 self._compare_error(id_, expected, given=response.body)
350 self._compare_error(id_, expected, given=response.body)
351
351
352 def test_api_create_user_with_existing_email(self):
352 def test_api_create_user_with_existing_email(self):
353 id_, params = _build_data(self.apikey, 'create_user',
353 id_, params = _build_data(self.apikey, 'create_user',
354 username=base.TEST_USER_ADMIN_LOGIN + 'new',
354 username=base.TEST_USER_ADMIN_LOGIN + 'new',
355 email=base.TEST_USER_REGULAR_EMAIL,
355 email=base.TEST_USER_REGULAR_EMAIL,
356 password='trololo')
356 password='trololo')
357 response = api_call(self, params)
357 response = api_call(self, params)
358
358
359 expected = "email `%s` already exist" % base.TEST_USER_REGULAR_EMAIL
359 expected = "email `%s` already exist" % base.TEST_USER_REGULAR_EMAIL
360 self._compare_error(id_, expected, given=response.body)
360 self._compare_error(id_, expected, given=response.body)
361
361
362 def test_api_create_user(self):
362 def test_api_create_user(self):
363 username = 'test_new_api_user'
363 username = 'test_new_api_user'
364 email = username + "@example.com"
364 email = username + "@example.com"
365
365
366 id_, params = _build_data(self.apikey, 'create_user',
366 id_, params = _build_data(self.apikey, 'create_user',
367 username=username,
367 username=username,
368 email=email,
368 email=email,
369 password='trololo')
369 password='trololo')
370 response = api_call(self, params)
370 response = api_call(self, params)
371
371
372 usr = db.User.get_by_username(username)
372 usr = db.User.get_by_username(username)
373 ret = dict(
373 ret = dict(
374 msg='created new user `%s`' % username,
374 msg='created new user `%s`' % username,
375 user=jsonify(usr.get_api_data())
375 user=jsonify(usr.get_api_data())
376 )
376 )
377
377
378 try:
378 try:
379 expected = ret
379 expected = ret
380 self._compare_ok(id_, expected, given=response.body)
380 self._compare_ok(id_, expected, given=response.body)
381 finally:
381 finally:
382 fixture.destroy_user(usr.user_id)
382 fixture.destroy_user(usr.user_id)
383
383
384 def test_api_create_user_without_password(self):
384 def test_api_create_user_without_password(self):
385 username = 'test_new_api_user_passwordless'
385 username = 'test_new_api_user_passwordless'
386 email = username + "@example.com"
386 email = username + "@example.com"
387
387
388 id_, params = _build_data(self.apikey, 'create_user',
388 id_, params = _build_data(self.apikey, 'create_user',
389 username=username,
389 username=username,
390 email=email)
390 email=email)
391 response = api_call(self, params)
391 response = api_call(self, params)
392
392
393 usr = db.User.get_by_username(username)
393 usr = db.User.get_by_username(username)
394 ret = dict(
394 ret = dict(
395 msg='created new user `%s`' % username,
395 msg='created new user `%s`' % username,
396 user=jsonify(usr.get_api_data())
396 user=jsonify(usr.get_api_data())
397 )
397 )
398 try:
398 try:
399 expected = ret
399 expected = ret
400 self._compare_ok(id_, expected, given=response.body)
400 self._compare_ok(id_, expected, given=response.body)
401 finally:
401 finally:
402 fixture.destroy_user(usr.user_id)
402 fixture.destroy_user(usr.user_id)
403
403
404 def test_api_create_user_with_extern_name(self):
404 def test_api_create_user_with_extern_name(self):
405 username = 'test_new_api_user_passwordless'
405 username = 'test_new_api_user_passwordless'
406 email = username + "@example.com"
406 email = username + "@example.com"
407
407
408 id_, params = _build_data(self.apikey, 'create_user',
408 id_, params = _build_data(self.apikey, 'create_user',
409 username=username,
409 username=username,
410 email=email, extern_name='internal')
410 email=email, extern_name='internal')
411 response = api_call(self, params)
411 response = api_call(self, params)
412
412
413 usr = db.User.get_by_username(username)
413 usr = db.User.get_by_username(username)
414 ret = dict(
414 ret = dict(
415 msg='created new user `%s`' % username,
415 msg='created new user `%s`' % username,
416 user=jsonify(usr.get_api_data())
416 user=jsonify(usr.get_api_data())
417 )
417 )
418 try:
418 try:
419 expected = ret
419 expected = ret
420 self._compare_ok(id_, expected, given=response.body)
420 self._compare_ok(id_, expected, given=response.body)
421 finally:
421 finally:
422 fixture.destroy_user(usr.user_id)
422 fixture.destroy_user(usr.user_id)
423
423
424 @mock.patch.object(UserModel, 'create_or_update', raise_exception)
424 @mock.patch.object(UserModel, 'create_or_update', raise_exception)
425 def test_api_create_user_when_exception_happened(self):
425 def test_api_create_user_when_exception_happened(self):
426
426
427 username = 'test_new_api_user'
427 username = 'test_new_api_user'
428 email = username + "@example.com"
428 email = username + "@example.com"
429
429
430 id_, params = _build_data(self.apikey, 'create_user',
430 id_, params = _build_data(self.apikey, 'create_user',
431 username=username,
431 username=username,
432 email=email,
432 email=email,
433 password='trololo')
433 password='trololo')
434 response = api_call(self, params)
434 response = api_call(self, params)
435 expected = 'failed to create user `%s`' % username
435 expected = 'failed to create user `%s`' % username
436 self._compare_error(id_, expected, given=response.body)
436 self._compare_error(id_, expected, given=response.body)
437
437
438 def test_api_delete_user(self):
438 def test_api_delete_user(self):
439 usr = UserModel().create_or_update(username='test_user',
439 usr = UserModel().create_or_update(username='test_user',
440 password='qweqwe',
440 password='qweqwe',
441 email='u232@example.com',
441 email='u232@example.com',
442 firstname='u1', lastname='u1')
442 firstname='u1', lastname='u1')
443 meta.Session().commit()
443 meta.Session().commit()
444 username = usr.username
444 username = usr.username
445 email = usr.email
445 email = usr.email
446 usr_id = usr.user_id
446 usr_id = usr.user_id
447 ## DELETE THIS USER NOW
447 ## DELETE THIS USER NOW
448
448
449 id_, params = _build_data(self.apikey, 'delete_user',
449 id_, params = _build_data(self.apikey, 'delete_user',
450 userid=username, )
450 userid=username, )
451 response = api_call(self, params)
451 response = api_call(self, params)
452
452
453 ret = {'msg': 'deleted user ID:%s %s' % (usr_id, username),
453 ret = {'msg': 'deleted user ID:%s %s' % (usr_id, username),
454 'user': None}
454 'user': None}
455 expected = ret
455 expected = ret
456 self._compare_ok(id_, expected, given=response.body)
456 self._compare_ok(id_, expected, given=response.body)
457
457
458 @mock.patch.object(UserModel, 'delete', raise_exception)
458 @mock.patch.object(UserModel, 'delete', raise_exception)
459 def test_api_delete_user_when_exception_happened(self):
459 def test_api_delete_user_when_exception_happened(self):
460 usr = UserModel().create_or_update(username='test_user',
460 usr = UserModel().create_or_update(username='test_user',
461 password='qweqwe',
461 password='qweqwe',
462 email='u232@example.com',
462 email='u232@example.com',
463 firstname='u1', lastname='u1')
463 firstname='u1', lastname='u1')
464 meta.Session().commit()
464 meta.Session().commit()
465 username = usr.username
465 username = usr.username
466
466
467 id_, params = _build_data(self.apikey, 'delete_user',
467 id_, params = _build_data(self.apikey, 'delete_user',
468 userid=username, )
468 userid=username, )
469 response = api_call(self, params)
469 response = api_call(self, params)
470 ret = 'failed to delete user ID:%s %s' % (usr.user_id,
470 ret = 'failed to delete user ID:%s %s' % (usr.user_id,
471 usr.username)
471 usr.username)
472 expected = ret
472 expected = ret
473 self._compare_error(id_, expected, given=response.body)
473 self._compare_error(id_, expected, given=response.body)
474
474
475 @base.parametrize('name,expected', [
475 @base.parametrize('name,expected', [
476 ('firstname', 'new_username'),
476 ('firstname', 'new_username'),
477 ('lastname', 'new_username'),
477 ('lastname', 'new_username'),
478 ('email', 'new_username'),
478 ('email', 'new_username'),
479 ('admin', True),
479 ('admin', True),
480 ('admin', False),
480 ('admin', False),
481 ('extern_type', 'ldap'),
481 ('extern_type', 'ldap'),
482 ('extern_type', None),
482 ('extern_type', None),
483 ('extern_name', 'test'),
483 ('extern_name', 'test'),
484 ('extern_name', None),
484 ('extern_name', None),
485 ('active', False),
485 ('active', False),
486 ('active', True),
486 ('active', True),
487 ('password', 'newpass'),
487 ('password', 'newpass'),
488 ])
488 ])
489 def test_api_update_user(self, name, expected):
489 def test_api_update_user(self, name, expected):
490 usr = db.User.get_by_username(self.TEST_USER_LOGIN)
490 usr = db.User.get_by_username(self.TEST_USER_LOGIN)
491 kw = {name: expected,
491 kw = {name: expected,
492 'userid': usr.user_id}
492 'userid': usr.user_id}
493 id_, params = _build_data(self.apikey, 'update_user', **kw)
493 id_, params = _build_data(self.apikey, 'update_user', **kw)
494 response = api_call(self, params)
494 response = api_call(self, params)
495
495
496 ret = {
496 ret = {
497 'msg': 'updated user ID:%s %s' % (
497 'msg': 'updated user ID:%s %s' % (
498 usr.user_id, self.TEST_USER_LOGIN),
498 usr.user_id, self.TEST_USER_LOGIN),
499 'user': jsonify(db.User \
499 'user': jsonify(db.User \
500 .get_by_username(self.TEST_USER_LOGIN) \
500 .get_by_username(self.TEST_USER_LOGIN) \
501 .get_api_data())
501 .get_api_data())
502 }
502 }
503
503
504 expected = ret
504 expected = ret
505 self._compare_ok(id_, expected, given=response.body)
505 self._compare_ok(id_, expected, given=response.body)
506
506
507 def test_api_update_user_no_changed_params(self):
507 def test_api_update_user_no_changed_params(self):
508 usr = db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN)
508 usr = db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN)
509 ret = jsonify(usr.get_api_data())
509 ret = jsonify(usr.get_api_data())
510 id_, params = _build_data(self.apikey, 'update_user',
510 id_, params = _build_data(self.apikey, 'update_user',
511 userid=base.TEST_USER_ADMIN_LOGIN)
511 userid=base.TEST_USER_ADMIN_LOGIN)
512
512
513 response = api_call(self, params)
513 response = api_call(self, params)
514 ret = {
514 ret = {
515 'msg': 'updated user ID:%s %s' % (
515 'msg': 'updated user ID:%s %s' % (
516 usr.user_id, base.TEST_USER_ADMIN_LOGIN),
516 usr.user_id, base.TEST_USER_ADMIN_LOGIN),
517 'user': ret
517 'user': ret
518 }
518 }
519 expected = ret
519 expected = ret
520 self._compare_ok(id_, expected, given=response.body)
520 self._compare_ok(id_, expected, given=response.body)
521
521
522 def test_api_update_user_by_user_id(self):
522 def test_api_update_user_by_user_id(self):
523 usr = db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN)
523 usr = db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN)
524 ret = jsonify(usr.get_api_data())
524 ret = jsonify(usr.get_api_data())
525 id_, params = _build_data(self.apikey, 'update_user',
525 id_, params = _build_data(self.apikey, 'update_user',
526 userid=usr.user_id)
526 userid=usr.user_id)
527
527
528 response = api_call(self, params)
528 response = api_call(self, params)
529 ret = {
529 ret = {
530 'msg': 'updated user ID:%s %s' % (
530 'msg': 'updated user ID:%s %s' % (
531 usr.user_id, base.TEST_USER_ADMIN_LOGIN),
531 usr.user_id, base.TEST_USER_ADMIN_LOGIN),
532 'user': ret
532 'user': ret
533 }
533 }
534 expected = ret
534 expected = ret
535 self._compare_ok(id_, expected, given=response.body)
535 self._compare_ok(id_, expected, given=response.body)
536
536
537 def test_api_update_user_default_user(self):
537 def test_api_update_user_default_user(self):
538 usr = db.User.get_default_user()
538 usr = db.User.get_default_user()
539 id_, params = _build_data(self.apikey, 'update_user',
539 id_, params = _build_data(self.apikey, 'update_user',
540 userid=usr.user_id)
540 userid=usr.user_id)
541
541
542 response = api_call(self, params)
542 response = api_call(self, params)
543 expected = 'editing default user is forbidden'
543 expected = 'editing default user is forbidden'
544 self._compare_error(id_, expected, given=response.body)
544 self._compare_error(id_, expected, given=response.body)
545
545
546 @mock.patch.object(UserModel, 'update_user', raise_exception)
546 @mock.patch.object(UserModel, 'update_user', raise_exception)
547 def test_api_update_user_when_exception_happens(self):
547 def test_api_update_user_when_exception_happens(self):
548 usr = db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN)
548 usr = db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN)
549 ret = jsonify(usr.get_api_data())
549 ret = jsonify(usr.get_api_data())
550 id_, params = _build_data(self.apikey, 'update_user',
550 id_, params = _build_data(self.apikey, 'update_user',
551 userid=usr.user_id)
551 userid=usr.user_id)
552
552
553 response = api_call(self, params)
553 response = api_call(self, params)
554 ret = 'failed to update user `%s`' % usr.user_id
554 ret = 'failed to update user `%s`' % usr.user_id
555
555
556 expected = ret
556 expected = ret
557 self._compare_error(id_, expected, given=response.body)
557 self._compare_error(id_, expected, given=response.body)
558
558
559 def test_api_get_repo(self):
559 def test_api_get_repo(self):
560 new_group = 'some_new_group'
560 new_group = 'some_new_group'
561 make_user_group(new_group)
561 make_user_group(new_group)
562 RepoModel().grant_user_group_permission(repo=self.REPO,
562 RepoModel().grant_user_group_permission(repo=self.REPO,
563 group_name=new_group,
563 group_name=new_group,
564 perm='repository.read')
564 perm='repository.read')
565 meta.Session().commit()
565 meta.Session().commit()
566 id_, params = _build_data(self.apikey, 'get_repo',
566 id_, params = _build_data(self.apikey, 'get_repo',
567 repoid=self.REPO)
567 repoid=self.REPO)
568 response = api_call(self, params)
568 response = api_call(self, params)
569 assert "tags" not in response.json['result']
569 assert "tags" not in response.json['result']
570 assert 'pull_requests' not in response.json['result']
570 assert 'pull_requests' not in response.json['result']
571
571
572 repo = RepoModel().get_by_repo_name(self.REPO)
572 repo = RepoModel().get_by_repo_name(self.REPO)
573 ret = repo.get_api_data()
573 ret = repo.get_api_data()
574
574
575 members = []
575 members = []
576 followers = []
576 followers = []
577 for user in repo.repo_to_perm:
577 for user in repo.repo_to_perm:
578 perm = user.permission.permission_name
578 perm = user.permission.permission_name
579 user = user.user
579 user = user.user
580 user_data = {'name': user.username, 'type': "user",
580 user_data = {'name': user.username, 'type': "user",
581 'permission': perm}
581 'permission': perm}
582 members.append(user_data)
582 members.append(user_data)
583
583
584 for user_group in repo.users_group_to_perm:
584 for user_group in repo.users_group_to_perm:
585 perm = user_group.permission.permission_name
585 perm = user_group.permission.permission_name
586 user_group = user_group.users_group
586 user_group = user_group.users_group
587 user_group_data = {'name': user_group.users_group_name,
587 user_group_data = {'name': user_group.users_group_name,
588 'type': "user_group", 'permission': perm}
588 'type': "user_group", 'permission': perm}
589 members.append(user_group_data)
589 members.append(user_group_data)
590
590
591 for user in repo.followers:
591 for user in repo.followers:
592 followers.append(user.user.get_api_data())
592 followers.append(user.user.get_api_data())
593
593
594 ret['members'] = members
594 ret['members'] = members
595 ret['followers'] = followers
595 ret['followers'] = followers
596
596
597 expected = ret
597 expected = ret
598 self._compare_ok(id_, expected, given=response.body)
598 self._compare_ok(id_, expected, given=response.body)
599 fixture.destroy_user_group(new_group)
599 fixture.destroy_user_group(new_group)
600
600
601 id_, params = _build_data(self.apikey, 'get_repo', repoid=self.REPO,
601 id_, params = _build_data(self.apikey, 'get_repo', repoid=self.REPO,
602 with_revision_names=True,
602 with_revision_names=True,
603 with_pullrequests=True)
603 with_pullrequests=True)
604 response = api_call(self, params)
604 response = api_call(self, params)
605 assert "v0.2.0" in response.json['result']['tags']
605 assert "v0.2.0" in response.json['result']['tags']
606 assert 'pull_requests' in response.json['result']
606 assert 'pull_requests' in response.json['result']
607
607
608 @base.parametrize('grant_perm', [
608 @base.parametrize('grant_perm', [
609 ('repository.admin'),
609 ('repository.admin'),
610 ('repository.write'),
610 ('repository.write'),
611 ('repository.read'),
611 ('repository.read'),
612 ])
612 ])
613 def test_api_get_repo_by_non_admin(self, grant_perm):
613 def test_api_get_repo_by_non_admin(self, grant_perm):
614 RepoModel().grant_user_permission(repo=self.REPO,
614 RepoModel().grant_user_permission(repo=self.REPO,
615 user=self.TEST_USER_LOGIN,
615 user=self.TEST_USER_LOGIN,
616 perm=grant_perm)
616 perm=grant_perm)
617 meta.Session().commit()
617 meta.Session().commit()
618 id_, params = _build_data(self.apikey_regular, 'get_repo',
618 id_, params = _build_data(self.apikey_regular, 'get_repo',
619 repoid=self.REPO)
619 repoid=self.REPO)
620 response = api_call(self, params)
620 response = api_call(self, params)
621
621
622 repo = RepoModel().get_by_repo_name(self.REPO)
622 repo = RepoModel().get_by_repo_name(self.REPO)
623 assert len(repo.repo_to_perm) >= 2 # make sure we actually are testing something - probably the default 2 permissions, possibly more
623 assert len(repo.repo_to_perm) >= 2 # make sure we actually are testing something - probably the default 2 permissions, possibly more
624
624
625 expected = repo.get_api_data()
625 expected = repo.get_api_data()
626
626
627 members = []
627 members = []
628 for user in repo.repo_to_perm:
628 for user in repo.repo_to_perm:
629 perm = user.permission.permission_name
629 perm = user.permission.permission_name
630 user_obj = user.user
630 user_obj = user.user
631 user_data = {'name': user_obj.username, 'type': "user",
631 user_data = {'name': user_obj.username, 'type': "user",
632 'permission': perm}
632 'permission': perm}
633 members.append(user_data)
633 members.append(user_data)
634 for user_group in repo.users_group_to_perm:
634 for user_group in repo.users_group_to_perm:
635 perm = user_group.permission.permission_name
635 perm = user_group.permission.permission_name
636 user_group_obj = user_group.users_group
636 user_group_obj = user_group.users_group
637 user_group_data = {'name': user_group_obj.users_group_name,
637 user_group_data = {'name': user_group_obj.users_group_name,
638 'type': "user_group", 'permission': perm}
638 'type': "user_group", 'permission': perm}
639 members.append(user_group_data)
639 members.append(user_group_data)
640 expected['members'] = members
640 expected['members'] = members
641
641
642 followers = []
642 followers = []
643
643
644 for user in repo.followers:
644 for user in repo.followers:
645 followers.append(user.user.get_api_data())
645 followers.append(user.user.get_api_data())
646
646
647 expected['followers'] = followers
647 expected['followers'] = followers
648
648
649 try:
649 try:
650 self._compare_ok(id_, expected, given=response.body)
650 self._compare_ok(id_, expected, given=response.body)
651 finally:
651 finally:
652 RepoModel().revoke_user_permission(self.REPO, self.TEST_USER_LOGIN)
652 RepoModel().revoke_user_permission(self.REPO, self.TEST_USER_LOGIN)
653
653
654 def test_api_get_repo_by_non_admin_no_permission_to_repo(self):
654 def test_api_get_repo_by_non_admin_no_permission_to_repo(self):
655 RepoModel().grant_user_permission(repo=self.REPO,
655 RepoModel().grant_user_permission(repo=self.REPO,
656 user=db.User.DEFAULT_USER_NAME,
656 user=db.User.DEFAULT_USER_NAME,
657 perm='repository.none')
657 perm='repository.none')
658 try:
658 try:
659 RepoModel().grant_user_permission(repo=self.REPO,
659 RepoModel().grant_user_permission(repo=self.REPO,
660 user=self.TEST_USER_LOGIN,
660 user=self.TEST_USER_LOGIN,
661 perm='repository.none')
661 perm='repository.none')
662
662
663 id_, params = _build_data(self.apikey_regular, 'get_repo',
663 id_, params = _build_data(self.apikey_regular, 'get_repo',
664 repoid=self.REPO)
664 repoid=self.REPO)
665 response = api_call(self, params)
665 response = api_call(self, params)
666
666
667 expected = 'repository `%s` does not exist' % (self.REPO)
667 expected = 'repository `%s` does not exist' % (self.REPO)
668 self._compare_error(id_, expected, given=response.body)
668 self._compare_error(id_, expected, given=response.body)
669 finally:
669 finally:
670 RepoModel().grant_user_permission(repo=self.REPO,
670 RepoModel().grant_user_permission(repo=self.REPO,
671 user=db.User.DEFAULT_USER_NAME,
671 user=db.User.DEFAULT_USER_NAME,
672 perm='repository.read')
672 perm='repository.read')
673
673
674 def test_api_get_repo_that_doesn_not_exist(self):
674 def test_api_get_repo_that_doesn_not_exist(self):
675 id_, params = _build_data(self.apikey, 'get_repo',
675 id_, params = _build_data(self.apikey, 'get_repo',
676 repoid='no-such-repo')
676 repoid='no-such-repo')
677 response = api_call(self, params)
677 response = api_call(self, params)
678
678
679 ret = 'repository `%s` does not exist' % 'no-such-repo'
679 ret = 'repository `%s` does not exist' % 'no-such-repo'
680 expected = ret
680 expected = ret
681 self._compare_error(id_, expected, given=response.body)
681 self._compare_error(id_, expected, given=response.body)
682
682
683 def test_api_get_repos(self):
683 def test_api_get_repos(self):
684 id_, params = _build_data(self.apikey, 'get_repos')
684 id_, params = _build_data(self.apikey, 'get_repos')
685 response = api_call(self, params)
685 response = api_call(self, params)
686
686
687 expected = jsonify([
687 expected = jsonify([
688 repo.get_api_data()
688 repo.get_api_data()
689 for repo in db.Repository.query()
689 for repo in db.Repository.query()
690 ])
690 ])
691
691
692 self._compare_ok(id_, expected, given=response.body)
692 self._compare_ok(id_, expected, given=response.body)
693
693
694 def test_api_get_repos_non_admin(self):
694 def test_api_get_repos_non_admin(self):
695 id_, params = _build_data(self.apikey_regular, 'get_repos')
695 id_, params = _build_data(self.apikey_regular, 'get_repos')
696 response = api_call(self, params)
696 response = api_call(self, params)
697
697
698 expected = jsonify([
698 expected = jsonify([
699 repo.get_api_data()
699 repo.get_api_data()
700 for repo in AuthUser(dbuser=db.User.get_by_username(self.TEST_USER_LOGIN)).get_all_user_repos()
700 for repo in AuthUser(dbuser=db.User.get_by_username(self.TEST_USER_LOGIN)).get_all_user_repos()
701 ])
701 ])
702
702
703 self._compare_ok(id_, expected, given=response.body)
703 self._compare_ok(id_, expected, given=response.body)
704
704
705 @base.parametrize('name,ret_type', [
705 @base.parametrize('name,ret_type', [
706 ('all', 'all'),
706 ('all', 'all'),
707 ('dirs', 'dirs'),
707 ('dirs', 'dirs'),
708 ('files', 'files'),
708 ('files', 'files'),
709 ])
709 ])
710 def test_api_get_repo_nodes(self, name, ret_type):
710 def test_api_get_repo_nodes(self, name, ret_type):
711 rev = 'tip'
711 rev = 'tip'
712 path = '/'
712 path = '/'
713 id_, params = _build_data(self.apikey, 'get_repo_nodes',
713 id_, params = _build_data(self.apikey, 'get_repo_nodes',
714 repoid=self.REPO, revision=rev,
714 repoid=self.REPO, revision=rev,
715 root_path=path,
715 root_path=path,
716 ret_type=ret_type)
716 ret_type=ret_type)
717 response = api_call(self, params)
717 response = api_call(self, params)
718
718
719 # we don't the actual return types here since it's tested somewhere
719 # we don't the actual return types here since it's tested somewhere
720 # else
720 # else
721 expected = response.json['result']
721 expected = response.json['result']
722 self._compare_ok(id_, expected, given=response.body)
722 self._compare_ok(id_, expected, given=response.body)
723
723
724 def test_api_get_repo_nodes_bad_revisions(self):
724 def test_api_get_repo_nodes_bad_revisions(self):
725 rev = 'i-dont-exist'
725 rev = 'i-dont-exist'
726 path = '/'
726 path = '/'
727 id_, params = _build_data(self.apikey, 'get_repo_nodes',
727 id_, params = _build_data(self.apikey, 'get_repo_nodes',
728 repoid=self.REPO, revision=rev,
728 repoid=self.REPO, revision=rev,
729 root_path=path, )
729 root_path=path, )
730 response = api_call(self, params)
730 response = api_call(self, params)
731
731
732 expected = 'failed to get repo: `%s` nodes' % self.REPO
732 expected = 'failed to get repo: `%s` nodes' % self.REPO
733 self._compare_error(id_, expected, given=response.body)
733 self._compare_error(id_, expected, given=response.body)
734
734
735 def test_api_get_repo_nodes_bad_path(self):
735 def test_api_get_repo_nodes_bad_path(self):
736 rev = 'tip'
736 rev = 'tip'
737 path = '/idontexits'
737 path = '/idontexits'
738 id_, params = _build_data(self.apikey, 'get_repo_nodes',
738 id_, params = _build_data(self.apikey, 'get_repo_nodes',
739 repoid=self.REPO, revision=rev,
739 repoid=self.REPO, revision=rev,
740 root_path=path, )
740 root_path=path, )
741 response = api_call(self, params)
741 response = api_call(self, params)
742
742
743 expected = 'failed to get repo: `%s` nodes' % self.REPO
743 expected = 'failed to get repo: `%s` nodes' % self.REPO
744 self._compare_error(id_, expected, given=response.body)
744 self._compare_error(id_, expected, given=response.body)
745
745
746 def test_api_get_repo_nodes_bad_ret_type(self):
746 def test_api_get_repo_nodes_bad_ret_type(self):
747 rev = 'tip'
747 rev = 'tip'
748 path = '/'
748 path = '/'
749 ret_type = 'error'
749 ret_type = 'error'
750 id_, params = _build_data(self.apikey, 'get_repo_nodes',
750 id_, params = _build_data(self.apikey, 'get_repo_nodes',
751 repoid=self.REPO, revision=rev,
751 repoid=self.REPO, revision=rev,
752 root_path=path,
752 root_path=path,
753 ret_type=ret_type)
753 ret_type=ret_type)
754 response = api_call(self, params)
754 response = api_call(self, params)
755
755
756 expected = ('ret_type must be one of %s'
756 expected = ('ret_type must be one of %s'
757 % (','.join(sorted(['files', 'dirs', 'all']))))
757 % (','.join(sorted(['files', 'dirs', 'all']))))
758 self._compare_error(id_, expected, given=response.body)
758 self._compare_error(id_, expected, given=response.body)
759
759
760 @base.parametrize('name,ret_type,grant_perm', [
760 @base.parametrize('name,ret_type,grant_perm', [
761 ('all', 'all', 'repository.write'),
761 ('all', 'all', 'repository.write'),
762 ('dirs', 'dirs', 'repository.admin'),
762 ('dirs', 'dirs', 'repository.admin'),
763 ('files', 'files', 'repository.read'),
763 ('files', 'files', 'repository.read'),
764 ])
764 ])
765 def test_api_get_repo_nodes_by_regular_user(self, name, ret_type, grant_perm):
765 def test_api_get_repo_nodes_by_regular_user(self, name, ret_type, grant_perm):
766 RepoModel().grant_user_permission(repo=self.REPO,
766 RepoModel().grant_user_permission(repo=self.REPO,
767 user=self.TEST_USER_LOGIN,
767 user=self.TEST_USER_LOGIN,
768 perm=grant_perm)
768 perm=grant_perm)
769 meta.Session().commit()
769 meta.Session().commit()
770
770
771 rev = 'tip'
771 rev = 'tip'
772 path = '/'
772 path = '/'
773 id_, params = _build_data(self.apikey_regular, 'get_repo_nodes',
773 id_, params = _build_data(self.apikey_regular, 'get_repo_nodes',
774 repoid=self.REPO, revision=rev,
774 repoid=self.REPO, revision=rev,
775 root_path=path,
775 root_path=path,
776 ret_type=ret_type)
776 ret_type=ret_type)
777 response = api_call(self, params)
777 response = api_call(self, params)
778
778
779 # we don't the actual return types here since it's tested somewhere
779 # we don't the actual return types here since it's tested somewhere
780 # else
780 # else
781 expected = response.json['result']
781 expected = response.json['result']
782 try:
782 try:
783 self._compare_ok(id_, expected, given=response.body)
783 self._compare_ok(id_, expected, given=response.body)
784 finally:
784 finally:
785 RepoModel().revoke_user_permission(self.REPO, self.TEST_USER_LOGIN)
785 RepoModel().revoke_user_permission(self.REPO, self.TEST_USER_LOGIN)
786
786
787 @base.parametrize('changing_attr,updates', [
787 @base.parametrize('changing_attr,updates', [
788 ('owner', {'owner': base.TEST_USER_REGULAR_LOGIN}),
788 ('owner', {'owner': base.TEST_USER_REGULAR_LOGIN}),
789 ('description', {'description': 'new description'}),
789 ('description', {'description': 'new description'}),
790 ('clone_uri', {'clone_uri': 'http://example.com/repo'}), # will fail - pulling from non-existing repo should fail
790 ('clone_uri', {'clone_uri': 'http://example.com/repo'}), # will fail - pulling from non-existing repo should fail
791 ('clone_uri', {'clone_uri': '/repo'}), # will fail - pulling from local repo was a misfeature - it would bypass access control
791 ('clone_uri', {'clone_uri': '/repo'}), # will fail - pulling from local repo was a misfeature - it would bypass access control
792 ('clone_uri', {'clone_uri': None}),
792 ('clone_uri', {'clone_uri': None}),
793 ('landing_rev', {'landing_rev': 'branch:master'}),
793 ('landing_rev', {'landing_rev': 'branch:master'}),
794 ('private', {'private': True}),
794 ('private', {'private': True}),
795 ('enable_statistics', {'enable_statistics': True}),
795 ('enable_statistics', {'enable_statistics': True}),
796 ('enable_downloads', {'enable_downloads': True}),
796 ('enable_downloads', {'enable_downloads': True}),
797 ('repo_group', {'group': 'test_group_for_update'}),
797 ('repo_group', {'group': 'test_group_for_update'}),
798 ])
798 ])
799 def test_api_create_repo(self, changing_attr, updates):
799 def test_api_create_repo(self, changing_attr, updates):
800 repo_name = repo_name_full = 'new_repo'
800 repo_name = repo_name_full = 'new_repo'
801
801
802 if changing_attr == 'repo_group':
802 if changing_attr == 'repo_group':
803 group_name = updates['group']
803 group_name = updates['group']
804 fixture.create_repo_group(group_name)
804 fixture.create_repo_group(group_name)
805 repo_name_full = '/'.join([group_name, repo_name])
805 repo_name_full = '/'.join([group_name, repo_name])
806 updates = {}
806 updates = {}
807
807
808 id_, params = _build_data(self.apikey, 'create_repo',
808 id_, params = _build_data(self.apikey, 'create_repo',
809 repo_type=self.REPO_TYPE, repo_name=repo_name_full, **updates)
809 repo_type=self.REPO_TYPE, repo_name=repo_name_full, **updates)
810 response = api_call(self, params)
810 response = api_call(self, params)
811
811
812 try:
812 try:
813 expected = {
813 expected = {
814 'msg': 'Created new repository `%s`' % repo_name_full,
814 'msg': 'Created new repository `%s`' % repo_name_full,
815 'success': True}
815 'success': True}
816 if changing_attr == 'clone_uri' and updates['clone_uri']:
816 if changing_attr == 'clone_uri' and updates['clone_uri']:
817 expected = 'failed to create repository `%s`' % repo_name
817 expected = 'failed to create repository `%s`' % repo_name
818 self._compare_error(id_, expected, given=response.body)
818 self._compare_error(id_, expected, given=response.body)
819 return
819 return
820 else:
820 else:
821 self._compare_ok(id_, expected, given=response.body)
821 self._compare_ok(id_, expected, given=response.body)
822
822
823 repo = db.Repository.get_by_repo_name(repo_name_full)
823 repo = db.Repository.get_by_repo_name(repo_name_full)
824 assert repo is not None
824 assert repo is not None
825
825
826 expected_data = {
826 expected_data = {
827 'clone_uri': None,
827 'clone_uri': None,
828 'created_on': repo.created_on,
828 'created_on': repo.created_on,
829 'description': repo_name,
829 'description': repo_name,
830 'enable_downloads': False,
830 'enable_downloads': False,
831 'enable_statistics': False,
831 'enable_statistics': False,
832 'fork_of': None,
832 'fork_of': None,
833 'landing_rev': ['rev', 'tip'],
833 'landing_rev': ['rev', 'tip'],
834 'last_changeset': {'author': '',
834 'last_changeset': {'author': '',
835 'date': datetime.datetime(1970, 1, 1, 0, 0),
835 'date': datetime.datetime(1970, 1, 1, 0, 0),
836 'message': '',
836 'message': '',
837 'raw_id': '0000000000000000000000000000000000000000',
837 'raw_id': '0000000000000000000000000000000000000000',
838 'revision': -1,
838 'revision': -1,
839 'short_id': '000000000000'},
839 'short_id': '000000000000'},
840 'owner': 'test_admin',
840 'owner': 'test_admin',
841 'private': False,
841 'private': False,
842 'repo_id': repo.repo_id,
842 'repo_id': repo.repo_id,
843 'repo_name': repo_name_full,
843 'repo_name': repo_name_full,
844 'repo_type': self.REPO_TYPE,
844 'repo_type': self.REPO_TYPE,
845 }
845 }
846 expected_data.update(updates)
846 expected_data.update(updates)
847 if changing_attr == 'landing_rev':
847 if changing_attr == 'landing_rev':
848 expected_data['landing_rev'] = expected_data['landing_rev'].split(':', 1)
848 expected_data['landing_rev'] = expected_data['landing_rev'].split(':', 1)
849 assert repo.get_api_data() == expected_data
849 assert repo.get_api_data() == expected_data
850 finally:
850 finally:
851 fixture.destroy_repo(repo_name_full)
851 fixture.destroy_repo(repo_name_full)
852 if changing_attr == 'repo_group':
852 if changing_attr == 'repo_group':
853 fixture.destroy_repo_group(group_name)
853 fixture.destroy_repo_group(group_name)
854
854
855 @base.parametrize('repo_name', [
855 @base.parametrize('repo_name', [
856 '',
856 '',
857 '.',
857 '.',
858 '..',
858 '..',
859 ':',
859 ':',
860 '/',
860 '/',
861 '<test>',
861 '<test>',
862 ])
862 ])
863 def test_api_create_repo_bad_names(self, repo_name):
863 def test_api_create_repo_bad_names(self, repo_name):
864 id_, params = _build_data(self.apikey, 'create_repo',
864 id_, params = _build_data(self.apikey, 'create_repo',
865 repo_name=repo_name,
865 repo_name=repo_name,
866 owner=base.TEST_USER_ADMIN_LOGIN,
866 owner=base.TEST_USER_ADMIN_LOGIN,
867 repo_type=self.REPO_TYPE,
867 repo_type=self.REPO_TYPE,
868 )
868 )
869 response = api_call(self, params)
869 response = api_call(self, params)
870 if repo_name == '/':
870 if repo_name == '/':
871 expected = "repo group `` not found"
871 expected = "repo group `` not found"
872 self._compare_error(id_, expected, given=response.body)
872 self._compare_error(id_, expected, given=response.body)
873 else:
873 else:
874 expected = "failed to create repository `%s`" % repo_name
874 expected = "failed to create repository `%s`" % repo_name
875 self._compare_error(id_, expected, given=response.body)
875 self._compare_error(id_, expected, given=response.body)
876 fixture.destroy_repo(repo_name)
876 fixture.destroy_repo(repo_name)
877
877
878 def test_api_create_repo_clone_uri_local(self):
878 def test_api_create_repo_clone_uri_local(self):
879 # cloning from local repos was a misfeature - it would bypass access control
879 # cloning from local repos was a misfeature - it would bypass access control
880 # TODO: introduce other test coverage of actual remote cloning
880 # TODO: introduce other test coverage of actual remote cloning
881 clone_uri = os.path.join(base.TESTS_TMP_PATH, self.REPO)
881 clone_uri = os.path.join(base.TESTS_TMP_PATH, self.REPO)
882 repo_name = 'api-repo'
882 repo_name = 'api-repo'
883 id_, params = _build_data(self.apikey, 'create_repo',
883 id_, params = _build_data(self.apikey, 'create_repo',
884 repo_name=repo_name,
884 repo_name=repo_name,
885 owner=base.TEST_USER_ADMIN_LOGIN,
885 owner=base.TEST_USER_ADMIN_LOGIN,
886 repo_type=self.REPO_TYPE,
886 repo_type=self.REPO_TYPE,
887 clone_uri=clone_uri,
887 clone_uri=clone_uri,
888 )
888 )
889 response = api_call(self, params)
889 response = api_call(self, params)
890 expected = "failed to create repository `%s`" % repo_name
890 expected = "failed to create repository `%s`" % repo_name
891 self._compare_error(id_, expected, given=response.body)
891 self._compare_error(id_, expected, given=response.body)
892 fixture.destroy_repo(repo_name)
892 fixture.destroy_repo(repo_name)
893
893
894 def test_api_create_repo_and_repo_group(self):
894 def test_api_create_repo_and_repo_group(self):
895 repo_group_name = 'my_gr'
895 repo_group_name = 'my_gr'
896 repo_name = '%s/api-repo' % repo_group_name
896 repo_name = '%s/api-repo' % repo_group_name
897
897
898 # repo creation can no longer also create repo group
898 # repo creation can no longer also create repo group
899 id_, params = _build_data(self.apikey, 'create_repo',
899 id_, params = _build_data(self.apikey, 'create_repo',
900 repo_name=repo_name,
900 repo_name=repo_name,
901 owner=base.TEST_USER_ADMIN_LOGIN,
901 owner=base.TEST_USER_ADMIN_LOGIN,
902 repo_type=self.REPO_TYPE,)
902 repo_type=self.REPO_TYPE,)
903 response = api_call(self, params)
903 response = api_call(self, params)
904 expected = 'repo group `%s` not found' % repo_group_name
904 expected = 'repo group `%s` not found' % repo_group_name
905 self._compare_error(id_, expected, given=response.body)
905 self._compare_error(id_, expected, given=response.body)
906 assert RepoModel().get_by_repo_name(repo_name) is None
906 assert RepoModel().get_by_repo_name(repo_name) is None
907
907
908 # create group before creating repo
908 # create group before creating repo
909 rg = fixture.create_repo_group(repo_group_name)
909 rg = fixture.create_repo_group(repo_group_name)
910 meta.Session().commit()
910 meta.Session().commit()
911
911
912 id_, params = _build_data(self.apikey, 'create_repo',
912 id_, params = _build_data(self.apikey, 'create_repo',
913 repo_name=repo_name,
913 repo_name=repo_name,
914 owner=base.TEST_USER_ADMIN_LOGIN,
914 owner=base.TEST_USER_ADMIN_LOGIN,
915 repo_type=self.REPO_TYPE,)
915 repo_type=self.REPO_TYPE,)
916 response = api_call(self, params)
916 response = api_call(self, params)
917 expected = {
917 expected = {
918 'msg': 'Created new repository `%s`' % repo_name,
918 'msg': 'Created new repository `%s`' % repo_name,
919 'success': True,
919 'success': True,
920 }
920 }
921 self._compare_ok(id_, expected, given=response.body)
921 self._compare_ok(id_, expected, given=response.body)
922 repo = RepoModel().get_by_repo_name(repo_name)
922 repo = RepoModel().get_by_repo_name(repo_name)
923 assert repo is not None
923 assert repo is not None
924
924
925 fixture.destroy_repo(repo_name)
925 fixture.destroy_repo(repo_name)
926 fixture.destroy_repo_group(repo_group_name)
926 fixture.destroy_repo_group(repo_group_name)
927
927
928 def test_api_create_repo_in_repo_group_without_permission(self):
928 def test_api_create_repo_in_repo_group_without_permission(self):
929 repo_group_basename = 'api-repo-repo'
929 repo_group_basename = 'api-repo-repo'
930 repo_group_name = '%s/%s' % (TEST_REPO_GROUP, repo_group_basename)
930 repo_group_name = '%s/%s' % (TEST_REPO_GROUP, repo_group_basename)
931 repo_name = '%s/api-repo' % repo_group_name
931 repo_name = '%s/api-repo' % repo_group_name
932
932
933 top_group = db.RepoGroup.get_by_group_name(TEST_REPO_GROUP)
933 top_group = db.RepoGroup.get_by_group_name(TEST_REPO_GROUP)
934 assert top_group
934 assert top_group
935 rg = fixture.create_repo_group(repo_group_basename, parent_group_id=top_group)
935 rg = fixture.create_repo_group(repo_group_basename, parent_group_id=top_group)
936 meta.Session().commit()
936 meta.Session().commit()
937 RepoGroupModel().grant_user_permission(repo_group_name,
937 RepoGroupModel().grant_user_permission(repo_group_name,
938 self.TEST_USER_LOGIN,
938 self.TEST_USER_LOGIN,
939 'group.none')
939 'group.none')
940 meta.Session().commit()
940 meta.Session().commit()
941
941
942 id_, params = _build_data(self.apikey_regular, 'create_repo',
942 id_, params = _build_data(self.apikey_regular, 'create_repo',
943 repo_name=repo_name,
943 repo_name=repo_name,
944 repo_type=self.REPO_TYPE,
944 repo_type=self.REPO_TYPE,
945 )
945 )
946 response = api_call(self, params)
946 response = api_call(self, params)
947
947
948 # API access control match Web access control:
948 # API access control match Web access control:
949 expected = 'no permission to create repo in test_repo_group/api-repo-repo'
949 expected = 'no permission to create repo in test_repo_group/api-repo-repo'
950 self._compare_error(id_, expected, given=response.body)
950 self._compare_error(id_, expected, given=response.body)
951
951
952 fixture.destroy_repo(repo_name)
952 fixture.destroy_repo(repo_name)
953 fixture.destroy_repo_group(repo_group_name)
953 fixture.destroy_repo_group(repo_group_name)
954
954
955 def test_api_create_repo_unknown_owner(self):
955 def test_api_create_repo_unknown_owner(self):
956 repo_name = 'api-repo'
956 repo_name = 'api-repo'
957 owner = 'i-dont-exist'
957 owner = 'i-dont-exist'
958 id_, params = _build_data(self.apikey, 'create_repo',
958 id_, params = _build_data(self.apikey, 'create_repo',
959 repo_name=repo_name,
959 repo_name=repo_name,
960 owner=owner,
960 owner=owner,
961 repo_type=self.REPO_TYPE,
961 repo_type=self.REPO_TYPE,
962 )
962 )
963 response = api_call(self, params)
963 response = api_call(self, params)
964 expected = 'user `%s` does not exist' % owner
964 expected = 'user `%s` does not exist' % owner
965 self._compare_error(id_, expected, given=response.body)
965 self._compare_error(id_, expected, given=response.body)
966
966
967 def test_api_create_repo_dont_specify_owner(self):
967 def test_api_create_repo_dont_specify_owner(self):
968 repo_name = 'api-repo'
968 repo_name = 'api-repo'
969 owner = 'i-dont-exist'
969 owner = 'i-dont-exist'
970 id_, params = _build_data(self.apikey, 'create_repo',
970 id_, params = _build_data(self.apikey, 'create_repo',
971 repo_name=repo_name,
971 repo_name=repo_name,
972 repo_type=self.REPO_TYPE,
972 repo_type=self.REPO_TYPE,
973 )
973 )
974 response = api_call(self, params)
974 response = api_call(self, params)
975
975
976 repo = RepoModel().get_by_repo_name(repo_name)
976 repo = RepoModel().get_by_repo_name(repo_name)
977 assert repo is not None
977 assert repo is not None
978 ret = {
978 ret = {
979 'msg': 'Created new repository `%s`' % repo_name,
979 'msg': 'Created new repository `%s`' % repo_name,
980 'success': True,
980 'success': True,
981 }
981 }
982 expected = ret
982 expected = ret
983 self._compare_ok(id_, expected, given=response.body)
983 self._compare_ok(id_, expected, given=response.body)
984 fixture.destroy_repo(repo_name)
984 fixture.destroy_repo(repo_name)
985
985
986 def test_api_create_repo_by_non_admin(self):
986 def test_api_create_repo_by_non_admin(self):
987 repo_name = 'api-repo'
987 repo_name = 'api-repo'
988 owner = 'i-dont-exist'
988 owner = 'i-dont-exist'
989 id_, params = _build_data(self.apikey_regular, 'create_repo',
989 id_, params = _build_data(self.apikey_regular, 'create_repo',
990 repo_name=repo_name,
990 repo_name=repo_name,
991 repo_type=self.REPO_TYPE,
991 repo_type=self.REPO_TYPE,
992 )
992 )
993 response = api_call(self, params)
993 response = api_call(self, params)
994
994
995 repo = RepoModel().get_by_repo_name(repo_name)
995 repo = RepoModel().get_by_repo_name(repo_name)
996 assert repo is not None
996 assert repo is not None
997 ret = {
997 ret = {
998 'msg': 'Created new repository `%s`' % repo_name,
998 'msg': 'Created new repository `%s`' % repo_name,
999 'success': True,
999 'success': True,
1000 }
1000 }
1001 expected = ret
1001 expected = ret
1002 self._compare_ok(id_, expected, given=response.body)
1002 self._compare_ok(id_, expected, given=response.body)
1003 fixture.destroy_repo(repo_name)
1003 fixture.destroy_repo(repo_name)
1004
1004
1005 def test_api_create_repo_by_non_admin_specify_owner(self):
1005 def test_api_create_repo_by_non_admin_specify_owner(self):
1006 repo_name = 'api-repo'
1006 repo_name = 'api-repo'
1007 owner = 'i-dont-exist'
1007 owner = 'i-dont-exist'
1008 id_, params = _build_data(self.apikey_regular, 'create_repo',
1008 id_, params = _build_data(self.apikey_regular, 'create_repo',
1009 repo_name=repo_name,
1009 repo_name=repo_name,
1010 repo_type=self.REPO_TYPE,
1010 repo_type=self.REPO_TYPE,
1011 owner=owner)
1011 owner=owner)
1012 response = api_call(self, params)
1012 response = api_call(self, params)
1013
1013
1014 expected = 'Only Kallithea admin can specify `owner` param'
1014 expected = 'Only Kallithea admin can specify `owner` param'
1015 self._compare_error(id_, expected, given=response.body)
1015 self._compare_error(id_, expected, given=response.body)
1016 fixture.destroy_repo(repo_name)
1016 fixture.destroy_repo(repo_name)
1017
1017
1018 def test_api_create_repo_exists(self):
1018 def test_api_create_repo_exists(self):
1019 repo_name = self.REPO
1019 repo_name = self.REPO
1020 id_, params = _build_data(self.apikey, 'create_repo',
1020 id_, params = _build_data(self.apikey, 'create_repo',
1021 repo_name=repo_name,
1021 repo_name=repo_name,
1022 owner=base.TEST_USER_ADMIN_LOGIN,
1022 owner=base.TEST_USER_ADMIN_LOGIN,
1023 repo_type=self.REPO_TYPE,)
1023 repo_type=self.REPO_TYPE,)
1024 response = api_call(self, params)
1024 response = api_call(self, params)
1025 expected = "repo `%s` already exist" % repo_name
1025 expected = "repo `%s` already exist" % repo_name
1026 self._compare_error(id_, expected, given=response.body)
1026 self._compare_error(id_, expected, given=response.body)
1027
1027
1028 def test_api_create_repo_dot_dot(self):
1028 def test_api_create_repo_dot_dot(self):
1029 # it is only possible to create repositories in existing repo groups - and '..' can't be used
1029 # it is only possible to create repositories in existing repo groups - and '..' can't be used
1030 group_name = '%s/..' % TEST_REPO_GROUP
1030 group_name = '%s/..' % TEST_REPO_GROUP
1031 repo_name = '%s/%s' % (group_name, 'could-be-outside')
1031 repo_name = '%s/%s' % (group_name, 'could-be-outside')
1032 id_, params = _build_data(self.apikey, 'create_repo',
1032 id_, params = _build_data(self.apikey, 'create_repo',
1033 repo_name=repo_name,
1033 repo_name=repo_name,
1034 owner=base.TEST_USER_ADMIN_LOGIN,
1034 owner=base.TEST_USER_ADMIN_LOGIN,
1035 repo_type=self.REPO_TYPE,)
1035 repo_type=self.REPO_TYPE,)
1036 response = api_call(self, params)
1036 response = api_call(self, params)
1037 expected = 'repo group `%s` not found' % group_name
1037 expected = 'repo group `%s` not found' % group_name
1038 self._compare_error(id_, expected, given=response.body)
1038 self._compare_error(id_, expected, given=response.body)
1039 fixture.destroy_repo(repo_name)
1039 fixture.destroy_repo(repo_name)
1040
1040
1041 @mock.patch.object(RepoModel, 'create', raise_exception)
1041 @mock.patch.object(RepoModel, 'create', raise_exception)
1042 def test_api_create_repo_exception_occurred(self):
1042 def test_api_create_repo_exception_occurred(self):
1043 repo_name = 'api-repo'
1043 repo_name = 'api-repo'
1044 id_, params = _build_data(self.apikey, 'create_repo',
1044 id_, params = _build_data(self.apikey, 'create_repo',
1045 repo_name=repo_name,
1045 repo_name=repo_name,
1046 owner=base.TEST_USER_ADMIN_LOGIN,
1046 owner=base.TEST_USER_ADMIN_LOGIN,
1047 repo_type=self.REPO_TYPE,)
1047 repo_type=self.REPO_TYPE,)
1048 response = api_call(self, params)
1048 response = api_call(self, params)
1049 expected = 'failed to create repository `%s`' % repo_name
1049 expected = 'failed to create repository `%s`' % repo_name
1050 self._compare_error(id_, expected, given=response.body)
1050 self._compare_error(id_, expected, given=response.body)
1051
1051
1052 @base.parametrize('changing_attr,updates', [
1052 @base.parametrize('changing_attr,updates', [
1053 ('owner', {'owner': base.TEST_USER_REGULAR_LOGIN}),
1053 ('owner', {'owner': base.TEST_USER_REGULAR_LOGIN}),
1054 ('description', {'description': 'new description'}),
1054 ('description', {'description': 'new description'}),
1055 ('clone_uri', {'clone_uri': 'http://example.com/repo'}), # will fail - pulling from non-existing repo should fail
1055 ('clone_uri', {'clone_uri': 'http://example.com/repo'}), # will fail - pulling from non-existing repo should fail
1056 ('clone_uri', {'clone_uri': '/repo'}), # will fail - pulling from local repo was a misfeature - it would bypass access control
1056 ('clone_uri', {'clone_uri': '/repo'}), # will fail - pulling from local repo was a misfeature - it would bypass access control
1057 ('clone_uri', {'clone_uri': None}),
1057 ('clone_uri', {'clone_uri': None}),
1058 ('landing_rev', {'landing_rev': 'branch:master'}),
1058 ('landing_rev', {'landing_rev': 'branch:master'}),
1059 ('private', {'private': True}),
1059 ('private', {'private': True}),
1060 ('enable_statistics', {'enable_statistics': True}),
1060 ('enable_statistics', {'enable_statistics': True}),
1061 ('enable_downloads', {'enable_downloads': True}),
1061 ('enable_downloads', {'enable_downloads': True}),
1062 ('name', {'name': 'new_repo_name'}),
1062 ('name', {'name': 'new_repo_name'}),
1063 ('repo_group', {'group': 'test_group_for_update'}),
1063 ('repo_group', {'group': 'test_group_for_update'}),
1064 ])
1064 ])
1065 def test_api_update_repo(self, changing_attr, updates):
1065 def test_api_update_repo(self, changing_attr, updates):
1066 repo_name = 'api_update_me'
1066 repo_name = 'api_update_me'
1067 repo = fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
1067 repo = fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
1068 if changing_attr == 'repo_group':
1068 if changing_attr == 'repo_group':
1069 fixture.create_repo_group(updates['group'])
1069 fixture.create_repo_group(updates['group'])
1070
1070
1071 id_, params = _build_data(self.apikey, 'update_repo',
1071 id_, params = _build_data(self.apikey, 'update_repo',
1072 repoid=repo_name, **updates)
1072 repoid=repo_name, **updates)
1073
1073
1074 if changing_attr == 'name':
1074 if changing_attr == 'name':
1075 repo_name = updates['name']
1075 repo_name = updates['name']
1076 if changing_attr == 'repo_group':
1076 if changing_attr == 'repo_group':
1077 repo_name = '/'.join([updates['group'], repo_name])
1077 repo_name = '/'.join([updates['group'], repo_name])
1078 expected = {
1078 expected = {
1079 'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo_name),
1079 'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo_name),
1080 'repository': repo.get_api_data()
1080 'repository': repo.get_api_data()
1081 }
1081 }
1082 expected['repository'].update(updates)
1082 expected['repository'].update(updates)
1083 if changing_attr == 'clone_uri' and updates['clone_uri'] is None:
1083 if changing_attr == 'clone_uri' and updates['clone_uri'] is None:
1084 expected['repository']['clone_uri'] = ''
1084 expected['repository']['clone_uri'] = ''
1085 if changing_attr == 'landing_rev':
1085 if changing_attr == 'landing_rev':
1086 expected['repository']['landing_rev'] = expected['repository']['landing_rev'].split(':', 1)
1086 expected['repository']['landing_rev'] = expected['repository']['landing_rev'].split(':', 1)
1087 if changing_attr == 'name':
1087 if changing_attr == 'name':
1088 expected['repository']['repo_name'] = expected['repository'].pop('name')
1088 expected['repository']['repo_name'] = expected['repository'].pop('name')
1089 if changing_attr == 'repo_group':
1089 if changing_attr == 'repo_group':
1090 expected['repository']['repo_name'] = expected['repository'].pop('group') + '/' + repo.repo_name
1090 expected['repository']['repo_name'] = expected['repository'].pop('group') + '/' + repo.repo_name
1091
1091
1092 response = api_call(self, params)
1092 response = api_call(self, params)
1093
1093
1094 try:
1094 try:
1095 if changing_attr == 'clone_uri' and updates['clone_uri']:
1095 if changing_attr == 'clone_uri' and updates['clone_uri']:
1096 expected = 'failed to update repo `%s`' % repo_name
1096 expected = 'failed to update repo `%s`' % repo_name
1097 self._compare_error(id_, expected, given=response.body)
1097 self._compare_error(id_, expected, given=response.body)
1098 else:
1098 else:
1099 self._compare_ok(id_, expected, given=response.body)
1099 self._compare_ok(id_, expected, given=response.body)
1100 finally:
1100 finally:
1101 fixture.destroy_repo(repo_name)
1101 fixture.destroy_repo(repo_name)
1102 if changing_attr == 'repo_group':
1102 if changing_attr == 'repo_group':
1103 fixture.destroy_repo_group(updates['group'])
1103 fixture.destroy_repo_group(updates['group'])
1104
1104
1105 @base.parametrize('changing_attr,updates', [
1105 @base.parametrize('changing_attr,updates', [
1106 ('owner', {'owner': base.TEST_USER_REGULAR_LOGIN}),
1106 ('owner', {'owner': base.TEST_USER_REGULAR_LOGIN}),
1107 ('description', {'description': 'new description'}),
1107 ('description', {'description': 'new description'}),
1108 ('clone_uri', {'clone_uri': 'http://example.com/repo'}), # will fail - pulling from non-existing repo should fail
1108 ('clone_uri', {'clone_uri': 'http://example.com/repo'}), # will fail - pulling from non-existing repo should fail
1109 ('clone_uri', {'clone_uri': '/repo'}), # will fail - pulling from local repo was a misfeature - it would bypass access control
1109 ('clone_uri', {'clone_uri': '/repo'}), # will fail - pulling from local repo was a misfeature - it would bypass access control
1110 ('clone_uri', {'clone_uri': None}),
1110 ('clone_uri', {'clone_uri': None}),
1111 ('landing_rev', {'landing_rev': 'branch:master'}),
1111 ('landing_rev', {'landing_rev': 'branch:master'}),
1112 ('enable_statistics', {'enable_statistics': True}),
1112 ('enable_statistics', {'enable_statistics': True}),
1113 ('enable_downloads', {'enable_downloads': True}),
1113 ('enable_downloads', {'enable_downloads': True}),
1114 ('name', {'name': 'new_repo_name'}),
1114 ('name', {'name': 'new_repo_name'}),
1115 ('repo_group', {'group': 'test_group_for_update'}),
1115 ('repo_group', {'group': 'test_group_for_update'}),
1116 ])
1116 ])
1117 def test_api_update_group_repo(self, changing_attr, updates):
1117 def test_api_update_group_repo(self, changing_attr, updates):
1118 group_name = 'lololo'
1118 group_name = 'lololo'
1119 fixture.create_repo_group(group_name)
1119 fixture.create_repo_group(group_name)
1120 repo_name = '%s/api_update_me' % group_name
1120 repo_name = '%s/api_update_me' % group_name
1121 repo = fixture.create_repo(repo_name, repo_group=group_name, repo_type=self.REPO_TYPE)
1121 repo = fixture.create_repo(repo_name, repo_group=group_name, repo_type=self.REPO_TYPE)
1122 if changing_attr == 'repo_group':
1122 if changing_attr == 'repo_group':
1123 fixture.create_repo_group(updates['group'])
1123 fixture.create_repo_group(updates['group'])
1124
1124
1125 id_, params = _build_data(self.apikey, 'update_repo',
1125 id_, params = _build_data(self.apikey, 'update_repo',
1126 repoid=repo_name, **updates)
1126 repoid=repo_name, **updates)
1127 response = api_call(self, params)
1127 response = api_call(self, params)
1128 if changing_attr == 'name':
1128 if changing_attr == 'name':
1129 repo_name = '%s/%s' % (group_name, updates['name'])
1129 repo_name = '%s/%s' % (group_name, updates['name'])
1130 if changing_attr == 'repo_group':
1130 if changing_attr == 'repo_group':
1131 repo_name = '/'.join([updates['group'], repo_name.rsplit('/', 1)[-1]])
1131 repo_name = '/'.join([updates['group'], repo_name.rsplit('/', 1)[-1]])
1132 try:
1132 try:
1133 if changing_attr == 'clone_uri' and updates['clone_uri']:
1133 if changing_attr == 'clone_uri' and updates['clone_uri']:
1134 expected = 'failed to update repo `%s`' % repo_name
1134 expected = 'failed to update repo `%s`' % repo_name
1135 self._compare_error(id_, expected, given=response.body)
1135 self._compare_error(id_, expected, given=response.body)
1136 else:
1136 else:
1137 expected = {
1137 expected = {
1138 'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo_name),
1138 'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo_name),
1139 'repository': repo.get_api_data()
1139 'repository': repo.get_api_data()
1140 }
1140 }
1141 self._compare_ok(id_, expected, given=response.body)
1141 self._compare_ok(id_, expected, given=response.body)
1142 finally:
1142 finally:
1143 fixture.destroy_repo(repo_name)
1143 fixture.destroy_repo(repo_name)
1144 if changing_attr == 'repo_group':
1144 if changing_attr == 'repo_group':
1145 fixture.destroy_repo_group(updates['group'])
1145 fixture.destroy_repo_group(updates['group'])
1146 fixture.destroy_repo_group(group_name)
1146 fixture.destroy_repo_group(group_name)
1147
1147
1148 def test_api_update_repo_repo_group_does_not_exist(self):
1148 def test_api_update_repo_repo_group_does_not_exist(self):
1149 repo_name = 'admin_owned'
1149 repo_name = 'admin_owned'
1150 fixture.create_repo(repo_name)
1150 fixture.create_repo(repo_name)
1151 updates = {'group': 'test_group_for_update'}
1151 updates = {'group': 'test_group_for_update'}
1152 id_, params = _build_data(self.apikey, 'update_repo',
1152 id_, params = _build_data(self.apikey, 'update_repo',
1153 repoid=repo_name, **updates)
1153 repoid=repo_name, **updates)
1154 response = api_call(self, params)
1154 response = api_call(self, params)
1155 try:
1155 try:
1156 expected = 'repository group `%s` does not exist' % updates['group']
1156 expected = 'repository group `%s` does not exist' % updates['group']
1157 self._compare_error(id_, expected, given=response.body)
1157 self._compare_error(id_, expected, given=response.body)
1158 finally:
1158 finally:
1159 fixture.destroy_repo(repo_name)
1159 fixture.destroy_repo(repo_name)
1160
1160
1161 def test_api_update_repo_regular_user_not_allowed(self):
1161 def test_api_update_repo_regular_user_not_allowed(self):
1162 repo_name = 'admin_owned'
1162 repo_name = 'admin_owned'
1163 fixture.create_repo(repo_name)
1163 fixture.create_repo(repo_name)
1164 updates = {'description': 'something else'}
1164 updates = {'description': 'something else'}
1165 id_, params = _build_data(self.apikey_regular, 'update_repo',
1165 id_, params = _build_data(self.apikey_regular, 'update_repo',
1166 repoid=repo_name, **updates)
1166 repoid=repo_name, **updates)
1167 response = api_call(self, params)
1167 response = api_call(self, params)
1168 try:
1168 try:
1169 expected = 'repository `%s` does not exist' % repo_name
1169 expected = 'repository `%s` does not exist' % repo_name
1170 self._compare_error(id_, expected, given=response.body)
1170 self._compare_error(id_, expected, given=response.body)
1171 finally:
1171 finally:
1172 fixture.destroy_repo(repo_name)
1172 fixture.destroy_repo(repo_name)
1173
1173
1174 @mock.patch.object(RepoModel, 'update', raise_exception)
1174 @mock.patch.object(RepoModel, 'update', raise_exception)
1175 def test_api_update_repo_exception_occurred(self):
1175 def test_api_update_repo_exception_occurred(self):
1176 repo_name = 'api_update_me'
1176 repo_name = 'api_update_me'
1177 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
1177 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
1178 id_, params = _build_data(self.apikey, 'update_repo',
1178 id_, params = _build_data(self.apikey, 'update_repo',
1179 repoid=repo_name, owner=base.TEST_USER_ADMIN_LOGIN,)
1179 repoid=repo_name, owner=base.TEST_USER_ADMIN_LOGIN,)
1180 response = api_call(self, params)
1180 response = api_call(self, params)
1181 try:
1181 try:
1182 expected = 'failed to update repo `%s`' % repo_name
1182 expected = 'failed to update repo `%s`' % repo_name
1183 self._compare_error(id_, expected, given=response.body)
1183 self._compare_error(id_, expected, given=response.body)
1184 finally:
1184 finally:
1185 fixture.destroy_repo(repo_name)
1185 fixture.destroy_repo(repo_name)
1186
1186
1187 def test_api_update_repo_regular_user_change_top_level_repo_name(self):
1187 def test_api_update_repo_regular_user_change_top_level_repo_name(self):
1188 repo_name = 'admin_owned'
1188 repo_name = 'admin_owned'
1189 new_repo_name = 'new_repo_name'
1189 new_repo_name = 'new_repo_name'
1190 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
1190 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
1191 RepoModel().grant_user_permission(repo=repo_name,
1191 RepoModel().grant_user_permission(repo=repo_name,
1192 user=self.TEST_USER_LOGIN,
1192 user=self.TEST_USER_LOGIN,
1193 perm='repository.admin')
1193 perm='repository.admin')
1194 UserModel().revoke_perm('default', 'hg.create.repository')
1194 UserModel().revoke_perm('default', 'hg.create.repository')
1195 UserModel().grant_perm('default', 'hg.create.none')
1195 UserModel().grant_perm('default', 'hg.create.none')
1196 updates = {'name': new_repo_name}
1196 updates = {'name': new_repo_name}
1197 id_, params = _build_data(self.apikey_regular, 'update_repo',
1197 id_, params = _build_data(self.apikey_regular, 'update_repo',
1198 repoid=repo_name, **updates)
1198 repoid=repo_name, **updates)
1199 response = api_call(self, params)
1199 response = api_call(self, params)
1200 try:
1200 try:
1201 expected = 'no permission to create (or move) top level repositories'
1201 expected = 'no permission to create (or move) top level repositories'
1202 self._compare_error(id_, expected, given=response.body)
1202 self._compare_error(id_, expected, given=response.body)
1203 finally:
1203 finally:
1204 fixture.destroy_repo(repo_name)
1204 fixture.destroy_repo(repo_name)
1205 fixture.destroy_repo(new_repo_name)
1205 fixture.destroy_repo(new_repo_name)
1206
1206
1207 def test_api_update_repo_regular_user_change_repo_name_allowed(self):
1207 def test_api_update_repo_regular_user_change_repo_name_allowed(self):
1208 repo_name = 'admin_owned'
1208 repo_name = 'admin_owned'
1209 new_repo_name = 'new_repo_name'
1209 new_repo_name = 'new_repo_name'
1210 repo = fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
1210 repo = fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
1211 RepoModel().grant_user_permission(repo=repo_name,
1211 RepoModel().grant_user_permission(repo=repo_name,
1212 user=self.TEST_USER_LOGIN,
1212 user=self.TEST_USER_LOGIN,
1213 perm='repository.admin')
1213 perm='repository.admin')
1214 UserModel().revoke_perm('default', 'hg.create.none')
1214 UserModel().revoke_perm('default', 'hg.create.none')
1215 UserModel().grant_perm('default', 'hg.create.repository')
1215 UserModel().grant_perm('default', 'hg.create.repository')
1216 updates = {'name': new_repo_name}
1216 updates = {'name': new_repo_name}
1217 id_, params = _build_data(self.apikey_regular, 'update_repo',
1217 id_, params = _build_data(self.apikey_regular, 'update_repo',
1218 repoid=repo_name, **updates)
1218 repoid=repo_name, **updates)
1219 response = api_call(self, params)
1219 response = api_call(self, params)
1220 try:
1220 try:
1221 expected = {
1221 expected = {
1222 'msg': 'updated repo ID:%s %s' % (repo.repo_id, new_repo_name),
1222 'msg': 'updated repo ID:%s %s' % (repo.repo_id, new_repo_name),
1223 'repository': repo.get_api_data()
1223 'repository': repo.get_api_data()
1224 }
1224 }
1225 self._compare_ok(id_, expected, given=response.body)
1225 self._compare_ok(id_, expected, given=response.body)
1226 finally:
1226 finally:
1227 fixture.destroy_repo(repo_name)
1227 fixture.destroy_repo(repo_name)
1228 fixture.destroy_repo(new_repo_name)
1228 fixture.destroy_repo(new_repo_name)
1229
1229
1230 def test_api_update_repo_regular_user_change_owner(self):
1230 def test_api_update_repo_regular_user_change_owner(self):
1231 repo_name = 'admin_owned'
1231 repo_name = 'admin_owned'
1232 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
1232 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
1233 RepoModel().grant_user_permission(repo=repo_name,
1233 RepoModel().grant_user_permission(repo=repo_name,
1234 user=self.TEST_USER_LOGIN,
1234 user=self.TEST_USER_LOGIN,
1235 perm='repository.admin')
1235 perm='repository.admin')
1236 updates = {'owner': base.TEST_USER_ADMIN_LOGIN}
1236 updates = {'owner': base.TEST_USER_ADMIN_LOGIN}
1237 id_, params = _build_data(self.apikey_regular, 'update_repo',
1237 id_, params = _build_data(self.apikey_regular, 'update_repo',
1238 repoid=repo_name, **updates)
1238 repoid=repo_name, **updates)
1239 response = api_call(self, params)
1239 response = api_call(self, params)
1240 try:
1240 try:
1241 expected = 'Only Kallithea admin can specify `owner` param'
1241 expected = 'Only Kallithea admin can specify `owner` param'
1242 self._compare_error(id_, expected, given=response.body)
1242 self._compare_error(id_, expected, given=response.body)
1243 finally:
1243 finally:
1244 fixture.destroy_repo(repo_name)
1244 fixture.destroy_repo(repo_name)
1245
1245
1246 def test_api_delete_repo(self):
1246 def test_api_delete_repo(self):
1247 repo_name = 'api_delete_me'
1247 repo_name = 'api_delete_me'
1248 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
1248 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
1249
1249
1250 id_, params = _build_data(self.apikey, 'delete_repo',
1250 id_, params = _build_data(self.apikey, 'delete_repo',
1251 repoid=repo_name, )
1251 repoid=repo_name, )
1252 response = api_call(self, params)
1252 response = api_call(self, params)
1253
1253
1254 ret = {
1254 ret = {
1255 'msg': 'Deleted repository `%s`' % repo_name,
1255 'msg': 'Deleted repository `%s`' % repo_name,
1256 'success': True
1256 'success': True
1257 }
1257 }
1258 try:
1258 try:
1259 expected = ret
1259 expected = ret
1260 self._compare_ok(id_, expected, given=response.body)
1260 self._compare_ok(id_, expected, given=response.body)
1261 finally:
1261 finally:
1262 fixture.destroy_repo(repo_name)
1262 fixture.destroy_repo(repo_name)
1263
1263
1264 def test_api_delete_repo_by_non_admin(self):
1264 def test_api_delete_repo_by_non_admin(self):
1265 repo_name = 'api_delete_me'
1265 repo_name = 'api_delete_me'
1266 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE,
1266 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE,
1267 cur_user=self.TEST_USER_LOGIN)
1267 cur_user=self.TEST_USER_LOGIN)
1268 id_, params = _build_data(self.apikey_regular, 'delete_repo',
1268 id_, params = _build_data(self.apikey_regular, 'delete_repo',
1269 repoid=repo_name, )
1269 repoid=repo_name, )
1270 response = api_call(self, params)
1270 response = api_call(self, params)
1271
1271
1272 ret = {
1272 ret = {
1273 'msg': 'Deleted repository `%s`' % repo_name,
1273 'msg': 'Deleted repository `%s`' % repo_name,
1274 'success': True
1274 'success': True
1275 }
1275 }
1276 try:
1276 try:
1277 expected = ret
1277 expected = ret
1278 self._compare_ok(id_, expected, given=response.body)
1278 self._compare_ok(id_, expected, given=response.body)
1279 finally:
1279 finally:
1280 fixture.destroy_repo(repo_name)
1280 fixture.destroy_repo(repo_name)
1281
1281
1282 def test_api_delete_repo_by_non_admin_no_permission(self):
1282 def test_api_delete_repo_by_non_admin_no_permission(self):
1283 repo_name = 'api_delete_me'
1283 repo_name = 'api_delete_me'
1284 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
1284 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
1285 try:
1285 try:
1286 id_, params = _build_data(self.apikey_regular, 'delete_repo',
1286 id_, params = _build_data(self.apikey_regular, 'delete_repo',
1287 repoid=repo_name, )
1287 repoid=repo_name, )
1288 response = api_call(self, params)
1288 response = api_call(self, params)
1289 expected = 'repository `%s` does not exist' % (repo_name)
1289 expected = 'repository `%s` does not exist' % (repo_name)
1290 self._compare_error(id_, expected, given=response.body)
1290 self._compare_error(id_, expected, given=response.body)
1291 finally:
1291 finally:
1292 fixture.destroy_repo(repo_name)
1292 fixture.destroy_repo(repo_name)
1293
1293
1294 def test_api_delete_repo_exception_occurred(self):
1294 def test_api_delete_repo_exception_occurred(self):
1295 repo_name = 'api_delete_me'
1295 repo_name = 'api_delete_me'
1296 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
1296 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
1297 try:
1297 try:
1298 with mock.patch.object(RepoModel, 'delete', raise_exception):
1298 with mock.patch.object(RepoModel, 'delete', raise_exception):
1299 id_, params = _build_data(self.apikey, 'delete_repo',
1299 id_, params = _build_data(self.apikey, 'delete_repo',
1300 repoid=repo_name, )
1300 repoid=repo_name, )
1301 response = api_call(self, params)
1301 response = api_call(self, params)
1302
1302
1303 expected = 'failed to delete repository `%s`' % repo_name
1303 expected = 'failed to delete repository `%s`' % repo_name
1304 self._compare_error(id_, expected, given=response.body)
1304 self._compare_error(id_, expected, given=response.body)
1305 finally:
1305 finally:
1306 fixture.destroy_repo(repo_name)
1306 fixture.destroy_repo(repo_name)
1307
1307
1308 def test_api_fork_repo(self):
1308 def test_api_fork_repo(self):
1309 fork_name = 'api-repo-fork'
1309 fork_name = 'api-repo-fork'
1310 id_, params = _build_data(self.apikey, 'fork_repo',
1310 id_, params = _build_data(self.apikey, 'fork_repo',
1311 repoid=self.REPO,
1311 repoid=self.REPO,
1312 fork_name=fork_name,
1312 fork_name=fork_name,
1313 owner=base.TEST_USER_ADMIN_LOGIN,
1313 owner=base.TEST_USER_ADMIN_LOGIN,
1314 )
1314 )
1315 response = api_call(self, params)
1315 response = api_call(self, params)
1316
1316
1317 ret = {
1317 ret = {
1318 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
1318 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
1319 fork_name),
1319 fork_name),
1320 'success': True,
1320 'success': True,
1321 }
1321 }
1322 expected = ret
1322 expected = ret
1323 self._compare_ok(id_, expected, given=response.body)
1323 self._compare_ok(id_, expected, given=response.body)
1324 fixture.destroy_repo(fork_name)
1324 fixture.destroy_repo(fork_name)
1325
1325
1326 @base.parametrize('fork_name', [
1326 @base.parametrize('fork_name', [
1327 'api-repo-fork',
1327 'api-repo-fork',
1328 '%s/api-repo-fork' % TEST_REPO_GROUP,
1328 '%s/api-repo-fork' % TEST_REPO_GROUP,
1329 ])
1329 ])
1330 def test_api_fork_repo_non_admin(self, fork_name):
1330 def test_api_fork_repo_non_admin(self, fork_name):
1331 RepoGroupModel().grant_user_permission(TEST_REPO_GROUP,
1331 RepoGroupModel().grant_user_permission(TEST_REPO_GROUP,
1332 self.TEST_USER_LOGIN,
1332 self.TEST_USER_LOGIN,
1333 'group.write')
1333 'group.write')
1334 id_, params = _build_data(self.apikey_regular, 'fork_repo',
1334 id_, params = _build_data(self.apikey_regular, 'fork_repo',
1335 repoid=self.REPO,
1335 repoid=self.REPO,
1336 fork_name=fork_name,
1336 fork_name=fork_name,
1337 )
1337 )
1338 response = api_call(self, params)
1338 response = api_call(self, params)
1339
1339
1340 ret = {
1340 ret = {
1341 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
1341 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
1342 fork_name),
1342 fork_name),
1343 'success': True,
1343 'success': True,
1344 }
1344 }
1345 expected = ret
1345 expected = ret
1346 self._compare_ok(id_, expected, given=response.body)
1346 self._compare_ok(id_, expected, given=response.body)
1347 fixture.destroy_repo(fork_name)
1347 fixture.destroy_repo(fork_name)
1348
1348
1349 def test_api_fork_repo_non_admin_specify_owner(self):
1349 def test_api_fork_repo_non_admin_specify_owner(self):
1350 fork_name = 'api-repo-fork'
1350 fork_name = 'api-repo-fork'
1351 id_, params = _build_data(self.apikey_regular, 'fork_repo',
1351 id_, params = _build_data(self.apikey_regular, 'fork_repo',
1352 repoid=self.REPO,
1352 repoid=self.REPO,
1353 fork_name=fork_name,
1353 fork_name=fork_name,
1354 owner=base.TEST_USER_ADMIN_LOGIN,
1354 owner=base.TEST_USER_ADMIN_LOGIN,
1355 )
1355 )
1356 response = api_call(self, params)
1356 response = api_call(self, params)
1357 expected = 'Only Kallithea admin can specify `owner` param'
1357 expected = 'Only Kallithea admin can specify `owner` param'
1358 self._compare_error(id_, expected, given=response.body)
1358 self._compare_error(id_, expected, given=response.body)
1359 fixture.destroy_repo(fork_name)
1359 fixture.destroy_repo(fork_name)
1360
1360
1361 def test_api_fork_repo_non_admin_no_permission_to_fork(self):
1361 def test_api_fork_repo_non_admin_no_permission_to_fork(self):
1362 RepoModel().grant_user_permission(repo=self.REPO,
1362 RepoModel().grant_user_permission(repo=self.REPO,
1363 user=db.User.DEFAULT_USER_NAME,
1363 user=db.User.DEFAULT_USER_NAME,
1364 perm='repository.none')
1364 perm='repository.none')
1365 fork_name = 'api-repo-fork'
1365 fork_name = 'api-repo-fork'
1366 try:
1366 try:
1367 id_, params = _build_data(self.apikey_regular, 'fork_repo',
1367 id_, params = _build_data(self.apikey_regular, 'fork_repo',
1368 repoid=self.REPO,
1368 repoid=self.REPO,
1369 fork_name=fork_name,
1369 fork_name=fork_name,
1370 )
1370 )
1371 response = api_call(self, params)
1371 response = api_call(self, params)
1372 expected = 'repository `%s` does not exist' % (self.REPO)
1372 expected = 'repository `%s` does not exist' % (self.REPO)
1373 self._compare_error(id_, expected, given=response.body)
1373 self._compare_error(id_, expected, given=response.body)
1374 finally:
1374 finally:
1375 RepoModel().grant_user_permission(repo=self.REPO,
1375 RepoModel().grant_user_permission(repo=self.REPO,
1376 user=db.User.DEFAULT_USER_NAME,
1376 user=db.User.DEFAULT_USER_NAME,
1377 perm='repository.read')
1377 perm='repository.read')
1378 fixture.destroy_repo(fork_name)
1378 fixture.destroy_repo(fork_name)
1379
1379
1380 @base.parametrize('name,perm', [
1380 @base.parametrize('name,perm', [
1381 ('read', 'repository.read'),
1381 ('read', 'repository.read'),
1382 ('write', 'repository.write'),
1382 ('write', 'repository.write'),
1383 ('admin', 'repository.admin'),
1383 ('admin', 'repository.admin'),
1384 ])
1384 ])
1385 def test_api_fork_repo_non_admin_no_create_repo_permission(self, name, perm):
1385 def test_api_fork_repo_non_admin_no_create_repo_permission(self, name, perm):
1386 fork_name = 'api-repo-fork'
1386 fork_name = 'api-repo-fork'
1387 # regardless of base repository permission, forking is disallowed
1387 # regardless of base repository permission, forking is disallowed
1388 # when repository creation is disabled
1388 # when repository creation is disabled
1389 RepoModel().grant_user_permission(repo=self.REPO,
1389 RepoModel().grant_user_permission(repo=self.REPO,
1390 user=self.TEST_USER_LOGIN,
1390 user=self.TEST_USER_LOGIN,
1391 perm=perm)
1391 perm=perm)
1392 UserModel().revoke_perm('default', 'hg.create.repository')
1392 UserModel().revoke_perm('default', 'hg.create.repository')
1393 UserModel().grant_perm('default', 'hg.create.none')
1393 UserModel().grant_perm('default', 'hg.create.none')
1394 id_, params = _build_data(self.apikey_regular, 'fork_repo',
1394 id_, params = _build_data(self.apikey_regular, 'fork_repo',
1395 repoid=self.REPO,
1395 repoid=self.REPO,
1396 fork_name=fork_name,
1396 fork_name=fork_name,
1397 )
1397 )
1398 response = api_call(self, params)
1398 response = api_call(self, params)
1399 expected = 'no permission to create top level repo'
1399 expected = 'no permission to create top level repo'
1400 self._compare_error(id_, expected, given=response.body)
1400 self._compare_error(id_, expected, given=response.body)
1401 fixture.destroy_repo(fork_name)
1401 fixture.destroy_repo(fork_name)
1402
1402
1403 def test_api_fork_repo_unknown_owner(self):
1403 def test_api_fork_repo_unknown_owner(self):
1404 fork_name = 'api-repo-fork'
1404 fork_name = 'api-repo-fork'
1405 owner = 'i-dont-exist'
1405 owner = 'i-dont-exist'
1406 id_, params = _build_data(self.apikey, 'fork_repo',
1406 id_, params = _build_data(self.apikey, 'fork_repo',
1407 repoid=self.REPO,
1407 repoid=self.REPO,
1408 fork_name=fork_name,
1408 fork_name=fork_name,
1409 owner=owner,
1409 owner=owner,
1410 )
1410 )
1411 response = api_call(self, params)
1411 response = api_call(self, params)
1412 expected = 'user `%s` does not exist' % owner
1412 expected = 'user `%s` does not exist' % owner
1413 self._compare_error(id_, expected, given=response.body)
1413 self._compare_error(id_, expected, given=response.body)
1414
1414
1415 def test_api_fork_repo_fork_exists(self):
1415 def test_api_fork_repo_fork_exists(self):
1416 fork_name = 'api-repo-fork'
1416 fork_name = 'api-repo-fork'
1417 fixture.create_fork(self.REPO, fork_name)
1417 fixture.create_fork(self.REPO, fork_name)
1418
1418
1419 try:
1419 try:
1420 fork_name = 'api-repo-fork'
1420 fork_name = 'api-repo-fork'
1421
1421
1422 id_, params = _build_data(self.apikey, 'fork_repo',
1422 id_, params = _build_data(self.apikey, 'fork_repo',
1423 repoid=self.REPO,
1423 repoid=self.REPO,
1424 fork_name=fork_name,
1424 fork_name=fork_name,
1425 owner=base.TEST_USER_ADMIN_LOGIN,
1425 owner=base.TEST_USER_ADMIN_LOGIN,
1426 )
1426 )
1427 response = api_call(self, params)
1427 response = api_call(self, params)
1428
1428
1429 expected = "fork `%s` already exist" % fork_name
1429 expected = "fork `%s` already exist" % fork_name
1430 self._compare_error(id_, expected, given=response.body)
1430 self._compare_error(id_, expected, given=response.body)
1431 finally:
1431 finally:
1432 fixture.destroy_repo(fork_name)
1432 fixture.destroy_repo(fork_name)
1433
1433
1434 def test_api_fork_repo_repo_exists(self):
1434 def test_api_fork_repo_repo_exists(self):
1435 fork_name = self.REPO
1435 fork_name = self.REPO
1436
1436
1437 id_, params = _build_data(self.apikey, 'fork_repo',
1437 id_, params = _build_data(self.apikey, 'fork_repo',
1438 repoid=self.REPO,
1438 repoid=self.REPO,
1439 fork_name=fork_name,
1439 fork_name=fork_name,
1440 owner=base.TEST_USER_ADMIN_LOGIN,
1440 owner=base.TEST_USER_ADMIN_LOGIN,
1441 )
1441 )
1442 response = api_call(self, params)
1442 response = api_call(self, params)
1443
1443
1444 expected = "repo `%s` already exist" % fork_name
1444 expected = "repo `%s` already exist" % fork_name
1445 self._compare_error(id_, expected, given=response.body)
1445 self._compare_error(id_, expected, given=response.body)
1446
1446
1447 @mock.patch.object(RepoModel, 'create_fork', raise_exception)
1447 @mock.patch.object(RepoModel, 'create_fork', raise_exception)
1448 def test_api_fork_repo_exception_occurred(self):
1448 def test_api_fork_repo_exception_occurred(self):
1449 fork_name = 'api-repo-fork'
1449 fork_name = 'api-repo-fork'
1450 id_, params = _build_data(self.apikey, 'fork_repo',
1450 id_, params = _build_data(self.apikey, 'fork_repo',
1451 repoid=self.REPO,
1451 repoid=self.REPO,
1452 fork_name=fork_name,
1452 fork_name=fork_name,
1453 owner=base.TEST_USER_ADMIN_LOGIN,
1453 owner=base.TEST_USER_ADMIN_LOGIN,
1454 )
1454 )
1455 response = api_call(self, params)
1455 response = api_call(self, params)
1456
1456
1457 expected = 'failed to fork repository `%s` as `%s`' % (self.REPO,
1457 expected = 'failed to fork repository `%s` as `%s`' % (self.REPO,
1458 fork_name)
1458 fork_name)
1459 self._compare_error(id_, expected, given=response.body)
1459 self._compare_error(id_, expected, given=response.body)
1460
1460
1461 def test_api_get_user_group(self):
1461 def test_api_get_user_group(self):
1462 id_, params = _build_data(self.apikey, 'get_user_group',
1462 id_, params = _build_data(self.apikey, 'get_user_group',
1463 usergroupid=TEST_USER_GROUP)
1463 usergroupid=TEST_USER_GROUP)
1464 response = api_call(self, params)
1464 response = api_call(self, params)
1465
1465
1466 user_group = UserGroupModel().get_group(TEST_USER_GROUP)
1466 user_group = UserGroupModel().get_group(TEST_USER_GROUP)
1467 members = []
1467 members = []
1468 for user in user_group.members:
1468 for user in user_group.members:
1469 user = user.user
1469 user = user.user
1470 members.append(user.get_api_data())
1470 members.append(user.get_api_data())
1471
1471
1472 ret = user_group.get_api_data()
1472 ret = user_group.get_api_data()
1473 ret['members'] = members
1473 ret['members'] = members
1474 expected = ret
1474 expected = ret
1475 self._compare_ok(id_, expected, given=response.body)
1475 self._compare_ok(id_, expected, given=response.body)
1476
1476
1477 def test_api_get_user_groups(self):
1477 def test_api_get_user_groups(self):
1478 gr_name = 'test_user_group2'
1478 gr_name = 'test_user_group2'
1479 make_user_group(gr_name)
1479 make_user_group(gr_name)
1480
1480
1481 try:
1481 try:
1482 id_, params = _build_data(self.apikey, 'get_user_groups', )
1482 id_, params = _build_data(self.apikey, 'get_user_groups', )
1483 response = api_call(self, params)
1483 response = api_call(self, params)
1484
1484
1485 expected = []
1485 expected = []
1486 for gr_name in [TEST_USER_GROUP, 'test_user_group2']:
1486 for gr_name in [TEST_USER_GROUP, 'test_user_group2']:
1487 user_group = UserGroupModel().get_group(gr_name)
1487 user_group = UserGroupModel().get_group(gr_name)
1488 ret = user_group.get_api_data()
1488 ret = user_group.get_api_data()
1489 expected.append(ret)
1489 expected.append(ret)
1490 self._compare_ok(id_, expected, given=response.body)
1490 self._compare_ok(id_, expected, given=response.body)
1491 finally:
1491 finally:
1492 fixture.destroy_user_group(gr_name)
1492 fixture.destroy_user_group(gr_name)
1493
1493
1494 def test_api_create_user_group(self):
1494 def test_api_create_user_group(self):
1495 group_name = 'some_new_group'
1495 group_name = 'some_new_group'
1496 id_, params = _build_data(self.apikey, 'create_user_group',
1496 id_, params = _build_data(self.apikey, 'create_user_group',
1497 group_name=group_name)
1497 group_name=group_name)
1498 response = api_call(self, params)
1498 response = api_call(self, params)
1499
1499
1500 ret = {
1500 ret = {
1501 'msg': 'created new user group `%s`' % group_name,
1501 'msg': 'created new user group `%s`' % group_name,
1502 'user_group': jsonify(UserGroupModel() \
1502 'user_group': jsonify(UserGroupModel() \
1503 .get_by_name(group_name) \
1503 .get_by_name(group_name) \
1504 .get_api_data())
1504 .get_api_data())
1505 }
1505 }
1506 expected = ret
1506 expected = ret
1507 self._compare_ok(id_, expected, given=response.body)
1507 self._compare_ok(id_, expected, given=response.body)
1508
1508
1509 fixture.destroy_user_group(group_name)
1509 fixture.destroy_user_group(group_name)
1510
1510
1511 def test_api_get_user_group_that_exist(self):
1511 def test_api_get_user_group_that_exist(self):
1512 id_, params = _build_data(self.apikey, 'create_user_group',
1512 id_, params = _build_data(self.apikey, 'create_user_group',
1513 group_name=TEST_USER_GROUP)
1513 group_name=TEST_USER_GROUP)
1514 response = api_call(self, params)
1514 response = api_call(self, params)
1515
1515
1516 expected = "user group `%s` already exist" % TEST_USER_GROUP
1516 expected = "user group `%s` already exist" % TEST_USER_GROUP
1517 self._compare_error(id_, expected, given=response.body)
1517 self._compare_error(id_, expected, given=response.body)
1518
1518
1519 @mock.patch.object(UserGroupModel, 'create', raise_exception)
1519 @mock.patch.object(UserGroupModel, 'create', raise_exception)
1520 def test_api_get_user_group_exception_occurred(self):
1520 def test_api_get_user_group_exception_occurred(self):
1521 group_name = 'exception_happens'
1521 group_name = 'exception_happens'
1522 id_, params = _build_data(self.apikey, 'create_user_group',
1522 id_, params = _build_data(self.apikey, 'create_user_group',
1523 group_name=group_name)
1523 group_name=group_name)
1524 response = api_call(self, params)
1524 response = api_call(self, params)
1525
1525
1526 expected = 'failed to create group `%s`' % group_name
1526 expected = 'failed to create group `%s`' % group_name
1527 self._compare_error(id_, expected, given=response.body)
1527 self._compare_error(id_, expected, given=response.body)
1528
1528
1529 @base.parametrize('changing_attr,updates', [
1529 @base.parametrize('changing_attr,updates', [
1530 ('group_name', {'group_name': 'new_group_name'}),
1530 ('group_name', {'group_name': 'new_group_name'}),
1531 ('group_name', {'group_name': 'test_group_for_update'}),
1531 ('group_name', {'group_name': 'test_group_for_update'}),
1532 ('owner', {'owner': base.TEST_USER_REGULAR_LOGIN}),
1532 ('owner', {'owner': base.TEST_USER_REGULAR_LOGIN}),
1533 ('active', {'active': False}),
1533 ('active', {'active': False}),
1534 ('active', {'active': True}),
1534 ('active', {'active': True}),
1535 ])
1535 ])
1536 def test_api_update_user_group(self, changing_attr, updates):
1536 def test_api_update_user_group(self, changing_attr, updates):
1537 gr_name = 'test_group_for_update'
1537 gr_name = 'test_group_for_update'
1538 user_group = fixture.create_user_group(gr_name)
1538 user_group = fixture.create_user_group(gr_name)
1539 try:
1539 try:
1540 id_, params = _build_data(self.apikey, 'update_user_group',
1540 id_, params = _build_data(self.apikey, 'update_user_group',
1541 usergroupid=gr_name, **updates)
1541 usergroupid=gr_name, **updates)
1542 response = api_call(self, params)
1542 response = api_call(self, params)
1543 expected = {
1543 expected = {
1544 'msg': 'updated user group ID:%s %s' % (user_group.users_group_id,
1544 'msg': 'updated user group ID:%s %s' % (user_group.users_group_id,
1545 user_group.users_group_name),
1545 user_group.users_group_name),
1546 'user_group': user_group.get_api_data()
1546 'user_group': user_group.get_api_data()
1547 }
1547 }
1548 self._compare_ok(id_, expected, given=response.body)
1548 self._compare_ok(id_, expected, given=response.body)
1549 finally:
1549 finally:
1550 if changing_attr == 'group_name':
1550 if changing_attr == 'group_name':
1551 # switch to updated name for proper cleanup
1551 # switch to updated name for proper cleanup
1552 gr_name = updates['group_name']
1552 gr_name = updates['group_name']
1553 fixture.destroy_user_group(gr_name)
1553 fixture.destroy_user_group(gr_name)
1554
1554
1555 @mock.patch.object(UserGroupModel, 'update', raise_exception)
1555 @mock.patch.object(UserGroupModel, 'update', raise_exception)
1556 def test_api_update_user_group_exception_occurred(self):
1556 def test_api_update_user_group_exception_occurred(self):
1557 gr_name = 'test_group'
1557 gr_name = 'test_group'
1558 fixture.create_user_group(gr_name)
1558 fixture.create_user_group(gr_name)
1559 try:
1559 try:
1560 id_, params = _build_data(self.apikey, 'update_user_group',
1560 id_, params = _build_data(self.apikey, 'update_user_group',
1561 usergroupid=gr_name)
1561 usergroupid=gr_name)
1562 response = api_call(self, params)
1562 response = api_call(self, params)
1563 expected = 'failed to update user group `%s`' % gr_name
1563 expected = 'failed to update user group `%s`' % gr_name
1564 self._compare_error(id_, expected, given=response.body)
1564 self._compare_error(id_, expected, given=response.body)
1565 finally:
1565 finally:
1566 fixture.destroy_user_group(gr_name)
1566 fixture.destroy_user_group(gr_name)
1567
1567
1568 def test_api_add_user_to_user_group(self):
1568 def test_api_add_user_to_user_group(self):
1569 gr_name = 'test_group'
1569 gr_name = 'test_group'
1570 fixture.create_user_group(gr_name)
1570 fixture.create_user_group(gr_name)
1571 try:
1571 try:
1572 id_, params = _build_data(self.apikey, 'add_user_to_user_group',
1572 id_, params = _build_data(self.apikey, 'add_user_to_user_group',
1573 usergroupid=gr_name,
1573 usergroupid=gr_name,
1574 userid=base.TEST_USER_ADMIN_LOGIN)
1574 userid=base.TEST_USER_ADMIN_LOGIN)
1575 response = api_call(self, params)
1575 response = api_call(self, params)
1576 expected = {
1576 expected = {
1577 'msg': 'added member `%s` to user group `%s`' % (
1577 'msg': 'added member `%s` to user group `%s`' % (
1578 base.TEST_USER_ADMIN_LOGIN, gr_name),
1578 base.TEST_USER_ADMIN_LOGIN, gr_name),
1579 'success': True
1579 'success': True
1580 }
1580 }
1581 self._compare_ok(id_, expected, given=response.body)
1581 self._compare_ok(id_, expected, given=response.body)
1582 finally:
1582 finally:
1583 fixture.destroy_user_group(gr_name)
1583 fixture.destroy_user_group(gr_name)
1584
1584
1585 def test_api_add_user_to_user_group_that_doesnt_exist(self):
1585 def test_api_add_user_to_user_group_that_doesnt_exist(self):
1586 id_, params = _build_data(self.apikey, 'add_user_to_user_group',
1586 id_, params = _build_data(self.apikey, 'add_user_to_user_group',
1587 usergroupid='false-group',
1587 usergroupid='false-group',
1588 userid=base.TEST_USER_ADMIN_LOGIN)
1588 userid=base.TEST_USER_ADMIN_LOGIN)
1589 response = api_call(self, params)
1589 response = api_call(self, params)
1590
1590
1591 expected = 'user group `%s` does not exist' % 'false-group'
1591 expected = 'user group `%s` does not exist' % 'false-group'
1592 self._compare_error(id_, expected, given=response.body)
1592 self._compare_error(id_, expected, given=response.body)
1593
1593
1594 @mock.patch.object(UserGroupModel, 'add_user_to_group', raise_exception)
1594 @mock.patch.object(UserGroupModel, 'add_user_to_group', raise_exception)
1595 def test_api_add_user_to_user_group_exception_occurred(self):
1595 def test_api_add_user_to_user_group_exception_occurred(self):
1596 gr_name = 'test_group'
1596 gr_name = 'test_group'
1597 fixture.create_user_group(gr_name)
1597 fixture.create_user_group(gr_name)
1598 try:
1598 try:
1599 id_, params = _build_data(self.apikey, 'add_user_to_user_group',
1599 id_, params = _build_data(self.apikey, 'add_user_to_user_group',
1600 usergroupid=gr_name,
1600 usergroupid=gr_name,
1601 userid=base.TEST_USER_ADMIN_LOGIN)
1601 userid=base.TEST_USER_ADMIN_LOGIN)
1602 response = api_call(self, params)
1602 response = api_call(self, params)
1603 expected = 'failed to add member to user group `%s`' % gr_name
1603 expected = 'failed to add member to user group `%s`' % gr_name
1604 self._compare_error(id_, expected, given=response.body)
1604 self._compare_error(id_, expected, given=response.body)
1605 finally:
1605 finally:
1606 fixture.destroy_user_group(gr_name)
1606 fixture.destroy_user_group(gr_name)
1607
1607
1608 def test_api_remove_user_from_user_group(self):
1608 def test_api_remove_user_from_user_group(self):
1609 gr_name = 'test_group_3'
1609 gr_name = 'test_group_3'
1610 gr = fixture.create_user_group(gr_name)
1610 gr = fixture.create_user_group(gr_name)
1611 UserGroupModel().add_user_to_group(gr, user=base.TEST_USER_ADMIN_LOGIN)
1611 UserGroupModel().add_user_to_group(gr, user=base.TEST_USER_ADMIN_LOGIN)
1612 meta.Session().commit()
1612 meta.Session().commit()
1613 try:
1613 try:
1614 id_, params = _build_data(self.apikey, 'remove_user_from_user_group',
1614 id_, params = _build_data(self.apikey, 'remove_user_from_user_group',
1615 usergroupid=gr_name,
1615 usergroupid=gr_name,
1616 userid=base.TEST_USER_ADMIN_LOGIN)
1616 userid=base.TEST_USER_ADMIN_LOGIN)
1617 response = api_call(self, params)
1617 response = api_call(self, params)
1618 expected = {
1618 expected = {
1619 'msg': 'removed member `%s` from user group `%s`' % (
1619 'msg': 'removed member `%s` from user group `%s`' % (
1620 base.TEST_USER_ADMIN_LOGIN, gr_name
1620 base.TEST_USER_ADMIN_LOGIN, gr_name
1621 ),
1621 ),
1622 'success': True}
1622 'success': True}
1623 self._compare_ok(id_, expected, given=response.body)
1623 self._compare_ok(id_, expected, given=response.body)
1624 finally:
1624 finally:
1625 fixture.destroy_user_group(gr_name)
1625 fixture.destroy_user_group(gr_name)
1626
1626
1627 @mock.patch.object(UserGroupModel, 'remove_user_from_group', raise_exception)
1627 @mock.patch.object(UserGroupModel, 'remove_user_from_group', raise_exception)
1628 def test_api_remove_user_from_user_group_exception_occurred(self):
1628 def test_api_remove_user_from_user_group_exception_occurred(self):
1629 gr_name = 'test_group_3'
1629 gr_name = 'test_group_3'
1630 gr = fixture.create_user_group(gr_name)
1630 gr = fixture.create_user_group(gr_name)
1631 UserGroupModel().add_user_to_group(gr, user=base.TEST_USER_ADMIN_LOGIN)
1631 UserGroupModel().add_user_to_group(gr, user=base.TEST_USER_ADMIN_LOGIN)
1632 meta.Session().commit()
1632 meta.Session().commit()
1633 try:
1633 try:
1634 id_, params = _build_data(self.apikey, 'remove_user_from_user_group',
1634 id_, params = _build_data(self.apikey, 'remove_user_from_user_group',
1635 usergroupid=gr_name,
1635 usergroupid=gr_name,
1636 userid=base.TEST_USER_ADMIN_LOGIN)
1636 userid=base.TEST_USER_ADMIN_LOGIN)
1637 response = api_call(self, params)
1637 response = api_call(self, params)
1638 expected = 'failed to remove member from user group `%s`' % gr_name
1638 expected = 'failed to remove member from user group `%s`' % gr_name
1639 self._compare_error(id_, expected, given=response.body)
1639 self._compare_error(id_, expected, given=response.body)
1640 finally:
1640 finally:
1641 fixture.destroy_user_group(gr_name)
1641 fixture.destroy_user_group(gr_name)
1642
1642
1643 def test_api_delete_user_group(self):
1643 def test_api_delete_user_group(self):
1644 gr_name = 'test_group'
1644 gr_name = 'test_group'
1645 ugroup = fixture.create_user_group(gr_name)
1645 ugroup = fixture.create_user_group(gr_name)
1646 gr_id = ugroup.users_group_id
1646 gr_id = ugroup.users_group_id
1647 try:
1647 try:
1648 id_, params = _build_data(self.apikey, 'delete_user_group',
1648 id_, params = _build_data(self.apikey, 'delete_user_group',
1649 usergroupid=gr_name)
1649 usergroupid=gr_name)
1650 response = api_call(self, params)
1650 response = api_call(self, params)
1651 expected = {
1651 expected = {
1652 'user_group': None,
1652 'user_group': None,
1653 'msg': 'deleted user group ID:%s %s' % (gr_id, gr_name)
1653 'msg': 'deleted user group ID:%s %s' % (gr_id, gr_name)
1654 }
1654 }
1655 self._compare_ok(id_, expected, given=response.body)
1655 self._compare_ok(id_, expected, given=response.body)
1656 finally:
1656 finally:
1657 if UserGroupModel().get_by_name(gr_name):
1657 if UserGroupModel().get_by_name(gr_name):
1658 fixture.destroy_user_group(gr_name)
1658 fixture.destroy_user_group(gr_name)
1659
1659
1660 def test_api_delete_user_group_that_is_assigned(self):
1660 def test_api_delete_user_group_that_is_assigned(self):
1661 gr_name = 'test_group'
1661 gr_name = 'test_group'
1662 ugroup = fixture.create_user_group(gr_name)
1662 ugroup = fixture.create_user_group(gr_name)
1663 gr_id = ugroup.users_group_id
1663 gr_id = ugroup.users_group_id
1664
1664
1665 ugr_to_perm = RepoModel().grant_user_group_permission(self.REPO, gr_name, 'repository.write')
1665 ugr_to_perm = RepoModel().grant_user_group_permission(self.REPO, gr_name, 'repository.write')
1666 msg = 'User Group assigned to %s' % ugr_to_perm.repository.repo_name
1666 msg = 'User Group assigned to %s' % ugr_to_perm.repository.repo_name
1667
1667
1668 try:
1668 try:
1669 id_, params = _build_data(self.apikey, 'delete_user_group',
1669 id_, params = _build_data(self.apikey, 'delete_user_group',
1670 usergroupid=gr_name)
1670 usergroupid=gr_name)
1671 response = api_call(self, params)
1671 response = api_call(self, params)
1672 expected = msg
1672 expected = msg
1673 self._compare_error(id_, expected, given=response.body)
1673 self._compare_error(id_, expected, given=response.body)
1674 finally:
1674 finally:
1675 if UserGroupModel().get_by_name(gr_name):
1675 if UserGroupModel().get_by_name(gr_name):
1676 fixture.destroy_user_group(gr_name)
1676 fixture.destroy_user_group(gr_name)
1677
1677
1678 def test_api_delete_user_group_exception_occurred(self):
1678 def test_api_delete_user_group_exception_occurred(self):
1679 gr_name = 'test_group'
1679 gr_name = 'test_group'
1680 ugroup = fixture.create_user_group(gr_name)
1680 ugroup = fixture.create_user_group(gr_name)
1681 gr_id = ugroup.users_group_id
1681 gr_id = ugroup.users_group_id
1682 id_, params = _build_data(self.apikey, 'delete_user_group',
1682 id_, params = _build_data(self.apikey, 'delete_user_group',
1683 usergroupid=gr_name)
1683 usergroupid=gr_name)
1684
1684
1685 try:
1685 try:
1686 with mock.patch.object(UserGroupModel, 'delete', raise_exception):
1686 with mock.patch.object(UserGroupModel, 'delete', raise_exception):
1687 response = api_call(self, params)
1687 response = api_call(self, params)
1688 expected = 'failed to delete user group ID:%s %s' % (gr_id, gr_name)
1688 expected = 'failed to delete user group ID:%s %s' % (gr_id, gr_name)
1689 self._compare_error(id_, expected, given=response.body)
1689 self._compare_error(id_, expected, given=response.body)
1690 finally:
1690 finally:
1691 fixture.destroy_user_group(gr_name)
1691 fixture.destroy_user_group(gr_name)
1692
1692
1693 @base.parametrize('name,perm', [
1693 @base.parametrize('name,perm', [
1694 ('none', 'repository.none'),
1694 ('none', 'repository.none'),
1695 ('read', 'repository.read'),
1695 ('read', 'repository.read'),
1696 ('write', 'repository.write'),
1696 ('write', 'repository.write'),
1697 ('admin', 'repository.admin'),
1697 ('admin', 'repository.admin'),
1698 ])
1698 ])
1699 def test_api_grant_user_permission(self, name, perm):
1699 def test_api_grant_user_permission(self, name, perm):
1700 id_, params = _build_data(self.apikey,
1700 id_, params = _build_data(self.apikey,
1701 'grant_user_permission',
1701 'grant_user_permission',
1702 repoid=self.REPO,
1702 repoid=self.REPO,
1703 userid=base.TEST_USER_ADMIN_LOGIN,
1703 userid=base.TEST_USER_ADMIN_LOGIN,
1704 perm=perm)
1704 perm=perm)
1705 response = api_call(self, params)
1705 response = api_call(self, params)
1706
1706
1707 ret = {
1707 ret = {
1708 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1708 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1709 perm, base.TEST_USER_ADMIN_LOGIN, self.REPO
1709 perm, base.TEST_USER_ADMIN_LOGIN, self.REPO
1710 ),
1710 ),
1711 'success': True
1711 'success': True
1712 }
1712 }
1713 expected = ret
1713 expected = ret
1714 self._compare_ok(id_, expected, given=response.body)
1714 self._compare_ok(id_, expected, given=response.body)
1715
1715
1716 def test_api_grant_user_permission_wrong_permission(self):
1716 def test_api_grant_user_permission_wrong_permission(self):
1717 perm = 'haha.no.permission'
1717 perm = 'haha.no.permission'
1718 id_, params = _build_data(self.apikey,
1718 id_, params = _build_data(self.apikey,
1719 'grant_user_permission',
1719 'grant_user_permission',
1720 repoid=self.REPO,
1720 repoid=self.REPO,
1721 userid=base.TEST_USER_ADMIN_LOGIN,
1721 userid=base.TEST_USER_ADMIN_LOGIN,
1722 perm=perm)
1722 perm=perm)
1723 response = api_call(self, params)
1723 response = api_call(self, params)
1724
1724
1725 expected = 'permission `%s` does not exist' % perm
1725 expected = 'permission `%s` does not exist' % perm
1726 self._compare_error(id_, expected, given=response.body)
1726 self._compare_error(id_, expected, given=response.body)
1727
1727
1728 @mock.patch.object(RepoModel, 'grant_user_permission', raise_exception)
1728 @mock.patch.object(RepoModel, 'grant_user_permission', raise_exception)
1729 def test_api_grant_user_permission_exception_when_adding(self):
1729 def test_api_grant_user_permission_exception_when_adding(self):
1730 perm = 'repository.read'
1730 perm = 'repository.read'
1731 id_, params = _build_data(self.apikey,
1731 id_, params = _build_data(self.apikey,
1732 'grant_user_permission',
1732 'grant_user_permission',
1733 repoid=self.REPO,
1733 repoid=self.REPO,
1734 userid=base.TEST_USER_ADMIN_LOGIN,
1734 userid=base.TEST_USER_ADMIN_LOGIN,
1735 perm=perm)
1735 perm=perm)
1736 response = api_call(self, params)
1736 response = api_call(self, params)
1737
1737
1738 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
1738 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
1739 base.TEST_USER_ADMIN_LOGIN, self.REPO
1739 base.TEST_USER_ADMIN_LOGIN, self.REPO
1740 )
1740 )
1741 self._compare_error(id_, expected, given=response.body)
1741 self._compare_error(id_, expected, given=response.body)
1742
1742
1743 def test_api_revoke_user_permission(self):
1743 def test_api_revoke_user_permission(self):
1744 id_, params = _build_data(self.apikey,
1744 id_, params = _build_data(self.apikey,
1745 'revoke_user_permission',
1745 'revoke_user_permission',
1746 repoid=self.REPO,
1746 repoid=self.REPO,
1747 userid=base.TEST_USER_ADMIN_LOGIN, )
1747 userid=base.TEST_USER_ADMIN_LOGIN, )
1748 response = api_call(self, params)
1748 response = api_call(self, params)
1749
1749
1750 expected = {
1750 expected = {
1751 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1751 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1752 base.TEST_USER_ADMIN_LOGIN, self.REPO
1752 base.TEST_USER_ADMIN_LOGIN, self.REPO
1753 ),
1753 ),
1754 'success': True
1754 'success': True
1755 }
1755 }
1756 self._compare_ok(id_, expected, given=response.body)
1756 self._compare_ok(id_, expected, given=response.body)
1757
1757
1758 @mock.patch.object(RepoModel, 'revoke_user_permission', raise_exception)
1758 @mock.patch.object(RepoModel, 'revoke_user_permission', raise_exception)
1759 def test_api_revoke_user_permission_exception_when_adding(self):
1759 def test_api_revoke_user_permission_exception_when_adding(self):
1760 id_, params = _build_data(self.apikey,
1760 id_, params = _build_data(self.apikey,
1761 'revoke_user_permission',
1761 'revoke_user_permission',
1762 repoid=self.REPO,
1762 repoid=self.REPO,
1763 userid=base.TEST_USER_ADMIN_LOGIN, )
1763 userid=base.TEST_USER_ADMIN_LOGIN, )
1764 response = api_call(self, params)
1764 response = api_call(self, params)
1765
1765
1766 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
1766 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
1767 base.TEST_USER_ADMIN_LOGIN, self.REPO
1767 base.TEST_USER_ADMIN_LOGIN, self.REPO
1768 )
1768 )
1769 self._compare_error(id_, expected, given=response.body)
1769 self._compare_error(id_, expected, given=response.body)
1770
1770
1771 @base.parametrize('name,perm', [
1771 @base.parametrize('name,perm', [
1772 ('none', 'repository.none'),
1772 ('none', 'repository.none'),
1773 ('read', 'repository.read'),
1773 ('read', 'repository.read'),
1774 ('write', 'repository.write'),
1774 ('write', 'repository.write'),
1775 ('admin', 'repository.admin'),
1775 ('admin', 'repository.admin'),
1776 ])
1776 ])
1777 def test_api_grant_user_group_permission(self, name, perm):
1777 def test_api_grant_user_group_permission(self, name, perm):
1778 id_, params = _build_data(self.apikey,
1778 id_, params = _build_data(self.apikey,
1779 'grant_user_group_permission',
1779 'grant_user_group_permission',
1780 repoid=self.REPO,
1780 repoid=self.REPO,
1781 usergroupid=TEST_USER_GROUP,
1781 usergroupid=TEST_USER_GROUP,
1782 perm=perm)
1782 perm=perm)
1783 response = api_call(self, params)
1783 response = api_call(self, params)
1784
1784
1785 ret = {
1785 ret = {
1786 'msg': 'Granted perm: `%s` for user group: `%s` in repo: `%s`' % (
1786 'msg': 'Granted perm: `%s` for user group: `%s` in repo: `%s`' % (
1787 perm, TEST_USER_GROUP, self.REPO
1787 perm, TEST_USER_GROUP, self.REPO
1788 ),
1788 ),
1789 'success': True
1789 'success': True
1790 }
1790 }
1791 expected = ret
1791 expected = ret
1792 self._compare_ok(id_, expected, given=response.body)
1792 self._compare_ok(id_, expected, given=response.body)
1793
1793
1794 def test_api_grant_user_group_permission_wrong_permission(self):
1794 def test_api_grant_user_group_permission_wrong_permission(self):
1795 perm = 'haha.no.permission'
1795 perm = 'haha.no.permission'
1796 id_, params = _build_data(self.apikey,
1796 id_, params = _build_data(self.apikey,
1797 'grant_user_group_permission',
1797 'grant_user_group_permission',
1798 repoid=self.REPO,
1798 repoid=self.REPO,
1799 usergroupid=TEST_USER_GROUP,
1799 usergroupid=TEST_USER_GROUP,
1800 perm=perm)
1800 perm=perm)
1801 response = api_call(self, params)
1801 response = api_call(self, params)
1802
1802
1803 expected = 'permission `%s` does not exist' % perm
1803 expected = 'permission `%s` does not exist' % perm
1804 self._compare_error(id_, expected, given=response.body)
1804 self._compare_error(id_, expected, given=response.body)
1805
1805
1806 @mock.patch.object(RepoModel, 'grant_user_group_permission', raise_exception)
1806 @mock.patch.object(RepoModel, 'grant_user_group_permission', raise_exception)
1807 def test_api_grant_user_group_permission_exception_when_adding(self):
1807 def test_api_grant_user_group_permission_exception_when_adding(self):
1808 perm = 'repository.read'
1808 perm = 'repository.read'
1809 id_, params = _build_data(self.apikey,
1809 id_, params = _build_data(self.apikey,
1810 'grant_user_group_permission',
1810 'grant_user_group_permission',
1811 repoid=self.REPO,
1811 repoid=self.REPO,
1812 usergroupid=TEST_USER_GROUP,
1812 usergroupid=TEST_USER_GROUP,
1813 perm=perm)
1813 perm=perm)
1814 response = api_call(self, params)
1814 response = api_call(self, params)
1815
1815
1816 expected = 'failed to edit permission for user group: `%s` in repo: `%s`' % (
1816 expected = 'failed to edit permission for user group: `%s` in repo: `%s`' % (
1817 TEST_USER_GROUP, self.REPO
1817 TEST_USER_GROUP, self.REPO
1818 )
1818 )
1819 self._compare_error(id_, expected, given=response.body)
1819 self._compare_error(id_, expected, given=response.body)
1820
1820
1821 def test_api_revoke_user_group_permission(self):
1821 def test_api_revoke_user_group_permission(self):
1822 RepoModel().grant_user_group_permission(repo=self.REPO,
1822 RepoModel().grant_user_group_permission(repo=self.REPO,
1823 group_name=TEST_USER_GROUP,
1823 group_name=TEST_USER_GROUP,
1824 perm='repository.read')
1824 perm='repository.read')
1825 meta.Session().commit()
1825 meta.Session().commit()
1826 id_, params = _build_data(self.apikey,
1826 id_, params = _build_data(self.apikey,
1827 'revoke_user_group_permission',
1827 'revoke_user_group_permission',
1828 repoid=self.REPO,
1828 repoid=self.REPO,
1829 usergroupid=TEST_USER_GROUP, )
1829 usergroupid=TEST_USER_GROUP, )
1830 response = api_call(self, params)
1830 response = api_call(self, params)
1831
1831
1832 expected = {
1832 expected = {
1833 'msg': 'Revoked perm for user group: `%s` in repo: `%s`' % (
1833 'msg': 'Revoked perm for user group: `%s` in repo: `%s`' % (
1834 TEST_USER_GROUP, self.REPO
1834 TEST_USER_GROUP, self.REPO
1835 ),
1835 ),
1836 'success': True
1836 'success': True
1837 }
1837 }
1838 self._compare_ok(id_, expected, given=response.body)
1838 self._compare_ok(id_, expected, given=response.body)
1839
1839
1840 @mock.patch.object(RepoModel, 'revoke_user_group_permission', raise_exception)
1840 @mock.patch.object(RepoModel, 'revoke_user_group_permission', raise_exception)
1841 def test_api_revoke_user_group_permission_exception_when_adding(self):
1841 def test_api_revoke_user_group_permission_exception_when_adding(self):
1842 id_, params = _build_data(self.apikey,
1842 id_, params = _build_data(self.apikey,
1843 'revoke_user_group_permission',
1843 'revoke_user_group_permission',
1844 repoid=self.REPO,
1844 repoid=self.REPO,
1845 usergroupid=TEST_USER_GROUP, )
1845 usergroupid=TEST_USER_GROUP, )
1846 response = api_call(self, params)
1846 response = api_call(self, params)
1847
1847
1848 expected = 'failed to edit permission for user group: `%s` in repo: `%s`' % (
1848 expected = 'failed to edit permission for user group: `%s` in repo: `%s`' % (
1849 TEST_USER_GROUP, self.REPO
1849 TEST_USER_GROUP, self.REPO
1850 )
1850 )
1851 self._compare_error(id_, expected, given=response.body)
1851 self._compare_error(id_, expected, given=response.body)
1852
1852
1853 @base.parametrize('changing_attr,updates', [
1853 @base.parametrize('changing_attr,updates', [
1854 #('owner', {'owner': base.TEST_USER_REGULAR_LOGIN}), # currently broken
1854 ('owner', {'owner': base.TEST_USER_REGULAR_LOGIN}),
1855 ('description', {'description': 'new description'}),
1855 ('description', {'description': 'new description'}),
1856 ('group_name', {'group_name': 'new_repo_name'}),
1856 ('group_name', {'group_name': 'new_repo_name'}),
1857 ('parent', {'parent': 'test_group_for_update'}),
1857 ('parent', {'parent': 'test_group_for_update'}),
1858 ])
1858 ])
1859 def test_api_update_repo_group(self, changing_attr, updates):
1859 def test_api_update_repo_group(self, changing_attr, updates):
1860 group_name = 'lololo'
1860 group_name = 'lololo'
1861 repo_group = fixture.create_repo_group(group_name)
1861 repo_group = fixture.create_repo_group(group_name)
1862
1862
1863 new_group_name = group_name
1863 new_group_name = group_name
1864 if changing_attr == 'group_name':
1864 if changing_attr == 'group_name':
1865 assert repo_group.parent_group_id is None # lazy assumption for this test
1865 assert repo_group.parent_group_id is None # lazy assumption for this test
1866 new_group_name = updates['group_name']
1866 new_group_name = updates['group_name']
1867 if changing_attr == 'parent':
1867 if changing_attr == 'parent':
1868 new_group_name = '/'.join([updates['parent'], group_name.rsplit('/', 1)[-1]])
1868 new_group_name = '/'.join([updates['parent'], group_name.rsplit('/', 1)[-1]])
1869
1869
1870 expected = {
1870 expected = {
1871 'msg': 'updated repository group ID:%s %s' % (repo_group.group_id, new_group_name),
1871 'msg': 'updated repository group ID:%s %s' % (repo_group.group_id, new_group_name),
1872 'repo_group': repo_group.get_api_data()
1872 'repo_group': repo_group.get_api_data()
1873 }
1873 }
1874 expected['repo_group'].update(updates)
1874 expected['repo_group'].update(updates)
1875 if 'description' in updates:
1875 if 'description' in updates:
1876 expected['repo_group']['group_description'] = expected['repo_group'].pop('description')
1876 expected['repo_group']['group_description'] = expected['repo_group'].pop('description')
1877
1877
1878 if changing_attr == 'parent':
1878 if changing_attr == 'parent':
1879 new_parent = fixture.create_repo_group(updates['parent'])
1879 new_parent = fixture.create_repo_group(updates['parent'])
1880 expected['repo_group']['parent_group'] = expected['repo_group'].pop('parent')
1880 expected['repo_group']['parent_group'] = expected['repo_group'].pop('parent')
1881 expected['repo_group']['group_name'] = new_group_name
1881 expected['repo_group']['group_name'] = new_group_name
1882
1882
1883 id_, params = _build_data(self.apikey, 'update_repo_group',
1883 id_, params = _build_data(self.apikey, 'update_repo_group',
1884 repogroupid=group_name, **updates)
1884 repogroupid=group_name, **updates)
1885 response = api_call(self, params)
1885 response = api_call(self, params)
1886
1886
1887 try:
1887 try:
1888 self._compare_ok(id_, expected, given=response.body)
1888 self._compare_ok(id_, expected, given=response.body)
1889 finally:
1889 finally:
1890 if changing_attr == 'parent':
1890 if changing_attr == 'parent':
1891 fixture.destroy_repo_group(new_parent.group_id)
1891 fixture.destroy_repo_group(new_parent.group_id)
1892 fixture.destroy_repo_group(new_group_name)
1892 fixture.destroy_repo_group(new_group_name)
1893
1893
1894 @base.parametrize('name,perm,apply_to_children', [
1894 @base.parametrize('name,perm,apply_to_children', [
1895 ('none', 'group.none', 'none'),
1895 ('none', 'group.none', 'none'),
1896 ('read', 'group.read', 'none'),
1896 ('read', 'group.read', 'none'),
1897 ('write', 'group.write', 'none'),
1897 ('write', 'group.write', 'none'),
1898 ('admin', 'group.admin', 'none'),
1898 ('admin', 'group.admin', 'none'),
1899
1899
1900 ('none', 'group.none', 'all'),
1900 ('none', 'group.none', 'all'),
1901 ('read', 'group.read', 'all'),
1901 ('read', 'group.read', 'all'),
1902 ('write', 'group.write', 'all'),
1902 ('write', 'group.write', 'all'),
1903 ('admin', 'group.admin', 'all'),
1903 ('admin', 'group.admin', 'all'),
1904
1904
1905 ('none', 'group.none', 'repos'),
1905 ('none', 'group.none', 'repos'),
1906 ('read', 'group.read', 'repos'),
1906 ('read', 'group.read', 'repos'),
1907 ('write', 'group.write', 'repos'),
1907 ('write', 'group.write', 'repos'),
1908 ('admin', 'group.admin', 'repos'),
1908 ('admin', 'group.admin', 'repos'),
1909
1909
1910 ('none', 'group.none', 'groups'),
1910 ('none', 'group.none', 'groups'),
1911 ('read', 'group.read', 'groups'),
1911 ('read', 'group.read', 'groups'),
1912 ('write', 'group.write', 'groups'),
1912 ('write', 'group.write', 'groups'),
1913 ('admin', 'group.admin', 'groups'),
1913 ('admin', 'group.admin', 'groups'),
1914 ])
1914 ])
1915 def test_api_grant_user_permission_to_repo_group(self, name, perm, apply_to_children):
1915 def test_api_grant_user_permission_to_repo_group(self, name, perm, apply_to_children):
1916 id_, params = _build_data(self.apikey,
1916 id_, params = _build_data(self.apikey,
1917 'grant_user_permission_to_repo_group',
1917 'grant_user_permission_to_repo_group',
1918 repogroupid=TEST_REPO_GROUP,
1918 repogroupid=TEST_REPO_GROUP,
1919 userid=base.TEST_USER_ADMIN_LOGIN,
1919 userid=base.TEST_USER_ADMIN_LOGIN,
1920 perm=perm, apply_to_children=apply_to_children)
1920 perm=perm, apply_to_children=apply_to_children)
1921 response = api_call(self, params)
1921 response = api_call(self, params)
1922
1922
1923 ret = {
1923 ret = {
1924 'msg': 'Granted perm: `%s` (recursive:%s) for user: `%s` in repo group: `%s`' % (
1924 'msg': 'Granted perm: `%s` (recursive:%s) for user: `%s` in repo group: `%s`' % (
1925 perm, apply_to_children, base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
1925 perm, apply_to_children, base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
1926 ),
1926 ),
1927 'success': True
1927 'success': True
1928 }
1928 }
1929 expected = ret
1929 expected = ret
1930 self._compare_ok(id_, expected, given=response.body)
1930 self._compare_ok(id_, expected, given=response.body)
1931
1931
1932 @base.parametrize('name,perm,apply_to_children,grant_admin,access_ok', [
1932 @base.parametrize('name,perm,apply_to_children,grant_admin,access_ok', [
1933 ('none_fails', 'group.none', 'none', False, False),
1933 ('none_fails', 'group.none', 'none', False, False),
1934 ('read_fails', 'group.read', 'none', False, False),
1934 ('read_fails', 'group.read', 'none', False, False),
1935 ('write_fails', 'group.write', 'none', False, False),
1935 ('write_fails', 'group.write', 'none', False, False),
1936 ('admin_fails', 'group.admin', 'none', False, False),
1936 ('admin_fails', 'group.admin', 'none', False, False),
1937
1937
1938 # with granted perms
1938 # with granted perms
1939 ('none_ok', 'group.none', 'none', True, True),
1939 ('none_ok', 'group.none', 'none', True, True),
1940 ('read_ok', 'group.read', 'none', True, True),
1940 ('read_ok', 'group.read', 'none', True, True),
1941 ('write_ok', 'group.write', 'none', True, True),
1941 ('write_ok', 'group.write', 'none', True, True),
1942 ('admin_ok', 'group.admin', 'none', True, True),
1942 ('admin_ok', 'group.admin', 'none', True, True),
1943 ])
1943 ])
1944 def test_api_grant_user_permission_to_repo_group_by_regular_user(
1944 def test_api_grant_user_permission_to_repo_group_by_regular_user(
1945 self, name, perm, apply_to_children, grant_admin, access_ok):
1945 self, name, perm, apply_to_children, grant_admin, access_ok):
1946 if grant_admin:
1946 if grant_admin:
1947 RepoGroupModel().grant_user_permission(TEST_REPO_GROUP,
1947 RepoGroupModel().grant_user_permission(TEST_REPO_GROUP,
1948 self.TEST_USER_LOGIN,
1948 self.TEST_USER_LOGIN,
1949 'group.admin')
1949 'group.admin')
1950 meta.Session().commit()
1950 meta.Session().commit()
1951
1951
1952 id_, params = _build_data(self.apikey_regular,
1952 id_, params = _build_data(self.apikey_regular,
1953 'grant_user_permission_to_repo_group',
1953 'grant_user_permission_to_repo_group',
1954 repogroupid=TEST_REPO_GROUP,
1954 repogroupid=TEST_REPO_GROUP,
1955 userid=base.TEST_USER_ADMIN_LOGIN,
1955 userid=base.TEST_USER_ADMIN_LOGIN,
1956 perm=perm, apply_to_children=apply_to_children)
1956 perm=perm, apply_to_children=apply_to_children)
1957 response = api_call(self, params)
1957 response = api_call(self, params)
1958 if access_ok:
1958 if access_ok:
1959 ret = {
1959 ret = {
1960 'msg': 'Granted perm: `%s` (recursive:%s) for user: `%s` in repo group: `%s`' % (
1960 'msg': 'Granted perm: `%s` (recursive:%s) for user: `%s` in repo group: `%s`' % (
1961 perm, apply_to_children, base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
1961 perm, apply_to_children, base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
1962 ),
1962 ),
1963 'success': True
1963 'success': True
1964 }
1964 }
1965 expected = ret
1965 expected = ret
1966 self._compare_ok(id_, expected, given=response.body)
1966 self._compare_ok(id_, expected, given=response.body)
1967 else:
1967 else:
1968 expected = 'repository group `%s` does not exist' % TEST_REPO_GROUP
1968 expected = 'repository group `%s` does not exist' % TEST_REPO_GROUP
1969 self._compare_error(id_, expected, given=response.body)
1969 self._compare_error(id_, expected, given=response.body)
1970
1970
1971 def test_api_grant_user_permission_to_repo_group_wrong_permission(self):
1971 def test_api_grant_user_permission_to_repo_group_wrong_permission(self):
1972 perm = 'haha.no.permission'
1972 perm = 'haha.no.permission'
1973 id_, params = _build_data(self.apikey,
1973 id_, params = _build_data(self.apikey,
1974 'grant_user_permission_to_repo_group',
1974 'grant_user_permission_to_repo_group',
1975 repogroupid=TEST_REPO_GROUP,
1975 repogroupid=TEST_REPO_GROUP,
1976 userid=base.TEST_USER_ADMIN_LOGIN,
1976 userid=base.TEST_USER_ADMIN_LOGIN,
1977 perm=perm)
1977 perm=perm)
1978 response = api_call(self, params)
1978 response = api_call(self, params)
1979
1979
1980 expected = 'permission `%s` does not exist' % perm
1980 expected = 'permission `%s` does not exist' % perm
1981 self._compare_error(id_, expected, given=response.body)
1981 self._compare_error(id_, expected, given=response.body)
1982
1982
1983 @mock.patch.object(RepoGroupModel, 'grant_user_permission', raise_exception)
1983 @mock.patch.object(RepoGroupModel, 'grant_user_permission', raise_exception)
1984 def test_api_grant_user_permission_to_repo_group_exception_when_adding(self):
1984 def test_api_grant_user_permission_to_repo_group_exception_when_adding(self):
1985 perm = 'group.read'
1985 perm = 'group.read'
1986 id_, params = _build_data(self.apikey,
1986 id_, params = _build_data(self.apikey,
1987 'grant_user_permission_to_repo_group',
1987 'grant_user_permission_to_repo_group',
1988 repogroupid=TEST_REPO_GROUP,
1988 repogroupid=TEST_REPO_GROUP,
1989 userid=base.TEST_USER_ADMIN_LOGIN,
1989 userid=base.TEST_USER_ADMIN_LOGIN,
1990 perm=perm)
1990 perm=perm)
1991 response = api_call(self, params)
1991 response = api_call(self, params)
1992
1992
1993 expected = 'failed to edit permission for user: `%s` in repo group: `%s`' % (
1993 expected = 'failed to edit permission for user: `%s` in repo group: `%s`' % (
1994 base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
1994 base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
1995 )
1995 )
1996 self._compare_error(id_, expected, given=response.body)
1996 self._compare_error(id_, expected, given=response.body)
1997
1997
1998 @base.parametrize('name,apply_to_children', [
1998 @base.parametrize('name,apply_to_children', [
1999 ('none', 'none'),
1999 ('none', 'none'),
2000 ('all', 'all'),
2000 ('all', 'all'),
2001 ('repos', 'repos'),
2001 ('repos', 'repos'),
2002 ('groups', 'groups'),
2002 ('groups', 'groups'),
2003 ])
2003 ])
2004 def test_api_revoke_user_permission_from_repo_group(self, name, apply_to_children):
2004 def test_api_revoke_user_permission_from_repo_group(self, name, apply_to_children):
2005 RepoGroupModel().grant_user_permission(repo_group=TEST_REPO_GROUP,
2005 RepoGroupModel().grant_user_permission(repo_group=TEST_REPO_GROUP,
2006 user=base.TEST_USER_ADMIN_LOGIN,
2006 user=base.TEST_USER_ADMIN_LOGIN,
2007 perm='group.read',)
2007 perm='group.read',)
2008 meta.Session().commit()
2008 meta.Session().commit()
2009
2009
2010 id_, params = _build_data(self.apikey,
2010 id_, params = _build_data(self.apikey,
2011 'revoke_user_permission_from_repo_group',
2011 'revoke_user_permission_from_repo_group',
2012 repogroupid=TEST_REPO_GROUP,
2012 repogroupid=TEST_REPO_GROUP,
2013 userid=base.TEST_USER_ADMIN_LOGIN,
2013 userid=base.TEST_USER_ADMIN_LOGIN,
2014 apply_to_children=apply_to_children,)
2014 apply_to_children=apply_to_children,)
2015 response = api_call(self, params)
2015 response = api_call(self, params)
2016
2016
2017 expected = {
2017 expected = {
2018 'msg': 'Revoked perm (recursive:%s) for user: `%s` in repo group: `%s`' % (
2018 'msg': 'Revoked perm (recursive:%s) for user: `%s` in repo group: `%s`' % (
2019 apply_to_children, base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
2019 apply_to_children, base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
2020 ),
2020 ),
2021 'success': True
2021 'success': True
2022 }
2022 }
2023 self._compare_ok(id_, expected, given=response.body)
2023 self._compare_ok(id_, expected, given=response.body)
2024
2024
2025 @base.parametrize('name,apply_to_children,grant_admin,access_ok', [
2025 @base.parametrize('name,apply_to_children,grant_admin,access_ok', [
2026 ('none', 'none', False, False),
2026 ('none', 'none', False, False),
2027 ('all', 'all', False, False),
2027 ('all', 'all', False, False),
2028 ('repos', 'repos', False, False),
2028 ('repos', 'repos', False, False),
2029 ('groups', 'groups', False, False),
2029 ('groups', 'groups', False, False),
2030
2030
2031 # after granting admin rights
2031 # after granting admin rights
2032 ('none', 'none', False, False),
2032 ('none', 'none', False, False),
2033 ('all', 'all', False, False),
2033 ('all', 'all', False, False),
2034 ('repos', 'repos', False, False),
2034 ('repos', 'repos', False, False),
2035 ('groups', 'groups', False, False),
2035 ('groups', 'groups', False, False),
2036 ])
2036 ])
2037 def test_api_revoke_user_permission_from_repo_group_by_regular_user(
2037 def test_api_revoke_user_permission_from_repo_group_by_regular_user(
2038 self, name, apply_to_children, grant_admin, access_ok):
2038 self, name, apply_to_children, grant_admin, access_ok):
2039 RepoGroupModel().grant_user_permission(repo_group=TEST_REPO_GROUP,
2039 RepoGroupModel().grant_user_permission(repo_group=TEST_REPO_GROUP,
2040 user=base.TEST_USER_ADMIN_LOGIN,
2040 user=base.TEST_USER_ADMIN_LOGIN,
2041 perm='group.read',)
2041 perm='group.read',)
2042 meta.Session().commit()
2042 meta.Session().commit()
2043
2043
2044 if grant_admin:
2044 if grant_admin:
2045 RepoGroupModel().grant_user_permission(TEST_REPO_GROUP,
2045 RepoGroupModel().grant_user_permission(TEST_REPO_GROUP,
2046 self.TEST_USER_LOGIN,
2046 self.TEST_USER_LOGIN,
2047 'group.admin')
2047 'group.admin')
2048 meta.Session().commit()
2048 meta.Session().commit()
2049
2049
2050 id_, params = _build_data(self.apikey_regular,
2050 id_, params = _build_data(self.apikey_regular,
2051 'revoke_user_permission_from_repo_group',
2051 'revoke_user_permission_from_repo_group',
2052 repogroupid=TEST_REPO_GROUP,
2052 repogroupid=TEST_REPO_GROUP,
2053 userid=base.TEST_USER_ADMIN_LOGIN,
2053 userid=base.TEST_USER_ADMIN_LOGIN,
2054 apply_to_children=apply_to_children,)
2054 apply_to_children=apply_to_children,)
2055 response = api_call(self, params)
2055 response = api_call(self, params)
2056 if access_ok:
2056 if access_ok:
2057 expected = {
2057 expected = {
2058 'msg': 'Revoked perm (recursive:%s) for user: `%s` in repo group: `%s`' % (
2058 'msg': 'Revoked perm (recursive:%s) for user: `%s` in repo group: `%s`' % (
2059 apply_to_children, base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
2059 apply_to_children, base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
2060 ),
2060 ),
2061 'success': True
2061 'success': True
2062 }
2062 }
2063 self._compare_ok(id_, expected, given=response.body)
2063 self._compare_ok(id_, expected, given=response.body)
2064 else:
2064 else:
2065 expected = 'repository group `%s` does not exist' % TEST_REPO_GROUP
2065 expected = 'repository group `%s` does not exist' % TEST_REPO_GROUP
2066 self._compare_error(id_, expected, given=response.body)
2066 self._compare_error(id_, expected, given=response.body)
2067
2067
2068 @mock.patch.object(RepoGroupModel, 'revoke_user_permission', raise_exception)
2068 @mock.patch.object(RepoGroupModel, 'revoke_user_permission', raise_exception)
2069 def test_api_revoke_user_permission_from_repo_group_exception_when_adding(self):
2069 def test_api_revoke_user_permission_from_repo_group_exception_when_adding(self):
2070 id_, params = _build_data(self.apikey,
2070 id_, params = _build_data(self.apikey,
2071 'revoke_user_permission_from_repo_group',
2071 'revoke_user_permission_from_repo_group',
2072 repogroupid=TEST_REPO_GROUP,
2072 repogroupid=TEST_REPO_GROUP,
2073 userid=base.TEST_USER_ADMIN_LOGIN, )
2073 userid=base.TEST_USER_ADMIN_LOGIN, )
2074 response = api_call(self, params)
2074 response = api_call(self, params)
2075
2075
2076 expected = 'failed to edit permission for user: `%s` in repo group: `%s`' % (
2076 expected = 'failed to edit permission for user: `%s` in repo group: `%s`' % (
2077 base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
2077 base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
2078 )
2078 )
2079 self._compare_error(id_, expected, given=response.body)
2079 self._compare_error(id_, expected, given=response.body)
2080
2080
2081 @base.parametrize('name,perm,apply_to_children', [
2081 @base.parametrize('name,perm,apply_to_children', [
2082 ('none', 'group.none', 'none'),
2082 ('none', 'group.none', 'none'),
2083 ('read', 'group.read', 'none'),
2083 ('read', 'group.read', 'none'),
2084 ('write', 'group.write', 'none'),
2084 ('write', 'group.write', 'none'),
2085 ('admin', 'group.admin', 'none'),
2085 ('admin', 'group.admin', 'none'),
2086
2086
2087 ('none', 'group.none', 'all'),
2087 ('none', 'group.none', 'all'),
2088 ('read', 'group.read', 'all'),
2088 ('read', 'group.read', 'all'),
2089 ('write', 'group.write', 'all'),
2089 ('write', 'group.write', 'all'),
2090 ('admin', 'group.admin', 'all'),
2090 ('admin', 'group.admin', 'all'),
2091
2091
2092 ('none', 'group.none', 'repos'),
2092 ('none', 'group.none', 'repos'),
2093 ('read', 'group.read', 'repos'),
2093 ('read', 'group.read', 'repos'),
2094 ('write', 'group.write', 'repos'),
2094 ('write', 'group.write', 'repos'),
2095 ('admin', 'group.admin', 'repos'),
2095 ('admin', 'group.admin', 'repos'),
2096
2096
2097 ('none', 'group.none', 'groups'),
2097 ('none', 'group.none', 'groups'),
2098 ('read', 'group.read', 'groups'),
2098 ('read', 'group.read', 'groups'),
2099 ('write', 'group.write', 'groups'),
2099 ('write', 'group.write', 'groups'),
2100 ('admin', 'group.admin', 'groups'),
2100 ('admin', 'group.admin', 'groups'),
2101 ])
2101 ])
2102 def test_api_grant_user_group_permission_to_repo_group(self, name, perm, apply_to_children):
2102 def test_api_grant_user_group_permission_to_repo_group(self, name, perm, apply_to_children):
2103 id_, params = _build_data(self.apikey,
2103 id_, params = _build_data(self.apikey,
2104 'grant_user_group_permission_to_repo_group',
2104 'grant_user_group_permission_to_repo_group',
2105 repogroupid=TEST_REPO_GROUP,
2105 repogroupid=TEST_REPO_GROUP,
2106 usergroupid=TEST_USER_GROUP,
2106 usergroupid=TEST_USER_GROUP,
2107 perm=perm,
2107 perm=perm,
2108 apply_to_children=apply_to_children,)
2108 apply_to_children=apply_to_children,)
2109 response = api_call(self, params)
2109 response = api_call(self, params)
2110
2110
2111 ret = {
2111 ret = {
2112 'msg': 'Granted perm: `%s` (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2112 'msg': 'Granted perm: `%s` (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2113 perm, apply_to_children, TEST_USER_GROUP, TEST_REPO_GROUP
2113 perm, apply_to_children, TEST_USER_GROUP, TEST_REPO_GROUP
2114 ),
2114 ),
2115 'success': True
2115 'success': True
2116 }
2116 }
2117 expected = ret
2117 expected = ret
2118 self._compare_ok(id_, expected, given=response.body)
2118 self._compare_ok(id_, expected, given=response.body)
2119
2119
2120 @base.parametrize('name,perm,apply_to_children,grant_admin,access_ok', [
2120 @base.parametrize('name,perm,apply_to_children,grant_admin,access_ok', [
2121 ('none_fails', 'group.none', 'none', False, False),
2121 ('none_fails', 'group.none', 'none', False, False),
2122 ('read_fails', 'group.read', 'none', False, False),
2122 ('read_fails', 'group.read', 'none', False, False),
2123 ('write_fails', 'group.write', 'none', False, False),
2123 ('write_fails', 'group.write', 'none', False, False),
2124 ('admin_fails', 'group.admin', 'none', False, False),
2124 ('admin_fails', 'group.admin', 'none', False, False),
2125
2125
2126 # with granted perms
2126 # with granted perms
2127 ('none_ok', 'group.none', 'none', True, True),
2127 ('none_ok', 'group.none', 'none', True, True),
2128 ('read_ok', 'group.read', 'none', True, True),
2128 ('read_ok', 'group.read', 'none', True, True),
2129 ('write_ok', 'group.write', 'none', True, True),
2129 ('write_ok', 'group.write', 'none', True, True),
2130 ('admin_ok', 'group.admin', 'none', True, True),
2130 ('admin_ok', 'group.admin', 'none', True, True),
2131 ])
2131 ])
2132 def test_api_grant_user_group_permission_to_repo_group_by_regular_user(
2132 def test_api_grant_user_group_permission_to_repo_group_by_regular_user(
2133 self, name, perm, apply_to_children, grant_admin, access_ok):
2133 self, name, perm, apply_to_children, grant_admin, access_ok):
2134 if grant_admin:
2134 if grant_admin:
2135 RepoGroupModel().grant_user_permission(TEST_REPO_GROUP,
2135 RepoGroupModel().grant_user_permission(TEST_REPO_GROUP,
2136 self.TEST_USER_LOGIN,
2136 self.TEST_USER_LOGIN,
2137 'group.admin')
2137 'group.admin')
2138 meta.Session().commit()
2138 meta.Session().commit()
2139
2139
2140 id_, params = _build_data(self.apikey_regular,
2140 id_, params = _build_data(self.apikey_regular,
2141 'grant_user_group_permission_to_repo_group',
2141 'grant_user_group_permission_to_repo_group',
2142 repogroupid=TEST_REPO_GROUP,
2142 repogroupid=TEST_REPO_GROUP,
2143 usergroupid=TEST_USER_GROUP,
2143 usergroupid=TEST_USER_GROUP,
2144 perm=perm,
2144 perm=perm,
2145 apply_to_children=apply_to_children,)
2145 apply_to_children=apply_to_children,)
2146 response = api_call(self, params)
2146 response = api_call(self, params)
2147 if access_ok:
2147 if access_ok:
2148 ret = {
2148 ret = {
2149 'msg': 'Granted perm: `%s` (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2149 'msg': 'Granted perm: `%s` (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2150 perm, apply_to_children, TEST_USER_GROUP, TEST_REPO_GROUP
2150 perm, apply_to_children, TEST_USER_GROUP, TEST_REPO_GROUP
2151 ),
2151 ),
2152 'success': True
2152 'success': True
2153 }
2153 }
2154 expected = ret
2154 expected = ret
2155 self._compare_ok(id_, expected, given=response.body)
2155 self._compare_ok(id_, expected, given=response.body)
2156 else:
2156 else:
2157 expected = 'repository group `%s` does not exist' % TEST_REPO_GROUP
2157 expected = 'repository group `%s` does not exist' % TEST_REPO_GROUP
2158 self._compare_error(id_, expected, given=response.body)
2158 self._compare_error(id_, expected, given=response.body)
2159
2159
2160 def test_api_grant_user_group_permission_to_repo_group_wrong_permission(self):
2160 def test_api_grant_user_group_permission_to_repo_group_wrong_permission(self):
2161 perm = 'haha.no.permission'
2161 perm = 'haha.no.permission'
2162 id_, params = _build_data(self.apikey,
2162 id_, params = _build_data(self.apikey,
2163 'grant_user_group_permission_to_repo_group',
2163 'grant_user_group_permission_to_repo_group',
2164 repogroupid=TEST_REPO_GROUP,
2164 repogroupid=TEST_REPO_GROUP,
2165 usergroupid=TEST_USER_GROUP,
2165 usergroupid=TEST_USER_GROUP,
2166 perm=perm)
2166 perm=perm)
2167 response = api_call(self, params)
2167 response = api_call(self, params)
2168
2168
2169 expected = 'permission `%s` does not exist' % perm
2169 expected = 'permission `%s` does not exist' % perm
2170 self._compare_error(id_, expected, given=response.body)
2170 self._compare_error(id_, expected, given=response.body)
2171
2171
2172 @mock.patch.object(RepoGroupModel, 'grant_user_group_permission', raise_exception)
2172 @mock.patch.object(RepoGroupModel, 'grant_user_group_permission', raise_exception)
2173 def test_api_grant_user_group_permission_exception_when_adding_to_repo_group(self):
2173 def test_api_grant_user_group_permission_exception_when_adding_to_repo_group(self):
2174 perm = 'group.read'
2174 perm = 'group.read'
2175 id_, params = _build_data(self.apikey,
2175 id_, params = _build_data(self.apikey,
2176 'grant_user_group_permission_to_repo_group',
2176 'grant_user_group_permission_to_repo_group',
2177 repogroupid=TEST_REPO_GROUP,
2177 repogroupid=TEST_REPO_GROUP,
2178 usergroupid=TEST_USER_GROUP,
2178 usergroupid=TEST_USER_GROUP,
2179 perm=perm)
2179 perm=perm)
2180 response = api_call(self, params)
2180 response = api_call(self, params)
2181
2181
2182 expected = 'failed to edit permission for user group: `%s` in repo group: `%s`' % (
2182 expected = 'failed to edit permission for user group: `%s` in repo group: `%s`' % (
2183 TEST_USER_GROUP, TEST_REPO_GROUP
2183 TEST_USER_GROUP, TEST_REPO_GROUP
2184 )
2184 )
2185 self._compare_error(id_, expected, given=response.body)
2185 self._compare_error(id_, expected, given=response.body)
2186
2186
2187 @base.parametrize('name,apply_to_children', [
2187 @base.parametrize('name,apply_to_children', [
2188 ('none', 'none'),
2188 ('none', 'none'),
2189 ('all', 'all'),
2189 ('all', 'all'),
2190 ('repos', 'repos'),
2190 ('repos', 'repos'),
2191 ('groups', 'groups'),
2191 ('groups', 'groups'),
2192 ])
2192 ])
2193 def test_api_revoke_user_group_permission_from_repo_group(self, name, apply_to_children):
2193 def test_api_revoke_user_group_permission_from_repo_group(self, name, apply_to_children):
2194 RepoGroupModel().grant_user_group_permission(repo_group=TEST_REPO_GROUP,
2194 RepoGroupModel().grant_user_group_permission(repo_group=TEST_REPO_GROUP,
2195 group_name=TEST_USER_GROUP,
2195 group_name=TEST_USER_GROUP,
2196 perm='group.read',)
2196 perm='group.read',)
2197 meta.Session().commit()
2197 meta.Session().commit()
2198 id_, params = _build_data(self.apikey,
2198 id_, params = _build_data(self.apikey,
2199 'revoke_user_group_permission_from_repo_group',
2199 'revoke_user_group_permission_from_repo_group',
2200 repogroupid=TEST_REPO_GROUP,
2200 repogroupid=TEST_REPO_GROUP,
2201 usergroupid=TEST_USER_GROUP,
2201 usergroupid=TEST_USER_GROUP,
2202 apply_to_children=apply_to_children,)
2202 apply_to_children=apply_to_children,)
2203 response = api_call(self, params)
2203 response = api_call(self, params)
2204
2204
2205 expected = {
2205 expected = {
2206 'msg': 'Revoked perm (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2206 'msg': 'Revoked perm (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2207 apply_to_children, TEST_USER_GROUP, TEST_REPO_GROUP
2207 apply_to_children, TEST_USER_GROUP, TEST_REPO_GROUP
2208 ),
2208 ),
2209 'success': True
2209 'success': True
2210 }
2210 }
2211 self._compare_ok(id_, expected, given=response.body)
2211 self._compare_ok(id_, expected, given=response.body)
2212
2212
2213 @base.parametrize('name,apply_to_children,grant_admin,access_ok', [
2213 @base.parametrize('name,apply_to_children,grant_admin,access_ok', [
2214 ('none', 'none', False, False),
2214 ('none', 'none', False, False),
2215 ('all', 'all', False, False),
2215 ('all', 'all', False, False),
2216 ('repos', 'repos', False, False),
2216 ('repos', 'repos', False, False),
2217 ('groups', 'groups', False, False),
2217 ('groups', 'groups', False, False),
2218
2218
2219 # after granting admin rights
2219 # after granting admin rights
2220 ('none', 'none', False, False),
2220 ('none', 'none', False, False),
2221 ('all', 'all', False, False),
2221 ('all', 'all', False, False),
2222 ('repos', 'repos', False, False),
2222 ('repos', 'repos', False, False),
2223 ('groups', 'groups', False, False),
2223 ('groups', 'groups', False, False),
2224 ])
2224 ])
2225 def test_api_revoke_user_group_permission_from_repo_group_by_regular_user(
2225 def test_api_revoke_user_group_permission_from_repo_group_by_regular_user(
2226 self, name, apply_to_children, grant_admin, access_ok):
2226 self, name, apply_to_children, grant_admin, access_ok):
2227 RepoGroupModel().grant_user_permission(repo_group=TEST_REPO_GROUP,
2227 RepoGroupModel().grant_user_permission(repo_group=TEST_REPO_GROUP,
2228 user=base.TEST_USER_ADMIN_LOGIN,
2228 user=base.TEST_USER_ADMIN_LOGIN,
2229 perm='group.read',)
2229 perm='group.read',)
2230 meta.Session().commit()
2230 meta.Session().commit()
2231
2231
2232 if grant_admin:
2232 if grant_admin:
2233 RepoGroupModel().grant_user_permission(TEST_REPO_GROUP,
2233 RepoGroupModel().grant_user_permission(TEST_REPO_GROUP,
2234 self.TEST_USER_LOGIN,
2234 self.TEST_USER_LOGIN,
2235 'group.admin')
2235 'group.admin')
2236 meta.Session().commit()
2236 meta.Session().commit()
2237
2237
2238 id_, params = _build_data(self.apikey_regular,
2238 id_, params = _build_data(self.apikey_regular,
2239 'revoke_user_group_permission_from_repo_group',
2239 'revoke_user_group_permission_from_repo_group',
2240 repogroupid=TEST_REPO_GROUP,
2240 repogroupid=TEST_REPO_GROUP,
2241 usergroupid=TEST_USER_GROUP,
2241 usergroupid=TEST_USER_GROUP,
2242 apply_to_children=apply_to_children,)
2242 apply_to_children=apply_to_children,)
2243 response = api_call(self, params)
2243 response = api_call(self, params)
2244 if access_ok:
2244 if access_ok:
2245 expected = {
2245 expected = {
2246 'msg': 'Revoked perm (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2246 'msg': 'Revoked perm (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2247 apply_to_children, base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
2247 apply_to_children, base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
2248 ),
2248 ),
2249 'success': True
2249 'success': True
2250 }
2250 }
2251 self._compare_ok(id_, expected, given=response.body)
2251 self._compare_ok(id_, expected, given=response.body)
2252 else:
2252 else:
2253 expected = 'repository group `%s` does not exist' % TEST_REPO_GROUP
2253 expected = 'repository group `%s` does not exist' % TEST_REPO_GROUP
2254 self._compare_error(id_, expected, given=response.body)
2254 self._compare_error(id_, expected, given=response.body)
2255
2255
2256 @mock.patch.object(RepoGroupModel, 'revoke_user_group_permission', raise_exception)
2256 @mock.patch.object(RepoGroupModel, 'revoke_user_group_permission', raise_exception)
2257 def test_api_revoke_user_group_permission_from_repo_group_exception_when_adding(self):
2257 def test_api_revoke_user_group_permission_from_repo_group_exception_when_adding(self):
2258 id_, params = _build_data(self.apikey, 'revoke_user_group_permission_from_repo_group',
2258 id_, params = _build_data(self.apikey, 'revoke_user_group_permission_from_repo_group',
2259 repogroupid=TEST_REPO_GROUP,
2259 repogroupid=TEST_REPO_GROUP,
2260 usergroupid=TEST_USER_GROUP,)
2260 usergroupid=TEST_USER_GROUP,)
2261 response = api_call(self, params)
2261 response = api_call(self, params)
2262
2262
2263 expected = 'failed to edit permission for user group: `%s` in repo group: `%s`' % (
2263 expected = 'failed to edit permission for user group: `%s` in repo group: `%s`' % (
2264 TEST_USER_GROUP, TEST_REPO_GROUP
2264 TEST_USER_GROUP, TEST_REPO_GROUP
2265 )
2265 )
2266 self._compare_error(id_, expected, given=response.body)
2266 self._compare_error(id_, expected, given=response.body)
2267
2267
2268 def test_api_get_gist(self):
2268 def test_api_get_gist(self):
2269 gist = fixture.create_gist()
2269 gist = fixture.create_gist()
2270 gist_id = gist.gist_access_id
2270 gist_id = gist.gist_access_id
2271 gist_created_on = gist.created_on
2271 gist_created_on = gist.created_on
2272 id_, params = _build_data(self.apikey, 'get_gist',
2272 id_, params = _build_data(self.apikey, 'get_gist',
2273 gistid=gist_id, )
2273 gistid=gist_id, )
2274 response = api_call(self, params)
2274 response = api_call(self, params)
2275
2275
2276 expected = {
2276 expected = {
2277 'access_id': gist_id,
2277 'access_id': gist_id,
2278 'created_on': gist_created_on,
2278 'created_on': gist_created_on,
2279 'description': 'new-gist',
2279 'description': 'new-gist',
2280 'expires': -1.0,
2280 'expires': -1.0,
2281 'gist_id': int(gist_id),
2281 'gist_id': int(gist_id),
2282 'type': 'public',
2282 'type': 'public',
2283 'url': 'http://localhost:80/_admin/gists/%s' % gist_id
2283 'url': 'http://localhost:80/_admin/gists/%s' % gist_id
2284 }
2284 }
2285
2285
2286 self._compare_ok(id_, expected, given=response.body)
2286 self._compare_ok(id_, expected, given=response.body)
2287
2287
2288 def test_api_get_gist_that_does_not_exist(self):
2288 def test_api_get_gist_that_does_not_exist(self):
2289 id_, params = _build_data(self.apikey_regular, 'get_gist',
2289 id_, params = _build_data(self.apikey_regular, 'get_gist',
2290 gistid='12345', )
2290 gistid='12345', )
2291 response = api_call(self, params)
2291 response = api_call(self, params)
2292 expected = 'gist `%s` does not exist' % ('12345',)
2292 expected = 'gist `%s` does not exist' % ('12345',)
2293 self._compare_error(id_, expected, given=response.body)
2293 self._compare_error(id_, expected, given=response.body)
2294
2294
2295 def test_api_get_gist_private_gist_without_permission(self):
2295 def test_api_get_gist_private_gist_without_permission(self):
2296 gist = fixture.create_gist()
2296 gist = fixture.create_gist()
2297 gist_id = gist.gist_access_id
2297 gist_id = gist.gist_access_id
2298 gist_created_on = gist.created_on
2298 gist_created_on = gist.created_on
2299 id_, params = _build_data(self.apikey_regular, 'get_gist',
2299 id_, params = _build_data(self.apikey_regular, 'get_gist',
2300 gistid=gist_id, )
2300 gistid=gist_id, )
2301 response = api_call(self, params)
2301 response = api_call(self, params)
2302
2302
2303 expected = 'gist `%s` does not exist' % gist_id
2303 expected = 'gist `%s` does not exist' % gist_id
2304 self._compare_error(id_, expected, given=response.body)
2304 self._compare_error(id_, expected, given=response.body)
2305
2305
2306 def test_api_get_gists(self):
2306 def test_api_get_gists(self):
2307 fixture.create_gist()
2307 fixture.create_gist()
2308 fixture.create_gist()
2308 fixture.create_gist()
2309
2309
2310 id_, params = _build_data(self.apikey, 'get_gists')
2310 id_, params = _build_data(self.apikey, 'get_gists')
2311 response = api_call(self, params)
2311 response = api_call(self, params)
2312 expected = response.json
2312 expected = response.json
2313 assert len(response.json['result']) == 2
2313 assert len(response.json['result']) == 2
2314 #self._compare_ok(id_, expected, given=response.body)
2314 #self._compare_ok(id_, expected, given=response.body)
2315
2315
2316 def test_api_get_gists_regular_user(self):
2316 def test_api_get_gists_regular_user(self):
2317 # by admin
2317 # by admin
2318 fixture.create_gist()
2318 fixture.create_gist()
2319 fixture.create_gist()
2319 fixture.create_gist()
2320
2320
2321 # by reg user
2321 # by reg user
2322 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2322 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2323 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2323 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2324 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2324 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2325
2325
2326 id_, params = _build_data(self.apikey_regular, 'get_gists')
2326 id_, params = _build_data(self.apikey_regular, 'get_gists')
2327 response = api_call(self, params)
2327 response = api_call(self, params)
2328 expected = response.json
2328 expected = response.json
2329 assert len(response.json['result']) == 3
2329 assert len(response.json['result']) == 3
2330 #self._compare_ok(id_, expected, given=response.body)
2330 #self._compare_ok(id_, expected, given=response.body)
2331
2331
2332 def test_api_get_gists_only_for_regular_user(self):
2332 def test_api_get_gists_only_for_regular_user(self):
2333 # by admin
2333 # by admin
2334 fixture.create_gist()
2334 fixture.create_gist()
2335 fixture.create_gist()
2335 fixture.create_gist()
2336
2336
2337 # by reg user
2337 # by reg user
2338 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2338 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2339 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2339 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2340 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2340 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2341
2341
2342 id_, params = _build_data(self.apikey, 'get_gists',
2342 id_, params = _build_data(self.apikey, 'get_gists',
2343 userid=self.TEST_USER_LOGIN)
2343 userid=self.TEST_USER_LOGIN)
2344 response = api_call(self, params)
2344 response = api_call(self, params)
2345 expected = response.json
2345 expected = response.json
2346 assert len(response.json['result']) == 3
2346 assert len(response.json['result']) == 3
2347 #self._compare_ok(id_, expected, given=response.body)
2347 #self._compare_ok(id_, expected, given=response.body)
2348
2348
2349 def test_api_get_gists_regular_user_with_different_userid(self):
2349 def test_api_get_gists_regular_user_with_different_userid(self):
2350 id_, params = _build_data(self.apikey_regular, 'get_gists',
2350 id_, params = _build_data(self.apikey_regular, 'get_gists',
2351 userid=base.TEST_USER_ADMIN_LOGIN)
2351 userid=base.TEST_USER_ADMIN_LOGIN)
2352 response = api_call(self, params)
2352 response = api_call(self, params)
2353 expected = 'userid is not the same as your user'
2353 expected = 'userid is not the same as your user'
2354 self._compare_error(id_, expected, given=response.body)
2354 self._compare_error(id_, expected, given=response.body)
2355
2355
2356 def test_api_create_gist(self):
2356 def test_api_create_gist(self):
2357 id_, params = _build_data(self.apikey_regular, 'create_gist',
2357 id_, params = _build_data(self.apikey_regular, 'create_gist',
2358 lifetime=10,
2358 lifetime=10,
2359 description='foobar-gist',
2359 description='foobar-gist',
2360 gist_type='public',
2360 gist_type='public',
2361 files={'foobar': {'content': 'foo'}})
2361 files={'foobar': {'content': 'foo'}})
2362 response = api_call(self, params)
2362 response = api_call(self, params)
2363 expected = {
2363 expected = {
2364 'gist': {
2364 'gist': {
2365 'access_id': response.json['result']['gist']['access_id'],
2365 'access_id': response.json['result']['gist']['access_id'],
2366 'created_on': response.json['result']['gist']['created_on'],
2366 'created_on': response.json['result']['gist']['created_on'],
2367 'description': 'foobar-gist',
2367 'description': 'foobar-gist',
2368 'expires': response.json['result']['gist']['expires'],
2368 'expires': response.json['result']['gist']['expires'],
2369 'gist_id': response.json['result']['gist']['gist_id'],
2369 'gist_id': response.json['result']['gist']['gist_id'],
2370 'type': 'public',
2370 'type': 'public',
2371 'url': response.json['result']['gist']['url']
2371 'url': response.json['result']['gist']['url']
2372 },
2372 },
2373 'msg': 'created new gist'
2373 'msg': 'created new gist'
2374 }
2374 }
2375 self._compare_ok(id_, expected, given=response.body)
2375 self._compare_ok(id_, expected, given=response.body)
2376
2376
2377 @mock.patch.object(GistModel, 'create', raise_exception)
2377 @mock.patch.object(GistModel, 'create', raise_exception)
2378 def test_api_create_gist_exception_occurred(self):
2378 def test_api_create_gist_exception_occurred(self):
2379 id_, params = _build_data(self.apikey_regular, 'create_gist',
2379 id_, params = _build_data(self.apikey_regular, 'create_gist',
2380 files={})
2380 files={})
2381 response = api_call(self, params)
2381 response = api_call(self, params)
2382 expected = 'failed to create gist'
2382 expected = 'failed to create gist'
2383 self._compare_error(id_, expected, given=response.body)
2383 self._compare_error(id_, expected, given=response.body)
2384
2384
2385 def test_api_delete_gist(self):
2385 def test_api_delete_gist(self):
2386 gist_id = fixture.create_gist().gist_access_id
2386 gist_id = fixture.create_gist().gist_access_id
2387 id_, params = _build_data(self.apikey, 'delete_gist',
2387 id_, params = _build_data(self.apikey, 'delete_gist',
2388 gistid=gist_id)
2388 gistid=gist_id)
2389 response = api_call(self, params)
2389 response = api_call(self, params)
2390 expected = {'gist': None, 'msg': 'deleted gist ID:%s' % gist_id}
2390 expected = {'gist': None, 'msg': 'deleted gist ID:%s' % gist_id}
2391 self._compare_ok(id_, expected, given=response.body)
2391 self._compare_ok(id_, expected, given=response.body)
2392
2392
2393 def test_api_delete_gist_regular_user(self):
2393 def test_api_delete_gist_regular_user(self):
2394 gist_id = fixture.create_gist(owner=self.TEST_USER_LOGIN).gist_access_id
2394 gist_id = fixture.create_gist(owner=self.TEST_USER_LOGIN).gist_access_id
2395 id_, params = _build_data(self.apikey_regular, 'delete_gist',
2395 id_, params = _build_data(self.apikey_regular, 'delete_gist',
2396 gistid=gist_id)
2396 gistid=gist_id)
2397 response = api_call(self, params)
2397 response = api_call(self, params)
2398 expected = {'gist': None, 'msg': 'deleted gist ID:%s' % gist_id}
2398 expected = {'gist': None, 'msg': 'deleted gist ID:%s' % gist_id}
2399 self._compare_ok(id_, expected, given=response.body)
2399 self._compare_ok(id_, expected, given=response.body)
2400
2400
2401 def test_api_delete_gist_regular_user_no_permission(self):
2401 def test_api_delete_gist_regular_user_no_permission(self):
2402 gist_id = fixture.create_gist().gist_access_id
2402 gist_id = fixture.create_gist().gist_access_id
2403 id_, params = _build_data(self.apikey_regular, 'delete_gist',
2403 id_, params = _build_data(self.apikey_regular, 'delete_gist',
2404 gistid=gist_id)
2404 gistid=gist_id)
2405 response = api_call(self, params)
2405 response = api_call(self, params)
2406 expected = 'gist `%s` does not exist' % (gist_id,)
2406 expected = 'gist `%s` does not exist' % (gist_id,)
2407 self._compare_error(id_, expected, given=response.body)
2407 self._compare_error(id_, expected, given=response.body)
2408
2408
2409 @mock.patch.object(GistModel, 'delete', raise_exception)
2409 @mock.patch.object(GistModel, 'delete', raise_exception)
2410 def test_api_delete_gist_exception_occurred(self):
2410 def test_api_delete_gist_exception_occurred(self):
2411 gist_id = fixture.create_gist().gist_access_id
2411 gist_id = fixture.create_gist().gist_access_id
2412 id_, params = _build_data(self.apikey, 'delete_gist',
2412 id_, params = _build_data(self.apikey, 'delete_gist',
2413 gistid=gist_id)
2413 gistid=gist_id)
2414 response = api_call(self, params)
2414 response = api_call(self, params)
2415 expected = 'failed to delete gist ID:%s' % (gist_id,)
2415 expected = 'failed to delete gist ID:%s' % (gist_id,)
2416 self._compare_error(id_, expected, given=response.body)
2416 self._compare_error(id_, expected, given=response.body)
2417
2417
2418 def test_api_get_ip(self):
2418 def test_api_get_ip(self):
2419 id_, params = _build_data(self.apikey, 'get_ip')
2419 id_, params = _build_data(self.apikey, 'get_ip')
2420 response = api_call(self, params)
2420 response = api_call(self, params)
2421 expected = {
2421 expected = {
2422 'server_ip_addr': '0.0.0.0',
2422 'server_ip_addr': '0.0.0.0',
2423 'user_ips': []
2423 'user_ips': []
2424 }
2424 }
2425 self._compare_ok(id_, expected, given=response.body)
2425 self._compare_ok(id_, expected, given=response.body)
2426
2426
2427 def test_api_get_server_info(self):
2427 def test_api_get_server_info(self):
2428 id_, params = _build_data(self.apikey, 'get_server_info')
2428 id_, params = _build_data(self.apikey, 'get_server_info')
2429 response = api_call(self, params)
2429 response = api_call(self, params)
2430 expected = db.Setting.get_server_info()
2430 expected = db.Setting.get_server_info()
2431 self._compare_ok(id_, expected, given=response.body)
2431 self._compare_ok(id_, expected, given=response.body)
2432
2432
2433 def test_api_get_changesets(self):
2433 def test_api_get_changesets(self):
2434 id_, params = _build_data(self.apikey, 'get_changesets',
2434 id_, params = _build_data(self.apikey, 'get_changesets',
2435 repoid=self.REPO, start=0, end=2)
2435 repoid=self.REPO, start=0, end=2)
2436 response = api_call(self, params)
2436 response = api_call(self, params)
2437 result = ext_json.loads(response.body)["result"]
2437 result = ext_json.loads(response.body)["result"]
2438 assert len(result) == 3
2438 assert len(result) == 3
2439 assert 'message' in result[0]
2439 assert 'message' in result[0]
2440 assert 'added' not in result[0]
2440 assert 'added' not in result[0]
2441
2441
2442 def test_api_get_changesets_with_max_revisions(self):
2442 def test_api_get_changesets_with_max_revisions(self):
2443 id_, params = _build_data(self.apikey, 'get_changesets',
2443 id_, params = _build_data(self.apikey, 'get_changesets',
2444 repoid=self.REPO, start_date="2011-02-24T00:00:00", max_revisions=10)
2444 repoid=self.REPO, start_date="2011-02-24T00:00:00", max_revisions=10)
2445 response = api_call(self, params)
2445 response = api_call(self, params)
2446 result = ext_json.loads(response.body)["result"]
2446 result = ext_json.loads(response.body)["result"]
2447 assert len(result) == 10
2447 assert len(result) == 10
2448 assert 'message' in result[0]
2448 assert 'message' in result[0]
2449 assert 'added' not in result[0]
2449 assert 'added' not in result[0]
2450
2450
2451 def test_api_get_changesets_with_branch(self):
2451 def test_api_get_changesets_with_branch(self):
2452 if self.REPO == 'vcs_test_hg':
2452 if self.REPO == 'vcs_test_hg':
2453 branch = 'stable'
2453 branch = 'stable'
2454 else:
2454 else:
2455 pytest.skip("skipping due to missing branches in git test repo")
2455 pytest.skip("skipping due to missing branches in git test repo")
2456 id_, params = _build_data(self.apikey, 'get_changesets',
2456 id_, params = _build_data(self.apikey, 'get_changesets',
2457 repoid=self.REPO, branch_name=branch, start_date="2011-02-24T00:00:00")
2457 repoid=self.REPO, branch_name=branch, start_date="2011-02-24T00:00:00")
2458 response = api_call(self, params)
2458 response = api_call(self, params)
2459 result = ext_json.loads(response.body)["result"]
2459 result = ext_json.loads(response.body)["result"]
2460 assert len(result) == 5
2460 assert len(result) == 5
2461 assert 'message' in result[0]
2461 assert 'message' in result[0]
2462 assert 'added' not in result[0]
2462 assert 'added' not in result[0]
2463
2463
2464 def test_api_get_changesets_with_file_list(self):
2464 def test_api_get_changesets_with_file_list(self):
2465 id_, params = _build_data(self.apikey, 'get_changesets',
2465 id_, params = _build_data(self.apikey, 'get_changesets',
2466 repoid=self.REPO, start_date="2010-04-07T23:30:30", end_date="2010-04-08T00:31:14", with_file_list=True)
2466 repoid=self.REPO, start_date="2010-04-07T23:30:30", end_date="2010-04-08T00:31:14", with_file_list=True)
2467 response = api_call(self, params)
2467 response = api_call(self, params)
2468 result = ext_json.loads(response.body)["result"]
2468 result = ext_json.loads(response.body)["result"]
2469 assert len(result) == 3
2469 assert len(result) == 3
2470 assert 'message' in result[0]
2470 assert 'message' in result[0]
2471 assert 'added' in result[0]
2471 assert 'added' in result[0]
2472
2472
2473 def test_api_get_changeset(self):
2473 def test_api_get_changeset(self):
2474 review = fixture.review_changeset(self.REPO, self.TEST_REVISION, "approved")
2474 review = fixture.review_changeset(self.REPO, self.TEST_REVISION, "approved")
2475 id_, params = _build_data(self.apikey, 'get_changeset',
2475 id_, params = _build_data(self.apikey, 'get_changeset',
2476 repoid=self.REPO, raw_id=self.TEST_REVISION)
2476 repoid=self.REPO, raw_id=self.TEST_REVISION)
2477 response = api_call(self, params)
2477 response = api_call(self, params)
2478 result = ext_json.loads(response.body)["result"]
2478 result = ext_json.loads(response.body)["result"]
2479 assert result["raw_id"] == self.TEST_REVISION
2479 assert result["raw_id"] == self.TEST_REVISION
2480 assert "reviews" not in result
2480 assert "reviews" not in result
2481
2481
2482 def test_api_get_changeset_with_reviews(self):
2482 def test_api_get_changeset_with_reviews(self):
2483 reviewobjs = fixture.review_changeset(self.REPO, self.TEST_REVISION, "approved")
2483 reviewobjs = fixture.review_changeset(self.REPO, self.TEST_REVISION, "approved")
2484 id_, params = _build_data(self.apikey, 'get_changeset',
2484 id_, params = _build_data(self.apikey, 'get_changeset',
2485 repoid=self.REPO, raw_id=self.TEST_REVISION,
2485 repoid=self.REPO, raw_id=self.TEST_REVISION,
2486 with_reviews=True)
2486 with_reviews=True)
2487 response = api_call(self, params)
2487 response = api_call(self, params)
2488 result = ext_json.loads(response.body)["result"]
2488 result = ext_json.loads(response.body)["result"]
2489 assert result["raw_id"] == self.TEST_REVISION
2489 assert result["raw_id"] == self.TEST_REVISION
2490 assert "reviews" in result
2490 assert "reviews" in result
2491 assert len(result["reviews"]) == 1
2491 assert len(result["reviews"]) == 1
2492 review = result["reviews"][0]
2492 review = result["reviews"][0]
2493 expected = {
2493 expected = {
2494 'status': 'approved',
2494 'status': 'approved',
2495 'modified_at': reviewobjs[0].modified_at.replace(microsecond=0).isoformat(),
2495 'modified_at': reviewobjs[0].modified_at.replace(microsecond=0).isoformat(),
2496 'reviewer': 'test_admin',
2496 'reviewer': 'test_admin',
2497 }
2497 }
2498 assert review == expected
2498 assert review == expected
2499
2499
2500 def test_api_get_changeset_that_does_not_exist(self):
2500 def test_api_get_changeset_that_does_not_exist(self):
2501 """ Fetch changeset status for non-existant changeset.
2501 """ Fetch changeset status for non-existant changeset.
2502 revision id is the above git hash used in the test above with the
2502 revision id is the above git hash used in the test above with the
2503 last 3 nibbles replaced with 0xf. Should not exist for git _or_ hg.
2503 last 3 nibbles replaced with 0xf. Should not exist for git _or_ hg.
2504 """
2504 """
2505 id_, params = _build_data(self.apikey, 'get_changeset',
2505 id_, params = _build_data(self.apikey, 'get_changeset',
2506 repoid=self.REPO, raw_id = '7ab37bc680b4aa72c34d07b230c866c28e9fcfff')
2506 repoid=self.REPO, raw_id = '7ab37bc680b4aa72c34d07b230c866c28e9fcfff')
2507 response = api_call(self, params)
2507 response = api_call(self, params)
2508 expected = 'Changeset %s does not exist' % ('7ab37bc680b4aa72c34d07b230c866c28e9fcfff',)
2508 expected = 'Changeset %s does not exist' % ('7ab37bc680b4aa72c34d07b230c866c28e9fcfff',)
2509 self._compare_error(id_, expected, given=response.body)
2509 self._compare_error(id_, expected, given=response.body)
2510
2510
2511 def test_api_get_changeset_without_permission(self):
2511 def test_api_get_changeset_without_permission(self):
2512 review = fixture.review_changeset(self.REPO, self.TEST_REVISION, "approved")
2512 review = fixture.review_changeset(self.REPO, self.TEST_REVISION, "approved")
2513 RepoModel().revoke_user_permission(repo=self.REPO, user=self.TEST_USER_LOGIN)
2513 RepoModel().revoke_user_permission(repo=self.REPO, user=self.TEST_USER_LOGIN)
2514 RepoModel().revoke_user_permission(repo=self.REPO, user="default")
2514 RepoModel().revoke_user_permission(repo=self.REPO, user="default")
2515 id_, params = _build_data(self.apikey_regular, 'get_changeset',
2515 id_, params = _build_data(self.apikey_regular, 'get_changeset',
2516 repoid=self.REPO, raw_id=self.TEST_REVISION)
2516 repoid=self.REPO, raw_id=self.TEST_REVISION)
2517 response = api_call(self, params)
2517 response = api_call(self, params)
2518 expected = 'Access denied to repo %s' % self.REPO
2518 expected = 'Access denied to repo %s' % self.REPO
2519 self._compare_error(id_, expected, given=response.body)
2519 self._compare_error(id_, expected, given=response.body)
2520
2520
2521 def test_api_get_pullrequest(self):
2521 def test_api_get_pullrequest(self):
2522 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'get test')
2522 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'get test')
2523 random_id = random.randrange(1, 9999)
2523 random_id = random.randrange(1, 9999)
2524 params = ascii_bytes(ext_json.dumps({
2524 params = ascii_bytes(ext_json.dumps({
2525 "id": random_id,
2525 "id": random_id,
2526 "api_key": self.apikey,
2526 "api_key": self.apikey,
2527 "method": 'get_pullrequest',
2527 "method": 'get_pullrequest',
2528 "args": {"pullrequest_id": pull_request_id},
2528 "args": {"pullrequest_id": pull_request_id},
2529 }))
2529 }))
2530 response = api_call(self, params)
2530 response = api_call(self, params)
2531 pullrequest = db.PullRequest().get(pull_request_id)
2531 pullrequest = db.PullRequest().get(pull_request_id)
2532 expected = {
2532 expected = {
2533 "status": "new",
2533 "status": "new",
2534 "pull_request_id": pull_request_id,
2534 "pull_request_id": pull_request_id,
2535 "description": "No description",
2535 "description": "No description",
2536 "url": "/%s/pull-request/%s/_/%s" % (self.REPO, pull_request_id, "stable"),
2536 "url": "/%s/pull-request/%s/_/%s" % (self.REPO, pull_request_id, "stable"),
2537 "reviewers": [{"username": "test_regular"}],
2537 "reviewers": [{"username": "test_regular"}],
2538 "org_repo_url": "http://localhost:80/%s" % self.REPO,
2538 "org_repo_url": "http://localhost:80/%s" % self.REPO,
2539 "org_ref_parts": ["branch", "stable", self.TEST_PR_SRC],
2539 "org_ref_parts": ["branch", "stable", self.TEST_PR_SRC],
2540 "other_ref_parts": ["branch", "default", self.TEST_PR_DST],
2540 "other_ref_parts": ["branch", "default", self.TEST_PR_DST],
2541 "comments": [{"username": base.TEST_USER_ADMIN_LOGIN, "text": "",
2541 "comments": [{"username": base.TEST_USER_ADMIN_LOGIN, "text": "",
2542 "comment_id": pullrequest.comments[0].comment_id}],
2542 "comment_id": pullrequest.comments[0].comment_id}],
2543 "owner": base.TEST_USER_ADMIN_LOGIN,
2543 "owner": base.TEST_USER_ADMIN_LOGIN,
2544 "statuses": [{"status": "under_review", "reviewer": base.TEST_USER_ADMIN_LOGIN, "modified_at": "2000-01-01T00:00:00"} for i in range(0, len(self.TEST_PR_REVISIONS))],
2544 "statuses": [{"status": "under_review", "reviewer": base.TEST_USER_ADMIN_LOGIN, "modified_at": "2000-01-01T00:00:00"} for i in range(0, len(self.TEST_PR_REVISIONS))],
2545 "title": "get test",
2545 "title": "get test",
2546 "revisions": self.TEST_PR_REVISIONS,
2546 "revisions": self.TEST_PR_REVISIONS,
2547 "created_on": "2000-01-01T00:00:00",
2547 "created_on": "2000-01-01T00:00:00",
2548 "updated_on": "2000-01-01T00:00:00",
2548 "updated_on": "2000-01-01T00:00:00",
2549 }
2549 }
2550 self._compare_ok(random_id, expected,
2550 self._compare_ok(random_id, expected,
2551 given=re.sub(br"\d\d\d\d\-\d\d\-\d\dT\d\d\:\d\d\:\d\d",
2551 given=re.sub(br"\d\d\d\d\-\d\d\-\d\dT\d\d\:\d\d\:\d\d",
2552 b"2000-01-01T00:00:00", response.body))
2552 b"2000-01-01T00:00:00", response.body))
2553
2553
2554 def test_api_close_pullrequest(self):
2554 def test_api_close_pullrequest(self):
2555 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'close test')
2555 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'close test')
2556 random_id = random.randrange(1, 9999)
2556 random_id = random.randrange(1, 9999)
2557 params = ascii_bytes(ext_json.dumps({
2557 params = ascii_bytes(ext_json.dumps({
2558 "id": random_id,
2558 "id": random_id,
2559 "api_key": self.apikey,
2559 "api_key": self.apikey,
2560 "method": "comment_pullrequest",
2560 "method": "comment_pullrequest",
2561 "args": {"pull_request_id": pull_request_id, "close_pr": True},
2561 "args": {"pull_request_id": pull_request_id, "close_pr": True},
2562 }))
2562 }))
2563 response = api_call(self, params)
2563 response = api_call(self, params)
2564 self._compare_ok(random_id, True, given=response.body)
2564 self._compare_ok(random_id, True, given=response.body)
2565 pullrequest = db.PullRequest().get(pull_request_id)
2565 pullrequest = db.PullRequest().get(pull_request_id)
2566 assert pullrequest.comments[-1].text == ''
2566 assert pullrequest.comments[-1].text == ''
2567 assert pullrequest.status == db.PullRequest.STATUS_CLOSED
2567 assert pullrequest.status == db.PullRequest.STATUS_CLOSED
2568 assert pullrequest.is_closed() == True
2568 assert pullrequest.is_closed() == True
2569
2569
2570 def test_api_status_pullrequest(self):
2570 def test_api_status_pullrequest(self):
2571 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, "status test")
2571 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, "status test")
2572
2572
2573 random_id = random.randrange(1, 9999)
2573 random_id = random.randrange(1, 9999)
2574 params = ascii_bytes(ext_json.dumps({
2574 params = ascii_bytes(ext_json.dumps({
2575 "id": random_id,
2575 "id": random_id,
2576 "api_key": db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN).api_key,
2576 "api_key": db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN).api_key,
2577 "method": "comment_pullrequest",
2577 "method": "comment_pullrequest",
2578 "args": {"pull_request_id": pull_request_id, "status": db.ChangesetStatus.STATUS_APPROVED},
2578 "args": {"pull_request_id": pull_request_id, "status": db.ChangesetStatus.STATUS_APPROVED},
2579 }))
2579 }))
2580 response = api_call(self, params)
2580 response = api_call(self, params)
2581 pullrequest = db.PullRequest().get(pull_request_id)
2581 pullrequest = db.PullRequest().get(pull_request_id)
2582 self._compare_error(random_id, "No permission to change pull request status. User needs to be admin, owner or reviewer.", given=response.body)
2582 self._compare_error(random_id, "No permission to change pull request status. User needs to be admin, owner or reviewer.", given=response.body)
2583 assert db.ChangesetStatus.STATUS_UNDER_REVIEW == ChangesetStatusModel().calculate_pull_request_result(pullrequest)[2]
2583 assert db.ChangesetStatus.STATUS_UNDER_REVIEW == ChangesetStatusModel().calculate_pull_request_result(pullrequest)[2]
2584 params = ascii_bytes(ext_json.dumps({
2584 params = ascii_bytes(ext_json.dumps({
2585 "id": random_id,
2585 "id": random_id,
2586 "api_key": db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN).api_key,
2586 "api_key": db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN).api_key,
2587 "method": "comment_pullrequest",
2587 "method": "comment_pullrequest",
2588 "args": {"pull_request_id": pull_request_id, "status": db.ChangesetStatus.STATUS_APPROVED},
2588 "args": {"pull_request_id": pull_request_id, "status": db.ChangesetStatus.STATUS_APPROVED},
2589 }))
2589 }))
2590 response = api_call(self, params)
2590 response = api_call(self, params)
2591 self._compare_ok(random_id, True, given=response.body)
2591 self._compare_ok(random_id, True, given=response.body)
2592 pullrequest = db.PullRequest().get(pull_request_id)
2592 pullrequest = db.PullRequest().get(pull_request_id)
2593 assert db.ChangesetStatus.STATUS_APPROVED == ChangesetStatusModel().calculate_pull_request_result(pullrequest)[2]
2593 assert db.ChangesetStatus.STATUS_APPROVED == ChangesetStatusModel().calculate_pull_request_result(pullrequest)[2]
2594
2594
2595 def test_api_comment_pullrequest(self):
2595 def test_api_comment_pullrequest(self):
2596 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, "comment test")
2596 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, "comment test")
2597 random_id = random.randrange(1, 9999)
2597 random_id = random.randrange(1, 9999)
2598 params = ascii_bytes(ext_json.dumps({
2598 params = ascii_bytes(ext_json.dumps({
2599 "id": random_id,
2599 "id": random_id,
2600 "api_key": self.apikey,
2600 "api_key": self.apikey,
2601 "method": "comment_pullrequest",
2601 "method": "comment_pullrequest",
2602 "args": {"pull_request_id": pull_request_id, "comment_msg": "Looks good to me"},
2602 "args": {"pull_request_id": pull_request_id, "comment_msg": "Looks good to me"},
2603 }))
2603 }))
2604 response = api_call(self, params)
2604 response = api_call(self, params)
2605 self._compare_ok(random_id, True, given=response.body)
2605 self._compare_ok(random_id, True, given=response.body)
2606 pullrequest = db.PullRequest().get(pull_request_id)
2606 pullrequest = db.PullRequest().get(pull_request_id)
2607 assert pullrequest.comments[-1].text == 'Looks good to me'
2607 assert pullrequest.comments[-1].text == 'Looks good to me'
2608
2608
2609 def test_api_edit_reviewers_add_single(self):
2609 def test_api_edit_reviewers_add_single(self):
2610 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2610 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2611 pullrequest = db.PullRequest().get(pull_request_id)
2611 pullrequest = db.PullRequest().get(pull_request_id)
2612 pullrequest.owner = self.test_user
2612 pullrequest.owner = self.test_user
2613 random_id = random.randrange(1, 9999)
2613 random_id = random.randrange(1, 9999)
2614 params = ascii_bytes(ext_json.dumps({
2614 params = ascii_bytes(ext_json.dumps({
2615 "id": random_id,
2615 "id": random_id,
2616 "api_key": self.apikey_regular,
2616 "api_key": self.apikey_regular,
2617 "method": "edit_reviewers",
2617 "method": "edit_reviewers",
2618 "args": {"pull_request_id": pull_request_id, "add": base.TEST_USER_REGULAR2_LOGIN},
2618 "args": {"pull_request_id": pull_request_id, "add": base.TEST_USER_REGULAR2_LOGIN},
2619 }))
2619 }))
2620 response = api_call(self, params)
2620 response = api_call(self, params)
2621 expected = { 'added': [base.TEST_USER_REGULAR2_LOGIN], 'already_present': [], 'removed': [] }
2621 expected = { 'added': [base.TEST_USER_REGULAR2_LOGIN], 'already_present': [], 'removed': [] }
2622
2622
2623 self._compare_ok(random_id, expected, given=response.body)
2623 self._compare_ok(random_id, expected, given=response.body)
2624 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2624 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2625
2625
2626 def test_api_edit_reviewers_add_nonexistent(self):
2626 def test_api_edit_reviewers_add_nonexistent(self):
2627 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2627 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2628 pullrequest = db.PullRequest().get(pull_request_id)
2628 pullrequest = db.PullRequest().get(pull_request_id)
2629 pullrequest.owner = self.test_user
2629 pullrequest.owner = self.test_user
2630 random_id = random.randrange(1, 9999)
2630 random_id = random.randrange(1, 9999)
2631 params = ascii_bytes(ext_json.dumps({
2631 params = ascii_bytes(ext_json.dumps({
2632 "id": random_id,
2632 "id": random_id,
2633 "api_key": self.apikey_regular,
2633 "api_key": self.apikey_regular,
2634 "method": "edit_reviewers",
2634 "method": "edit_reviewers",
2635 "args": {"pull_request_id": pull_request_id, "add": 999},
2635 "args": {"pull_request_id": pull_request_id, "add": 999},
2636 }))
2636 }))
2637 response = api_call(self, params)
2637 response = api_call(self, params)
2638
2638
2639 self._compare_error(random_id, "user `999` does not exist", given=response.body)
2639 self._compare_error(random_id, "user `999` does not exist", given=response.body)
2640
2640
2641 def test_api_edit_reviewers_add_multiple(self):
2641 def test_api_edit_reviewers_add_multiple(self):
2642 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2642 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2643 pullrequest = db.PullRequest().get(pull_request_id)
2643 pullrequest = db.PullRequest().get(pull_request_id)
2644 pullrequest.owner = self.test_user
2644 pullrequest.owner = self.test_user
2645 random_id = random.randrange(1, 9999)
2645 random_id = random.randrange(1, 9999)
2646 params = ascii_bytes(ext_json.dumps({
2646 params = ascii_bytes(ext_json.dumps({
2647 "id": random_id,
2647 "id": random_id,
2648 "api_key": self.apikey_regular,
2648 "api_key": self.apikey_regular,
2649 "method": "edit_reviewers",
2649 "method": "edit_reviewers",
2650 "args": {
2650 "args": {
2651 "pull_request_id": pull_request_id,
2651 "pull_request_id": pull_request_id,
2652 "add": [ self.TEST_USER_LOGIN, base.TEST_USER_REGULAR2_LOGIN ]
2652 "add": [ self.TEST_USER_LOGIN, base.TEST_USER_REGULAR2_LOGIN ]
2653 },
2653 },
2654 }))
2654 }))
2655 response = api_call(self, params)
2655 response = api_call(self, params)
2656 # list order depends on python sorting hash, which is randomized
2656 # list order depends on python sorting hash, which is randomized
2657 assert set(ext_json.loads(response.body)['result']['added']) == set([base.TEST_USER_REGULAR2_LOGIN, self.TEST_USER_LOGIN])
2657 assert set(ext_json.loads(response.body)['result']['added']) == set([base.TEST_USER_REGULAR2_LOGIN, self.TEST_USER_LOGIN])
2658 assert set(ext_json.loads(response.body)['result']['already_present']) == set()
2658 assert set(ext_json.loads(response.body)['result']['already_present']) == set()
2659 assert set(ext_json.loads(response.body)['result']['removed']) == set()
2659 assert set(ext_json.loads(response.body)['result']['removed']) == set()
2660
2660
2661 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2661 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2662 assert db.User.get_by_username(self.TEST_USER_LOGIN) in pullrequest.get_reviewer_users()
2662 assert db.User.get_by_username(self.TEST_USER_LOGIN) in pullrequest.get_reviewer_users()
2663
2663
2664 def test_api_edit_reviewers_add_already_present(self):
2664 def test_api_edit_reviewers_add_already_present(self):
2665 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2665 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2666 pullrequest = db.PullRequest().get(pull_request_id)
2666 pullrequest = db.PullRequest().get(pull_request_id)
2667 pullrequest.owner = self.test_user
2667 pullrequest.owner = self.test_user
2668 random_id = random.randrange(1, 9999)
2668 random_id = random.randrange(1, 9999)
2669 params = ascii_bytes(ext_json.dumps({
2669 params = ascii_bytes(ext_json.dumps({
2670 "id": random_id,
2670 "id": random_id,
2671 "api_key": self.apikey_regular,
2671 "api_key": self.apikey_regular,
2672 "method": "edit_reviewers",
2672 "method": "edit_reviewers",
2673 "args": {
2673 "args": {
2674 "pull_request_id": pull_request_id,
2674 "pull_request_id": pull_request_id,
2675 "add": [ base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_REGULAR2_LOGIN ]
2675 "add": [ base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_REGULAR2_LOGIN ]
2676 },
2676 },
2677 }))
2677 }))
2678 response = api_call(self, params)
2678 response = api_call(self, params)
2679 expected = { 'added': [base.TEST_USER_REGULAR2_LOGIN],
2679 expected = { 'added': [base.TEST_USER_REGULAR2_LOGIN],
2680 'already_present': [base.TEST_USER_REGULAR_LOGIN],
2680 'already_present': [base.TEST_USER_REGULAR_LOGIN],
2681 'removed': [],
2681 'removed': [],
2682 }
2682 }
2683
2683
2684 self._compare_ok(random_id, expected, given=response.body)
2684 self._compare_ok(random_id, expected, given=response.body)
2685 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2685 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2686 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2686 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2687
2687
2688 def test_api_edit_reviewers_add_closed(self):
2688 def test_api_edit_reviewers_add_closed(self):
2689 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2689 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2690 pullrequest = db.PullRequest().get(pull_request_id)
2690 pullrequest = db.PullRequest().get(pull_request_id)
2691 pullrequest.owner = self.test_user
2691 pullrequest.owner = self.test_user
2692 PullRequestModel().close_pull_request(pull_request_id)
2692 PullRequestModel().close_pull_request(pull_request_id)
2693 random_id = random.randrange(1, 9999)
2693 random_id = random.randrange(1, 9999)
2694 params = ascii_bytes(ext_json.dumps({
2694 params = ascii_bytes(ext_json.dumps({
2695 "id": random_id,
2695 "id": random_id,
2696 "api_key": self.apikey_regular,
2696 "api_key": self.apikey_regular,
2697 "method": "edit_reviewers",
2697 "method": "edit_reviewers",
2698 "args": {"pull_request_id": pull_request_id, "add": base.TEST_USER_REGULAR2_LOGIN},
2698 "args": {"pull_request_id": pull_request_id, "add": base.TEST_USER_REGULAR2_LOGIN},
2699 }))
2699 }))
2700 response = api_call(self, params)
2700 response = api_call(self, params)
2701 self._compare_error(random_id, "Cannot edit reviewers of a closed pull request.", given=response.body)
2701 self._compare_error(random_id, "Cannot edit reviewers of a closed pull request.", given=response.body)
2702
2702
2703 def test_api_edit_reviewers_add_not_owner(self):
2703 def test_api_edit_reviewers_add_not_owner(self):
2704 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2704 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2705 pullrequest = db.PullRequest().get(pull_request_id)
2705 pullrequest = db.PullRequest().get(pull_request_id)
2706 pullrequest.owner = db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN)
2706 pullrequest.owner = db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN)
2707 random_id = random.randrange(1, 9999)
2707 random_id = random.randrange(1, 9999)
2708 params = ascii_bytes(ext_json.dumps({
2708 params = ascii_bytes(ext_json.dumps({
2709 "id": random_id,
2709 "id": random_id,
2710 "api_key": self.apikey_regular,
2710 "api_key": self.apikey_regular,
2711 "method": "edit_reviewers",
2711 "method": "edit_reviewers",
2712 "args": {"pull_request_id": pull_request_id, "add": base.TEST_USER_REGULAR2_LOGIN},
2712 "args": {"pull_request_id": pull_request_id, "add": base.TEST_USER_REGULAR2_LOGIN},
2713 }))
2713 }))
2714 response = api_call(self, params)
2714 response = api_call(self, params)
2715 self._compare_error(random_id, "No permission to edit reviewers of this pull request. User needs to be admin or pull request owner.", given=response.body)
2715 self._compare_error(random_id, "No permission to edit reviewers of this pull request. User needs to be admin or pull request owner.", given=response.body)
2716
2716
2717
2717
2718 def test_api_edit_reviewers_remove_single(self):
2718 def test_api_edit_reviewers_remove_single(self):
2719 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2719 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2720 pullrequest = db.PullRequest().get(pull_request_id)
2720 pullrequest = db.PullRequest().get(pull_request_id)
2721 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2721 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2722
2722
2723 pullrequest.owner = self.test_user
2723 pullrequest.owner = self.test_user
2724 random_id = random.randrange(1, 9999)
2724 random_id = random.randrange(1, 9999)
2725 params = ascii_bytes(ext_json.dumps({
2725 params = ascii_bytes(ext_json.dumps({
2726 "id": random_id,
2726 "id": random_id,
2727 "api_key": self.apikey_regular,
2727 "api_key": self.apikey_regular,
2728 "method": "edit_reviewers",
2728 "method": "edit_reviewers",
2729 "args": {"pull_request_id": pull_request_id, "remove": base.TEST_USER_REGULAR_LOGIN},
2729 "args": {"pull_request_id": pull_request_id, "remove": base.TEST_USER_REGULAR_LOGIN},
2730 }))
2730 }))
2731 response = api_call(self, params)
2731 response = api_call(self, params)
2732
2732
2733 expected = { 'added': [],
2733 expected = { 'added': [],
2734 'already_present': [],
2734 'already_present': [],
2735 'removed': [base.TEST_USER_REGULAR_LOGIN],
2735 'removed': [base.TEST_USER_REGULAR_LOGIN],
2736 }
2736 }
2737 self._compare_ok(random_id, expected, given=response.body)
2737 self._compare_ok(random_id, expected, given=response.body)
2738 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) not in pullrequest.get_reviewer_users()
2738 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) not in pullrequest.get_reviewer_users()
2739
2739
2740 def test_api_edit_reviewers_remove_nonexistent(self):
2740 def test_api_edit_reviewers_remove_nonexistent(self):
2741 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2741 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2742 pullrequest = db.PullRequest().get(pull_request_id)
2742 pullrequest = db.PullRequest().get(pull_request_id)
2743 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2743 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2744
2744
2745 pullrequest.owner = self.test_user
2745 pullrequest.owner = self.test_user
2746 random_id = random.randrange(1, 9999)
2746 random_id = random.randrange(1, 9999)
2747 params = ascii_bytes(ext_json.dumps({
2747 params = ascii_bytes(ext_json.dumps({
2748 "id": random_id,
2748 "id": random_id,
2749 "api_key": self.apikey_regular,
2749 "api_key": self.apikey_regular,
2750 "method": "edit_reviewers",
2750 "method": "edit_reviewers",
2751 "args": {"pull_request_id": pull_request_id, "remove": 999},
2751 "args": {"pull_request_id": pull_request_id, "remove": 999},
2752 }))
2752 }))
2753 response = api_call(self, params)
2753 response = api_call(self, params)
2754
2754
2755 self._compare_error(random_id, "user `999` does not exist", given=response.body)
2755 self._compare_error(random_id, "user `999` does not exist", given=response.body)
2756 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2756 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2757
2757
2758 def test_api_edit_reviewers_remove_nonpresent(self):
2758 def test_api_edit_reviewers_remove_nonpresent(self):
2759 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2759 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2760 pullrequest = db.PullRequest().get(pull_request_id)
2760 pullrequest = db.PullRequest().get(pull_request_id)
2761 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2761 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2762 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) not in pullrequest.get_reviewer_users()
2762 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) not in pullrequest.get_reviewer_users()
2763
2763
2764 pullrequest.owner = self.test_user
2764 pullrequest.owner = self.test_user
2765 random_id = random.randrange(1, 9999)
2765 random_id = random.randrange(1, 9999)
2766 params = ascii_bytes(ext_json.dumps({
2766 params = ascii_bytes(ext_json.dumps({
2767 "id": random_id,
2767 "id": random_id,
2768 "api_key": self.apikey_regular,
2768 "api_key": self.apikey_regular,
2769 "method": "edit_reviewers",
2769 "method": "edit_reviewers",
2770 "args": {"pull_request_id": pull_request_id, "remove": base.TEST_USER_REGULAR2_LOGIN},
2770 "args": {"pull_request_id": pull_request_id, "remove": base.TEST_USER_REGULAR2_LOGIN},
2771 }))
2771 }))
2772 response = api_call(self, params)
2772 response = api_call(self, params)
2773
2773
2774 # NOTE: no explicit indication that removed user was not even a reviewer
2774 # NOTE: no explicit indication that removed user was not even a reviewer
2775 expected = { 'added': [],
2775 expected = { 'added': [],
2776 'already_present': [],
2776 'already_present': [],
2777 'removed': [base.TEST_USER_REGULAR2_LOGIN],
2777 'removed': [base.TEST_USER_REGULAR2_LOGIN],
2778 }
2778 }
2779 self._compare_ok(random_id, expected, given=response.body)
2779 self._compare_ok(random_id, expected, given=response.body)
2780 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2780 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2781 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) not in pullrequest.get_reviewer_users()
2781 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) not in pullrequest.get_reviewer_users()
2782
2782
2783 def test_api_edit_reviewers_remove_multiple(self):
2783 def test_api_edit_reviewers_remove_multiple(self):
2784 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2784 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2785 pullrequest = db.PullRequest().get(pull_request_id)
2785 pullrequest = db.PullRequest().get(pull_request_id)
2786 prr = db.PullRequestReviewer(db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN), pullrequest)
2786 prr = db.PullRequestReviewer(db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN), pullrequest)
2787 meta.Session().add(prr)
2787 meta.Session().add(prr)
2788 meta.Session().commit()
2788 meta.Session().commit()
2789
2789
2790 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2790 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2791 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2791 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2792
2792
2793 pullrequest.owner = self.test_user
2793 pullrequest.owner = self.test_user
2794 random_id = random.randrange(1, 9999)
2794 random_id = random.randrange(1, 9999)
2795 params = ascii_bytes(ext_json.dumps({
2795 params = ascii_bytes(ext_json.dumps({
2796 "id": random_id,
2796 "id": random_id,
2797 "api_key": self.apikey_regular,
2797 "api_key": self.apikey_regular,
2798 "method": "edit_reviewers",
2798 "method": "edit_reviewers",
2799 "args": {"pull_request_id": pull_request_id, "remove": [ base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_REGULAR2_LOGIN ] },
2799 "args": {"pull_request_id": pull_request_id, "remove": [ base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_REGULAR2_LOGIN ] },
2800 }))
2800 }))
2801 response = api_call(self, params)
2801 response = api_call(self, params)
2802
2802
2803 # list order depends on python sorting hash, which is randomized
2803 # list order depends on python sorting hash, which is randomized
2804 assert set(ext_json.loads(response.body)['result']['added']) == set()
2804 assert set(ext_json.loads(response.body)['result']['added']) == set()
2805 assert set(ext_json.loads(response.body)['result']['already_present']) == set()
2805 assert set(ext_json.loads(response.body)['result']['already_present']) == set()
2806 assert set(ext_json.loads(response.body)['result']['removed']) == set([base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_REGULAR2_LOGIN])
2806 assert set(ext_json.loads(response.body)['result']['removed']) == set([base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_REGULAR2_LOGIN])
2807 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) not in pullrequest.get_reviewer_users()
2807 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) not in pullrequest.get_reviewer_users()
2808 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) not in pullrequest.get_reviewer_users()
2808 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) not in pullrequest.get_reviewer_users()
2809
2809
2810 def test_api_edit_reviewers_remove_closed(self):
2810 def test_api_edit_reviewers_remove_closed(self):
2811 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2811 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2812 pullrequest = db.PullRequest().get(pull_request_id)
2812 pullrequest = db.PullRequest().get(pull_request_id)
2813 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2813 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2814 PullRequestModel().close_pull_request(pull_request_id)
2814 PullRequestModel().close_pull_request(pull_request_id)
2815
2815
2816 pullrequest.owner = self.test_user
2816 pullrequest.owner = self.test_user
2817 random_id = random.randrange(1, 9999)
2817 random_id = random.randrange(1, 9999)
2818 params = ascii_bytes(ext_json.dumps({
2818 params = ascii_bytes(ext_json.dumps({
2819 "id": random_id,
2819 "id": random_id,
2820 "api_key": self.apikey_regular,
2820 "api_key": self.apikey_regular,
2821 "method": "edit_reviewers",
2821 "method": "edit_reviewers",
2822 "args": {"pull_request_id": pull_request_id, "remove": base.TEST_USER_REGULAR_LOGIN},
2822 "args": {"pull_request_id": pull_request_id, "remove": base.TEST_USER_REGULAR_LOGIN},
2823 }))
2823 }))
2824 response = api_call(self, params)
2824 response = api_call(self, params)
2825
2825
2826 self._compare_error(random_id, "Cannot edit reviewers of a closed pull request.", given=response.body)
2826 self._compare_error(random_id, "Cannot edit reviewers of a closed pull request.", given=response.body)
2827 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2827 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2828
2828
2829 def test_api_edit_reviewers_remove_not_owner(self):
2829 def test_api_edit_reviewers_remove_not_owner(self):
2830 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2830 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2831 pullrequest = db.PullRequest().get(pull_request_id)
2831 pullrequest = db.PullRequest().get(pull_request_id)
2832 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2832 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2833
2833
2834 pullrequest.owner = db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN)
2834 pullrequest.owner = db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN)
2835 random_id = random.randrange(1, 9999)
2835 random_id = random.randrange(1, 9999)
2836 params = ascii_bytes(ext_json.dumps({
2836 params = ascii_bytes(ext_json.dumps({
2837 "id": random_id,
2837 "id": random_id,
2838 "api_key": self.apikey_regular,
2838 "api_key": self.apikey_regular,
2839 "method": "edit_reviewers",
2839 "method": "edit_reviewers",
2840 "args": {"pull_request_id": pull_request_id, "remove": base.TEST_USER_REGULAR_LOGIN},
2840 "args": {"pull_request_id": pull_request_id, "remove": base.TEST_USER_REGULAR_LOGIN},
2841 }))
2841 }))
2842 response = api_call(self, params)
2842 response = api_call(self, params)
2843
2843
2844 self._compare_error(random_id, "No permission to edit reviewers of this pull request. User needs to be admin or pull request owner.", given=response.body)
2844 self._compare_error(random_id, "No permission to edit reviewers of this pull request. User needs to be admin or pull request owner.", given=response.body)
2845 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2845 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2846
2846
2847 def test_api_edit_reviewers_add_remove_single(self):
2847 def test_api_edit_reviewers_add_remove_single(self):
2848 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2848 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2849 pullrequest = db.PullRequest().get(pull_request_id)
2849 pullrequest = db.PullRequest().get(pull_request_id)
2850 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2850 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2851 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) not in pullrequest.get_reviewer_users()
2851 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) not in pullrequest.get_reviewer_users()
2852
2852
2853 pullrequest.owner = self.test_user
2853 pullrequest.owner = self.test_user
2854 random_id = random.randrange(1, 9999)
2854 random_id = random.randrange(1, 9999)
2855 params = ascii_bytes(ext_json.dumps({
2855 params = ascii_bytes(ext_json.dumps({
2856 "id": random_id,
2856 "id": random_id,
2857 "api_key": self.apikey_regular,
2857 "api_key": self.apikey_regular,
2858 "method": "edit_reviewers",
2858 "method": "edit_reviewers",
2859 "args": {"pull_request_id": pull_request_id,
2859 "args": {"pull_request_id": pull_request_id,
2860 "add": base.TEST_USER_REGULAR2_LOGIN,
2860 "add": base.TEST_USER_REGULAR2_LOGIN,
2861 "remove": base.TEST_USER_REGULAR_LOGIN
2861 "remove": base.TEST_USER_REGULAR_LOGIN
2862 },
2862 },
2863 }))
2863 }))
2864 response = api_call(self, params)
2864 response = api_call(self, params)
2865
2865
2866 expected = { 'added': [base.TEST_USER_REGULAR2_LOGIN],
2866 expected = { 'added': [base.TEST_USER_REGULAR2_LOGIN],
2867 'already_present': [],
2867 'already_present': [],
2868 'removed': [base.TEST_USER_REGULAR_LOGIN],
2868 'removed': [base.TEST_USER_REGULAR_LOGIN],
2869 }
2869 }
2870 self._compare_ok(random_id, expected, given=response.body)
2870 self._compare_ok(random_id, expected, given=response.body)
2871 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) not in pullrequest.get_reviewer_users()
2871 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) not in pullrequest.get_reviewer_users()
2872 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2872 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2873
2873
2874 def test_api_edit_reviewers_add_remove_multiple(self):
2874 def test_api_edit_reviewers_add_remove_multiple(self):
2875 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2875 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2876 pullrequest = db.PullRequest().get(pull_request_id)
2876 pullrequest = db.PullRequest().get(pull_request_id)
2877 prr = db.PullRequestReviewer(db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN), pullrequest)
2877 prr = db.PullRequestReviewer(db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN), pullrequest)
2878 meta.Session().add(prr)
2878 meta.Session().add(prr)
2879 meta.Session().commit()
2879 meta.Session().commit()
2880 assert db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN) in pullrequest.get_reviewer_users()
2880 assert db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN) in pullrequest.get_reviewer_users()
2881 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2881 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2882 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) not in pullrequest.get_reviewer_users()
2882 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) not in pullrequest.get_reviewer_users()
2883
2883
2884 pullrequest.owner = self.test_user
2884 pullrequest.owner = self.test_user
2885 random_id = random.randrange(1, 9999)
2885 random_id = random.randrange(1, 9999)
2886 params = ascii_bytes(ext_json.dumps({
2886 params = ascii_bytes(ext_json.dumps({
2887 "id": random_id,
2887 "id": random_id,
2888 "api_key": self.apikey_regular,
2888 "api_key": self.apikey_regular,
2889 "method": "edit_reviewers",
2889 "method": "edit_reviewers",
2890 "args": {"pull_request_id": pull_request_id,
2890 "args": {"pull_request_id": pull_request_id,
2891 "add": [ base.TEST_USER_REGULAR2_LOGIN ],
2891 "add": [ base.TEST_USER_REGULAR2_LOGIN ],
2892 "remove": [ base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_ADMIN_LOGIN ],
2892 "remove": [ base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_ADMIN_LOGIN ],
2893 },
2893 },
2894 }))
2894 }))
2895 response = api_call(self, params)
2895 response = api_call(self, params)
2896
2896
2897 # list order depends on python sorting hash, which is randomized
2897 # list order depends on python sorting hash, which is randomized
2898 assert set(ext_json.loads(response.body)['result']['added']) == set([base.TEST_USER_REGULAR2_LOGIN])
2898 assert set(ext_json.loads(response.body)['result']['added']) == set([base.TEST_USER_REGULAR2_LOGIN])
2899 assert set(ext_json.loads(response.body)['result']['already_present']) == set()
2899 assert set(ext_json.loads(response.body)['result']['already_present']) == set()
2900 assert set(ext_json.loads(response.body)['result']['removed']) == set([base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_ADMIN_LOGIN])
2900 assert set(ext_json.loads(response.body)['result']['removed']) == set([base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_ADMIN_LOGIN])
2901 assert db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN) not in pullrequest.get_reviewer_users()
2901 assert db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN) not in pullrequest.get_reviewer_users()
2902 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) not in pullrequest.get_reviewer_users()
2902 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) not in pullrequest.get_reviewer_users()
2903 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2903 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2904
2904
2905 def test_api_edit_reviewers_invalid_params(self):
2905 def test_api_edit_reviewers_invalid_params(self):
2906 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2906 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2907 pullrequest = db.PullRequest().get(pull_request_id)
2907 pullrequest = db.PullRequest().get(pull_request_id)
2908 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2908 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2909
2909
2910 pullrequest.owner = db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN)
2910 pullrequest.owner = db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN)
2911 random_id = random.randrange(1, 9999)
2911 random_id = random.randrange(1, 9999)
2912 params = ascii_bytes(ext_json.dumps({
2912 params = ascii_bytes(ext_json.dumps({
2913 "id": random_id,
2913 "id": random_id,
2914 "api_key": self.apikey_regular,
2914 "api_key": self.apikey_regular,
2915 "method": "edit_reviewers",
2915 "method": "edit_reviewers",
2916 "args": {"pull_request_id": pull_request_id},
2916 "args": {"pull_request_id": pull_request_id},
2917 }))
2917 }))
2918 response = api_call(self, params)
2918 response = api_call(self, params)
2919
2919
2920 self._compare_error(random_id, "Invalid request. Neither 'add' nor 'remove' is specified.", given=response.body)
2920 self._compare_error(random_id, "Invalid request. Neither 'add' nor 'remove' is specified.", given=response.body)
2921 assert ext_json.loads(response.body)['result'] is None
2921 assert ext_json.loads(response.body)['result'] is None
@@ -1,150 +1,151 b''
1 from kallithea.model import db, meta
1 from kallithea.model import db, meta
2 from kallithea.model.repo_group import RepoGroupModel
2 from kallithea.model.repo_group import RepoGroupModel
3 from kallithea.tests import base
3 from kallithea.tests import base
4 from kallithea.tests.fixture import Fixture
4 from kallithea.tests.fixture import Fixture
5
5
6
6
7 fixture = Fixture()
7 fixture = Fixture()
8
8
9
9
10 class TestRepoGroupsController(base.TestController):
10 class TestRepoGroupsController(base.TestController):
11
11
12 def test_index(self):
12 def test_index(self):
13 self.log_user()
13 self.log_user()
14 response = self.app.get(base.url('repos_groups'))
14 response = self.app.get(base.url('repos_groups'))
15 response.mustcontain('"records": []')
15 response.mustcontain('"records": []')
16
16
17 def test_new(self):
17 def test_new(self):
18 self.log_user()
18 self.log_user()
19 response = self.app.get(base.url('new_repos_group'))
19 response = self.app.get(base.url('new_repos_group'))
20
20
21 def test_create(self):
21 def test_create(self):
22 self.log_user()
22 self.log_user()
23
23
24 group_name = 'foo'
24 group_name = 'foo'
25
25
26 # creation with form error
26 # creation with form error
27 response = self.app.post(base.url('repos_groups'),
27 response = self.app.post(base.url('repos_groups'),
28 {'group_name': group_name,
28 {'group_name': group_name,
29 '_session_csrf_secret_token': self.session_csrf_secret_token()})
29 '_session_csrf_secret_token': self.session_csrf_secret_token()})
30 response.mustcontain('name="group_name" type="text" value="%s"' % group_name)
30 response.mustcontain('name="group_name" type="text" value="%s"' % group_name)
31 response.mustcontain('<!-- for: group_description -->')
31 response.mustcontain('<!-- for: group_description -->')
32
32
33 # creation
33 # creation
34 response = self.app.post(base.url('repos_groups'),
34 response = self.app.post(base.url('repos_groups'),
35 {'group_name': group_name,
35 {'group_name': group_name,
36 'group_description': 'lala',
36 'group_description': 'lala',
37 'parent_group_id': '-1',
37 'parent_group_id': '-1',
38 'group_copy_permissions': 'True',
38 'group_copy_permissions': 'True',
39 '_session_csrf_secret_token': self.session_csrf_secret_token()})
39 '_session_csrf_secret_token': self.session_csrf_secret_token()})
40 self.checkSessionFlash(response, 'Created repository group %s' % group_name)
40 self.checkSessionFlash(response, 'Created repository group %s' % group_name)
41
41
42 # edit form
42 # edit form
43 response = self.app.get(base.url('edit_repo_group', group_name=group_name))
43 response = self.app.get(base.url('edit_repo_group', group_name=group_name))
44 response.mustcontain('>lala<')
44 response.mustcontain('>lala<')
45
45
46 # edit with form error
46 # edit with form error
47 response = self.app.post(base.url('update_repos_group', group_name=group_name),
47 response = self.app.post(base.url('update_repos_group', group_name=group_name),
48 {'group_name': group_name,
48 {'group_name': group_name,
49 '_session_csrf_secret_token': self.session_csrf_secret_token()})
49 '_session_csrf_secret_token': self.session_csrf_secret_token()})
50 response.mustcontain('name="group_name" type="text" value="%s"' % group_name)
50 response.mustcontain('name="group_name" type="text" value="%s"' % group_name)
51 response.mustcontain('<!-- for: group_description -->')
51 response.mustcontain('<!-- for: group_description -->')
52
52
53 # edit
53 # edit
54 response = self.app.post(base.url('update_repos_group', group_name=group_name),
54 response = self.app.post(base.url('update_repos_group', group_name=group_name),
55 {'group_name': group_name,
55 {'group_name': group_name,
56 'owner': base.TEST_USER_REGULAR2_LOGIN,
56 'group_description': 'lolo',
57 'group_description': 'lolo',
57 '_session_csrf_secret_token': self.session_csrf_secret_token()})
58 '_session_csrf_secret_token': self.session_csrf_secret_token()})
58 self.checkSessionFlash(response, 'Updated repository group %s' % group_name)
59 self.checkSessionFlash(response, 'Updated repository group %s' % group_name)
59 response = response.follow()
60 response = response.follow()
60 response.mustcontain('name="group_name" type="text" value="%s"' % group_name)
61 response.mustcontain('name="group_name" type="text" value="%s"' % group_name)
61 response.mustcontain(no='<!-- for: group_description -->')
62 response.mustcontain(no='<!-- for: group_description -->')
62 response.mustcontain('>lolo<')
63 response.mustcontain('>lolo<')
63
64
64 # listing
65 # listing
65 response = self.app.get(base.url('repos_groups'))
66 response = self.app.get(base.url('repos_groups'))
66 response.mustcontain('raw_name": "%s"' % group_name)
67 response.mustcontain('raw_name": "%s"' % group_name)
67
68
68 # show
69 # show
69 response = self.app.get(base.url('repos_group', group_name=group_name))
70 response = self.app.get(base.url('repos_group', group_name=group_name))
70 response.mustcontain('href="/_admin/repo_groups/%s/edit"' % group_name)
71 response.mustcontain('href="/_admin/repo_groups/%s/edit"' % group_name)
71
72
72 # show ignores extra trailing slashes in the URL
73 # show ignores extra trailing slashes in the URL
73 response = self.app.get(base.url('repos_group', group_name='%s//' % group_name))
74 response = self.app.get(base.url('repos_group', group_name='%s//' % group_name))
74 response.mustcontain('href="/_admin/repo_groups/%s/edit"' % group_name)
75 response.mustcontain('href="/_admin/repo_groups/%s/edit"' % group_name)
75
76
76 # delete
77 # delete
77 response = self.app.post(base.url('delete_repo_group', group_name=group_name),
78 response = self.app.post(base.url('delete_repo_group', group_name=group_name),
78 {'_session_csrf_secret_token': self.session_csrf_secret_token()})
79 {'_session_csrf_secret_token': self.session_csrf_secret_token()})
79 self.checkSessionFlash(response, 'Removed repository group %s' % group_name)
80 self.checkSessionFlash(response, 'Removed repository group %s' % group_name)
80
81
81 def test_new_by_regular_user(self):
82 def test_new_by_regular_user(self):
82 self.log_user(base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_REGULAR_PASS)
83 self.log_user(base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_REGULAR_PASS)
83 response = self.app.get(base.url('new_repos_group'), status=403)
84 response = self.app.get(base.url('new_repos_group'), status=403)
84
85
85 def test_case_insensitivity(self):
86 def test_case_insensitivity(self):
86 self.log_user()
87 self.log_user()
87 group_name = 'newgroup'
88 group_name = 'newgroup'
88 response = self.app.post(base.url('repos_groups'),
89 response = self.app.post(base.url('repos_groups'),
89 fixture._get_repo_group_create_params(group_name=group_name,
90 fixture._get_repo_group_create_params(group_name=group_name,
90 _session_csrf_secret_token=self.session_csrf_secret_token()))
91 _session_csrf_secret_token=self.session_csrf_secret_token()))
91 # try to create repo group with swapped case
92 # try to create repo group with swapped case
92 swapped_group_name = group_name.swapcase()
93 swapped_group_name = group_name.swapcase()
93 response = self.app.post(base.url('repos_groups'),
94 response = self.app.post(base.url('repos_groups'),
94 fixture._get_repo_group_create_params(group_name=swapped_group_name,
95 fixture._get_repo_group_create_params(group_name=swapped_group_name,
95 _session_csrf_secret_token=self.session_csrf_secret_token()))
96 _session_csrf_secret_token=self.session_csrf_secret_token()))
96 response.mustcontain('already exists')
97 response.mustcontain('already exists')
97
98
98 RepoGroupModel().delete(group_name)
99 RepoGroupModel().delete(group_name)
99 meta.Session().commit()
100 meta.Session().commit()
100
101
101 def test_subgroup_deletion(self):
102 def test_subgroup_deletion(self):
102 self.log_user()
103 self.log_user()
103 parent = None
104 parent = None
104 parent_name = 'parent'
105 parent_name = 'parent'
105 sub = None
106 sub = None
106 sub_name = 'sub'
107 sub_name = 'sub'
107 sub_path = 'parent/sub'
108 sub_path = 'parent/sub'
108
109
109 try:
110 try:
110 # create parent group
111 # create parent group
111 assert db.RepoGroup.guess_instance(parent_name) is None
112 assert db.RepoGroup.guess_instance(parent_name) is None
112 response = self.app.post(
113 response = self.app.post(
113 base.url('repos_groups'),
114 base.url('repos_groups'),
114 fixture._get_repo_group_create_params(
115 fixture._get_repo_group_create_params(
115 group_name=parent_name,
116 group_name=parent_name,
116 _session_csrf_secret_token=self.session_csrf_secret_token()
117 _session_csrf_secret_token=self.session_csrf_secret_token()
117 )
118 )
118 )
119 )
119 parent = db.RepoGroup.guess_instance(parent_name)
120 parent = db.RepoGroup.guess_instance(parent_name)
120 assert parent is not None
121 assert parent is not None
121
122
122 # create sub group
123 # create sub group
123 assert db.RepoGroup.guess_instance(sub_path) is None
124 assert db.RepoGroup.guess_instance(sub_path) is None
124 response = self.app.post(
125 response = self.app.post(
125 base.url('repos_groups'),
126 base.url('repos_groups'),
126 fixture._get_repo_group_create_params(
127 fixture._get_repo_group_create_params(
127 group_name=sub_name,
128 group_name=sub_name,
128 parent_group_id=parent.group_id,
129 parent_group_id=parent.group_id,
129 _session_csrf_secret_token=self.session_csrf_secret_token()
130 _session_csrf_secret_token=self.session_csrf_secret_token()
130 )
131 )
131 )
132 )
132 sub = db.RepoGroup.guess_instance(sub_path)
133 sub = db.RepoGroup.guess_instance(sub_path)
133 assert sub is not None
134 assert sub is not None
134
135
135 # delete sub group
136 # delete sub group
136 response = self.app.post(
137 response = self.app.post(
137 base.url('delete_repo_group', group_name=sub_path),
138 base.url('delete_repo_group', group_name=sub_path),
138 params={
139 params={
139 '_session_csrf_secret_token': self.session_csrf_secret_token()
140 '_session_csrf_secret_token': self.session_csrf_secret_token()
140 },
141 },
141 )
142 )
142 sub = db.RepoGroup.guess_instance(sub_path)
143 sub = db.RepoGroup.guess_instance(sub_path)
143 assert sub is None
144 assert sub is None
144
145
145 finally:
146 finally:
146 if sub:
147 if sub:
147 RepoGroupModel().delete(sub)
148 RepoGroupModel().delete(sub)
148 if parent:
149 if parent:
149 RepoGroupModel().delete(parent)
150 RepoGroupModel().delete(parent)
150 meta.Session().commit()
151 meta.Session().commit()
General Comments 0
You need to be logged in to leave comments. Login now