##// END OF EJS Templates
repo: introduce enable_downloads and enable_statistics when creating repos...
Mads Kiilerich -
r8729:f2dc57c1 stable
parent child Browse files
Show More
@@ -1,2391 +1,2391 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.api.api
15 kallithea.controllers.api.api
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17
17
18 API controller for Kallithea
18 API 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: Aug 20, 2011
22 :created_on: Aug 20, 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 import logging
28 import logging
29 import traceback
29 import traceback
30 from datetime import datetime
30 from datetime import datetime
31
31
32 from tg import request
32 from tg import request
33
33
34 from kallithea.controllers.api import JSONRPCController, JSONRPCError
34 from kallithea.controllers.api import JSONRPCController, JSONRPCError
35 from kallithea.lib.auth import (AuthUser, HasPermissionAny, HasPermissionAnyDecorator, HasRepoGroupPermissionLevel, HasRepoPermissionLevel,
35 from kallithea.lib.auth import (AuthUser, HasPermissionAny, HasPermissionAnyDecorator, HasRepoGroupPermissionLevel, HasRepoPermissionLevel,
36 HasUserGroupPermissionLevel)
36 HasUserGroupPermissionLevel)
37 from kallithea.lib.exceptions import DefaultUserException, UserGroupsAssignedException
37 from kallithea.lib.exceptions import DefaultUserException, UserGroupsAssignedException
38 from kallithea.lib.utils import repo2db_mapper
38 from kallithea.lib.utils import repo2db_mapper
39 from kallithea.lib.vcs.backends.base import EmptyChangeset
39 from kallithea.lib.vcs.backends.base import EmptyChangeset
40 from kallithea.lib.vcs.exceptions import EmptyRepositoryError
40 from kallithea.lib.vcs.exceptions import EmptyRepositoryError
41 from kallithea.model import db, meta, userlog
41 from kallithea.model import db, meta, userlog
42 from kallithea.model.changeset_status import ChangesetStatusModel
42 from kallithea.model.changeset_status import ChangesetStatusModel
43 from kallithea.model.comment import ChangesetCommentsModel
43 from kallithea.model.comment import ChangesetCommentsModel
44 from kallithea.model.gist import GistModel
44 from kallithea.model.gist import GistModel
45 from kallithea.model.pull_request import PullRequestModel
45 from kallithea.model.pull_request import PullRequestModel
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 ScmModel, UserGroupList
48 from kallithea.model.scm import ScmModel, UserGroupList
49 from kallithea.model.user import UserModel
49 from kallithea.model.user import UserModel
50 from kallithea.model.user_group import UserGroupModel
50 from kallithea.model.user_group import UserGroupModel
51
51
52
52
53 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
54
54
55
55
56 def store_update(updates, attr, name):
56 def store_update(updates, attr, name):
57 """
57 """
58 Stores param in updates dict if it's not None (i.e. if user explicitly set
58 Stores param in updates dict if it's not None (i.e. if user explicitly set
59 a parameter). This allows easy updates of passed in params.
59 a parameter). This allows easy updates of passed in params.
60 """
60 """
61 if attr is not None:
61 if attr is not None:
62 updates[name] = attr
62 updates[name] = attr
63
63
64
64
65 def get_user_or_error(userid):
65 def get_user_or_error(userid):
66 """
66 """
67 Get user by id or name or return JsonRPCError if not found
67 Get user by id or name or return JsonRPCError if not found
68
68
69 :param userid:
69 :param userid:
70 """
70 """
71 user = UserModel().get_user(userid)
71 user = UserModel().get_user(userid)
72 if user is None:
72 if user is None:
73 raise JSONRPCError("user `%s` does not exist" % (userid,))
73 raise JSONRPCError("user `%s` does not exist" % (userid,))
74 return user
74 return user
75
75
76
76
77 def get_repo_or_error(repoid):
77 def get_repo_or_error(repoid):
78 """
78 """
79 Get repo by id or name or return JsonRPCError if not found
79 Get repo by id or name or return JsonRPCError if not found
80
80
81 :param repoid:
81 :param repoid:
82 """
82 """
83 repo = RepoModel().get_repo(repoid)
83 repo = RepoModel().get_repo(repoid)
84 if repo is None:
84 if repo is None:
85 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
85 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
86 return repo
86 return repo
87
87
88
88
89 def get_repo_group_or_error(repogroupid):
89 def get_repo_group_or_error(repogroupid):
90 """
90 """
91 Get repo group by id or name or return JsonRPCError if not found
91 Get repo group by id or name or return JsonRPCError if not found
92
92
93 :param repogroupid:
93 :param repogroupid:
94 """
94 """
95 repo_group = db.RepoGroup.guess_instance(repogroupid)
95 repo_group = db.RepoGroup.guess_instance(repogroupid)
96 if repo_group is None:
96 if repo_group is None:
97 raise JSONRPCError(
97 raise JSONRPCError(
98 'repository group `%s` does not exist' % (repogroupid,))
98 'repository group `%s` does not exist' % (repogroupid,))
99 return repo_group
99 return repo_group
100
100
101
101
102 def get_user_group_or_error(usergroupid):
102 def get_user_group_or_error(usergroupid):
103 """
103 """
104 Get user group by id or name or return JsonRPCError if not found
104 Get user group by id or name or return JsonRPCError if not found
105
105
106 :param usergroupid:
106 :param usergroupid:
107 """
107 """
108 user_group = UserGroupModel().get_group(usergroupid)
108 user_group = UserGroupModel().get_group(usergroupid)
109 if user_group is None:
109 if user_group is None:
110 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
110 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
111 return user_group
111 return user_group
112
112
113
113
114 def get_perm_or_error(permid, prefix=None):
114 def get_perm_or_error(permid, prefix=None):
115 """
115 """
116 Get permission by id or name or return JsonRPCError if not found
116 Get permission by id or name or return JsonRPCError if not found
117
117
118 :param permid:
118 :param permid:
119 """
119 """
120 perm = db.Permission.get_by_key(permid)
120 perm = db.Permission.get_by_key(permid)
121 if perm is None:
121 if perm is None:
122 raise JSONRPCError('permission `%s` does not exist' % (permid,))
122 raise JSONRPCError('permission `%s` does not exist' % (permid,))
123 if prefix:
123 if prefix:
124 if not perm.permission_name.startswith(prefix):
124 if not perm.permission_name.startswith(prefix):
125 raise JSONRPCError('permission `%s` is invalid, '
125 raise JSONRPCError('permission `%s` is invalid, '
126 'should start with %s' % (permid, prefix))
126 'should start with %s' % (permid, prefix))
127 return perm
127 return perm
128
128
129
129
130 def get_gist_or_error(gistid):
130 def get_gist_or_error(gistid):
131 """
131 """
132 Get gist by id or gist_access_id or return JsonRPCError if not found
132 Get gist by id or gist_access_id or return JsonRPCError if not found
133
133
134 :param gistid:
134 :param gistid:
135 """
135 """
136 gist = GistModel().get_gist(gistid)
136 gist = GistModel().get_gist(gistid)
137 if gist is None:
137 if gist is None:
138 raise JSONRPCError('gist `%s` does not exist' % (gistid,))
138 raise JSONRPCError('gist `%s` does not exist' % (gistid,))
139 return gist
139 return gist
140
140
141
141
142 class ApiController(JSONRPCController):
142 class ApiController(JSONRPCController):
143 """
143 """
144 API Controller
144 API Controller
145
145
146 The authenticated user can be found as request.authuser.
146 The authenticated user can be found as request.authuser.
147
147
148 Example function::
148 Example function::
149
149
150 def func(arg1, arg2,...):
150 def func(arg1, arg2,...):
151 pass
151 pass
152
152
153 Each function should also **raise** JSONRPCError for any
153 Each function should also **raise** JSONRPCError for any
154 errors that happens.
154 errors that happens.
155 """
155 """
156
156
157 @HasPermissionAnyDecorator('hg.admin')
157 @HasPermissionAnyDecorator('hg.admin')
158 def test(self, args):
158 def test(self, args):
159 return args
159 return args
160
160
161 @HasPermissionAnyDecorator('hg.admin')
161 @HasPermissionAnyDecorator('hg.admin')
162 def pull(self, repoid, clone_uri=None):
162 def pull(self, repoid, clone_uri=None):
163 """
163 """
164 Triggers a pull from remote location on given repo. Can be used to
164 Triggers a pull from remote location on given repo. Can be used to
165 automatically keep remote repos up to date. This command can be executed
165 automatically keep remote repos up to date. This command can be executed
166 only using api_key belonging to user with admin rights
166 only using api_key belonging to user with admin rights
167
167
168 :param repoid: repository name or repository id
168 :param repoid: repository name or repository id
169 :type repoid: str or int
169 :type repoid: str or int
170 :param clone_uri: repository URI to pull from (optional)
170 :param clone_uri: repository URI to pull from (optional)
171 :type clone_uri: str
171 :type clone_uri: str
172
172
173 OUTPUT::
173 OUTPUT::
174
174
175 id : <id_given_in_input>
175 id : <id_given_in_input>
176 result : {
176 result : {
177 "msg" : "Pulled from `<repository name>`",
177 "msg" : "Pulled from `<repository name>`",
178 "repository" : "<repository name>"
178 "repository" : "<repository name>"
179 }
179 }
180 error : null
180 error : null
181
181
182 ERROR OUTPUT::
182 ERROR OUTPUT::
183
183
184 id : <id_given_in_input>
184 id : <id_given_in_input>
185 result : null
185 result : null
186 error : {
186 error : {
187 "Unable to pull changes from `<reponame>`"
187 "Unable to pull changes from `<reponame>`"
188 }
188 }
189 """
189 """
190 repo = get_repo_or_error(repoid)
190 repo = get_repo_or_error(repoid)
191
191
192 try:
192 try:
193 ScmModel().pull_changes(repo.repo_name,
193 ScmModel().pull_changes(repo.repo_name,
194 request.authuser.username,
194 request.authuser.username,
195 request.ip_addr,
195 request.ip_addr,
196 clone_uri=clone_uri)
196 clone_uri=clone_uri)
197 return dict(
197 return dict(
198 msg='Pulled from `%s`' % repo.repo_name,
198 msg='Pulled from `%s`' % repo.repo_name,
199 repository=repo.repo_name
199 repository=repo.repo_name
200 )
200 )
201 except Exception:
201 except Exception:
202 log.error(traceback.format_exc())
202 log.error(traceback.format_exc())
203 raise JSONRPCError(
203 raise JSONRPCError(
204 'Unable to pull changes from `%s`' % repo.repo_name
204 'Unable to pull changes from `%s`' % repo.repo_name
205 )
205 )
206
206
207 @HasPermissionAnyDecorator('hg.admin')
207 @HasPermissionAnyDecorator('hg.admin')
208 def rescan_repos(self, remove_obsolete=False):
208 def rescan_repos(self, remove_obsolete=False):
209 """
209 """
210 Triggers rescan repositories action. If remove_obsolete is set
210 Triggers rescan repositories action. If remove_obsolete is set
211 than also delete repos that are in database but not in the filesystem.
211 than also delete repos that are in database but not in the filesystem.
212 aka "clean zombies". This command can be executed only using api_key
212 aka "clean zombies". This command can be executed only using api_key
213 belonging to user with admin rights.
213 belonging to user with admin rights.
214
214
215 :param remove_obsolete: deletes repositories from
215 :param remove_obsolete: deletes repositories from
216 database that are not found on the filesystem
216 database that are not found on the filesystem
217 :type remove_obsolete: Optional(bool)
217 :type remove_obsolete: Optional(bool)
218
218
219 OUTPUT::
219 OUTPUT::
220
220
221 id : <id_given_in_input>
221 id : <id_given_in_input>
222 result : {
222 result : {
223 'added': [<added repository name>,...]
223 'added': [<added repository name>,...]
224 'removed': [<removed repository name>,...]
224 'removed': [<removed repository name>,...]
225 }
225 }
226 error : null
226 error : null
227
227
228 ERROR OUTPUT::
228 ERROR OUTPUT::
229
229
230 id : <id_given_in_input>
230 id : <id_given_in_input>
231 result : null
231 result : null
232 error : {
232 error : {
233 'Error occurred during rescan repositories action'
233 'Error occurred during rescan repositories action'
234 }
234 }
235 """
235 """
236 try:
236 try:
237 rm_obsolete = remove_obsolete
237 rm_obsolete = remove_obsolete
238 added, removed = repo2db_mapper(ScmModel().repo_scan(),
238 added, removed = repo2db_mapper(ScmModel().repo_scan(),
239 remove_obsolete=rm_obsolete)
239 remove_obsolete=rm_obsolete)
240 return {'added': added, 'removed': removed}
240 return {'added': added, 'removed': removed}
241 except Exception:
241 except Exception:
242 log.error(traceback.format_exc())
242 log.error(traceback.format_exc())
243 raise JSONRPCError(
243 raise JSONRPCError(
244 'Error occurred during rescan repositories action'
244 'Error occurred during rescan repositories action'
245 )
245 )
246
246
247 def invalidate_cache(self, repoid):
247 def invalidate_cache(self, repoid):
248 """
248 """
249 Invalidate cache for repository.
249 Invalidate cache for repository.
250 This command can be executed only using api_key belonging to user with admin
250 This command can be executed only using api_key belonging to user with admin
251 rights or regular user that have write or admin or write access to repository.
251 rights or regular user that have write or admin or write access to repository.
252
252
253 :param repoid: repository name or repository id
253 :param repoid: repository name or repository id
254 :type repoid: str or int
254 :type repoid: str or int
255
255
256 OUTPUT::
256 OUTPUT::
257
257
258 id : <id_given_in_input>
258 id : <id_given_in_input>
259 result : {
259 result : {
260 'msg': Cache for repository `<repository name>` was invalidated,
260 'msg': Cache for repository `<repository name>` was invalidated,
261 'repository': <repository name>
261 'repository': <repository name>
262 }
262 }
263 error : null
263 error : null
264
264
265 ERROR OUTPUT::
265 ERROR OUTPUT::
266
266
267 id : <id_given_in_input>
267 id : <id_given_in_input>
268 result : null
268 result : null
269 error : {
269 error : {
270 'Error occurred during cache invalidation action'
270 'Error occurred during cache invalidation action'
271 }
271 }
272 """
272 """
273 repo = get_repo_or_error(repoid)
273 repo = get_repo_or_error(repoid)
274 if not HasPermissionAny('hg.admin')():
274 if not HasPermissionAny('hg.admin')():
275 if not HasRepoPermissionLevel('write')(repo.repo_name):
275 if not HasRepoPermissionLevel('write')(repo.repo_name):
276 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
276 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
277
277
278 try:
278 try:
279 ScmModel().mark_for_invalidation(repo.repo_name)
279 ScmModel().mark_for_invalidation(repo.repo_name)
280 return dict(
280 return dict(
281 msg='Cache for repository `%s` was invalidated' % (repoid,),
281 msg='Cache for repository `%s` was invalidated' % (repoid,),
282 repository=repo.repo_name
282 repository=repo.repo_name
283 )
283 )
284 except Exception:
284 except Exception:
285 log.error(traceback.format_exc())
285 log.error(traceback.format_exc())
286 raise JSONRPCError(
286 raise JSONRPCError(
287 'Error occurred during cache invalidation action'
287 'Error occurred during cache invalidation action'
288 )
288 )
289
289
290 @HasPermissionAnyDecorator('hg.admin')
290 @HasPermissionAnyDecorator('hg.admin')
291 def get_ip(self, userid=None):
291 def get_ip(self, userid=None):
292 """
292 """
293 Shows IP address as seen from Kallithea server, together with all
293 Shows IP address as seen from Kallithea server, together with all
294 defined IP addresses for given user. If userid is not passed data is
294 defined IP addresses for given user. If userid is not passed data is
295 returned for user who's calling this function.
295 returned for user who's calling this function.
296 This command can be executed only using api_key belonging to user with
296 This command can be executed only using api_key belonging to user with
297 admin rights.
297 admin rights.
298
298
299 :param userid: username to show ips for
299 :param userid: username to show ips for
300 :type userid: Optional(str or int)
300 :type userid: Optional(str or int)
301
301
302 OUTPUT::
302 OUTPUT::
303
303
304 id : <id_given_in_input>
304 id : <id_given_in_input>
305 result : {
305 result : {
306 "server_ip_addr" : "<ip_from_client>",
306 "server_ip_addr" : "<ip_from_client>",
307 "user_ips" : [
307 "user_ips" : [
308 {
308 {
309 "ip_addr" : "<ip_with_mask>",
309 "ip_addr" : "<ip_with_mask>",
310 "ip_range" : ["<start_ip>", "<end_ip>"]
310 "ip_range" : ["<start_ip>", "<end_ip>"]
311 },
311 },
312 ...
312 ...
313 ]
313 ]
314 }
314 }
315 error : null
315 error : null
316 """
316 """
317 if userid is None:
317 if userid is None:
318 userid = request.authuser.user_id
318 userid = request.authuser.user_id
319 user = get_user_or_error(userid)
319 user = get_user_or_error(userid)
320 ips = db.UserIpMap.query().filter(db.UserIpMap.user == user).all()
320 ips = db.UserIpMap.query().filter(db.UserIpMap.user == user).all()
321 return dict(
321 return dict(
322 server_ip_addr=request.ip_addr,
322 server_ip_addr=request.ip_addr,
323 user_ips=ips
323 user_ips=ips
324 )
324 )
325
325
326 # alias for old
326 # alias for old
327 show_ip = get_ip
327 show_ip = get_ip
328
328
329 @HasPermissionAnyDecorator('hg.admin')
329 @HasPermissionAnyDecorator('hg.admin')
330 def get_server_info(self):
330 def get_server_info(self):
331 """
331 """
332 return server info, including Kallithea version and installed packages
332 return server info, including Kallithea version and installed packages
333
333
334 OUTPUT::
334 OUTPUT::
335
335
336 id : <id_given_in_input>
336 id : <id_given_in_input>
337 result : {
337 result : {
338 'modules' : [ [<module name>, <module version>], ...]
338 'modules' : [ [<module name>, <module version>], ...]
339 'py_version' : <python version>,
339 'py_version' : <python version>,
340 'platform' : <platform type>,
340 'platform' : <platform type>,
341 'kallithea_version' : <kallithea version>,
341 'kallithea_version' : <kallithea version>,
342 'git_version' : '<git version>',
342 'git_version' : '<git version>',
343 'git_path' : '<git path>'
343 'git_path' : '<git path>'
344 }
344 }
345 error : null
345 error : null
346 """
346 """
347 return db.Setting.get_server_info()
347 return db.Setting.get_server_info()
348
348
349 def get_user(self, userid=None):
349 def get_user(self, userid=None):
350 """
350 """
351 Gets a user by username or user_id, Returns empty result if user is
351 Gets a user by username or user_id, Returns empty result if user is
352 not found. If userid param is skipped it is set to id of user who is
352 not found. If userid param is skipped it is set to id of user who is
353 calling this method. This command can be executed only using api_key
353 calling this method. This command can be executed only using api_key
354 belonging to user with admin rights, or regular users that cannot
354 belonging to user with admin rights, or regular users that cannot
355 specify different userid than theirs
355 specify different userid than theirs
356
356
357 :param userid: user to get data for
357 :param userid: user to get data for
358 :type userid: Optional(str or int)
358 :type userid: Optional(str or int)
359
359
360 OUTPUT::
360 OUTPUT::
361
361
362 id : <id_given_in_input>
362 id : <id_given_in_input>
363 result : None if user does not exist or
363 result : None if user does not exist or
364 {
364 {
365 "user_id" : "<user_id>",
365 "user_id" : "<user_id>",
366 "username" : "<username>",
366 "username" : "<username>",
367 "firstname" : "<firstname>",
367 "firstname" : "<firstname>",
368 "lastname" : "<lastname>",
368 "lastname" : "<lastname>",
369 "email" : "<email>",
369 "email" : "<email>",
370 "emails" : "[<list of all emails including additional ones>]",
370 "emails" : "[<list of all emails including additional ones>]",
371 "active" : "<bool: user active>",
371 "active" : "<bool: user active>",
372 "admin" : "<bool: user is admin>",
372 "admin" : "<bool: user is admin>",
373 "permissions" : {
373 "permissions" : {
374 "global" : ["hg.create.repository",
374 "global" : ["hg.create.repository",
375 "repository.read",
375 "repository.read",
376 "hg.register.manual_activate"],
376 "hg.register.manual_activate"],
377 "repositories" : {"repo1" : "repository.none"},
377 "repositories" : {"repo1" : "repository.none"},
378 "repositories_groups" : {"Group1" : "group.read"},
378 "repositories_groups" : {"Group1" : "group.read"},
379 "user_groups" : { "usrgrp1" : "usergroup.admin" }
379 "user_groups" : { "usrgrp1" : "usergroup.admin" }
380 }
380 }
381 }
381 }
382 error : null
382 error : null
383 """
383 """
384 if not HasPermissionAny('hg.admin')():
384 if not HasPermissionAny('hg.admin')():
385 # make sure normal user does not pass someone else userid,
385 # make sure normal user does not pass someone else userid,
386 # he is not allowed to do that
386 # he is not allowed to do that
387 if userid is not None and userid != request.authuser.user_id:
387 if userid is not None and userid != request.authuser.user_id:
388 raise JSONRPCError(
388 raise JSONRPCError(
389 'userid is not the same as your user'
389 'userid is not the same as your user'
390 )
390 )
391
391
392 if userid is None:
392 if userid is None:
393 userid = request.authuser.user_id
393 userid = request.authuser.user_id
394
394
395 user = get_user_or_error(userid)
395 user = get_user_or_error(userid)
396 data = user.get_api_data()
396 data = user.get_api_data()
397 data['permissions'] = AuthUser(user_id=user.user_id).permissions
397 data['permissions'] = AuthUser(user_id=user.user_id).permissions
398 return data
398 return data
399
399
400 @HasPermissionAnyDecorator('hg.admin')
400 @HasPermissionAnyDecorator('hg.admin')
401 def get_users(self):
401 def get_users(self):
402 """
402 """
403 Lists all existing users. This command can be executed only using api_key
403 Lists all existing users. This command can be executed only using api_key
404 belonging to user with admin rights.
404 belonging to user with admin rights.
405
405
406 OUTPUT::
406 OUTPUT::
407
407
408 id : <id_given_in_input>
408 id : <id_given_in_input>
409 result : [<user_object>, ...]
409 result : [<user_object>, ...]
410 error : null
410 error : null
411 """
411 """
412 return [
412 return [
413 user.get_api_data()
413 user.get_api_data()
414 for user in db.User.query()
414 for user in db.User.query()
415 .order_by(db.User.username)
415 .order_by(db.User.username)
416 .filter_by(is_default_user=False)
416 .filter_by(is_default_user=False)
417 ]
417 ]
418
418
419 @HasPermissionAnyDecorator('hg.admin')
419 @HasPermissionAnyDecorator('hg.admin')
420 def create_user(self, username, email, password='',
420 def create_user(self, username, email, password='',
421 firstname='', lastname='',
421 firstname='', lastname='',
422 active=True, admin=False,
422 active=True, admin=False,
423 extern_type=db.User.DEFAULT_AUTH_TYPE,
423 extern_type=db.User.DEFAULT_AUTH_TYPE,
424 extern_name=''):
424 extern_name=''):
425 """
425 """
426 Creates new user. Returns new user object. This command can
426 Creates new user. Returns new user object. This command can
427 be executed only using api_key belonging to user with admin rights.
427 be executed only using api_key belonging to user with admin rights.
428
428
429 :param username: new username
429 :param username: new username
430 :type username: str or int
430 :type username: str or int
431 :param email: email
431 :param email: email
432 :type email: str
432 :type email: str
433 :param password: password
433 :param password: password
434 :type password: Optional(str)
434 :type password: Optional(str)
435 :param firstname: firstname
435 :param firstname: firstname
436 :type firstname: str
436 :type firstname: str
437 :param lastname: lastname
437 :param lastname: lastname
438 :type lastname: str
438 :type lastname: str
439 :param active: active
439 :param active: active
440 :type active: Optional(bool)
440 :type active: Optional(bool)
441 :param admin: admin
441 :param admin: admin
442 :type admin: Optional(bool)
442 :type admin: Optional(bool)
443 :param extern_name: name of extern
443 :param extern_name: name of extern
444 :type extern_name: Optional(str)
444 :type extern_name: Optional(str)
445 :param extern_type: extern_type
445 :param extern_type: extern_type
446 :type extern_type: Optional(str)
446 :type extern_type: Optional(str)
447
447
448 OUTPUT::
448 OUTPUT::
449
449
450 id : <id_given_in_input>
450 id : <id_given_in_input>
451 result : {
451 result : {
452 "msg" : "created new user `<username>`",
452 "msg" : "created new user `<username>`",
453 "user" : <user_obj>
453 "user" : <user_obj>
454 }
454 }
455 error : null
455 error : null
456
456
457 ERROR OUTPUT::
457 ERROR OUTPUT::
458
458
459 id : <id_given_in_input>
459 id : <id_given_in_input>
460 result : null
460 result : null
461 error : {
461 error : {
462 "user `<username>` already exist"
462 "user `<username>` already exist"
463 or
463 or
464 "email `<email>` already exist"
464 "email `<email>` already exist"
465 or
465 or
466 "failed to create user `<username>`"
466 "failed to create user `<username>`"
467 }
467 }
468 """
468 """
469 if db.User.get_by_username(username):
469 if db.User.get_by_username(username):
470 raise JSONRPCError("user `%s` already exist" % (username,))
470 raise JSONRPCError("user `%s` already exist" % (username,))
471
471
472 if db.User.get_by_email(email):
472 if db.User.get_by_email(email):
473 raise JSONRPCError("email `%s` already exist" % (email,))
473 raise JSONRPCError("email `%s` already exist" % (email,))
474
474
475 try:
475 try:
476 user = UserModel().create_or_update(
476 user = UserModel().create_or_update(
477 username=username,
477 username=username,
478 password=password,
478 password=password,
479 email=email,
479 email=email,
480 firstname=firstname,
480 firstname=firstname,
481 lastname=lastname,
481 lastname=lastname,
482 active=active,
482 active=active,
483 admin=admin,
483 admin=admin,
484 extern_type=extern_type,
484 extern_type=extern_type,
485 extern_name=extern_name
485 extern_name=extern_name
486 )
486 )
487 meta.Session().commit()
487 meta.Session().commit()
488 return dict(
488 return dict(
489 msg='created new user `%s`' % username,
489 msg='created new user `%s`' % username,
490 user=user.get_api_data()
490 user=user.get_api_data()
491 )
491 )
492 except Exception:
492 except Exception:
493 log.error(traceback.format_exc())
493 log.error(traceback.format_exc())
494 raise JSONRPCError('failed to create user `%s`' % (username,))
494 raise JSONRPCError('failed to create user `%s`' % (username,))
495
495
496 @HasPermissionAnyDecorator('hg.admin')
496 @HasPermissionAnyDecorator('hg.admin')
497 def update_user(self, userid, username=None,
497 def update_user(self, userid, username=None,
498 email=None, password=None,
498 email=None, password=None,
499 firstname=None, lastname=None,
499 firstname=None, lastname=None,
500 active=None, admin=None,
500 active=None, admin=None,
501 extern_type=None, extern_name=None):
501 extern_type=None, extern_name=None):
502 """
502 """
503 updates given user if such user exists. This command can
503 updates given user if such user exists. This command can
504 be executed only using api_key belonging to user with admin rights.
504 be executed only using api_key belonging to user with admin rights.
505
505
506 :param userid: userid to update
506 :param userid: userid to update
507 :type userid: str or int
507 :type userid: str or int
508 :param username: new username
508 :param username: new username
509 :type username: Optional(str or int)
509 :type username: Optional(str or int)
510 :param email: email
510 :param email: email
511 :type email: Optional(str)
511 :type email: Optional(str)
512 :param password: password
512 :param password: password
513 :type password: Optional(str)
513 :type password: Optional(str)
514 :param firstname: firstname
514 :param firstname: firstname
515 :type firstname: Optional(str)
515 :type firstname: Optional(str)
516 :param lastname: lastname
516 :param lastname: lastname
517 :type lastname: Optional(str)
517 :type lastname: Optional(str)
518 :param active: active
518 :param active: active
519 :type active: Optional(bool)
519 :type active: Optional(bool)
520 :param admin: admin
520 :param admin: admin
521 :type admin: Optional(bool)
521 :type admin: Optional(bool)
522 :param extern_name:
522 :param extern_name:
523 :type extern_name: Optional(str)
523 :type extern_name: Optional(str)
524 :param extern_type:
524 :param extern_type:
525 :type extern_type: Optional(str)
525 :type extern_type: Optional(str)
526
526
527 OUTPUT::
527 OUTPUT::
528
528
529 id : <id_given_in_input>
529 id : <id_given_in_input>
530 result : {
530 result : {
531 "msg" : "updated user ID:<userid> <username>",
531 "msg" : "updated user ID:<userid> <username>",
532 "user" : <user_object>
532 "user" : <user_object>
533 }
533 }
534 error : null
534 error : null
535
535
536 ERROR OUTPUT::
536 ERROR OUTPUT::
537
537
538 id : <id_given_in_input>
538 id : <id_given_in_input>
539 result : null
539 result : null
540 error : {
540 error : {
541 "failed to update user `<username>`"
541 "failed to update user `<username>`"
542 }
542 }
543 """
543 """
544 user = get_user_or_error(userid)
544 user = get_user_or_error(userid)
545
545
546 # only non optional arguments will be stored in updates
546 # only non optional arguments will be stored in updates
547 updates = {}
547 updates = {}
548
548
549 try:
549 try:
550
550
551 store_update(updates, username, 'username')
551 store_update(updates, username, 'username')
552 store_update(updates, password, 'password')
552 store_update(updates, password, 'password')
553 store_update(updates, email, 'email')
553 store_update(updates, email, 'email')
554 store_update(updates, firstname, 'name')
554 store_update(updates, firstname, 'name')
555 store_update(updates, lastname, 'lastname')
555 store_update(updates, lastname, 'lastname')
556 store_update(updates, active, 'active')
556 store_update(updates, active, 'active')
557 store_update(updates, admin, 'admin')
557 store_update(updates, admin, 'admin')
558 store_update(updates, extern_name, 'extern_name')
558 store_update(updates, extern_name, 'extern_name')
559 store_update(updates, extern_type, 'extern_type')
559 store_update(updates, extern_type, 'extern_type')
560
560
561 user = UserModel().update_user(user, **updates)
561 user = UserModel().update_user(user, **updates)
562 meta.Session().commit()
562 meta.Session().commit()
563 return dict(
563 return dict(
564 msg='updated user ID:%s %s' % (user.user_id, user.username),
564 msg='updated user ID:%s %s' % (user.user_id, user.username),
565 user=user.get_api_data()
565 user=user.get_api_data()
566 )
566 )
567 except DefaultUserException:
567 except DefaultUserException:
568 log.error(traceback.format_exc())
568 log.error(traceback.format_exc())
569 raise JSONRPCError('editing default user is forbidden')
569 raise JSONRPCError('editing default user is forbidden')
570 except Exception:
570 except Exception:
571 log.error(traceback.format_exc())
571 log.error(traceback.format_exc())
572 raise JSONRPCError('failed to update user `%s`' % (userid,))
572 raise JSONRPCError('failed to update user `%s`' % (userid,))
573
573
574 @HasPermissionAnyDecorator('hg.admin')
574 @HasPermissionAnyDecorator('hg.admin')
575 def delete_user(self, userid):
575 def delete_user(self, userid):
576 """
576 """
577 deletes given user if such user exists. This command can
577 deletes given user if such user exists. This command can
578 be executed only using api_key belonging to user with admin rights.
578 be executed only using api_key belonging to user with admin rights.
579
579
580 :param userid: user to delete
580 :param userid: user to delete
581 :type userid: str or int
581 :type userid: str or int
582
582
583 OUTPUT::
583 OUTPUT::
584
584
585 id : <id_given_in_input>
585 id : <id_given_in_input>
586 result : {
586 result : {
587 "msg" : "deleted user ID:<userid> <username>",
587 "msg" : "deleted user ID:<userid> <username>",
588 "user" : null
588 "user" : null
589 }
589 }
590 error : null
590 error : null
591
591
592 ERROR OUTPUT::
592 ERROR OUTPUT::
593
593
594 id : <id_given_in_input>
594 id : <id_given_in_input>
595 result : null
595 result : null
596 error : {
596 error : {
597 "failed to delete user ID:<userid> <username>"
597 "failed to delete user ID:<userid> <username>"
598 }
598 }
599 """
599 """
600 user = get_user_or_error(userid)
600 user = get_user_or_error(userid)
601
601
602 try:
602 try:
603 UserModel().delete(userid)
603 UserModel().delete(userid)
604 meta.Session().commit()
604 meta.Session().commit()
605 return dict(
605 return dict(
606 msg='deleted user ID:%s %s' % (user.user_id, user.username),
606 msg='deleted user ID:%s %s' % (user.user_id, user.username),
607 user=None
607 user=None
608 )
608 )
609 except Exception:
609 except Exception:
610
610
611 log.error(traceback.format_exc())
611 log.error(traceback.format_exc())
612 raise JSONRPCError('failed to delete user ID:%s %s'
612 raise JSONRPCError('failed to delete user ID:%s %s'
613 % (user.user_id, user.username))
613 % (user.user_id, user.username))
614
614
615 # permission check inside
615 # permission check inside
616 def get_user_group(self, usergroupid):
616 def get_user_group(self, usergroupid):
617 """
617 """
618 Gets an existing user group. This command can be executed only using api_key
618 Gets an existing user group. This command can be executed only using api_key
619 belonging to user with admin rights or user who has at least
619 belonging to user with admin rights or user who has at least
620 read access to user group.
620 read access to user group.
621
621
622 :param usergroupid: id of user_group to edit
622 :param usergroupid: id of user_group to edit
623 :type usergroupid: str or int
623 :type usergroupid: str or int
624
624
625 OUTPUT::
625 OUTPUT::
626
626
627 id : <id_given_in_input>
627 id : <id_given_in_input>
628 result : None if group not exist
628 result : None if group not exist
629 {
629 {
630 "users_group_id" : "<id>",
630 "users_group_id" : "<id>",
631 "group_name" : "<groupname>",
631 "group_name" : "<groupname>",
632 "group_description" : "<description>",
632 "group_description" : "<description>",
633 "active" : "<bool>",
633 "active" : "<bool>",
634 "owner" : "<username>",
634 "owner" : "<username>",
635 "members" : [<user_obj>,...]
635 "members" : [<user_obj>,...]
636 }
636 }
637 error : null
637 error : null
638 """
638 """
639 user_group = get_user_group_or_error(usergroupid)
639 user_group = get_user_group_or_error(usergroupid)
640 if not HasPermissionAny('hg.admin')():
640 if not HasPermissionAny('hg.admin')():
641 if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
641 if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
642 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
642 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
643
643
644 data = user_group.get_api_data()
644 data = user_group.get_api_data()
645 return data
645 return data
646
646
647 # permission check inside
647 # permission check inside
648 def get_user_groups(self):
648 def get_user_groups(self):
649 """
649 """
650 Lists all existing user groups. This command can be executed only using
650 Lists all existing user groups. This command can be executed only using
651 api_key belonging to user with admin rights or user who has at least
651 api_key belonging to user with admin rights or user who has at least
652 read access to user group.
652 read access to user group.
653
653
654 OUTPUT::
654 OUTPUT::
655
655
656 id : <id_given_in_input>
656 id : <id_given_in_input>
657 result : [<user_group_obj>,...]
657 result : [<user_group_obj>,...]
658 error : null
658 error : null
659 """
659 """
660 return [
660 return [
661 user_group.get_api_data()
661 user_group.get_api_data()
662 for user_group in UserGroupList(db.UserGroup.query().all(), perm_level='read')
662 for user_group in UserGroupList(db.UserGroup.query().all(), perm_level='read')
663 ]
663 ]
664
664
665 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
665 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
666 def create_user_group(self, group_name, description='',
666 def create_user_group(self, group_name, description='',
667 owner=None, active=True):
667 owner=None, active=True):
668 """
668 """
669 Creates new user group. This command can be executed only using api_key
669 Creates new user group. This command can be executed only using api_key
670 belonging to user with admin rights or an user who has create user group
670 belonging to user with admin rights or an user who has create user group
671 permission
671 permission
672
672
673 :param group_name: name of new user group
673 :param group_name: name of new user group
674 :type group_name: str
674 :type group_name: str
675 :param description: group description
675 :param description: group description
676 :type description: Optional(str)
676 :type description: Optional(str)
677 :param owner: owner of group. If not passed apiuser is the owner
677 :param owner: owner of group. If not passed apiuser is the owner
678 :type owner: Optional(str or int)
678 :type owner: Optional(str or int)
679 :param active: group is active
679 :param active: group is active
680 :type active: Optional(bool)
680 :type active: Optional(bool)
681
681
682 OUTPUT::
682 OUTPUT::
683
683
684 id : <id_given_in_input>
684 id : <id_given_in_input>
685 result : {
685 result : {
686 "msg" : "created new user group `<groupname>`",
686 "msg" : "created new user group `<groupname>`",
687 "user_group" : <user_group_object>
687 "user_group" : <user_group_object>
688 }
688 }
689 error : null
689 error : null
690
690
691 ERROR OUTPUT::
691 ERROR OUTPUT::
692
692
693 id : <id_given_in_input>
693 id : <id_given_in_input>
694 result : null
694 result : null
695 error : {
695 error : {
696 "user group `<group name>` already exist"
696 "user group `<group name>` already exist"
697 or
697 or
698 "failed to create group `<group name>`"
698 "failed to create group `<group name>`"
699 }
699 }
700 """
700 """
701 if UserGroupModel().get_by_name(group_name):
701 if UserGroupModel().get_by_name(group_name):
702 raise JSONRPCError("user group `%s` already exist" % (group_name,))
702 raise JSONRPCError("user group `%s` already exist" % (group_name,))
703
703
704 try:
704 try:
705 if owner is None:
705 if owner is None:
706 owner = request.authuser.user_id
706 owner = request.authuser.user_id
707
707
708 owner = get_user_or_error(owner)
708 owner = get_user_or_error(owner)
709 ug = UserGroupModel().create(name=group_name, description=description,
709 ug = UserGroupModel().create(name=group_name, description=description,
710 owner=owner, active=active)
710 owner=owner, active=active)
711 meta.Session().commit()
711 meta.Session().commit()
712 return dict(
712 return dict(
713 msg='created new user group `%s`' % group_name,
713 msg='created new user group `%s`' % group_name,
714 user_group=ug.get_api_data()
714 user_group=ug.get_api_data()
715 )
715 )
716 except Exception:
716 except Exception:
717 log.error(traceback.format_exc())
717 log.error(traceback.format_exc())
718 raise JSONRPCError('failed to create group `%s`' % (group_name,))
718 raise JSONRPCError('failed to create group `%s`' % (group_name,))
719
719
720 # permission check inside
720 # permission check inside
721 def update_user_group(self, usergroupid, group_name=None,
721 def update_user_group(self, usergroupid, group_name=None,
722 description=None, owner=None,
722 description=None, owner=None,
723 active=None):
723 active=None):
724 """
724 """
725 Updates given usergroup. This command can be executed only using api_key
725 Updates given usergroup. This command can be executed only using api_key
726 belonging to user with admin rights or an admin of given user group
726 belonging to user with admin rights or an admin of given user group
727
727
728 :param usergroupid: id of user group to update
728 :param usergroupid: id of user group to update
729 :type usergroupid: str or int
729 :type usergroupid: str or int
730 :param group_name: name of new user group
730 :param group_name: name of new user group
731 :type group_name: str
731 :type group_name: str
732 :param description: group description
732 :param description: group description
733 :type description: str
733 :type description: str
734 :param owner: owner of group.
734 :param owner: owner of group.
735 :type owner: Optional(str or int)
735 :type owner: Optional(str or int)
736 :param active: group is active
736 :param active: group is active
737 :type active: Optional(bool)
737 :type active: Optional(bool)
738
738
739 OUTPUT::
739 OUTPUT::
740
740
741 id : <id_given_in_input>
741 id : <id_given_in_input>
742 result : {
742 result : {
743 "msg" : 'updated user group ID:<user group id> <user group name>',
743 "msg" : 'updated user group ID:<user group id> <user group name>',
744 "user_group" : <user_group_object>
744 "user_group" : <user_group_object>
745 }
745 }
746 error : null
746 error : null
747
747
748 ERROR OUTPUT::
748 ERROR OUTPUT::
749
749
750 id : <id_given_in_input>
750 id : <id_given_in_input>
751 result : null
751 result : null
752 error : {
752 error : {
753 "failed to update user group `<user group name>`"
753 "failed to update user group `<user group name>`"
754 }
754 }
755 """
755 """
756 user_group = get_user_group_or_error(usergroupid)
756 user_group = get_user_group_or_error(usergroupid)
757 if not HasPermissionAny('hg.admin')():
757 if not HasPermissionAny('hg.admin')():
758 if not HasUserGroupPermissionLevel('admin')(user_group.users_group_name):
758 if not HasUserGroupPermissionLevel('admin')(user_group.users_group_name):
759 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
759 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
760
760
761 if owner is not None:
761 if owner is not None:
762 owner = get_user_or_error(owner)
762 owner = get_user_or_error(owner)
763
763
764 updates = {}
764 updates = {}
765 store_update(updates, group_name, 'users_group_name')
765 store_update(updates, group_name, 'users_group_name')
766 store_update(updates, description, 'user_group_description')
766 store_update(updates, description, 'user_group_description')
767 store_update(updates, owner, 'owner')
767 store_update(updates, owner, 'owner')
768 store_update(updates, active, 'users_group_active')
768 store_update(updates, active, 'users_group_active')
769 try:
769 try:
770 UserGroupModel().update(user_group, updates)
770 UserGroupModel().update(user_group, updates)
771 meta.Session().commit()
771 meta.Session().commit()
772 return dict(
772 return dict(
773 msg='updated user group ID:%s %s' % (user_group.users_group_id,
773 msg='updated user group ID:%s %s' % (user_group.users_group_id,
774 user_group.users_group_name),
774 user_group.users_group_name),
775 user_group=user_group.get_api_data()
775 user_group=user_group.get_api_data()
776 )
776 )
777 except Exception:
777 except Exception:
778 log.error(traceback.format_exc())
778 log.error(traceback.format_exc())
779 raise JSONRPCError('failed to update user group `%s`' % (usergroupid,))
779 raise JSONRPCError('failed to update user group `%s`' % (usergroupid,))
780
780
781 # permission check inside
781 # permission check inside
782 def delete_user_group(self, usergroupid):
782 def delete_user_group(self, usergroupid):
783 """
783 """
784 Delete given user group by user group id or name.
784 Delete given user group by user group id or name.
785 This command can be executed only using api_key
785 This command can be executed only using api_key
786 belonging to user with admin rights or an admin of given user group
786 belonging to user with admin rights or an admin of given user group
787
787
788 :param usergroupid:
788 :param usergroupid:
789 :type usergroupid: str or int
789 :type usergroupid: str or int
790
790
791 OUTPUT::
791 OUTPUT::
792
792
793 id : <id_given_in_input>
793 id : <id_given_in_input>
794 result : {
794 result : {
795 "msg" : "deleted user group ID:<user_group_id> <user_group_name>"
795 "msg" : "deleted user group ID:<user_group_id> <user_group_name>"
796 }
796 }
797 error : null
797 error : null
798
798
799 ERROR OUTPUT::
799 ERROR OUTPUT::
800
800
801 id : <id_given_in_input>
801 id : <id_given_in_input>
802 result : null
802 result : null
803 error : {
803 error : {
804 "failed to delete user group ID:<user_group_id> <user_group_name>"
804 "failed to delete user group ID:<user_group_id> <user_group_name>"
805 or
805 or
806 "RepoGroup assigned to <repo_groups_list>"
806 "RepoGroup assigned to <repo_groups_list>"
807 }
807 }
808 """
808 """
809 user_group = get_user_group_or_error(usergroupid)
809 user_group = get_user_group_or_error(usergroupid)
810 if not HasPermissionAny('hg.admin')():
810 if not HasPermissionAny('hg.admin')():
811 if not HasUserGroupPermissionLevel('admin')(user_group.users_group_name):
811 if not HasUserGroupPermissionLevel('admin')(user_group.users_group_name):
812 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
812 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
813
813
814 try:
814 try:
815 UserGroupModel().delete(user_group)
815 UserGroupModel().delete(user_group)
816 meta.Session().commit()
816 meta.Session().commit()
817 return dict(
817 return dict(
818 msg='deleted user group ID:%s %s' %
818 msg='deleted user group ID:%s %s' %
819 (user_group.users_group_id, user_group.users_group_name),
819 (user_group.users_group_id, user_group.users_group_name),
820 user_group=None
820 user_group=None
821 )
821 )
822 except UserGroupsAssignedException as e:
822 except UserGroupsAssignedException as e:
823 log.error(traceback.format_exc())
823 log.error(traceback.format_exc())
824 raise JSONRPCError(str(e))
824 raise JSONRPCError(str(e))
825 except Exception:
825 except Exception:
826 log.error(traceback.format_exc())
826 log.error(traceback.format_exc())
827 raise JSONRPCError('failed to delete user group ID:%s %s' %
827 raise JSONRPCError('failed to delete user group ID:%s %s' %
828 (user_group.users_group_id,
828 (user_group.users_group_id,
829 user_group.users_group_name)
829 user_group.users_group_name)
830 )
830 )
831
831
832 # permission check inside
832 # permission check inside
833 def add_user_to_user_group(self, usergroupid, userid):
833 def add_user_to_user_group(self, usergroupid, userid):
834 """
834 """
835 Adds a user to a user group. If user exists in that group success will be
835 Adds a user to a user group. If user exists in that group success will be
836 `false`. This command can be executed only using api_key
836 `false`. This command can be executed only using api_key
837 belonging to user with admin rights or an admin of a given user group
837 belonging to user with admin rights or an admin of a given user group
838
838
839 :param usergroupid:
839 :param usergroupid:
840 :type usergroupid: str or int
840 :type usergroupid: str or int
841 :param userid:
841 :param userid:
842 :type userid: str or int
842 :type userid: str or int
843
843
844 OUTPUT::
844 OUTPUT::
845
845
846 id : <id_given_in_input>
846 id : <id_given_in_input>
847 result : {
847 result : {
848 "success" : True|False # depends on if member is in group
848 "success" : True|False # depends on if member is in group
849 "msg" : "added member `<username>` to a user group `<groupname>` |
849 "msg" : "added member `<username>` to a user group `<groupname>` |
850 User is already in that group"
850 User is already in that group"
851 }
851 }
852 error : null
852 error : null
853
853
854 ERROR OUTPUT::
854 ERROR OUTPUT::
855
855
856 id : <id_given_in_input>
856 id : <id_given_in_input>
857 result : null
857 result : null
858 error : {
858 error : {
859 "failed to add member to user group `<user_group_name>`"
859 "failed to add member to user group `<user_group_name>`"
860 }
860 }
861 """
861 """
862 user = get_user_or_error(userid)
862 user = get_user_or_error(userid)
863 user_group = get_user_group_or_error(usergroupid)
863 user_group = get_user_group_or_error(usergroupid)
864 if not HasPermissionAny('hg.admin')():
864 if not HasPermissionAny('hg.admin')():
865 if not HasUserGroupPermissionLevel('admin')(user_group.users_group_name):
865 if not HasUserGroupPermissionLevel('admin')(user_group.users_group_name):
866 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
866 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
867
867
868 try:
868 try:
869 ugm = UserGroupModel().add_user_to_group(user_group, user)
869 ugm = UserGroupModel().add_user_to_group(user_group, user)
870 success = True if ugm is not True else False
870 success = True if ugm is not True else False
871 msg = 'added member `%s` to user group `%s`' % (
871 msg = 'added member `%s` to user group `%s`' % (
872 user.username, user_group.users_group_name
872 user.username, user_group.users_group_name
873 )
873 )
874 msg = msg if success else 'User is already in that group'
874 msg = msg if success else 'User is already in that group'
875 meta.Session().commit()
875 meta.Session().commit()
876
876
877 return dict(
877 return dict(
878 success=success,
878 success=success,
879 msg=msg
879 msg=msg
880 )
880 )
881 except Exception:
881 except Exception:
882 log.error(traceback.format_exc())
882 log.error(traceback.format_exc())
883 raise JSONRPCError(
883 raise JSONRPCError(
884 'failed to add member to user group `%s`' % (
884 'failed to add member to user group `%s`' % (
885 user_group.users_group_name,
885 user_group.users_group_name,
886 )
886 )
887 )
887 )
888
888
889 # permission check inside
889 # permission check inside
890 def remove_user_from_user_group(self, usergroupid, userid):
890 def remove_user_from_user_group(self, usergroupid, userid):
891 """
891 """
892 Removes a user from a user group. If user is not in given group success will
892 Removes a user from a user group. If user is not in given group success will
893 be `false`. This command can be executed only
893 be `false`. This command can be executed only
894 using api_key belonging to user with admin rights or an admin of given user group
894 using api_key belonging to user with admin rights or an admin of given user group
895
895
896 :param usergroupid:
896 :param usergroupid:
897 :param userid:
897 :param userid:
898
898
899 OUTPUT::
899 OUTPUT::
900
900
901 id : <id_given_in_input>
901 id : <id_given_in_input>
902 result : {
902 result : {
903 "success" : True|False, # depends on if member is in group
903 "success" : True|False, # depends on if member is in group
904 "msg" : "removed member <username> from user group <groupname> |
904 "msg" : "removed member <username> from user group <groupname> |
905 User wasn't in group"
905 User wasn't in group"
906 }
906 }
907 error : null
907 error : null
908 """
908 """
909 user = get_user_or_error(userid)
909 user = get_user_or_error(userid)
910 user_group = get_user_group_or_error(usergroupid)
910 user_group = get_user_group_or_error(usergroupid)
911 if not HasPermissionAny('hg.admin')():
911 if not HasPermissionAny('hg.admin')():
912 if not HasUserGroupPermissionLevel('admin')(user_group.users_group_name):
912 if not HasUserGroupPermissionLevel('admin')(user_group.users_group_name):
913 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
913 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
914
914
915 try:
915 try:
916 success = UserGroupModel().remove_user_from_group(user_group, user)
916 success = UserGroupModel().remove_user_from_group(user_group, user)
917 msg = 'removed member `%s` from user group `%s`' % (
917 msg = 'removed member `%s` from user group `%s`' % (
918 user.username, user_group.users_group_name
918 user.username, user_group.users_group_name
919 )
919 )
920 msg = msg if success else "User wasn't in group"
920 msg = msg if success else "User wasn't in group"
921 meta.Session().commit()
921 meta.Session().commit()
922 return dict(success=success, msg=msg)
922 return dict(success=success, msg=msg)
923 except Exception:
923 except Exception:
924 log.error(traceback.format_exc())
924 log.error(traceback.format_exc())
925 raise JSONRPCError(
925 raise JSONRPCError(
926 'failed to remove member from user group `%s`' % (
926 'failed to remove member from user group `%s`' % (
927 user_group.users_group_name,
927 user_group.users_group_name,
928 )
928 )
929 )
929 )
930
930
931 # permission check inside
931 # permission check inside
932 def get_repo(self, repoid,
932 def get_repo(self, repoid,
933 with_revision_names=False,
933 with_revision_names=False,
934 with_pullrequests=False):
934 with_pullrequests=False):
935 """
935 """
936 Gets an existing repository by it's name or repository_id. Members will return
936 Gets an existing repository by it's name or repository_id. Members will return
937 either users_group or user associated to that repository. This command can be
937 either users_group or user associated to that repository. This command can be
938 executed only using api_key belonging to user with admin
938 executed only using api_key belonging to user with admin
939 rights or regular user that have at least read access to repository.
939 rights or regular user that have at least read access to repository.
940
940
941 :param repoid: repository name or repository id
941 :param repoid: repository name or repository id
942 :type repoid: str or int
942 :type repoid: str or int
943
943
944 OUTPUT::
944 OUTPUT::
945
945
946 id : <id_given_in_input>
946 id : <id_given_in_input>
947 result : {
947 result : {
948 "repo_id" : "<repo_id>",
948 "repo_id" : "<repo_id>",
949 "repo_name" : "<reponame>",
949 "repo_name" : "<reponame>",
950 "repo_type" : "<repo_type>",
950 "repo_type" : "<repo_type>",
951 "clone_uri" : "<clone_uri>",
951 "clone_uri" : "<clone_uri>",
952 "enable_downloads" : "<bool>",
952 "enable_downloads" : "<bool>",
953 "enable_statistics": "<bool>",
953 "enable_statistics": "<bool>",
954 "private" : "<bool>",
954 "private" : "<bool>",
955 "created_on" : "<date_time_created>",
955 "created_on" : "<date_time_created>",
956 "description" : "<description>",
956 "description" : "<description>",
957 "landing_rev" : "<landing_rev>",
957 "landing_rev" : "<landing_rev>",
958 "last_changeset" : {
958 "last_changeset" : {
959 "author" : "<full_author>",
959 "author" : "<full_author>",
960 "date" : "<date_time_of_commit>",
960 "date" : "<date_time_of_commit>",
961 "message" : "<commit_message>",
961 "message" : "<commit_message>",
962 "raw_id" : "<raw_id>",
962 "raw_id" : "<raw_id>",
963 "revision": "<numeric_revision>",
963 "revision": "<numeric_revision>",
964 "short_id": "<short_id>"
964 "short_id": "<short_id>"
965 },
965 },
966 "owner" : "<repo_owner>",
966 "owner" : "<repo_owner>",
967 "fork_of" : "<name_of_fork_parent>",
967 "fork_of" : "<name_of_fork_parent>",
968 "members" : [
968 "members" : [
969 {
969 {
970 "name" : "<username>",
970 "name" : "<username>",
971 "type" : "user",
971 "type" : "user",
972 "permission" : "repository.(read|write|admin)"
972 "permission" : "repository.(read|write|admin)"
973 },
973 },
974 …
974 …
975 {
975 {
976 "name" : "<usergroup name>",
976 "name" : "<usergroup name>",
977 "type" : "user_group",
977 "type" : "user_group",
978 "permission" : "usergroup.(read|write|admin)"
978 "permission" : "usergroup.(read|write|admin)"
979 },
979 },
980 …
980 …
981 ],
981 ],
982 "followers" : [<user_obj>, ...],
982 "followers" : [<user_obj>, ...],
983 <if with_revision_names == True>
983 <if with_revision_names == True>
984 "tags" : {
984 "tags" : {
985 "<tagname>" : "<raw_id>",
985 "<tagname>" : "<raw_id>",
986 ...
986 ...
987 },
987 },
988 "branches" : {
988 "branches" : {
989 "<branchname>" : "<raw_id>",
989 "<branchname>" : "<raw_id>",
990 ...
990 ...
991 },
991 },
992 "bookmarks" : {
992 "bookmarks" : {
993 "<bookmarkname>" : "<raw_id>",
993 "<bookmarkname>" : "<raw_id>",
994 ...
994 ...
995 }
995 }
996 }
996 }
997 error : null
997 error : null
998 """
998 """
999 repo = get_repo_or_error(repoid)
999 repo = get_repo_or_error(repoid)
1000
1000
1001 if not HasPermissionAny('hg.admin')():
1001 if not HasPermissionAny('hg.admin')():
1002 if not HasRepoPermissionLevel('read')(repo.repo_name):
1002 if not HasRepoPermissionLevel('read')(repo.repo_name):
1003 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1003 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1004
1004
1005 members = []
1005 members = []
1006 for user in repo.repo_to_perm:
1006 for user in repo.repo_to_perm:
1007 perm = user.permission.permission_name
1007 perm = user.permission.permission_name
1008 user = user.user
1008 user = user.user
1009 user_data = {
1009 user_data = {
1010 'name': user.username,
1010 'name': user.username,
1011 'type': "user",
1011 'type': "user",
1012 'permission': perm
1012 'permission': perm
1013 }
1013 }
1014 members.append(user_data)
1014 members.append(user_data)
1015
1015
1016 for user_group in repo.users_group_to_perm:
1016 for user_group in repo.users_group_to_perm:
1017 perm = user_group.permission.permission_name
1017 perm = user_group.permission.permission_name
1018 user_group = user_group.users_group
1018 user_group = user_group.users_group
1019 user_group_data = {
1019 user_group_data = {
1020 'name': user_group.users_group_name,
1020 'name': user_group.users_group_name,
1021 'type': "user_group",
1021 'type': "user_group",
1022 'permission': perm
1022 'permission': perm
1023 }
1023 }
1024 members.append(user_group_data)
1024 members.append(user_group_data)
1025
1025
1026 followers = [
1026 followers = [
1027 uf.user.get_api_data()
1027 uf.user.get_api_data()
1028 for uf in repo.followers
1028 for uf in repo.followers
1029 ]
1029 ]
1030
1030
1031 data = repo.get_api_data(with_revision_names=with_revision_names,
1031 data = repo.get_api_data(with_revision_names=with_revision_names,
1032 with_pullrequests=with_pullrequests)
1032 with_pullrequests=with_pullrequests)
1033 data['members'] = members
1033 data['members'] = members
1034 data['followers'] = followers
1034 data['followers'] = followers
1035 return data
1035 return data
1036
1036
1037 # permission check inside
1037 # permission check inside
1038 def get_repos(self):
1038 def get_repos(self):
1039 """
1039 """
1040 Lists all existing repositories. This command can be executed only using
1040 Lists all existing repositories. This command can be executed only using
1041 api_key belonging to user with admin rights or regular user that have
1041 api_key belonging to user with admin rights or regular user that have
1042 admin, write or read access to repository.
1042 admin, write or read access to repository.
1043
1043
1044 OUTPUT::
1044 OUTPUT::
1045
1045
1046 id : <id_given_in_input>
1046 id : <id_given_in_input>
1047 result : [
1047 result : [
1048 {
1048 {
1049 "repo_id" : "<repo_id>",
1049 "repo_id" : "<repo_id>",
1050 "repo_name" : "<reponame>",
1050 "repo_name" : "<reponame>",
1051 "repo_type" : "<repo_type>",
1051 "repo_type" : "<repo_type>",
1052 "clone_uri" : "<clone_uri>",
1052 "clone_uri" : "<clone_uri>",
1053 "private" : "<bool>",
1053 "private" : "<bool>",
1054 "created_on" : "<datetimecreated>",
1054 "created_on" : "<datetimecreated>",
1055 "description" : "<description>",
1055 "description" : "<description>",
1056 "landing_rev" : "<landing_rev>",
1056 "landing_rev" : "<landing_rev>",
1057 "owner" : "<repo_owner>",
1057 "owner" : "<repo_owner>",
1058 "fork_of" : "<name_of_fork_parent>",
1058 "fork_of" : "<name_of_fork_parent>",
1059 "enable_downloads" : "<bool>",
1059 "enable_downloads" : "<bool>",
1060 "enable_statistics": "<bool>"
1060 "enable_statistics": "<bool>"
1061 },
1061 },
1062 …
1062 …
1063 ]
1063 ]
1064 error : null
1064 error : null
1065 """
1065 """
1066 if not HasPermissionAny('hg.admin')():
1066 if not HasPermissionAny('hg.admin')():
1067 repos = request.authuser.get_all_user_repos()
1067 repos = request.authuser.get_all_user_repos()
1068 else:
1068 else:
1069 repos = db.Repository.query()
1069 repos = db.Repository.query()
1070
1070
1071 return [
1071 return [
1072 repo.get_api_data()
1072 repo.get_api_data()
1073 for repo in repos
1073 for repo in repos
1074 ]
1074 ]
1075
1075
1076 # permission check inside
1076 # permission check inside
1077 def get_repo_nodes(self, repoid, revision, root_path,
1077 def get_repo_nodes(self, repoid, revision, root_path,
1078 ret_type='all'):
1078 ret_type='all'):
1079 """
1079 """
1080 returns a list of nodes and it's children in a flat list for a given path
1080 returns a list of nodes and it's children in a flat list for a given path
1081 at given revision. It's possible to specify ret_type to show only `files` or
1081 at given revision. It's possible to specify ret_type to show only `files` or
1082 `dirs`. This command can be executed only using api_key belonging to
1082 `dirs`. This command can be executed only using api_key belonging to
1083 user with admin rights or regular user that have at least read access to repository.
1083 user with admin rights or regular user that have at least read access to repository.
1084
1084
1085 :param repoid: repository name or repository id
1085 :param repoid: repository name or repository id
1086 :type repoid: str or int
1086 :type repoid: str or int
1087 :param revision: revision for which listing should be done
1087 :param revision: revision for which listing should be done
1088 :type revision: str
1088 :type revision: str
1089 :param root_path: path from which start displaying
1089 :param root_path: path from which start displaying
1090 :type root_path: str
1090 :type root_path: str
1091 :param ret_type: return type 'all|files|dirs' nodes
1091 :param ret_type: return type 'all|files|dirs' nodes
1092 :type ret_type: Optional(str)
1092 :type ret_type: Optional(str)
1093
1093
1094 OUTPUT::
1094 OUTPUT::
1095
1095
1096 id : <id_given_in_input>
1096 id : <id_given_in_input>
1097 result : [
1097 result : [
1098 {
1098 {
1099 "name" : "<name>",
1099 "name" : "<name>",
1100 "type" : "<type>"
1100 "type" : "<type>"
1101 },
1101 },
1102 …
1102 …
1103 ]
1103 ]
1104 error : null
1104 error : null
1105 """
1105 """
1106 repo = get_repo_or_error(repoid)
1106 repo = get_repo_or_error(repoid)
1107
1107
1108 if not HasPermissionAny('hg.admin')():
1108 if not HasPermissionAny('hg.admin')():
1109 if not HasRepoPermissionLevel('read')(repo.repo_name):
1109 if not HasRepoPermissionLevel('read')(repo.repo_name):
1110 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1110 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1111
1111
1112 _map = {}
1112 _map = {}
1113 try:
1113 try:
1114 _d, _f = ScmModel().get_nodes(repo, revision, root_path,
1114 _d, _f = ScmModel().get_nodes(repo, revision, root_path,
1115 flat=False)
1115 flat=False)
1116 _map = {
1116 _map = {
1117 'all': _d + _f,
1117 'all': _d + _f,
1118 'files': _f,
1118 'files': _f,
1119 'dirs': _d,
1119 'dirs': _d,
1120 }
1120 }
1121 return _map[ret_type]
1121 return _map[ret_type]
1122 except KeyError:
1122 except KeyError:
1123 raise JSONRPCError('ret_type must be one of %s'
1123 raise JSONRPCError('ret_type must be one of %s'
1124 % (','.join(sorted(_map))))
1124 % (','.join(sorted(_map))))
1125 except Exception:
1125 except Exception:
1126 log.error(traceback.format_exc())
1126 log.error(traceback.format_exc())
1127 raise JSONRPCError(
1127 raise JSONRPCError(
1128 'failed to get repo: `%s` nodes' % repo.repo_name
1128 'failed to get repo: `%s` nodes' % repo.repo_name
1129 )
1129 )
1130
1130
1131 # permission check inside
1131 # permission check inside
1132 def create_repo(self, repo_name, owner=None,
1132 def create_repo(self, repo_name, owner=None,
1133 repo_type=None, description='',
1133 repo_type=None, description='',
1134 private=False, clone_uri=None,
1134 private=False, clone_uri=None,
1135 landing_rev='rev:tip',
1135 landing_rev='rev:tip',
1136 enable_statistics=None,
1136 enable_statistics=None,
1137 enable_downloads=None,
1137 enable_downloads=None,
1138 copy_permissions=False):
1138 copy_permissions=False):
1139 """
1139 """
1140 Creates a repository. The repository name contains the full path, but the
1140 Creates a repository. The repository name contains the full path, but the
1141 parent repository group must exist. For example "foo/bar/baz" require the groups
1141 parent repository group must exist. For example "foo/bar/baz" require the groups
1142 "foo" and "bar" (with "foo" as parent), and create "baz" repository with
1142 "foo" and "bar" (with "foo" as parent), and create "baz" repository with
1143 "bar" as group. This command can be executed only using api_key
1143 "bar" as group. This command can be executed only using api_key
1144 belonging to user with admin rights or regular user that have create
1144 belonging to user with admin rights or regular user that have create
1145 repository permission. Regular users cannot specify owner parameter
1145 repository permission. Regular users cannot specify owner parameter
1146
1146
1147 :param repo_name: repository name
1147 :param repo_name: repository name
1148 :type repo_name: str
1148 :type repo_name: str
1149 :param owner: user_id or username
1149 :param owner: user_id or username
1150 :type owner: Optional(str)
1150 :type owner: Optional(str)
1151 :param repo_type: 'hg' or 'git'
1151 :param repo_type: 'hg' or 'git'
1152 :type repo_type: Optional(str)
1152 :type repo_type: Optional(str)
1153 :param description: repository description
1153 :param description: repository description
1154 :type description: Optional(str)
1154 :type description: Optional(str)
1155 :param private:
1155 :param private:
1156 :type private: bool
1156 :type private: bool
1157 :param clone_uri:
1157 :param clone_uri:
1158 :type clone_uri: str
1158 :type clone_uri: str
1159 :param landing_rev: <rev_type>:<rev>
1159 :param landing_rev: <rev_type>:<rev>
1160 :type landing_rev: str
1160 :type landing_rev: str
1161 :param enable_downloads:
1161 :param enable_downloads:
1162 :type enable_downloads: bool
1162 :type enable_downloads: bool
1163 :param enable_statistics:
1163 :param enable_statistics:
1164 :type enable_statistics: bool
1164 :type enable_statistics: bool
1165 :param copy_permissions: Copy permission from group that repository is
1165 :param copy_permissions: Copy permission from group that repository is
1166 being created.
1166 being created.
1167 :type copy_permissions: bool
1167 :type copy_permissions: bool
1168
1168
1169 OUTPUT::
1169 OUTPUT::
1170
1170
1171 id : <id_given_in_input>
1171 id : <id_given_in_input>
1172 result : {
1172 result : {
1173 "msg" : "Created new repository `<reponame>`",
1173 "msg" : "Created new repository `<reponame>`",
1174 "success" : true
1174 "success" : true
1175 }
1175 }
1176 error : null
1176 error : null
1177
1177
1178 ERROR OUTPUT::
1178 ERROR OUTPUT::
1179
1179
1180 id : <id_given_in_input>
1180 id : <id_given_in_input>
1181 result : null
1181 result : null
1182 error : {
1182 error : {
1183 'failed to create repository `<repo_name>`
1183 'failed to create repository `<repo_name>`
1184 }
1184 }
1185 """
1185 """
1186 group_name = None
1186 group_name = None
1187 repo_name_parts = repo_name.split('/')
1187 repo_name_parts = repo_name.split('/')
1188 if len(repo_name_parts) > 1:
1188 if len(repo_name_parts) > 1:
1189 group_name = '/'.join(repo_name_parts[:-1])
1189 group_name = '/'.join(repo_name_parts[:-1])
1190 repo_group = db.RepoGroup.get_by_group_name(group_name)
1190 repo_group = db.RepoGroup.get_by_group_name(group_name)
1191 if repo_group is None:
1191 if repo_group is None:
1192 raise JSONRPCError("repo group `%s` not found" % group_name)
1192 raise JSONRPCError("repo group `%s` not found" % group_name)
1193 if not(HasPermissionAny('hg.admin')() or HasRepoGroupPermissionLevel('write')(group_name)):
1193 if not(HasPermissionAny('hg.admin')() or HasRepoGroupPermissionLevel('write')(group_name)):
1194 raise JSONRPCError("no permission to create repo in %s" % group_name)
1194 raise JSONRPCError("no permission to create repo in %s" % group_name)
1195 else:
1195 else:
1196 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
1196 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
1197 raise JSONRPCError("no permission to create top level repo")
1197 raise JSONRPCError("no permission to create top level repo")
1198
1198
1199 if not HasPermissionAny('hg.admin')():
1199 if not HasPermissionAny('hg.admin')():
1200 if owner is not None:
1200 if owner is not None:
1201 # forbid setting owner for non-admins
1201 # forbid setting owner for non-admins
1202 raise JSONRPCError(
1202 raise JSONRPCError(
1203 'Only Kallithea admin can specify `owner` param'
1203 'Only Kallithea admin can specify `owner` param'
1204 )
1204 )
1205 if owner is None:
1205 if owner is None:
1206 owner = request.authuser.user_id
1206 owner = request.authuser.user_id
1207
1207
1208 owner = get_user_or_error(owner)
1208 owner = get_user_or_error(owner)
1209
1209
1210 if RepoModel().get_by_repo_name(repo_name):
1210 if RepoModel().get_by_repo_name(repo_name):
1211 raise JSONRPCError("repo `%s` already exist" % repo_name)
1211 raise JSONRPCError("repo `%s` already exist" % repo_name)
1212
1212
1213 defs = db.Setting.get_default_repo_settings(strip_prefix=True)
1213 defs = db.Setting.get_default_repo_settings(strip_prefix=True)
1214 if private is None:
1214 if private is None:
1215 private = defs.get('repo_private') or False
1215 private = defs.get('repo_private') or False
1216 if repo_type is None:
1216 if repo_type is None:
1217 repo_type = defs.get('repo_type')
1217 repo_type = defs.get('repo_type')
1218 if enable_statistics is None:
1218 if enable_statistics is None:
1219 enable_statistics = defs.get('repo_enable_statistics')
1219 enable_statistics = defs.get('repo_enable_statistics')
1220 if enable_downloads is None:
1220 if enable_downloads is None:
1221 enable_downloads = defs.get('repo_enable_downloads')
1221 enable_downloads = defs.get('repo_enable_downloads')
1222
1222
1223 try:
1223 try:
1224 data = dict(
1224 data = dict(
1225 repo_name=repo_name_parts[-1],
1225 repo_name=repo_name_parts[-1],
1226 repo_name_full=repo_name,
1226 repo_name_full=repo_name,
1227 repo_type=repo_type,
1227 repo_type=repo_type,
1228 repo_description=description,
1228 repo_description=description,
1229 repo_private=private,
1229 repo_private=private,
1230 clone_uri=clone_uri,
1230 clone_uri=clone_uri,
1231 repo_group=group_name,
1231 repo_group=group_name,
1232 repo_landing_rev=landing_rev,
1232 repo_landing_rev=landing_rev,
1233 enable_statistics=enable_statistics,
1233 repo_enable_statistics=enable_statistics,
1234 enable_downloads=enable_downloads,
1234 repo_enable_downloads=enable_downloads,
1235 repo_copy_permissions=copy_permissions,
1235 repo_copy_permissions=copy_permissions,
1236 )
1236 )
1237
1237
1238 RepoModel().create(form_data=data, cur_user=owner.username)
1238 RepoModel().create(form_data=data, cur_user=owner.username)
1239 # no commit, it's done in RepoModel, or async via celery
1239 # no commit, it's done in RepoModel, or async via celery
1240 return dict(
1240 return dict(
1241 msg="Created new repository `%s`" % (repo_name,),
1241 msg="Created new repository `%s`" % (repo_name,),
1242 success=True, # cannot return the repo data here since fork
1242 success=True, # cannot return the repo data here since fork
1243 # can be done async
1243 # can be done async
1244 )
1244 )
1245 except Exception:
1245 except Exception:
1246 log.error(traceback.format_exc())
1246 log.error(traceback.format_exc())
1247 raise JSONRPCError(
1247 raise JSONRPCError(
1248 'failed to create repository `%s`' % (repo_name,))
1248 'failed to create repository `%s`' % (repo_name,))
1249
1249
1250 # permission check inside
1250 # permission check inside
1251 def update_repo(self, repoid, name=None,
1251 def update_repo(self, repoid, name=None,
1252 owner=None,
1252 owner=None,
1253 group=None,
1253 group=None,
1254 description=None, private=None,
1254 description=None, private=None,
1255 clone_uri=None, landing_rev=None,
1255 clone_uri=None, landing_rev=None,
1256 enable_statistics=None,
1256 enable_statistics=None,
1257 enable_downloads=None):
1257 enable_downloads=None):
1258 """
1258 """
1259 Updates repo
1259 Updates repo
1260
1260
1261 :param repoid: repository name or repository id
1261 :param repoid: repository name or repository id
1262 :type repoid: str or int
1262 :type repoid: str or int
1263 :param name:
1263 :param name:
1264 :param owner:
1264 :param owner:
1265 :param group:
1265 :param group:
1266 :param description:
1266 :param description:
1267 :param private:
1267 :param private:
1268 :param clone_uri:
1268 :param clone_uri:
1269 :param landing_rev:
1269 :param landing_rev:
1270 :param enable_statistics:
1270 :param enable_statistics:
1271 :param enable_downloads:
1271 :param enable_downloads:
1272 """
1272 """
1273 repo = get_repo_or_error(repoid)
1273 repo = get_repo_or_error(repoid)
1274 if not HasPermissionAny('hg.admin')():
1274 if not HasPermissionAny('hg.admin')():
1275 if not HasRepoPermissionLevel('admin')(repo.repo_name):
1275 if not HasRepoPermissionLevel('admin')(repo.repo_name):
1276 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1276 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1277
1277
1278 if (name != repo.repo_name and repo.group_id is None and
1278 if (name != repo.repo_name and repo.group_id is None and
1279 not HasPermissionAny('hg.create.repository')()
1279 not HasPermissionAny('hg.create.repository')()
1280 ):
1280 ):
1281 raise JSONRPCError('no permission to create (or move) top level repositories')
1281 raise JSONRPCError('no permission to create (or move) top level repositories')
1282
1282
1283 if owner is not None:
1283 if owner is not None:
1284 # forbid setting owner for non-admins
1284 # forbid setting owner for non-admins
1285 raise JSONRPCError(
1285 raise JSONRPCError(
1286 'Only Kallithea admin can specify `owner` param'
1286 'Only Kallithea admin can specify `owner` param'
1287 )
1287 )
1288
1288
1289 updates = {}
1289 updates = {}
1290 repo_group = group
1290 repo_group = group
1291 if repo_group is not None:
1291 if repo_group is not None:
1292 repo_group = get_repo_group_or_error(repo_group) # TODO: repos can thus currently not be moved to root
1292 repo_group = get_repo_group_or_error(repo_group) # TODO: repos can thus currently not be moved to root
1293 if repo_group.group_id != repo.group_id:
1293 if repo_group.group_id != repo.group_id:
1294 if not(HasPermissionAny('hg.admin')() or HasRepoGroupPermissionLevel('write')(repo_group.group_name)):
1294 if not(HasPermissionAny('hg.admin')() or HasRepoGroupPermissionLevel('write')(repo_group.group_name)):
1295 raise JSONRPCError("no permission to create (or move) repo in %s" % repo_group.group_name)
1295 raise JSONRPCError("no permission to create (or move) repo in %s" % repo_group.group_name)
1296 repo_group = repo_group.group_id
1296 repo_group = repo_group.group_id
1297 try:
1297 try:
1298 store_update(updates, name, 'repo_name')
1298 store_update(updates, name, 'repo_name')
1299 store_update(updates, repo_group, 'repo_group')
1299 store_update(updates, repo_group, 'repo_group')
1300 store_update(updates, owner, 'owner')
1300 store_update(updates, owner, 'owner')
1301 store_update(updates, description, 'repo_description')
1301 store_update(updates, description, 'repo_description')
1302 store_update(updates, private, 'repo_private')
1302 store_update(updates, private, 'repo_private')
1303 store_update(updates, clone_uri, 'clone_uri')
1303 store_update(updates, clone_uri, 'clone_uri')
1304 store_update(updates, landing_rev, 'repo_landing_rev')
1304 store_update(updates, landing_rev, 'repo_landing_rev')
1305 store_update(updates, enable_statistics, 'repo_enable_statistics')
1305 store_update(updates, enable_statistics, 'repo_enable_statistics')
1306 store_update(updates, enable_downloads, 'repo_enable_downloads')
1306 store_update(updates, enable_downloads, 'repo_enable_downloads')
1307
1307
1308 RepoModel().update(repo, **updates)
1308 RepoModel().update(repo, **updates)
1309 meta.Session().commit()
1309 meta.Session().commit()
1310 return dict(
1310 return dict(
1311 msg='updated repo ID:%s %s' % (repo.repo_id, repo.repo_name),
1311 msg='updated repo ID:%s %s' % (repo.repo_id, repo.repo_name),
1312 repository=repo.get_api_data()
1312 repository=repo.get_api_data()
1313 )
1313 )
1314 except Exception:
1314 except Exception:
1315 log.error(traceback.format_exc())
1315 log.error(traceback.format_exc())
1316 raise JSONRPCError('failed to update repo `%s`' % repoid)
1316 raise JSONRPCError('failed to update repo `%s`' % repoid)
1317
1317
1318 # permission check inside
1318 # permission check inside
1319 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
1319 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
1320 def fork_repo(self, repoid, fork_name,
1320 def fork_repo(self, repoid, fork_name,
1321 owner=None,
1321 owner=None,
1322 description='', copy_permissions=False,
1322 description='', copy_permissions=False,
1323 private=False, landing_rev='rev:tip'):
1323 private=False, landing_rev='rev:tip'):
1324 """
1324 """
1325 Creates a fork of given repo. In case of using celery this will
1325 Creates a fork of given repo. In case of using celery this will
1326 immediately return success message, while fork is going to be created
1326 immediately return success message, while fork is going to be created
1327 asynchronous. This command can be executed only using api_key belonging to
1327 asynchronous. This command can be executed only using api_key belonging to
1328 user with admin rights or regular user that have fork permission, and at least
1328 user with admin rights or regular user that have fork permission, and at least
1329 read access to forking repository. Regular users cannot specify owner parameter.
1329 read access to forking repository. Regular users cannot specify owner parameter.
1330
1330
1331 :param repoid: repository name or repository id
1331 :param repoid: repository name or repository id
1332 :type repoid: str or int
1332 :type repoid: str or int
1333 :param fork_name:
1333 :param fork_name:
1334 :param owner:
1334 :param owner:
1335 :param description:
1335 :param description:
1336 :param copy_permissions:
1336 :param copy_permissions:
1337 :param private:
1337 :param private:
1338 :param landing_rev:
1338 :param landing_rev:
1339
1339
1340 INPUT::
1340 INPUT::
1341
1341
1342 id : <id_for_response>
1342 id : <id_for_response>
1343 api_key : "<api_key>"
1343 api_key : "<api_key>"
1344 method : "fork_repo"
1344 method : "fork_repo"
1345 args : {
1345 args : {
1346 "repoid" : "<reponame or repo_id>",
1346 "repoid" : "<reponame or repo_id>",
1347 "fork_name" : "<forkname>",
1347 "fork_name" : "<forkname>",
1348 "owner" : "<username or user_id = Optional(=apiuser)>",
1348 "owner" : "<username or user_id = Optional(=apiuser)>",
1349 "description" : "<description>",
1349 "description" : "<description>",
1350 "copy_permissions": "<bool>",
1350 "copy_permissions": "<bool>",
1351 "private" : "<bool>",
1351 "private" : "<bool>",
1352 "landing_rev" : "<landing_rev>"
1352 "landing_rev" : "<landing_rev>"
1353 }
1353 }
1354
1354
1355 OUTPUT::
1355 OUTPUT::
1356
1356
1357 id : <id_given_in_input>
1357 id : <id_given_in_input>
1358 result : {
1358 result : {
1359 "msg" : "Created fork of `<reponame>` as `<forkname>`",
1359 "msg" : "Created fork of `<reponame>` as `<forkname>`",
1360 "success" : true
1360 "success" : true
1361 }
1361 }
1362 error : null
1362 error : null
1363 """
1363 """
1364 repo = get_repo_or_error(repoid)
1364 repo = get_repo_or_error(repoid)
1365 repo_name = repo.repo_name
1365 repo_name = repo.repo_name
1366
1366
1367 _repo = RepoModel().get_by_repo_name(fork_name)
1367 _repo = RepoModel().get_by_repo_name(fork_name)
1368 if _repo:
1368 if _repo:
1369 type_ = 'fork' if _repo.fork else 'repo'
1369 type_ = 'fork' if _repo.fork else 'repo'
1370 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
1370 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
1371
1371
1372 group_name = None
1372 group_name = None
1373 fork_name_parts = fork_name.split('/')
1373 fork_name_parts = fork_name.split('/')
1374 if len(fork_name_parts) > 1:
1374 if len(fork_name_parts) > 1:
1375 group_name = '/'.join(fork_name_parts[:-1])
1375 group_name = '/'.join(fork_name_parts[:-1])
1376 repo_group = db.RepoGroup.get_by_group_name(group_name)
1376 repo_group = db.RepoGroup.get_by_group_name(group_name)
1377 if repo_group is None:
1377 if repo_group is None:
1378 raise JSONRPCError("repo group `%s` not found" % group_name)
1378 raise JSONRPCError("repo group `%s` not found" % group_name)
1379 if not(HasPermissionAny('hg.admin')() or HasRepoGroupPermissionLevel('write')(group_name)):
1379 if not(HasPermissionAny('hg.admin')() or HasRepoGroupPermissionLevel('write')(group_name)):
1380 raise JSONRPCError("no permission to create repo in %s" % group_name)
1380 raise JSONRPCError("no permission to create repo in %s" % group_name)
1381 else:
1381 else:
1382 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
1382 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
1383 raise JSONRPCError("no permission to create top level repo")
1383 raise JSONRPCError("no permission to create top level repo")
1384
1384
1385 if HasPermissionAny('hg.admin')():
1385 if HasPermissionAny('hg.admin')():
1386 pass
1386 pass
1387 elif HasRepoPermissionLevel('read')(repo.repo_name):
1387 elif HasRepoPermissionLevel('read')(repo.repo_name):
1388 if owner is not None:
1388 if owner is not None:
1389 # forbid setting owner for non-admins
1389 # forbid setting owner for non-admins
1390 raise JSONRPCError(
1390 raise JSONRPCError(
1391 'Only Kallithea admin can specify `owner` param'
1391 'Only Kallithea admin can specify `owner` param'
1392 )
1392 )
1393 else:
1393 else:
1394 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1394 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1395
1395
1396 if owner is None:
1396 if owner is None:
1397 owner = request.authuser.user_id
1397 owner = request.authuser.user_id
1398
1398
1399 owner = get_user_or_error(owner)
1399 owner = get_user_or_error(owner)
1400
1400
1401 try:
1401 try:
1402 form_data = dict(
1402 form_data = dict(
1403 repo_name=fork_name_parts[-1],
1403 repo_name=fork_name_parts[-1],
1404 repo_name_full=fork_name,
1404 repo_name_full=fork_name,
1405 repo_group=group_name,
1405 repo_group=group_name,
1406 repo_type=repo.repo_type,
1406 repo_type=repo.repo_type,
1407 description=description,
1407 description=description,
1408 private=private,
1408 private=private,
1409 copy_permissions=copy_permissions,
1409 copy_permissions=copy_permissions,
1410 landing_rev=landing_rev,
1410 landing_rev=landing_rev,
1411 update_after_clone=False,
1411 update_after_clone=False,
1412 fork_parent_id=repo.repo_id,
1412 fork_parent_id=repo.repo_id,
1413 )
1413 )
1414 RepoModel().create_fork(form_data, cur_user=owner.username)
1414 RepoModel().create_fork(form_data, cur_user=owner.username)
1415 # no commit, it's done in RepoModel, or async via celery
1415 # no commit, it's done in RepoModel, or async via celery
1416 return dict(
1416 return dict(
1417 msg='Created fork of `%s` as `%s`' % (repo.repo_name,
1417 msg='Created fork of `%s` as `%s`' % (repo.repo_name,
1418 fork_name),
1418 fork_name),
1419 success=True, # cannot return the repo data here since fork
1419 success=True, # cannot return the repo data here since fork
1420 # can be done async
1420 # can be done async
1421 )
1421 )
1422 except Exception:
1422 except Exception:
1423 log.error(traceback.format_exc())
1423 log.error(traceback.format_exc())
1424 raise JSONRPCError(
1424 raise JSONRPCError(
1425 'failed to fork repository `%s` as `%s`' % (repo_name,
1425 'failed to fork repository `%s` as `%s`' % (repo_name,
1426 fork_name)
1426 fork_name)
1427 )
1427 )
1428
1428
1429 # permission check inside
1429 # permission check inside
1430 def delete_repo(self, repoid, forks=''):
1430 def delete_repo(self, repoid, forks=''):
1431 """
1431 """
1432 Deletes a repository. This command can be executed only using api_key belonging
1432 Deletes a repository. This command can be executed only using api_key belonging
1433 to user with admin rights or regular user that have admin access to repository.
1433 to user with admin rights or regular user that have admin access to repository.
1434 When `forks` param is set it's possible to detach or delete forks of deleting
1434 When `forks` param is set it's possible to detach or delete forks of deleting
1435 repository
1435 repository
1436
1436
1437 :param repoid: repository name or repository id
1437 :param repoid: repository name or repository id
1438 :type repoid: str or int
1438 :type repoid: str or int
1439 :param forks: `detach` or `delete`, what do do with attached forks for repo
1439 :param forks: `detach` or `delete`, what do do with attached forks for repo
1440 :type forks: Optional(str)
1440 :type forks: Optional(str)
1441
1441
1442 OUTPUT::
1442 OUTPUT::
1443
1443
1444 id : <id_given_in_input>
1444 id : <id_given_in_input>
1445 result : {
1445 result : {
1446 "msg" : "Deleted repository `<reponame>`",
1446 "msg" : "Deleted repository `<reponame>`",
1447 "success" : true
1447 "success" : true
1448 }
1448 }
1449 error : null
1449 error : null
1450 """
1450 """
1451 repo = get_repo_or_error(repoid)
1451 repo = get_repo_or_error(repoid)
1452
1452
1453 if not HasPermissionAny('hg.admin')():
1453 if not HasPermissionAny('hg.admin')():
1454 if not HasRepoPermissionLevel('admin')(repo.repo_name):
1454 if not HasRepoPermissionLevel('admin')(repo.repo_name):
1455 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1455 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1456
1456
1457 try:
1457 try:
1458 handle_forks = forks
1458 handle_forks = forks
1459 _forks_msg = ''
1459 _forks_msg = ''
1460 _forks = [f for f in repo.forks]
1460 _forks = [f for f in repo.forks]
1461 if handle_forks == 'detach':
1461 if handle_forks == 'detach':
1462 _forks_msg = ' ' + 'Detached %s forks' % len(_forks)
1462 _forks_msg = ' ' + 'Detached %s forks' % len(_forks)
1463 elif handle_forks == 'delete':
1463 elif handle_forks == 'delete':
1464 _forks_msg = ' ' + 'Deleted %s forks' % len(_forks)
1464 _forks_msg = ' ' + 'Deleted %s forks' % len(_forks)
1465 elif _forks:
1465 elif _forks:
1466 raise JSONRPCError(
1466 raise JSONRPCError(
1467 'Cannot delete `%s` it still contains attached forks' %
1467 'Cannot delete `%s` it still contains attached forks' %
1468 (repo.repo_name,)
1468 (repo.repo_name,)
1469 )
1469 )
1470
1470
1471 RepoModel().delete(repo, forks=forks)
1471 RepoModel().delete(repo, forks=forks)
1472 meta.Session().commit()
1472 meta.Session().commit()
1473 return dict(
1473 return dict(
1474 msg='Deleted repository `%s`%s' % (repo.repo_name, _forks_msg),
1474 msg='Deleted repository `%s`%s' % (repo.repo_name, _forks_msg),
1475 success=True
1475 success=True
1476 )
1476 )
1477 except Exception:
1477 except Exception:
1478 log.error(traceback.format_exc())
1478 log.error(traceback.format_exc())
1479 raise JSONRPCError(
1479 raise JSONRPCError(
1480 'failed to delete repository `%s`' % (repo.repo_name,)
1480 'failed to delete repository `%s`' % (repo.repo_name,)
1481 )
1481 )
1482
1482
1483 @HasPermissionAnyDecorator('hg.admin')
1483 @HasPermissionAnyDecorator('hg.admin')
1484 def grant_user_permission(self, repoid, userid, perm):
1484 def grant_user_permission(self, repoid, userid, perm):
1485 """
1485 """
1486 Grant permission for user on given repository, or update existing one
1486 Grant permission for user on given repository, or update existing one
1487 if found. This command can be executed only using api_key belonging to user
1487 if found. This command can be executed only using api_key belonging to user
1488 with admin rights.
1488 with admin rights.
1489
1489
1490 :param repoid: repository name or repository id
1490 :param repoid: repository name or repository id
1491 :type repoid: str or int
1491 :type repoid: str or int
1492 :param userid:
1492 :param userid:
1493 :param perm: (repository.(none|read|write|admin))
1493 :param perm: (repository.(none|read|write|admin))
1494 :type perm: str
1494 :type perm: str
1495
1495
1496 OUTPUT::
1496 OUTPUT::
1497
1497
1498 id : <id_given_in_input>
1498 id : <id_given_in_input>
1499 result : {
1499 result : {
1500 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
1500 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
1501 "success" : true
1501 "success" : true
1502 }
1502 }
1503 error : null
1503 error : null
1504 """
1504 """
1505 repo = get_repo_or_error(repoid)
1505 repo = get_repo_or_error(repoid)
1506 user = get_user_or_error(userid)
1506 user = get_user_or_error(userid)
1507 perm = get_perm_or_error(perm)
1507 perm = get_perm_or_error(perm)
1508
1508
1509 try:
1509 try:
1510
1510
1511 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
1511 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
1512
1512
1513 meta.Session().commit()
1513 meta.Session().commit()
1514 return dict(
1514 return dict(
1515 msg='Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1515 msg='Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1516 perm.permission_name, user.username, repo.repo_name
1516 perm.permission_name, user.username, repo.repo_name
1517 ),
1517 ),
1518 success=True
1518 success=True
1519 )
1519 )
1520 except Exception:
1520 except Exception:
1521 log.error(traceback.format_exc())
1521 log.error(traceback.format_exc())
1522 raise JSONRPCError(
1522 raise JSONRPCError(
1523 'failed to edit permission for user: `%s` in repo: `%s`' % (
1523 'failed to edit permission for user: `%s` in repo: `%s`' % (
1524 userid, repoid
1524 userid, repoid
1525 )
1525 )
1526 )
1526 )
1527
1527
1528 @HasPermissionAnyDecorator('hg.admin')
1528 @HasPermissionAnyDecorator('hg.admin')
1529 def revoke_user_permission(self, repoid, userid):
1529 def revoke_user_permission(self, repoid, userid):
1530 """
1530 """
1531 Revoke permission for user on given repository. This command can be executed
1531 Revoke permission for user on given repository. This command can be executed
1532 only using api_key belonging to user with admin rights.
1532 only using api_key belonging to user with admin rights.
1533
1533
1534 :param repoid: repository name or repository id
1534 :param repoid: repository name or repository id
1535 :type repoid: str or int
1535 :type repoid: str or int
1536 :param userid:
1536 :param userid:
1537
1537
1538 OUTPUT::
1538 OUTPUT::
1539
1539
1540 id : <id_given_in_input>
1540 id : <id_given_in_input>
1541 result : {
1541 result : {
1542 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
1542 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
1543 "success" : true
1543 "success" : true
1544 }
1544 }
1545 error : null
1545 error : null
1546 """
1546 """
1547 repo = get_repo_or_error(repoid)
1547 repo = get_repo_or_error(repoid)
1548 user = get_user_or_error(userid)
1548 user = get_user_or_error(userid)
1549 try:
1549 try:
1550 RepoModel().revoke_user_permission(repo=repo, user=user)
1550 RepoModel().revoke_user_permission(repo=repo, user=user)
1551 meta.Session().commit()
1551 meta.Session().commit()
1552 return dict(
1552 return dict(
1553 msg='Revoked perm for user: `%s` in repo: `%s`' % (
1553 msg='Revoked perm for user: `%s` in repo: `%s`' % (
1554 user.username, repo.repo_name
1554 user.username, repo.repo_name
1555 ),
1555 ),
1556 success=True
1556 success=True
1557 )
1557 )
1558 except Exception:
1558 except Exception:
1559 log.error(traceback.format_exc())
1559 log.error(traceback.format_exc())
1560 raise JSONRPCError(
1560 raise JSONRPCError(
1561 'failed to edit permission for user: `%s` in repo: `%s`' % (
1561 'failed to edit permission for user: `%s` in repo: `%s`' % (
1562 userid, repoid
1562 userid, repoid
1563 )
1563 )
1564 )
1564 )
1565
1565
1566 # permission check inside
1566 # permission check inside
1567 def grant_user_group_permission(self, repoid, usergroupid, perm):
1567 def grant_user_group_permission(self, repoid, usergroupid, perm):
1568 """
1568 """
1569 Grant permission for user group on given repository, or update
1569 Grant permission for user group on given repository, or update
1570 existing one if found. This command can be executed only using
1570 existing one if found. This command can be executed only using
1571 api_key belonging to user with admin rights.
1571 api_key belonging to user with admin rights.
1572
1572
1573 :param repoid: repository name or repository id
1573 :param repoid: repository name or repository id
1574 :type repoid: str or int
1574 :type repoid: str or int
1575 :param usergroupid: id of usergroup
1575 :param usergroupid: id of usergroup
1576 :type usergroupid: str or int
1576 :type usergroupid: str or int
1577 :param perm: (repository.(none|read|write|admin))
1577 :param perm: (repository.(none|read|write|admin))
1578 :type perm: str
1578 :type perm: str
1579
1579
1580 OUTPUT::
1580 OUTPUT::
1581
1581
1582 id : <id_given_in_input>
1582 id : <id_given_in_input>
1583 result : {
1583 result : {
1584 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
1584 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
1585 "success" : true
1585 "success" : true
1586 }
1586 }
1587 error : null
1587 error : null
1588
1588
1589 ERROR OUTPUT::
1589 ERROR OUTPUT::
1590
1590
1591 id : <id_given_in_input>
1591 id : <id_given_in_input>
1592 result : null
1592 result : null
1593 error : {
1593 error : {
1594 "failed to edit permission for user group: `<usergroup>` in repo `<repo>`'
1594 "failed to edit permission for user group: `<usergroup>` in repo `<repo>`'
1595 }
1595 }
1596 """
1596 """
1597 repo = get_repo_or_error(repoid)
1597 repo = get_repo_or_error(repoid)
1598 perm = get_perm_or_error(perm)
1598 perm = get_perm_or_error(perm)
1599 user_group = get_user_group_or_error(usergroupid)
1599 user_group = get_user_group_or_error(usergroupid)
1600 if not HasPermissionAny('hg.admin')():
1600 if not HasPermissionAny('hg.admin')():
1601 if not HasRepoPermissionLevel('admin')(repo.repo_name):
1601 if not HasRepoPermissionLevel('admin')(repo.repo_name):
1602 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1602 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1603
1603
1604 if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
1604 if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
1605 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
1605 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
1606
1606
1607 try:
1607 try:
1608 RepoModel().grant_user_group_permission(
1608 RepoModel().grant_user_group_permission(
1609 repo=repo, group_name=user_group, perm=perm)
1609 repo=repo, group_name=user_group, perm=perm)
1610
1610
1611 meta.Session().commit()
1611 meta.Session().commit()
1612 return dict(
1612 return dict(
1613 msg='Granted perm: `%s` for user group: `%s` in '
1613 msg='Granted perm: `%s` for user group: `%s` in '
1614 'repo: `%s`' % (
1614 'repo: `%s`' % (
1615 perm.permission_name, user_group.users_group_name,
1615 perm.permission_name, user_group.users_group_name,
1616 repo.repo_name
1616 repo.repo_name
1617 ),
1617 ),
1618 success=True
1618 success=True
1619 )
1619 )
1620 except Exception:
1620 except Exception:
1621 log.error(traceback.format_exc())
1621 log.error(traceback.format_exc())
1622 raise JSONRPCError(
1622 raise JSONRPCError(
1623 'failed to edit permission for user group: `%s` in '
1623 'failed to edit permission for user group: `%s` in '
1624 'repo: `%s`' % (
1624 'repo: `%s`' % (
1625 usergroupid, repo.repo_name
1625 usergroupid, repo.repo_name
1626 )
1626 )
1627 )
1627 )
1628
1628
1629 # permission check inside
1629 # permission check inside
1630 def revoke_user_group_permission(self, repoid, usergroupid):
1630 def revoke_user_group_permission(self, repoid, usergroupid):
1631 """
1631 """
1632 Revoke permission for user group on given repository. This command can be
1632 Revoke permission for user group on given repository. This command can be
1633 executed only using api_key belonging to user with admin rights.
1633 executed only using api_key belonging to user with admin rights.
1634
1634
1635 :param repoid: repository name or repository id
1635 :param repoid: repository name or repository id
1636 :type repoid: str or int
1636 :type repoid: str or int
1637 :param usergroupid:
1637 :param usergroupid:
1638
1638
1639 OUTPUT::
1639 OUTPUT::
1640
1640
1641 id : <id_given_in_input>
1641 id : <id_given_in_input>
1642 result : {
1642 result : {
1643 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
1643 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
1644 "success" : true
1644 "success" : true
1645 }
1645 }
1646 error : null
1646 error : null
1647 """
1647 """
1648 repo = get_repo_or_error(repoid)
1648 repo = get_repo_or_error(repoid)
1649 user_group = get_user_group_or_error(usergroupid)
1649 user_group = get_user_group_or_error(usergroupid)
1650 if not HasPermissionAny('hg.admin')():
1650 if not HasPermissionAny('hg.admin')():
1651 if not HasRepoPermissionLevel('admin')(repo.repo_name):
1651 if not HasRepoPermissionLevel('admin')(repo.repo_name):
1652 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1652 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1653
1653
1654 if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
1654 if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
1655 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
1655 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
1656
1656
1657 try:
1657 try:
1658 RepoModel().revoke_user_group_permission(
1658 RepoModel().revoke_user_group_permission(
1659 repo=repo, group_name=user_group)
1659 repo=repo, group_name=user_group)
1660
1660
1661 meta.Session().commit()
1661 meta.Session().commit()
1662 return dict(
1662 return dict(
1663 msg='Revoked perm for user group: `%s` in repo: `%s`' % (
1663 msg='Revoked perm for user group: `%s` in repo: `%s`' % (
1664 user_group.users_group_name, repo.repo_name
1664 user_group.users_group_name, repo.repo_name
1665 ),
1665 ),
1666 success=True
1666 success=True
1667 )
1667 )
1668 except Exception:
1668 except Exception:
1669 log.error(traceback.format_exc())
1669 log.error(traceback.format_exc())
1670 raise JSONRPCError(
1670 raise JSONRPCError(
1671 'failed to edit permission for user group: `%s` in '
1671 'failed to edit permission for user group: `%s` in '
1672 'repo: `%s`' % (
1672 'repo: `%s`' % (
1673 user_group.users_group_name, repo.repo_name
1673 user_group.users_group_name, repo.repo_name
1674 )
1674 )
1675 )
1675 )
1676
1676
1677 @HasPermissionAnyDecorator('hg.admin')
1677 @HasPermissionAnyDecorator('hg.admin')
1678 def get_repo_group(self, repogroupid):
1678 def get_repo_group(self, repogroupid):
1679 """
1679 """
1680 Returns given repo group together with permissions, and repositories
1680 Returns given repo group together with permissions, and repositories
1681 inside the group
1681 inside the group
1682
1682
1683 :param repogroupid: id/name of repository group
1683 :param repogroupid: id/name of repository group
1684 :type repogroupid: str or int
1684 :type repogroupid: str or int
1685 """
1685 """
1686 repo_group = get_repo_group_or_error(repogroupid)
1686 repo_group = get_repo_group_or_error(repogroupid)
1687
1687
1688 members = []
1688 members = []
1689 for user in repo_group.repo_group_to_perm:
1689 for user in repo_group.repo_group_to_perm:
1690 perm = user.permission.permission_name
1690 perm = user.permission.permission_name
1691 user = user.user
1691 user = user.user
1692 user_data = {
1692 user_data = {
1693 'name': user.username,
1693 'name': user.username,
1694 'type': "user",
1694 'type': "user",
1695 'permission': perm
1695 'permission': perm
1696 }
1696 }
1697 members.append(user_data)
1697 members.append(user_data)
1698
1698
1699 for user_group in repo_group.users_group_to_perm:
1699 for user_group in repo_group.users_group_to_perm:
1700 perm = user_group.permission.permission_name
1700 perm = user_group.permission.permission_name
1701 user_group = user_group.users_group
1701 user_group = user_group.users_group
1702 user_group_data = {
1702 user_group_data = {
1703 'name': user_group.users_group_name,
1703 'name': user_group.users_group_name,
1704 'type': "user_group",
1704 'type': "user_group",
1705 'permission': perm
1705 'permission': perm
1706 }
1706 }
1707 members.append(user_group_data)
1707 members.append(user_group_data)
1708
1708
1709 data = repo_group.get_api_data()
1709 data = repo_group.get_api_data()
1710 data["members"] = members
1710 data["members"] = members
1711 return data
1711 return data
1712
1712
1713 @HasPermissionAnyDecorator('hg.admin')
1713 @HasPermissionAnyDecorator('hg.admin')
1714 def get_repo_groups(self):
1714 def get_repo_groups(self):
1715 """
1715 """
1716 Returns all repository groups
1716 Returns all repository groups
1717 """
1717 """
1718 return [
1718 return [
1719 repo_group.get_api_data()
1719 repo_group.get_api_data()
1720 for repo_group in db.RepoGroup.query()
1720 for repo_group in db.RepoGroup.query()
1721 ]
1721 ]
1722
1722
1723 @HasPermissionAnyDecorator('hg.admin')
1723 @HasPermissionAnyDecorator('hg.admin')
1724 def create_repo_group(self, group_name, description='',
1724 def create_repo_group(self, group_name, description='',
1725 owner=None,
1725 owner=None,
1726 parent=None,
1726 parent=None,
1727 copy_permissions=False):
1727 copy_permissions=False):
1728 """
1728 """
1729 Creates a repository group. This command can be executed only using
1729 Creates a repository group. This command can be executed only using
1730 api_key belonging to user with admin rights.
1730 api_key belonging to user with admin rights.
1731
1731
1732 :param group_name:
1732 :param group_name:
1733 :type group_name:
1733 :type group_name:
1734 :param description:
1734 :param description:
1735 :type description:
1735 :type description:
1736 :param owner:
1736 :param owner:
1737 :type owner:
1737 :type owner:
1738 :param parent:
1738 :param parent:
1739 :type parent:
1739 :type parent:
1740 :param copy_permissions:
1740 :param copy_permissions:
1741 :type copy_permissions:
1741 :type copy_permissions:
1742
1742
1743 OUTPUT::
1743 OUTPUT::
1744
1744
1745 id : <id_given_in_input>
1745 id : <id_given_in_input>
1746 result : {
1746 result : {
1747 "msg" : "created new repo group `<repo_group_name>`",
1747 "msg" : "created new repo group `<repo_group_name>`",
1748 "repo_group" : <repogroup_object>
1748 "repo_group" : <repogroup_object>
1749 }
1749 }
1750 error : null
1750 error : null
1751
1751
1752 ERROR OUTPUT::
1752 ERROR OUTPUT::
1753
1753
1754 id : <id_given_in_input>
1754 id : <id_given_in_input>
1755 result : null
1755 result : null
1756 error : {
1756 error : {
1757 failed to create repo group `<repogroupid>`
1757 failed to create repo group `<repogroupid>`
1758 }
1758 }
1759 """
1759 """
1760 if db.RepoGroup.get_by_group_name(group_name):
1760 if db.RepoGroup.get_by_group_name(group_name):
1761 raise JSONRPCError("repo group `%s` already exist" % (group_name,))
1761 raise JSONRPCError("repo group `%s` already exist" % (group_name,))
1762
1762
1763 if owner is None:
1763 if owner is None:
1764 owner = request.authuser.user_id
1764 owner = request.authuser.user_id
1765 group_description = description
1765 group_description = description
1766 parent_group = None
1766 parent_group = None
1767 if parent is not None:
1767 if parent is not None:
1768 parent_group = get_repo_group_or_error(parent)
1768 parent_group = get_repo_group_or_error(parent)
1769
1769
1770 try:
1770 try:
1771 repo_group = RepoGroupModel().create(
1771 repo_group = RepoGroupModel().create(
1772 group_name=group_name,
1772 group_name=group_name,
1773 group_description=group_description,
1773 group_description=group_description,
1774 owner=owner,
1774 owner=owner,
1775 parent=parent_group,
1775 parent=parent_group,
1776 copy_permissions=copy_permissions
1776 copy_permissions=copy_permissions
1777 )
1777 )
1778 meta.Session().commit()
1778 meta.Session().commit()
1779 return dict(
1779 return dict(
1780 msg='created new repo group `%s`' % group_name,
1780 msg='created new repo group `%s`' % group_name,
1781 repo_group=repo_group.get_api_data()
1781 repo_group=repo_group.get_api_data()
1782 )
1782 )
1783 except Exception:
1783 except Exception:
1784
1784
1785 log.error(traceback.format_exc())
1785 log.error(traceback.format_exc())
1786 raise JSONRPCError('failed to create repo group `%s`' % (group_name,))
1786 raise JSONRPCError('failed to create repo group `%s`' % (group_name,))
1787
1787
1788 @HasPermissionAnyDecorator('hg.admin')
1788 @HasPermissionAnyDecorator('hg.admin')
1789 def update_repo_group(self, repogroupid, group_name=None,
1789 def update_repo_group(self, repogroupid, group_name=None,
1790 description=None,
1790 description=None,
1791 owner=None,
1791 owner=None,
1792 parent=None):
1792 parent=None):
1793 repo_group = get_repo_group_or_error(repogroupid)
1793 repo_group = get_repo_group_or_error(repogroupid)
1794
1794
1795 updates = {}
1795 updates = {}
1796 try:
1796 try:
1797 store_update(updates, group_name, 'group_name')
1797 store_update(updates, group_name, 'group_name')
1798 store_update(updates, description, 'group_description')
1798 store_update(updates, description, 'group_description')
1799 store_update(updates, owner, 'owner')
1799 store_update(updates, owner, 'owner')
1800 store_update(updates, parent, 'parent_group')
1800 store_update(updates, parent, 'parent_group')
1801 repo_group = RepoGroupModel().update(repo_group, updates)
1801 repo_group = RepoGroupModel().update(repo_group, updates)
1802 meta.Session().commit()
1802 meta.Session().commit()
1803 return dict(
1803 return dict(
1804 msg='updated repository group ID:%s %s' % (repo_group.group_id,
1804 msg='updated repository group ID:%s %s' % (repo_group.group_id,
1805 repo_group.group_name),
1805 repo_group.group_name),
1806 repo_group=repo_group.get_api_data()
1806 repo_group=repo_group.get_api_data()
1807 )
1807 )
1808 except Exception:
1808 except Exception:
1809 log.error(traceback.format_exc())
1809 log.error(traceback.format_exc())
1810 raise JSONRPCError('failed to update repository group `%s`'
1810 raise JSONRPCError('failed to update repository group `%s`'
1811 % (repogroupid,))
1811 % (repogroupid,))
1812
1812
1813 @HasPermissionAnyDecorator('hg.admin')
1813 @HasPermissionAnyDecorator('hg.admin')
1814 def delete_repo_group(self, repogroupid):
1814 def delete_repo_group(self, repogroupid):
1815 """
1815 """
1816 :param repogroupid: name or id of repository group
1816 :param repogroupid: name or id of repository group
1817 :type repogroupid: str or int
1817 :type repogroupid: str or int
1818
1818
1819 OUTPUT::
1819 OUTPUT::
1820
1820
1821 id : <id_given_in_input>
1821 id : <id_given_in_input>
1822 result : {
1822 result : {
1823 'msg' : 'deleted repo group ID:<repogroupid> <repogroupname>
1823 'msg' : 'deleted repo group ID:<repogroupid> <repogroupname>
1824 'repo_group' : null
1824 'repo_group' : null
1825 }
1825 }
1826 error : null
1826 error : null
1827
1827
1828 ERROR OUTPUT::
1828 ERROR OUTPUT::
1829
1829
1830 id : <id_given_in_input>
1830 id : <id_given_in_input>
1831 result : null
1831 result : null
1832 error : {
1832 error : {
1833 "failed to delete repo group ID:<repogroupid> <repogroupname>"
1833 "failed to delete repo group ID:<repogroupid> <repogroupname>"
1834 }
1834 }
1835 """
1835 """
1836 repo_group = get_repo_group_or_error(repogroupid)
1836 repo_group = get_repo_group_or_error(repogroupid)
1837
1837
1838 try:
1838 try:
1839 RepoGroupModel().delete(repo_group)
1839 RepoGroupModel().delete(repo_group)
1840 meta.Session().commit()
1840 meta.Session().commit()
1841 return dict(
1841 return dict(
1842 msg='deleted repo group ID:%s %s' %
1842 msg='deleted repo group ID:%s %s' %
1843 (repo_group.group_id, repo_group.group_name),
1843 (repo_group.group_id, repo_group.group_name),
1844 repo_group=None
1844 repo_group=None
1845 )
1845 )
1846 except Exception:
1846 except Exception:
1847 log.error(traceback.format_exc())
1847 log.error(traceback.format_exc())
1848 raise JSONRPCError('failed to delete repo group ID:%s %s' %
1848 raise JSONRPCError('failed to delete repo group ID:%s %s' %
1849 (repo_group.group_id, repo_group.group_name)
1849 (repo_group.group_id, repo_group.group_name)
1850 )
1850 )
1851
1851
1852 # permission check inside
1852 # permission check inside
1853 def grant_user_permission_to_repo_group(self, repogroupid, userid,
1853 def grant_user_permission_to_repo_group(self, repogroupid, userid,
1854 perm, apply_to_children='none'):
1854 perm, apply_to_children='none'):
1855 """
1855 """
1856 Grant permission for user on given repository group, or update existing
1856 Grant permission for user on given repository group, or update existing
1857 one if found. This command can be executed only using api_key belonging
1857 one if found. This command can be executed only using api_key belonging
1858 to user with admin rights, or user who has admin right to given repository
1858 to user with admin rights, or user who has admin right to given repository
1859 group.
1859 group.
1860
1860
1861 :param repogroupid: name or id of repository group
1861 :param repogroupid: name or id of repository group
1862 :type repogroupid: str or int
1862 :type repogroupid: str or int
1863 :param userid:
1863 :param userid:
1864 :param perm: (group.(none|read|write|admin))
1864 :param perm: (group.(none|read|write|admin))
1865 :type perm: str
1865 :type perm: str
1866 :param apply_to_children: 'none', 'repos', 'groups', 'all'
1866 :param apply_to_children: 'none', 'repos', 'groups', 'all'
1867 :type apply_to_children: str
1867 :type apply_to_children: str
1868
1868
1869 OUTPUT::
1869 OUTPUT::
1870
1870
1871 id : <id_given_in_input>
1871 id : <id_given_in_input>
1872 result : {
1872 result : {
1873 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
1873 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
1874 "success" : true
1874 "success" : true
1875 }
1875 }
1876 error : null
1876 error : null
1877
1877
1878 ERROR OUTPUT::
1878 ERROR OUTPUT::
1879
1879
1880 id : <id_given_in_input>
1880 id : <id_given_in_input>
1881 result : null
1881 result : null
1882 error : {
1882 error : {
1883 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
1883 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
1884 }
1884 }
1885 """
1885 """
1886 repo_group = get_repo_group_or_error(repogroupid)
1886 repo_group = get_repo_group_or_error(repogroupid)
1887
1887
1888 if not HasPermissionAny('hg.admin')():
1888 if not HasPermissionAny('hg.admin')():
1889 if not HasRepoGroupPermissionLevel('admin')(repo_group.group_name):
1889 if not HasRepoGroupPermissionLevel('admin')(repo_group.group_name):
1890 raise JSONRPCError('repository group `%s` does not exist' % (repogroupid,))
1890 raise JSONRPCError('repository group `%s` does not exist' % (repogroupid,))
1891
1891
1892 user = get_user_or_error(userid)
1892 user = get_user_or_error(userid)
1893 perm = get_perm_or_error(perm, prefix='group.')
1893 perm = get_perm_or_error(perm, prefix='group.')
1894
1894
1895 try:
1895 try:
1896 RepoGroupModel().add_permission(repo_group=repo_group,
1896 RepoGroupModel().add_permission(repo_group=repo_group,
1897 obj=user,
1897 obj=user,
1898 obj_type="user",
1898 obj_type="user",
1899 perm=perm,
1899 perm=perm,
1900 recursive=apply_to_children)
1900 recursive=apply_to_children)
1901 meta.Session().commit()
1901 meta.Session().commit()
1902 return dict(
1902 return dict(
1903 msg='Granted perm: `%s` (recursive:%s) for user: `%s` in repo group: `%s`' % (
1903 msg='Granted perm: `%s` (recursive:%s) for user: `%s` in repo group: `%s`' % (
1904 perm.permission_name, apply_to_children, user.username, repo_group.name
1904 perm.permission_name, apply_to_children, user.username, repo_group.name
1905 ),
1905 ),
1906 success=True
1906 success=True
1907 )
1907 )
1908 except Exception:
1908 except Exception:
1909 log.error(traceback.format_exc())
1909 log.error(traceback.format_exc())
1910 raise JSONRPCError(
1910 raise JSONRPCError(
1911 'failed to edit permission for user: `%s` in repo group: `%s`' % (
1911 'failed to edit permission for user: `%s` in repo group: `%s`' % (
1912 userid, repo_group.name))
1912 userid, repo_group.name))
1913
1913
1914 # permission check inside
1914 # permission check inside
1915 def revoke_user_permission_from_repo_group(self, repogroupid, userid,
1915 def revoke_user_permission_from_repo_group(self, repogroupid, userid,
1916 apply_to_children='none'):
1916 apply_to_children='none'):
1917 """
1917 """
1918 Revoke permission for user on given repository group. This command can
1918 Revoke permission for user on given repository group. This command can
1919 be executed only using api_key belonging to user with admin rights, or
1919 be executed only using api_key belonging to user with admin rights, or
1920 user who has admin right to given repository group.
1920 user who has admin right to given repository group.
1921
1921
1922 :param repogroupid: name or id of repository group
1922 :param repogroupid: name or id of repository group
1923 :type repogroupid: str or int
1923 :type repogroupid: str or int
1924 :param userid:
1924 :param userid:
1925 :type userid:
1925 :type userid:
1926 :param apply_to_children: 'none', 'repos', 'groups', 'all'
1926 :param apply_to_children: 'none', 'repos', 'groups', 'all'
1927 :type apply_to_children: str
1927 :type apply_to_children: str
1928
1928
1929 OUTPUT::
1929 OUTPUT::
1930
1930
1931 id : <id_given_in_input>
1931 id : <id_given_in_input>
1932 result : {
1932 result : {
1933 "msg" : "Revoked perm (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
1933 "msg" : "Revoked perm (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
1934 "success" : true
1934 "success" : true
1935 }
1935 }
1936 error : null
1936 error : null
1937
1937
1938 ERROR OUTPUT::
1938 ERROR OUTPUT::
1939
1939
1940 id : <id_given_in_input>
1940 id : <id_given_in_input>
1941 result : null
1941 result : null
1942 error : {
1942 error : {
1943 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
1943 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
1944 }
1944 }
1945 """
1945 """
1946 repo_group = get_repo_group_or_error(repogroupid)
1946 repo_group = get_repo_group_or_error(repogroupid)
1947
1947
1948 if not HasPermissionAny('hg.admin')():
1948 if not HasPermissionAny('hg.admin')():
1949 if not HasRepoGroupPermissionLevel('admin')(repo_group.group_name):
1949 if not HasRepoGroupPermissionLevel('admin')(repo_group.group_name):
1950 raise JSONRPCError('repository group `%s` does not exist' % (repogroupid,))
1950 raise JSONRPCError('repository group `%s` does not exist' % (repogroupid,))
1951
1951
1952 user = get_user_or_error(userid)
1952 user = get_user_or_error(userid)
1953
1953
1954 try:
1954 try:
1955 RepoGroupModel().delete_permission(repo_group=repo_group,
1955 RepoGroupModel().delete_permission(repo_group=repo_group,
1956 obj=user,
1956 obj=user,
1957 obj_type="user",
1957 obj_type="user",
1958 recursive=apply_to_children)
1958 recursive=apply_to_children)
1959
1959
1960 meta.Session().commit()
1960 meta.Session().commit()
1961 return dict(
1961 return dict(
1962 msg='Revoked perm (recursive:%s) for user: `%s` in repo group: `%s`' % (
1962 msg='Revoked perm (recursive:%s) for user: `%s` in repo group: `%s`' % (
1963 apply_to_children, user.username, repo_group.name
1963 apply_to_children, user.username, repo_group.name
1964 ),
1964 ),
1965 success=True
1965 success=True
1966 )
1966 )
1967 except Exception:
1967 except Exception:
1968 log.error(traceback.format_exc())
1968 log.error(traceback.format_exc())
1969 raise JSONRPCError(
1969 raise JSONRPCError(
1970 'failed to edit permission for user: `%s` in repo group: `%s`' % (
1970 'failed to edit permission for user: `%s` in repo group: `%s`' % (
1971 userid, repo_group.name))
1971 userid, repo_group.name))
1972
1972
1973 # permission check inside
1973 # permission check inside
1974 def grant_user_group_permission_to_repo_group(
1974 def grant_user_group_permission_to_repo_group(
1975 self, repogroupid, usergroupid, perm,
1975 self, repogroupid, usergroupid, perm,
1976 apply_to_children='none'):
1976 apply_to_children='none'):
1977 """
1977 """
1978 Grant permission for user group on given repository group, or update
1978 Grant permission for user group on given repository group, or update
1979 existing one if found. This command can be executed only using
1979 existing one if found. This command can be executed only using
1980 api_key belonging to user with admin rights, or user who has admin
1980 api_key belonging to user with admin rights, or user who has admin
1981 right to given repository group.
1981 right to given repository group.
1982
1982
1983 :param repogroupid: name or id of repository group
1983 :param repogroupid: name or id of repository group
1984 :type repogroupid: str or int
1984 :type repogroupid: str or int
1985 :param usergroupid: id of usergroup
1985 :param usergroupid: id of usergroup
1986 :type usergroupid: str or int
1986 :type usergroupid: str or int
1987 :param perm: (group.(none|read|write|admin))
1987 :param perm: (group.(none|read|write|admin))
1988 :type perm: str
1988 :type perm: str
1989 :param apply_to_children: 'none', 'repos', 'groups', 'all'
1989 :param apply_to_children: 'none', 'repos', 'groups', 'all'
1990 :type apply_to_children: str
1990 :type apply_to_children: str
1991
1991
1992 OUTPUT::
1992 OUTPUT::
1993
1993
1994 id : <id_given_in_input>
1994 id : <id_given_in_input>
1995 result : {
1995 result : {
1996 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
1996 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
1997 "success" : true
1997 "success" : true
1998 }
1998 }
1999 error : null
1999 error : null
2000
2000
2001 ERROR OUTPUT::
2001 ERROR OUTPUT::
2002
2002
2003 id : <id_given_in_input>
2003 id : <id_given_in_input>
2004 result : null
2004 result : null
2005 error : {
2005 error : {
2006 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
2006 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
2007 }
2007 }
2008 """
2008 """
2009 repo_group = get_repo_group_or_error(repogroupid)
2009 repo_group = get_repo_group_or_error(repogroupid)
2010 perm = get_perm_or_error(perm, prefix='group.')
2010 perm = get_perm_or_error(perm, prefix='group.')
2011 user_group = get_user_group_or_error(usergroupid)
2011 user_group = get_user_group_or_error(usergroupid)
2012 if not HasPermissionAny('hg.admin')():
2012 if not HasPermissionAny('hg.admin')():
2013 if not HasRepoGroupPermissionLevel('admin')(repo_group.group_name):
2013 if not HasRepoGroupPermissionLevel('admin')(repo_group.group_name):
2014 raise JSONRPCError(
2014 raise JSONRPCError(
2015 'repository group `%s` does not exist' % (repogroupid,))
2015 'repository group `%s` does not exist' % (repogroupid,))
2016
2016
2017 if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
2017 if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
2018 raise JSONRPCError(
2018 raise JSONRPCError(
2019 'user group `%s` does not exist' % (usergroupid,))
2019 'user group `%s` does not exist' % (usergroupid,))
2020
2020
2021 try:
2021 try:
2022 RepoGroupModel().add_permission(repo_group=repo_group,
2022 RepoGroupModel().add_permission(repo_group=repo_group,
2023 obj=user_group,
2023 obj=user_group,
2024 obj_type="user_group",
2024 obj_type="user_group",
2025 perm=perm,
2025 perm=perm,
2026 recursive=apply_to_children)
2026 recursive=apply_to_children)
2027 meta.Session().commit()
2027 meta.Session().commit()
2028 return dict(
2028 return dict(
2029 msg='Granted perm: `%s` (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2029 msg='Granted perm: `%s` (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2030 perm.permission_name, apply_to_children,
2030 perm.permission_name, apply_to_children,
2031 user_group.users_group_name, repo_group.name
2031 user_group.users_group_name, repo_group.name
2032 ),
2032 ),
2033 success=True
2033 success=True
2034 )
2034 )
2035 except Exception:
2035 except Exception:
2036 log.error(traceback.format_exc())
2036 log.error(traceback.format_exc())
2037 raise JSONRPCError(
2037 raise JSONRPCError(
2038 'failed to edit permission for user group: `%s` in '
2038 'failed to edit permission for user group: `%s` in '
2039 'repo group: `%s`' % (
2039 'repo group: `%s`' % (
2040 usergroupid, repo_group.name
2040 usergroupid, repo_group.name
2041 )
2041 )
2042 )
2042 )
2043
2043
2044 # permission check inside
2044 # permission check inside
2045 def revoke_user_group_permission_from_repo_group(
2045 def revoke_user_group_permission_from_repo_group(
2046 self, repogroupid, usergroupid,
2046 self, repogroupid, usergroupid,
2047 apply_to_children='none'):
2047 apply_to_children='none'):
2048 """
2048 """
2049 Revoke permission for user group on given repository. This command can be
2049 Revoke permission for user group on given repository. This command can be
2050 executed only using api_key belonging to user with admin rights, or
2050 executed only using api_key belonging to user with admin rights, or
2051 user who has admin right to given repository group.
2051 user who has admin right to given repository group.
2052
2052
2053 :param repogroupid: name or id of repository group
2053 :param repogroupid: name or id of repository group
2054 :type repogroupid: str or int
2054 :type repogroupid: str or int
2055 :param usergroupid:
2055 :param usergroupid:
2056 :param apply_to_children: 'none', 'repos', 'groups', 'all'
2056 :param apply_to_children: 'none', 'repos', 'groups', 'all'
2057 :type apply_to_children: str
2057 :type apply_to_children: str
2058
2058
2059 OUTPUT::
2059 OUTPUT::
2060
2060
2061 id : <id_given_in_input>
2061 id : <id_given_in_input>
2062 result : {
2062 result : {
2063 "msg" : "Revoked perm (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
2063 "msg" : "Revoked perm (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
2064 "success" : true
2064 "success" : true
2065 }
2065 }
2066 error : null
2066 error : null
2067
2067
2068 ERROR OUTPUT::
2068 ERROR OUTPUT::
2069
2069
2070 id : <id_given_in_input>
2070 id : <id_given_in_input>
2071 result : null
2071 result : null
2072 error : {
2072 error : {
2073 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
2073 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
2074 }
2074 }
2075 """
2075 """
2076 repo_group = get_repo_group_or_error(repogroupid)
2076 repo_group = get_repo_group_or_error(repogroupid)
2077 user_group = get_user_group_or_error(usergroupid)
2077 user_group = get_user_group_or_error(usergroupid)
2078 if not HasPermissionAny('hg.admin')():
2078 if not HasPermissionAny('hg.admin')():
2079 if not HasRepoGroupPermissionLevel('admin')(repo_group.group_name):
2079 if not HasRepoGroupPermissionLevel('admin')(repo_group.group_name):
2080 raise JSONRPCError(
2080 raise JSONRPCError(
2081 'repository group `%s` does not exist' % (repogroupid,))
2081 'repository group `%s` does not exist' % (repogroupid,))
2082
2082
2083 if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
2083 if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
2084 raise JSONRPCError(
2084 raise JSONRPCError(
2085 'user group `%s` does not exist' % (usergroupid,))
2085 'user group `%s` does not exist' % (usergroupid,))
2086
2086
2087 try:
2087 try:
2088 RepoGroupModel().delete_permission(repo_group=repo_group,
2088 RepoGroupModel().delete_permission(repo_group=repo_group,
2089 obj=user_group,
2089 obj=user_group,
2090 obj_type="user_group",
2090 obj_type="user_group",
2091 recursive=apply_to_children)
2091 recursive=apply_to_children)
2092 meta.Session().commit()
2092 meta.Session().commit()
2093 return dict(
2093 return dict(
2094 msg='Revoked perm (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2094 msg='Revoked perm (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2095 apply_to_children, user_group.users_group_name, repo_group.name
2095 apply_to_children, user_group.users_group_name, repo_group.name
2096 ),
2096 ),
2097 success=True
2097 success=True
2098 )
2098 )
2099 except Exception:
2099 except Exception:
2100 log.error(traceback.format_exc())
2100 log.error(traceback.format_exc())
2101 raise JSONRPCError(
2101 raise JSONRPCError(
2102 'failed to edit permission for user group: `%s` in repo group: `%s`' % (
2102 'failed to edit permission for user group: `%s` in repo group: `%s`' % (
2103 user_group.users_group_name, repo_group.name
2103 user_group.users_group_name, repo_group.name
2104 )
2104 )
2105 )
2105 )
2106
2106
2107 def get_gist(self, gistid):
2107 def get_gist(self, gistid):
2108 """
2108 """
2109 Get given gist by id
2109 Get given gist by id
2110
2110
2111 :param gistid: id of private or public gist
2111 :param gistid: id of private or public gist
2112 :type gistid: str
2112 :type gistid: str
2113 """
2113 """
2114 gist = get_gist_or_error(gistid)
2114 gist = get_gist_or_error(gistid)
2115 if not HasPermissionAny('hg.admin')():
2115 if not HasPermissionAny('hg.admin')():
2116 if gist.owner_id != request.authuser.user_id:
2116 if gist.owner_id != request.authuser.user_id:
2117 raise JSONRPCError('gist `%s` does not exist' % (gistid,))
2117 raise JSONRPCError('gist `%s` does not exist' % (gistid,))
2118 return gist.get_api_data()
2118 return gist.get_api_data()
2119
2119
2120 def get_gists(self, userid=None):
2120 def get_gists(self, userid=None):
2121 """
2121 """
2122 Get all gists for given user. If userid is empty returned gists
2122 Get all gists for given user. If userid is empty returned gists
2123 are for user who called the api
2123 are for user who called the api
2124
2124
2125 :param userid: user to get gists for
2125 :param userid: user to get gists for
2126 :type userid: Optional(str or int)
2126 :type userid: Optional(str or int)
2127 """
2127 """
2128 if not HasPermissionAny('hg.admin')():
2128 if not HasPermissionAny('hg.admin')():
2129 # make sure normal user does not pass someone else userid,
2129 # make sure normal user does not pass someone else userid,
2130 # he is not allowed to do that
2130 # he is not allowed to do that
2131 if userid is not None and userid != request.authuser.user_id:
2131 if userid is not None and userid != request.authuser.user_id:
2132 raise JSONRPCError(
2132 raise JSONRPCError(
2133 'userid is not the same as your user'
2133 'userid is not the same as your user'
2134 )
2134 )
2135
2135
2136 if userid is None:
2136 if userid is None:
2137 user_id = request.authuser.user_id
2137 user_id = request.authuser.user_id
2138 else:
2138 else:
2139 user_id = get_user_or_error(userid).user_id
2139 user_id = get_user_or_error(userid).user_id
2140
2140
2141 return [
2141 return [
2142 gist.get_api_data()
2142 gist.get_api_data()
2143 for gist in db.Gist().query()
2143 for gist in db.Gist().query()
2144 .filter_by(is_expired=False)
2144 .filter_by(is_expired=False)
2145 .filter(db.Gist.owner_id == user_id)
2145 .filter(db.Gist.owner_id == user_id)
2146 .order_by(db.Gist.created_on.desc())
2146 .order_by(db.Gist.created_on.desc())
2147 ]
2147 ]
2148
2148
2149 def create_gist(self, files, owner=None,
2149 def create_gist(self, files, owner=None,
2150 gist_type=db.Gist.GIST_PUBLIC, lifetime=-1,
2150 gist_type=db.Gist.GIST_PUBLIC, lifetime=-1,
2151 description=''):
2151 description=''):
2152 """
2152 """
2153 Creates new Gist
2153 Creates new Gist
2154
2154
2155 :param files: files to be added to gist
2155 :param files: files to be added to gist
2156 {'filename': {'content':'...', 'lexer': null},
2156 {'filename': {'content':'...', 'lexer': null},
2157 'filename2': {'content':'...', 'lexer': null}}
2157 'filename2': {'content':'...', 'lexer': null}}
2158 :type files: dict
2158 :type files: dict
2159 :param owner: gist owner, defaults to api method caller
2159 :param owner: gist owner, defaults to api method caller
2160 :type owner: Optional(str or int)
2160 :type owner: Optional(str or int)
2161 :param gist_type: type of gist 'public' or 'private'
2161 :param gist_type: type of gist 'public' or 'private'
2162 :type gist_type: Optional(str)
2162 :type gist_type: Optional(str)
2163 :param lifetime: time in minutes of gist lifetime
2163 :param lifetime: time in minutes of gist lifetime
2164 :type lifetime: Optional(int)
2164 :type lifetime: Optional(int)
2165 :param description: gist description
2165 :param description: gist description
2166 :type description: str
2166 :type description: str
2167
2167
2168 OUTPUT::
2168 OUTPUT::
2169
2169
2170 id : <id_given_in_input>
2170 id : <id_given_in_input>
2171 result : {
2171 result : {
2172 "msg" : "created new gist",
2172 "msg" : "created new gist",
2173 "gist" : <gist_object>
2173 "gist" : <gist_object>
2174 }
2174 }
2175 error : null
2175 error : null
2176
2176
2177 ERROR OUTPUT::
2177 ERROR OUTPUT::
2178
2178
2179 id : <id_given_in_input>
2179 id : <id_given_in_input>
2180 result : null
2180 result : null
2181 error : {
2181 error : {
2182 "failed to create gist"
2182 "failed to create gist"
2183 }
2183 }
2184 """
2184 """
2185 try:
2185 try:
2186 if owner is None:
2186 if owner is None:
2187 owner = request.authuser.user_id
2187 owner = request.authuser.user_id
2188
2188
2189 owner = get_user_or_error(owner)
2189 owner = get_user_or_error(owner)
2190
2190
2191 gist = GistModel().create(description=description,
2191 gist = GistModel().create(description=description,
2192 owner=owner,
2192 owner=owner,
2193 ip_addr=request.ip_addr,
2193 ip_addr=request.ip_addr,
2194 gist_mapping=files,
2194 gist_mapping=files,
2195 gist_type=gist_type,
2195 gist_type=gist_type,
2196 lifetime=lifetime)
2196 lifetime=lifetime)
2197 meta.Session().commit()
2197 meta.Session().commit()
2198 return dict(
2198 return dict(
2199 msg='created new gist',
2199 msg='created new gist',
2200 gist=gist.get_api_data()
2200 gist=gist.get_api_data()
2201 )
2201 )
2202 except Exception:
2202 except Exception:
2203 log.error(traceback.format_exc())
2203 log.error(traceback.format_exc())
2204 raise JSONRPCError('failed to create gist')
2204 raise JSONRPCError('failed to create gist')
2205
2205
2206 # permission check inside
2206 # permission check inside
2207 def delete_gist(self, gistid):
2207 def delete_gist(self, gistid):
2208 """
2208 """
2209 Deletes existing gist
2209 Deletes existing gist
2210
2210
2211 :param gistid: id of gist to delete
2211 :param gistid: id of gist to delete
2212 :type gistid: str
2212 :type gistid: str
2213
2213
2214 OUTPUT::
2214 OUTPUT::
2215
2215
2216 id : <id_given_in_input>
2216 id : <id_given_in_input>
2217 result : {
2217 result : {
2218 "msg" : "deleted gist ID: <gist_id>",
2218 "msg" : "deleted gist ID: <gist_id>",
2219 "gist" : null
2219 "gist" : null
2220 }
2220 }
2221 error : null
2221 error : null
2222
2222
2223 ERROR OUTPUT::
2223 ERROR OUTPUT::
2224
2224
2225 id : <id_given_in_input>
2225 id : <id_given_in_input>
2226 result : null
2226 result : null
2227 error : {
2227 error : {
2228 "failed to delete gist ID:<gist_id>"
2228 "failed to delete gist ID:<gist_id>"
2229 }
2229 }
2230 """
2230 """
2231 gist = get_gist_or_error(gistid)
2231 gist = get_gist_or_error(gistid)
2232 if not HasPermissionAny('hg.admin')():
2232 if not HasPermissionAny('hg.admin')():
2233 if gist.owner_id != request.authuser.user_id:
2233 if gist.owner_id != request.authuser.user_id:
2234 raise JSONRPCError('gist `%s` does not exist' % (gistid,))
2234 raise JSONRPCError('gist `%s` does not exist' % (gistid,))
2235
2235
2236 try:
2236 try:
2237 GistModel().delete(gist)
2237 GistModel().delete(gist)
2238 meta.Session().commit()
2238 meta.Session().commit()
2239 return dict(
2239 return dict(
2240 msg='deleted gist ID:%s' % (gist.gist_access_id,),
2240 msg='deleted gist ID:%s' % (gist.gist_access_id,),
2241 gist=None
2241 gist=None
2242 )
2242 )
2243 except Exception:
2243 except Exception:
2244 log.error(traceback.format_exc())
2244 log.error(traceback.format_exc())
2245 raise JSONRPCError('failed to delete gist ID:%s'
2245 raise JSONRPCError('failed to delete gist ID:%s'
2246 % (gist.gist_access_id,))
2246 % (gist.gist_access_id,))
2247
2247
2248 # permission check inside
2248 # permission check inside
2249 def get_changesets(self, repoid, start=None, end=None, start_date=None,
2249 def get_changesets(self, repoid, start=None, end=None, start_date=None,
2250 end_date=None, branch_name=None, reverse=False, with_file_list=False, max_revisions=None):
2250 end_date=None, branch_name=None, reverse=False, with_file_list=False, max_revisions=None):
2251 repo = get_repo_or_error(repoid)
2251 repo = get_repo_or_error(repoid)
2252 if not HasRepoPermissionLevel('read')(repo.repo_name):
2252 if not HasRepoPermissionLevel('read')(repo.repo_name):
2253 raise JSONRPCError('Access denied to repo %s' % repo.repo_name)
2253 raise JSONRPCError('Access denied to repo %s' % repo.repo_name)
2254
2254
2255 format = "%Y-%m-%dT%H:%M:%S"
2255 format = "%Y-%m-%dT%H:%M:%S"
2256 try:
2256 try:
2257 return [e.__json__(with_file_list) for e in
2257 return [e.__json__(with_file_list) for e in
2258 repo.scm_instance.get_changesets(start,
2258 repo.scm_instance.get_changesets(start,
2259 end,
2259 end,
2260 datetime.strptime(start_date, format) if start_date else None,
2260 datetime.strptime(start_date, format) if start_date else None,
2261 datetime.strptime(end_date, format) if end_date else None,
2261 datetime.strptime(end_date, format) if end_date else None,
2262 branch_name,
2262 branch_name,
2263 reverse, max_revisions)]
2263 reverse, max_revisions)]
2264 except EmptyRepositoryError as e:
2264 except EmptyRepositoryError as e:
2265 raise JSONRPCError('Repository is empty')
2265 raise JSONRPCError('Repository is empty')
2266
2266
2267 # permission check inside
2267 # permission check inside
2268 def get_changeset(self, repoid, raw_id, with_reviews=False):
2268 def get_changeset(self, repoid, raw_id, with_reviews=False):
2269 repo = get_repo_or_error(repoid)
2269 repo = get_repo_or_error(repoid)
2270 if not HasRepoPermissionLevel('read')(repo.repo_name):
2270 if not HasRepoPermissionLevel('read')(repo.repo_name):
2271 raise JSONRPCError('Access denied to repo %s' % repo.repo_name)
2271 raise JSONRPCError('Access denied to repo %s' % repo.repo_name)
2272 changeset = repo.get_changeset(raw_id)
2272 changeset = repo.get_changeset(raw_id)
2273 if isinstance(changeset, EmptyChangeset):
2273 if isinstance(changeset, EmptyChangeset):
2274 raise JSONRPCError('Changeset %s does not exist' % raw_id)
2274 raise JSONRPCError('Changeset %s does not exist' % raw_id)
2275
2275
2276 info = dict(changeset.as_dict())
2276 info = dict(changeset.as_dict())
2277
2277
2278 if with_reviews:
2278 if with_reviews:
2279 reviews = ChangesetStatusModel().get_statuses(
2279 reviews = ChangesetStatusModel().get_statuses(
2280 repo.repo_name, raw_id)
2280 repo.repo_name, raw_id)
2281 info["reviews"] = reviews
2281 info["reviews"] = reviews
2282
2282
2283 return info
2283 return info
2284
2284
2285 # permission check inside
2285 # permission check inside
2286 def get_pullrequest(self, pullrequest_id):
2286 def get_pullrequest(self, pullrequest_id):
2287 """
2287 """
2288 Get given pull request by id
2288 Get given pull request by id
2289 """
2289 """
2290 pull_request = db.PullRequest.get(pullrequest_id)
2290 pull_request = db.PullRequest.get(pullrequest_id)
2291 if pull_request is None:
2291 if pull_request is None:
2292 raise JSONRPCError('pull request `%s` does not exist' % (pullrequest_id,))
2292 raise JSONRPCError('pull request `%s` does not exist' % (pullrequest_id,))
2293 if not HasRepoPermissionLevel('read')(pull_request.org_repo.repo_name):
2293 if not HasRepoPermissionLevel('read')(pull_request.org_repo.repo_name):
2294 raise JSONRPCError('not allowed')
2294 raise JSONRPCError('not allowed')
2295 return pull_request.get_api_data()
2295 return pull_request.get_api_data()
2296
2296
2297 # permission check inside
2297 # permission check inside
2298 def comment_pullrequest(self, pull_request_id, comment_msg='', status=None, close_pr=False):
2298 def comment_pullrequest(self, pull_request_id, comment_msg='', status=None, close_pr=False):
2299 """
2299 """
2300 Add comment, close and change status of pull request.
2300 Add comment, close and change status of pull request.
2301 """
2301 """
2302 apiuser = get_user_or_error(request.authuser.user_id)
2302 apiuser = get_user_or_error(request.authuser.user_id)
2303 pull_request = db.PullRequest.get(pull_request_id)
2303 pull_request = db.PullRequest.get(pull_request_id)
2304 if pull_request is None:
2304 if pull_request is None:
2305 raise JSONRPCError('pull request `%s` does not exist' % (pull_request_id,))
2305 raise JSONRPCError('pull request `%s` does not exist' % (pull_request_id,))
2306 if (not HasRepoPermissionLevel('read')(pull_request.org_repo.repo_name)):
2306 if (not HasRepoPermissionLevel('read')(pull_request.org_repo.repo_name)):
2307 raise JSONRPCError('No permission to add comment. User needs at least reading permissions'
2307 raise JSONRPCError('No permission to add comment. User needs at least reading permissions'
2308 ' to the source repository.')
2308 ' to the source repository.')
2309 owner = apiuser.user_id == pull_request.owner_id
2309 owner = apiuser.user_id == pull_request.owner_id
2310 reviewer = apiuser.user_id in [reviewer.user_id for reviewer in pull_request.reviewers]
2310 reviewer = apiuser.user_id in [reviewer.user_id for reviewer in pull_request.reviewers]
2311 if close_pr and not (apiuser.admin or owner):
2311 if close_pr and not (apiuser.admin or owner):
2312 raise JSONRPCError('No permission to close pull request. User needs to be admin or owner.')
2312 raise JSONRPCError('No permission to close pull request. User needs to be admin or owner.')
2313 if status and not (apiuser.admin or owner or reviewer):
2313 if status and not (apiuser.admin or owner or reviewer):
2314 raise JSONRPCError('No permission to change pull request status. User needs to be admin, owner or reviewer.')
2314 raise JSONRPCError('No permission to change pull request status. User needs to be admin, owner or reviewer.')
2315 if pull_request.is_closed():
2315 if pull_request.is_closed():
2316 raise JSONRPCError('pull request is already closed')
2316 raise JSONRPCError('pull request is already closed')
2317
2317
2318 comment = ChangesetCommentsModel().create(
2318 comment = ChangesetCommentsModel().create(
2319 text=comment_msg,
2319 text=comment_msg,
2320 repo=pull_request.org_repo.repo_id,
2320 repo=pull_request.org_repo.repo_id,
2321 author=apiuser.user_id,
2321 author=apiuser.user_id,
2322 pull_request=pull_request.pull_request_id,
2322 pull_request=pull_request.pull_request_id,
2323 f_path=None,
2323 f_path=None,
2324 line_no=None,
2324 line_no=None,
2325 status_change=db.ChangesetStatus.get_status_lbl(status),
2325 status_change=db.ChangesetStatus.get_status_lbl(status),
2326 closing_pr=close_pr
2326 closing_pr=close_pr
2327 )
2327 )
2328 userlog.action_logger(apiuser,
2328 userlog.action_logger(apiuser,
2329 'user_commented_pull_request:%s' % pull_request_id,
2329 'user_commented_pull_request:%s' % pull_request_id,
2330 pull_request.org_repo, request.ip_addr)
2330 pull_request.org_repo, request.ip_addr)
2331 if status:
2331 if status:
2332 ChangesetStatusModel().set_status(
2332 ChangesetStatusModel().set_status(
2333 pull_request.org_repo_id,
2333 pull_request.org_repo_id,
2334 status,
2334 status,
2335 apiuser.user_id,
2335 apiuser.user_id,
2336 comment,
2336 comment,
2337 pull_request=pull_request_id
2337 pull_request=pull_request_id
2338 )
2338 )
2339 if close_pr:
2339 if close_pr:
2340 PullRequestModel().close_pull_request(pull_request_id)
2340 PullRequestModel().close_pull_request(pull_request_id)
2341 userlog.action_logger(apiuser,
2341 userlog.action_logger(apiuser,
2342 'user_closed_pull_request:%s' % pull_request_id,
2342 'user_closed_pull_request:%s' % pull_request_id,
2343 pull_request.org_repo, request.ip_addr)
2343 pull_request.org_repo, request.ip_addr)
2344 meta.Session().commit()
2344 meta.Session().commit()
2345 return True
2345 return True
2346
2346
2347 # permission check inside
2347 # permission check inside
2348 def edit_reviewers(self, pull_request_id, add=None, remove=None):
2348 def edit_reviewers(self, pull_request_id, add=None, remove=None):
2349 """
2349 """
2350 Add and/or remove one or more reviewers to a pull request, by username
2350 Add and/or remove one or more reviewers to a pull request, by username
2351 or user ID. Reviewers are specified either as a single-user string or
2351 or user ID. Reviewers are specified either as a single-user string or
2352 as a JSON list of one or more strings.
2352 as a JSON list of one or more strings.
2353 """
2353 """
2354 if add is None and remove is None:
2354 if add is None and remove is None:
2355 raise JSONRPCError('''Invalid request. Neither 'add' nor 'remove' is specified.''')
2355 raise JSONRPCError('''Invalid request. Neither 'add' nor 'remove' is specified.''')
2356
2356
2357 pull_request = db.PullRequest.get(pull_request_id)
2357 pull_request = db.PullRequest.get(pull_request_id)
2358 if pull_request is None:
2358 if pull_request is None:
2359 raise JSONRPCError('pull request `%s` does not exist' % (pull_request_id,))
2359 raise JSONRPCError('pull request `%s` does not exist' % (pull_request_id,))
2360
2360
2361 apiuser = get_user_or_error(request.authuser.user_id)
2361 apiuser = get_user_or_error(request.authuser.user_id)
2362 is_owner = apiuser.user_id == pull_request.owner_id
2362 is_owner = apiuser.user_id == pull_request.owner_id
2363 is_repo_admin = HasRepoPermissionLevel('admin')(pull_request.other_repo.repo_name)
2363 is_repo_admin = HasRepoPermissionLevel('admin')(pull_request.other_repo.repo_name)
2364 if not (apiuser.admin or is_repo_admin or is_owner):
2364 if not (apiuser.admin or is_repo_admin or is_owner):
2365 raise JSONRPCError('No permission to edit reviewers of this pull request. User needs to be admin or pull request owner.')
2365 raise JSONRPCError('No permission to edit reviewers of this pull request. User needs to be admin or pull request owner.')
2366 if pull_request.is_closed():
2366 if pull_request.is_closed():
2367 raise JSONRPCError('Cannot edit reviewers of a closed pull request.')
2367 raise JSONRPCError('Cannot edit reviewers of a closed pull request.')
2368
2368
2369 if not isinstance(add, list):
2369 if not isinstance(add, list):
2370 add = [add]
2370 add = [add]
2371 if not isinstance(remove, list):
2371 if not isinstance(remove, list):
2372 remove = [remove]
2372 remove = [remove]
2373
2373
2374 # look up actual user objects from given name or id. Bail out if unknown.
2374 # look up actual user objects from given name or id. Bail out if unknown.
2375 add_objs = set(get_user_or_error(user) for user in add if user is not None)
2375 add_objs = set(get_user_or_error(user) for user in add if user is not None)
2376 remove_objs = set(get_user_or_error(user) for user in remove if user is not None)
2376 remove_objs = set(get_user_or_error(user) for user in remove if user is not None)
2377
2377
2378 new_reviewers = redundant_reviewers = set()
2378 new_reviewers = redundant_reviewers = set()
2379 if add_objs:
2379 if add_objs:
2380 new_reviewers, redundant_reviewers = PullRequestModel().add_reviewers(apiuser, pull_request, add_objs)
2380 new_reviewers, redundant_reviewers = PullRequestModel().add_reviewers(apiuser, pull_request, add_objs)
2381 if remove_objs:
2381 if remove_objs:
2382 PullRequestModel().remove_reviewers(apiuser, pull_request, remove_objs)
2382 PullRequestModel().remove_reviewers(apiuser, pull_request, remove_objs)
2383
2383
2384 meta.Session().commit()
2384 meta.Session().commit()
2385
2385
2386 return {
2386 return {
2387 'added': [x.username for x in new_reviewers],
2387 'added': [x.username for x in new_reviewers],
2388 'already_present': [x.username for x in redundant_reviewers],
2388 'already_present': [x.username for x in redundant_reviewers],
2389 # NOTE: no explicit check that removed reviewers were actually present.
2389 # NOTE: no explicit check that removed reviewers were actually present.
2390 'removed': [x.username for x in remove_objs],
2390 'removed': [x.username for x in remove_objs],
2391 }
2391 }
@@ -1,839 +1,836 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
15 kallithea.model.repo
16 ~~~~~~~~~~~~~~~~~~~~
16 ~~~~~~~~~~~~~~~~~~~~
17
17
18 Repository model for kallithea
18 Repository 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: Jun 5, 2010
22 :created_on: Jun 5, 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
28
29 import logging
29 import logging
30 import os
30 import os
31 import shutil
31 import shutil
32 import traceback
32 import traceback
33 from datetime import datetime
33 from datetime import datetime
34
34
35 import kallithea.lib.utils2
35 import kallithea.lib.utils2
36 from kallithea.lib import celerylib, hooks, webutils
36 from kallithea.lib import celerylib, hooks, webutils
37 from kallithea.lib.auth import HasRepoPermissionLevel, HasUserGroupPermissionLevel
37 from kallithea.lib.auth import HasRepoPermissionLevel, HasUserGroupPermissionLevel
38 from kallithea.lib.exceptions import AttachedForksError
38 from kallithea.lib.exceptions import AttachedForksError
39 from kallithea.lib.utils import is_valid_repo_uri, make_ui
39 from kallithea.lib.utils import is_valid_repo_uri, make_ui
40 from kallithea.lib.utils2 import LazyProperty, get_current_authuser, obfuscate_url_pw, remove_prefix
40 from kallithea.lib.utils2 import LazyProperty, get_current_authuser, obfuscate_url_pw, remove_prefix
41 from kallithea.lib.vcs.backends import get_backend
41 from kallithea.lib.vcs.backends import get_backend
42 from kallithea.model import db, meta, scm, userlog
42 from kallithea.model import db, meta, scm, userlog
43
43
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47
47
48 class RepoModel(object):
48 class RepoModel(object):
49
49
50 def _create_default_perms(self, repository, private):
50 def _create_default_perms(self, repository, private):
51 # create default permission
51 # create default permission
52 default = 'repository.read'
52 default = 'repository.read'
53 def_user = db.User.get_default_user()
53 def_user = db.User.get_default_user()
54 for p in def_user.user_perms:
54 for p in def_user.user_perms:
55 if p.permission.permission_name.startswith('repository.'):
55 if p.permission.permission_name.startswith('repository.'):
56 default = p.permission.permission_name
56 default = p.permission.permission_name
57 break
57 break
58
58
59 default_perm = 'repository.none' if private else default
59 default_perm = 'repository.none' if private else default
60
60
61 repo_to_perm = db.UserRepoToPerm()
61 repo_to_perm = db.UserRepoToPerm()
62 repo_to_perm.permission = db.Permission.get_by_key(default_perm)
62 repo_to_perm.permission = db.Permission.get_by_key(default_perm)
63
63
64 repo_to_perm.repository = repository
64 repo_to_perm.repository = repository
65 repo_to_perm.user_id = def_user.user_id
65 repo_to_perm.user_id = def_user.user_id
66 meta.Session().add(repo_to_perm)
66 meta.Session().add(repo_to_perm)
67
67
68 return repo_to_perm
68 return repo_to_perm
69
69
70 @LazyProperty
70 @LazyProperty
71 def repos_path(self):
71 def repos_path(self):
72 """
72 """
73 Gets the repositories root path from database
73 Gets the repositories root path from database
74 """
74 """
75
75
76 q = db.Ui.query().filter(db.Ui.ui_key == '/').one()
76 q = db.Ui.query().filter(db.Ui.ui_key == '/').one()
77 return q.ui_value
77 return q.ui_value
78
78
79 def get(self, repo_id):
79 def get(self, repo_id):
80 repo = db.Repository.query() \
80 repo = db.Repository.query() \
81 .filter(db.Repository.repo_id == repo_id)
81 .filter(db.Repository.repo_id == repo_id)
82 return repo.scalar()
82 return repo.scalar()
83
83
84 def get_repo(self, repository):
84 def get_repo(self, repository):
85 return db.Repository.guess_instance(repository)
85 return db.Repository.guess_instance(repository)
86
86
87 def get_by_repo_name(self, repo_name):
87 def get_by_repo_name(self, repo_name):
88 repo = db.Repository.query() \
88 repo = db.Repository.query() \
89 .filter(db.Repository.repo_name == repo_name)
89 .filter(db.Repository.repo_name == repo_name)
90 return repo.scalar()
90 return repo.scalar()
91
91
92 @classmethod
92 @classmethod
93 def _render_datatable(cls, tmpl, *args, **kwargs):
93 def _render_datatable(cls, tmpl, *args, **kwargs):
94 from tg import app_globals, request
94 from tg import app_globals, request
95 from tg import tmpl_context as c
95 from tg import tmpl_context as c
96 from tg.i18n import ugettext as _
96 from tg.i18n import ugettext as _
97
97
98 _tmpl_lookup = app_globals.mako_lookup
98 _tmpl_lookup = app_globals.mako_lookup
99 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
99 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
100
100
101 tmpl = template.get_def(tmpl)
101 tmpl = template.get_def(tmpl)
102 return tmpl.render_unicode(
102 return tmpl.render_unicode(
103 *args,
103 *args,
104 _=_,
104 _=_,
105 webutils=webutils,
105 webutils=webutils,
106 c=c,
106 c=c,
107 request=request,
107 request=request,
108 **kwargs)
108 **kwargs)
109
109
110 def get_repos_as_dict(self, repos_list, repo_groups_list=None,
110 def get_repos_as_dict(self, repos_list, repo_groups_list=None,
111 admin=False,
111 admin=False,
112 short_name=False):
112 short_name=False):
113 """Return repository list for use by DataTable.
113 """Return repository list for use by DataTable.
114 repos_list: list of repositories - but will be filtered for read permission.
114 repos_list: list of repositories - but will be filtered for read permission.
115 repo_groups_list: added at top of list without permission check.
115 repo_groups_list: added at top of list without permission check.
116 admin: return data for action column.
116 admin: return data for action column.
117 """
117 """
118 _render = self._render_datatable
118 _render = self._render_datatable
119 from tg import request
119 from tg import request
120 from tg import tmpl_context as c
120 from tg import tmpl_context as c
121
121
122 def repo_lnk(name, rtype, rstate, private, fork_of):
122 def repo_lnk(name, rtype, rstate, private, fork_of):
123 return _render('repo_name', name, rtype, rstate, private, fork_of,
123 return _render('repo_name', name, rtype, rstate, private, fork_of,
124 short_name=short_name)
124 short_name=short_name)
125
125
126 def following(repo_id, is_following):
126 def following(repo_id, is_following):
127 return _render('following', repo_id, is_following)
127 return _render('following', repo_id, is_following)
128
128
129 def last_change(last_change):
129 def last_change(last_change):
130 return _render("last_change", last_change)
130 return _render("last_change", last_change)
131
131
132 def rss_lnk(repo_name):
132 def rss_lnk(repo_name):
133 return _render("rss", repo_name)
133 return _render("rss", repo_name)
134
134
135 def atom_lnk(repo_name):
135 def atom_lnk(repo_name):
136 return _render("atom", repo_name)
136 return _render("atom", repo_name)
137
137
138 def last_rev(repo_name, cs_cache):
138 def last_rev(repo_name, cs_cache):
139 return _render('revision', repo_name, cs_cache.get('revision'),
139 return _render('revision', repo_name, cs_cache.get('revision'),
140 cs_cache.get('raw_id'), cs_cache.get('author'),
140 cs_cache.get('raw_id'), cs_cache.get('author'),
141 cs_cache.get('message'))
141 cs_cache.get('message'))
142
142
143 def desc(desc):
143 def desc(desc):
144 return webutils.urlify_text(desc, truncate=80, stylize=c.visual.stylify_metalabels)
144 return webutils.urlify_text(desc, truncate=80, stylize=c.visual.stylify_metalabels)
145
145
146 def state(repo_state):
146 def state(repo_state):
147 return _render("repo_state", repo_state)
147 return _render("repo_state", repo_state)
148
148
149 def repo_actions(repo_name):
149 def repo_actions(repo_name):
150 return _render('repo_actions', repo_name)
150 return _render('repo_actions', repo_name)
151
151
152 def owner_actions(owner_id, username):
152 def owner_actions(owner_id, username):
153 return _render('user_name', owner_id, username)
153 return _render('user_name', owner_id, username)
154
154
155 repos_data = []
155 repos_data = []
156
156
157 for gr in repo_groups_list or []:
157 for gr in repo_groups_list or []:
158 repos_data.append(dict(
158 repos_data.append(dict(
159 raw_name='\0' + webutils.html_escape(gr.name), # sort before repositories
159 raw_name='\0' + webutils.html_escape(gr.name), # sort before repositories
160 just_name=webutils.html_escape(gr.name),
160 just_name=webutils.html_escape(gr.name),
161 name=_render('group_name_html', group_name=gr.group_name, name=gr.name),
161 name=_render('group_name_html', group_name=gr.group_name, name=gr.name),
162 desc=desc(gr.group_description)))
162 desc=desc(gr.group_description)))
163
163
164 for repo in repos_list:
164 for repo in repos_list:
165 if not HasRepoPermissionLevel('read')(repo.repo_name, 'get_repos_as_dict check'):
165 if not HasRepoPermissionLevel('read')(repo.repo_name, 'get_repos_as_dict check'):
166 continue
166 continue
167 cs_cache = repo.changeset_cache
167 cs_cache = repo.changeset_cache
168 row = {
168 row = {
169 "raw_name": webutils.html_escape(repo.repo_name),
169 "raw_name": webutils.html_escape(repo.repo_name),
170 "just_name": webutils.html_escape(repo.just_name),
170 "just_name": webutils.html_escape(repo.just_name),
171 "name": repo_lnk(repo.repo_name, repo.repo_type,
171 "name": repo_lnk(repo.repo_name, repo.repo_type,
172 repo.repo_state, repo.private, repo.fork),
172 repo.repo_state, repo.private, repo.fork),
173 "following": following(
173 "following": following(
174 repo.repo_id,
174 repo.repo_id,
175 scm.ScmModel().is_following_repo(repo.repo_name, request.authuser.user_id),
175 scm.ScmModel().is_following_repo(repo.repo_name, request.authuser.user_id),
176 ),
176 ),
177 "last_change_iso": repo.last_db_change.isoformat(),
177 "last_change_iso": repo.last_db_change.isoformat(),
178 "last_change": last_change(repo.last_db_change),
178 "last_change": last_change(repo.last_db_change),
179 "last_changeset": last_rev(repo.repo_name, cs_cache),
179 "last_changeset": last_rev(repo.repo_name, cs_cache),
180 "last_rev_raw": cs_cache.get('revision'),
180 "last_rev_raw": cs_cache.get('revision'),
181 "desc": desc(repo.description),
181 "desc": desc(repo.description),
182 "owner": repo.owner.username,
182 "owner": repo.owner.username,
183 "state": state(repo.repo_state),
183 "state": state(repo.repo_state),
184 "rss": rss_lnk(repo.repo_name),
184 "rss": rss_lnk(repo.repo_name),
185 "atom": atom_lnk(repo.repo_name),
185 "atom": atom_lnk(repo.repo_name),
186 }
186 }
187 if admin:
187 if admin:
188 row.update({
188 row.update({
189 "action": repo_actions(repo.repo_name),
189 "action": repo_actions(repo.repo_name),
190 "owner": owner_actions(repo.owner_id, repo.owner.username)
190 "owner": owner_actions(repo.owner_id, repo.owner.username)
191 })
191 })
192 repos_data.append(row)
192 repos_data.append(row)
193
193
194 return {
194 return {
195 "sort": "name",
195 "sort": "name",
196 "dir": "asc",
196 "dir": "asc",
197 "records": repos_data
197 "records": repos_data
198 }
198 }
199
199
200 def _get_defaults(self, repo_name):
200 def _get_defaults(self, repo_name):
201 """
201 """
202 Gets information about repository, and returns a dict for
202 Gets information about repository, and returns a dict for
203 usage in forms
203 usage in forms
204
204
205 :param repo_name:
205 :param repo_name:
206 """
206 """
207
207
208 repo_info = db.Repository.get_by_repo_name(repo_name)
208 repo_info = db.Repository.get_by_repo_name(repo_name)
209
209
210 if repo_info is None:
210 if repo_info is None:
211 return None
211 return None
212
212
213 defaults = repo_info.get_dict()
213 defaults = repo_info.get_dict()
214 defaults['repo_name'] = repo_info.just_name
214 defaults['repo_name'] = repo_info.just_name
215 defaults['repo_group'] = repo_info.group_id
215 defaults['repo_group'] = repo_info.group_id
216
216
217 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
217 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
218 (1, 'repo_description'),
218 (1, 'repo_description'),
219 (1, 'repo_landing_rev'), (0, 'clone_uri'),
219 (1, 'repo_landing_rev'), (0, 'clone_uri'),
220 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
220 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
221 attr = k
221 attr = k
222 if strip:
222 if strip:
223 attr = remove_prefix(k, 'repo_')
223 attr = remove_prefix(k, 'repo_')
224
224
225 val = defaults[attr]
225 val = defaults[attr]
226 if k == 'repo_landing_rev':
226 if k == 'repo_landing_rev':
227 val = ':'.join(defaults[attr])
227 val = ':'.join(defaults[attr])
228 defaults[k] = val
228 defaults[k] = val
229 if k == 'clone_uri':
229 if k == 'clone_uri':
230 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
230 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
231
231
232 # fill owner
232 # fill owner
233 if repo_info.owner:
233 if repo_info.owner:
234 defaults.update({'owner': repo_info.owner.username})
234 defaults.update({'owner': repo_info.owner.username})
235 else:
235 else:
236 replacement_user = db.User.query().filter(db.User.admin ==
236 replacement_user = db.User.query().filter(db.User.admin ==
237 True).first().username
237 True).first().username
238 defaults.update({'owner': replacement_user})
238 defaults.update({'owner': replacement_user})
239
239
240 # fill repository users
240 # fill repository users
241 for p in repo_info.repo_to_perm:
241 for p in repo_info.repo_to_perm:
242 defaults.update({'u_perm_%s' % p.user.username:
242 defaults.update({'u_perm_%s' % p.user.username:
243 p.permission.permission_name})
243 p.permission.permission_name})
244
244
245 # fill repository groups
245 # fill repository groups
246 for p in repo_info.users_group_to_perm:
246 for p in repo_info.users_group_to_perm:
247 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
247 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
248 p.permission.permission_name})
248 p.permission.permission_name})
249
249
250 return defaults
250 return defaults
251
251
252 def update(self, repo, **kwargs):
252 def update(self, repo, **kwargs):
253 try:
253 try:
254 cur_repo = db.Repository.guess_instance(repo)
254 cur_repo = db.Repository.guess_instance(repo)
255 org_repo_name = cur_repo.repo_name
255 org_repo_name = cur_repo.repo_name
256 if 'owner' in kwargs:
256 if 'owner' in kwargs:
257 cur_repo.owner = db.User.get_by_username(kwargs['owner'])
257 cur_repo.owner = db.User.get_by_username(kwargs['owner'])
258
258
259 if 'repo_group' in kwargs:
259 if 'repo_group' in kwargs:
260 assert kwargs['repo_group'] != '-1', kwargs # RepoForm should have converted to None
260 assert kwargs['repo_group'] != '-1', kwargs # RepoForm should have converted to None
261 cur_repo.group = db.RepoGroup.get(kwargs['repo_group'])
261 cur_repo.group = db.RepoGroup.get(kwargs['repo_group'])
262 cur_repo.repo_name = cur_repo.get_new_name(cur_repo.just_name)
262 cur_repo.repo_name = cur_repo.get_new_name(cur_repo.just_name)
263 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
263 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
264 for k in ['repo_enable_downloads',
264 for k in ['repo_enable_downloads',
265 'repo_description',
265 'repo_description',
266 'repo_landing_rev',
266 'repo_landing_rev',
267 'repo_private',
267 'repo_private',
268 'repo_enable_statistics',
268 'repo_enable_statistics',
269 ]:
269 ]:
270 if k in kwargs:
270 if k in kwargs:
271 setattr(cur_repo, remove_prefix(k, 'repo_'), kwargs[k])
271 setattr(cur_repo, remove_prefix(k, 'repo_'), kwargs[k])
272 clone_uri = kwargs.get('clone_uri')
272 clone_uri = kwargs.get('clone_uri')
273 if clone_uri is not None and clone_uri != cur_repo.clone_uri_hidden:
273 if clone_uri is not None and clone_uri != cur_repo.clone_uri_hidden:
274 # clone_uri is modified - if given a value, check it is valid
274 # clone_uri is modified - if given a value, check it is valid
275 if clone_uri != '':
275 if clone_uri != '':
276 # will raise exception on error
276 # will raise exception on error
277 is_valid_repo_uri(cur_repo.repo_type, clone_uri, make_ui())
277 is_valid_repo_uri(cur_repo.repo_type, clone_uri, make_ui())
278 cur_repo.clone_uri = clone_uri
278 cur_repo.clone_uri = clone_uri
279
279
280 if 'repo_name' in kwargs:
280 if 'repo_name' in kwargs:
281 repo_name = kwargs['repo_name']
281 repo_name = kwargs['repo_name']
282 if kallithea.lib.utils2.repo_name_slug(repo_name) != repo_name:
282 if kallithea.lib.utils2.repo_name_slug(repo_name) != repo_name:
283 raise Exception('invalid repo name %s' % repo_name)
283 raise Exception('invalid repo name %s' % repo_name)
284 cur_repo.repo_name = cur_repo.get_new_name(repo_name)
284 cur_repo.repo_name = cur_repo.get_new_name(repo_name)
285
285
286 # if private flag is set, reset default permission to NONE
286 # if private flag is set, reset default permission to NONE
287 if kwargs.get('repo_private'):
287 if kwargs.get('repo_private'):
288 EMPTY_PERM = 'repository.none'
288 EMPTY_PERM = 'repository.none'
289 RepoModel().grant_user_permission(
289 RepoModel().grant_user_permission(
290 repo=cur_repo, user='default', perm=EMPTY_PERM
290 repo=cur_repo, user='default', perm=EMPTY_PERM
291 )
291 )
292 # handle extra fields
292 # handle extra fields
293 for field in [k for k in kwargs if k.startswith(db.RepositoryField.PREFIX)]:
293 for field in [k for k in kwargs if k.startswith(db.RepositoryField.PREFIX)]:
294 k = db.RepositoryField.un_prefix_key(field)
294 k = db.RepositoryField.un_prefix_key(field)
295 ex_field = db.RepositoryField.get_by_key_name(key=k, repo=cur_repo)
295 ex_field = db.RepositoryField.get_by_key_name(key=k, repo=cur_repo)
296 if ex_field:
296 if ex_field:
297 ex_field.field_value = kwargs[field]
297 ex_field.field_value = kwargs[field]
298
298
299 if org_repo_name != cur_repo.repo_name:
299 if org_repo_name != cur_repo.repo_name:
300 # rename repository
300 # rename repository
301 self._rename_filesystem_repo(old=org_repo_name, new=cur_repo.repo_name)
301 self._rename_filesystem_repo(old=org_repo_name, new=cur_repo.repo_name)
302
302
303 return cur_repo
303 return cur_repo
304 except Exception:
304 except Exception:
305 log.error(traceback.format_exc())
305 log.error(traceback.format_exc())
306 raise
306 raise
307
307
308 def _create_repo(self, repo_name, repo_type, description, owner,
308 def _create_repo(self, repo_name, repo_type, description, owner,
309 private=False, clone_uri=None, repo_group=None,
309 private=False, clone_uri=None, repo_group=None,
310 landing_rev='rev:tip', fork_of=None,
310 landing_rev='rev:tip', fork_of=None,
311 copy_fork_permissions=False, enable_statistics=False,
311 copy_fork_permissions=False, enable_statistics=False,
312 enable_downloads=False,
312 enable_downloads=False,
313 copy_group_permissions=False, state=db.Repository.STATE_PENDING):
313 copy_group_permissions=False, state=db.Repository.STATE_PENDING):
314 """
314 """
315 Create repository inside database with PENDING state. This should only be
315 Create repository inside database with PENDING state. This should only be
316 executed by create() repo, with exception of importing existing repos.
316 executed by create() repo, with exception of importing existing repos.
317
317
318 """
318 """
319 owner = db.User.guess_instance(owner)
319 owner = db.User.guess_instance(owner)
320 fork_of = db.Repository.guess_instance(fork_of)
320 fork_of = db.Repository.guess_instance(fork_of)
321 repo_group = db.RepoGroup.guess_instance(repo_group)
321 repo_group = db.RepoGroup.guess_instance(repo_group)
322 try:
322 try:
323 # repo name is just a name of repository
323 # repo name is just a name of repository
324 # while repo_name_full is a full qualified name that is combined
324 # while repo_name_full is a full qualified name that is combined
325 # with name and path of group
325 # with name and path of group
326 repo_name_full = repo_name
326 repo_name_full = repo_name
327 repo_name = repo_name.split(kallithea.URL_SEP)[-1]
327 repo_name = repo_name.split(kallithea.URL_SEP)[-1]
328 if kallithea.lib.utils2.repo_name_slug(repo_name) != repo_name:
328 if kallithea.lib.utils2.repo_name_slug(repo_name) != repo_name:
329 raise Exception('invalid repo name %s' % repo_name)
329 raise Exception('invalid repo name %s' % repo_name)
330
330
331 new_repo = db.Repository()
331 new_repo = db.Repository()
332 new_repo.repo_state = state
332 new_repo.repo_state = state
333 new_repo.enable_statistics = False
333 new_repo.enable_statistics = False
334 new_repo.repo_name = repo_name_full
334 new_repo.repo_name = repo_name_full
335 new_repo.repo_type = repo_type
335 new_repo.repo_type = repo_type
336 new_repo.owner = owner
336 new_repo.owner = owner
337 new_repo.group = repo_group
337 new_repo.group = repo_group
338 new_repo.description = description or repo_name
338 new_repo.description = description or repo_name
339 new_repo.private = private
339 new_repo.private = private
340 if clone_uri:
340 if clone_uri:
341 # will raise exception on error
341 # will raise exception on error
342 is_valid_repo_uri(repo_type, clone_uri, make_ui())
342 is_valid_repo_uri(repo_type, clone_uri, make_ui())
343 new_repo.clone_uri = clone_uri
343 new_repo.clone_uri = clone_uri
344 new_repo.landing_rev = landing_rev
344 new_repo.landing_rev = landing_rev
345
345
346 new_repo.enable_statistics = enable_statistics
346 new_repo.enable_statistics = enable_statistics
347 new_repo.enable_downloads = enable_downloads
347 new_repo.enable_downloads = enable_downloads
348
348
349 if fork_of:
349 if fork_of:
350 parent_repo = fork_of
350 parent_repo = fork_of
351 new_repo.fork = parent_repo
351 new_repo.fork = parent_repo
352
352
353 meta.Session().add(new_repo)
353 meta.Session().add(new_repo)
354
354
355 if fork_of and copy_fork_permissions:
355 if fork_of and copy_fork_permissions:
356 repo = fork_of
356 repo = fork_of
357 user_perms = db.UserRepoToPerm.query() \
357 user_perms = db.UserRepoToPerm.query() \
358 .filter(db.UserRepoToPerm.repository == repo).all()
358 .filter(db.UserRepoToPerm.repository == repo).all()
359 group_perms = db.UserGroupRepoToPerm.query() \
359 group_perms = db.UserGroupRepoToPerm.query() \
360 .filter(db.UserGroupRepoToPerm.repository == repo).all()
360 .filter(db.UserGroupRepoToPerm.repository == repo).all()
361
361
362 for perm in user_perms:
362 for perm in user_perms:
363 db.UserRepoToPerm.create(perm.user, new_repo, perm.permission)
363 db.UserRepoToPerm.create(perm.user, new_repo, perm.permission)
364
364
365 for perm in group_perms:
365 for perm in group_perms:
366 db.UserGroupRepoToPerm.create(perm.users_group, new_repo,
366 db.UserGroupRepoToPerm.create(perm.users_group, new_repo,
367 perm.permission)
367 perm.permission)
368
368
369 elif repo_group and copy_group_permissions:
369 elif repo_group and copy_group_permissions:
370
370
371 user_perms = db.UserRepoGroupToPerm.query() \
371 user_perms = db.UserRepoGroupToPerm.query() \
372 .filter(db.UserRepoGroupToPerm.group == repo_group).all()
372 .filter(db.UserRepoGroupToPerm.group == repo_group).all()
373
373
374 group_perms = db.UserGroupRepoGroupToPerm.query() \
374 group_perms = db.UserGroupRepoGroupToPerm.query() \
375 .filter(db.UserGroupRepoGroupToPerm.group == repo_group).all()
375 .filter(db.UserGroupRepoGroupToPerm.group == repo_group).all()
376
376
377 for perm in user_perms:
377 for perm in user_perms:
378 perm_name = perm.permission.permission_name.replace('group.', 'repository.')
378 perm_name = perm.permission.permission_name.replace('group.', 'repository.')
379 perm_obj = db.Permission.get_by_key(perm_name)
379 perm_obj = db.Permission.get_by_key(perm_name)
380 db.UserRepoToPerm.create(perm.user, new_repo, perm_obj)
380 db.UserRepoToPerm.create(perm.user, new_repo, perm_obj)
381
381
382 for perm in group_perms:
382 for perm in group_perms:
383 perm_name = perm.permission.permission_name.replace('group.', 'repository.')
383 perm_name = perm.permission.permission_name.replace('group.', 'repository.')
384 perm_obj = db.Permission.get_by_key(perm_name)
384 perm_obj = db.Permission.get_by_key(perm_name)
385 db.UserGroupRepoToPerm.create(perm.users_group, new_repo, perm_obj)
385 db.UserGroupRepoToPerm.create(perm.users_group, new_repo, perm_obj)
386
386
387 else:
387 else:
388 self._create_default_perms(new_repo, private)
388 self._create_default_perms(new_repo, private)
389
389
390 # now automatically start following this repository as owner
390 # now automatically start following this repository as owner
391 scm.ScmModel().toggle_following_repo(new_repo.repo_id, owner.user_id)
391 scm.ScmModel().toggle_following_repo(new_repo.repo_id, owner.user_id)
392 # we need to flush here, in order to check if database won't
392 # we need to flush here, in order to check if database won't
393 # throw any exceptions, create filesystem dirs at the very end
393 # throw any exceptions, create filesystem dirs at the very end
394 meta.Session().flush()
394 meta.Session().flush()
395 return new_repo
395 return new_repo
396 except Exception:
396 except Exception:
397 log.error(traceback.format_exc())
397 log.error(traceback.format_exc())
398 raise
398 raise
399
399
400 def create(self, form_data, cur_user):
400 def create(self, form_data, cur_user):
401 """
401 """
402 Create repository using celery tasks
402 Create repository using celery tasks
403
403
404 :param form_data:
404 :param form_data:
405 :param cur_user:
405 :param cur_user:
406 """
406 """
407 return create_repo(form_data, cur_user)
407 return create_repo(form_data, cur_user)
408
408
409 def _update_permissions(self, repo, perms_new=None, perms_updates=None,
409 def _update_permissions(self, repo, perms_new=None, perms_updates=None,
410 check_perms=True):
410 check_perms=True):
411 if not perms_new:
411 if not perms_new:
412 perms_new = []
412 perms_new = []
413 if not perms_updates:
413 if not perms_updates:
414 perms_updates = []
414 perms_updates = []
415
415
416 # update permissions
416 # update permissions
417 for member, perm, member_type in perms_updates:
417 for member, perm, member_type in perms_updates:
418 if member_type == 'user':
418 if member_type == 'user':
419 # this updates existing one
419 # this updates existing one
420 self.grant_user_permission(
420 self.grant_user_permission(
421 repo=repo, user=member, perm=perm
421 repo=repo, user=member, perm=perm
422 )
422 )
423 else:
423 else:
424 # check if we have permissions to alter this usergroup's access
424 # check if we have permissions to alter this usergroup's access
425 if not check_perms or HasUserGroupPermissionLevel('read')(member):
425 if not check_perms or HasUserGroupPermissionLevel('read')(member):
426 self.grant_user_group_permission(
426 self.grant_user_group_permission(
427 repo=repo, group_name=member, perm=perm
427 repo=repo, group_name=member, perm=perm
428 )
428 )
429 # set new permissions
429 # set new permissions
430 for member, perm, member_type in perms_new:
430 for member, perm, member_type in perms_new:
431 if member_type == 'user':
431 if member_type == 'user':
432 self.grant_user_permission(
432 self.grant_user_permission(
433 repo=repo, user=member, perm=perm
433 repo=repo, user=member, perm=perm
434 )
434 )
435 else:
435 else:
436 # check if we have permissions to alter this usergroup's access
436 # check if we have permissions to alter this usergroup's access
437 if not check_perms or HasUserGroupPermissionLevel('read')(member):
437 if not check_perms or HasUserGroupPermissionLevel('read')(member):
438 self.grant_user_group_permission(
438 self.grant_user_group_permission(
439 repo=repo, group_name=member, perm=perm
439 repo=repo, group_name=member, perm=perm
440 )
440 )
441
441
442 def create_fork(self, form_data, cur_user):
442 def create_fork(self, form_data, cur_user):
443 """
443 """
444 Simple wrapper into executing celery task for fork creation
444 Simple wrapper into executing celery task for fork creation
445
445
446 :param form_data:
446 :param form_data:
447 :param cur_user:
447 :param cur_user:
448 """
448 """
449 return create_repo_fork(form_data, cur_user)
449 return create_repo_fork(form_data, cur_user)
450
450
451 def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
451 def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
452 """
452 """
453 Delete given repository, forks parameter defines what do do with
453 Delete given repository, forks parameter defines what do do with
454 attached forks. Throws AttachedForksError if deleted repo has attached
454 attached forks. Throws AttachedForksError if deleted repo has attached
455 forks
455 forks
456
456
457 :param repo:
457 :param repo:
458 :param forks: str 'delete' or 'detach'
458 :param forks: str 'delete' or 'detach'
459 :param fs_remove: remove(archive) repo from filesystem
459 :param fs_remove: remove(archive) repo from filesystem
460 """
460 """
461 if not cur_user:
461 if not cur_user:
462 cur_user = getattr(get_current_authuser(), 'username', None)
462 cur_user = getattr(get_current_authuser(), 'username', None)
463 repo = db.Repository.guess_instance(repo)
463 repo = db.Repository.guess_instance(repo)
464 if repo is not None:
464 if repo is not None:
465 if forks == 'detach':
465 if forks == 'detach':
466 for r in repo.forks:
466 for r in repo.forks:
467 r.fork = None
467 r.fork = None
468 elif forks == 'delete':
468 elif forks == 'delete':
469 for r in repo.forks:
469 for r in repo.forks:
470 self.delete(r, forks='delete')
470 self.delete(r, forks='delete')
471 elif [f for f in repo.forks]:
471 elif [f for f in repo.forks]:
472 raise AttachedForksError()
472 raise AttachedForksError()
473
473
474 old_repo_dict = repo.get_dict()
474 old_repo_dict = repo.get_dict()
475 try:
475 try:
476 meta.Session().delete(repo)
476 meta.Session().delete(repo)
477 if fs_remove:
477 if fs_remove:
478 self._delete_filesystem_repo(repo)
478 self._delete_filesystem_repo(repo)
479 else:
479 else:
480 log.debug('skipping removal from filesystem')
480 log.debug('skipping removal from filesystem')
481 hooks.log_delete_repository(old_repo_dict,
481 hooks.log_delete_repository(old_repo_dict,
482 deleted_by=cur_user)
482 deleted_by=cur_user)
483 except Exception:
483 except Exception:
484 log.error(traceback.format_exc())
484 log.error(traceback.format_exc())
485 raise
485 raise
486
486
487 def grant_user_permission(self, repo, user, perm):
487 def grant_user_permission(self, repo, user, perm):
488 """
488 """
489 Grant permission for user on given repository, or update existing one
489 Grant permission for user on given repository, or update existing one
490 if found
490 if found
491
491
492 :param repo: Instance of Repository, repository_id, or repository name
492 :param repo: Instance of Repository, repository_id, or repository name
493 :param user: Instance of User, user_id or username
493 :param user: Instance of User, user_id or username
494 :param perm: Instance of Permission, or permission_name
494 :param perm: Instance of Permission, or permission_name
495 """
495 """
496 user = db.User.guess_instance(user)
496 user = db.User.guess_instance(user)
497 repo = db.Repository.guess_instance(repo)
497 repo = db.Repository.guess_instance(repo)
498 permission = db.Permission.guess_instance(perm)
498 permission = db.Permission.guess_instance(perm)
499
499
500 # check if we have that permission already
500 # check if we have that permission already
501 obj = db.UserRepoToPerm.query() \
501 obj = db.UserRepoToPerm.query() \
502 .filter(db.UserRepoToPerm.user == user) \
502 .filter(db.UserRepoToPerm.user == user) \
503 .filter(db.UserRepoToPerm.repository == repo) \
503 .filter(db.UserRepoToPerm.repository == repo) \
504 .scalar()
504 .scalar()
505 if obj is None:
505 if obj is None:
506 # create new !
506 # create new !
507 obj = db.UserRepoToPerm()
507 obj = db.UserRepoToPerm()
508 meta.Session().add(obj)
508 meta.Session().add(obj)
509 obj.repository = repo
509 obj.repository = repo
510 obj.user = user
510 obj.user = user
511 obj.permission = permission
511 obj.permission = permission
512 log.debug('Granted perm %s to %s on %s', perm, user, repo)
512 log.debug('Granted perm %s to %s on %s', perm, user, repo)
513 return obj
513 return obj
514
514
515 def revoke_user_permission(self, repo, user):
515 def revoke_user_permission(self, repo, user):
516 """
516 """
517 Revoke permission for user on given repository
517 Revoke permission for user on given repository
518
518
519 :param repo: Instance of Repository, repository_id, or repository name
519 :param repo: Instance of Repository, repository_id, or repository name
520 :param user: Instance of User, user_id or username
520 :param user: Instance of User, user_id or username
521 """
521 """
522
522
523 user = db.User.guess_instance(user)
523 user = db.User.guess_instance(user)
524 repo = db.Repository.guess_instance(repo)
524 repo = db.Repository.guess_instance(repo)
525
525
526 obj = db.UserRepoToPerm.query() \
526 obj = db.UserRepoToPerm.query() \
527 .filter(db.UserRepoToPerm.repository == repo) \
527 .filter(db.UserRepoToPerm.repository == repo) \
528 .filter(db.UserRepoToPerm.user == user) \
528 .filter(db.UserRepoToPerm.user == user) \
529 .scalar()
529 .scalar()
530 if obj is not None:
530 if obj is not None:
531 meta.Session().delete(obj)
531 meta.Session().delete(obj)
532 log.debug('Revoked perm on %s on %s', repo, user)
532 log.debug('Revoked perm on %s on %s', repo, user)
533
533
534 def grant_user_group_permission(self, repo, group_name, perm):
534 def grant_user_group_permission(self, repo, group_name, perm):
535 """
535 """
536 Grant permission for user group on given repository, or update
536 Grant permission for user group on given repository, or update
537 existing one if found
537 existing one if found
538
538
539 :param repo: Instance of Repository, repository_id, or repository name
539 :param repo: Instance of Repository, repository_id, or repository name
540 :param group_name: Instance of UserGroup, users_group_id,
540 :param group_name: Instance of UserGroup, users_group_id,
541 or user group name
541 or user group name
542 :param perm: Instance of Permission, or permission_name
542 :param perm: Instance of Permission, or permission_name
543 """
543 """
544 repo = db.Repository.guess_instance(repo)
544 repo = db.Repository.guess_instance(repo)
545 group_name = db.UserGroup.guess_instance(group_name)
545 group_name = db.UserGroup.guess_instance(group_name)
546 permission = db.Permission.guess_instance(perm)
546 permission = db.Permission.guess_instance(perm)
547
547
548 # check if we have that permission already
548 # check if we have that permission already
549 obj = db.UserGroupRepoToPerm.query() \
549 obj = db.UserGroupRepoToPerm.query() \
550 .filter(db.UserGroupRepoToPerm.users_group == group_name) \
550 .filter(db.UserGroupRepoToPerm.users_group == group_name) \
551 .filter(db.UserGroupRepoToPerm.repository == repo) \
551 .filter(db.UserGroupRepoToPerm.repository == repo) \
552 .scalar()
552 .scalar()
553
553
554 if obj is None:
554 if obj is None:
555 # create new
555 # create new
556 obj = db.UserGroupRepoToPerm()
556 obj = db.UserGroupRepoToPerm()
557 meta.Session().add(obj)
557 meta.Session().add(obj)
558
558
559 obj.repository = repo
559 obj.repository = repo
560 obj.users_group = group_name
560 obj.users_group = group_name
561 obj.permission = permission
561 obj.permission = permission
562 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
562 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
563 return obj
563 return obj
564
564
565 def revoke_user_group_permission(self, repo, group_name):
565 def revoke_user_group_permission(self, repo, group_name):
566 """
566 """
567 Revoke permission for user group on given repository
567 Revoke permission for user group on given repository
568
568
569 :param repo: Instance of Repository, repository_id, or repository name
569 :param repo: Instance of Repository, repository_id, or repository name
570 :param group_name: Instance of UserGroup, users_group_id,
570 :param group_name: Instance of UserGroup, users_group_id,
571 or user group name
571 or user group name
572 """
572 """
573 repo = db.Repository.guess_instance(repo)
573 repo = db.Repository.guess_instance(repo)
574 group_name = db.UserGroup.guess_instance(group_name)
574 group_name = db.UserGroup.guess_instance(group_name)
575
575
576 obj = db.UserGroupRepoToPerm.query() \
576 obj = db.UserGroupRepoToPerm.query() \
577 .filter(db.UserGroupRepoToPerm.repository == repo) \
577 .filter(db.UserGroupRepoToPerm.repository == repo) \
578 .filter(db.UserGroupRepoToPerm.users_group == group_name) \
578 .filter(db.UserGroupRepoToPerm.users_group == group_name) \
579 .scalar()
579 .scalar()
580 if obj is not None:
580 if obj is not None:
581 meta.Session().delete(obj)
581 meta.Session().delete(obj)
582 log.debug('Revoked perm to %s on %s', repo, group_name)
582 log.debug('Revoked perm to %s on %s', repo, group_name)
583
583
584 def delete_stats(self, repo_name):
584 def delete_stats(self, repo_name):
585 """
585 """
586 removes stats for given repo
586 removes stats for given repo
587
587
588 :param repo_name:
588 :param repo_name:
589 """
589 """
590 repo = db.Repository.guess_instance(repo_name)
590 repo = db.Repository.guess_instance(repo_name)
591 try:
591 try:
592 obj = db.Statistics.query() \
592 obj = db.Statistics.query() \
593 .filter(db.Statistics.repository == repo).scalar()
593 .filter(db.Statistics.repository == repo).scalar()
594 if obj is not None:
594 if obj is not None:
595 meta.Session().delete(obj)
595 meta.Session().delete(obj)
596 except Exception:
596 except Exception:
597 log.error(traceback.format_exc())
597 log.error(traceback.format_exc())
598 raise
598 raise
599
599
600 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
600 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
601 clone_uri=None, repo_store_location=None):
601 clone_uri=None, repo_store_location=None):
602 """
602 """
603 Makes repository on filesystem. Operation is group aware, meaning that it will create
603 Makes repository on filesystem. Operation is group aware, meaning that it will create
604 a repository within a group, and alter the paths accordingly to the group location.
604 a repository within a group, and alter the paths accordingly to the group location.
605
605
606 Note: clone_uri is low level and not validated - it might be a file system path used for validated cloning
606 Note: clone_uri is low level and not validated - it might be a file system path used for validated cloning
607 """
607 """
608 from kallithea.lib.utils import is_valid_repo, is_valid_repo_group
608 from kallithea.lib.utils import is_valid_repo, is_valid_repo_group
609
609
610 if '/' in repo_name:
610 if '/' in repo_name:
611 raise ValueError('repo_name must not contain groups got `%s`' % repo_name)
611 raise ValueError('repo_name must not contain groups got `%s`' % repo_name)
612
612
613 if isinstance(repo_group, db.RepoGroup):
613 if isinstance(repo_group, db.RepoGroup):
614 new_parent_path = os.sep.join(repo_group.full_path_splitted)
614 new_parent_path = os.sep.join(repo_group.full_path_splitted)
615 else:
615 else:
616 new_parent_path = repo_group or ''
616 new_parent_path = repo_group or ''
617
617
618 if repo_store_location:
618 if repo_store_location:
619 _paths = [repo_store_location]
619 _paths = [repo_store_location]
620 else:
620 else:
621 _paths = [self.repos_path, new_parent_path, repo_name]
621 _paths = [self.repos_path, new_parent_path, repo_name]
622 repo_path = os.path.join(*_paths)
622 repo_path = os.path.join(*_paths)
623
623
624 # check if this path is not a repository
624 # check if this path is not a repository
625 if is_valid_repo(repo_path, self.repos_path):
625 if is_valid_repo(repo_path, self.repos_path):
626 raise Exception('This path %s is a valid repository' % repo_path)
626 raise Exception('This path %s is a valid repository' % repo_path)
627
627
628 # check if this path is a group
628 # check if this path is a group
629 if is_valid_repo_group(repo_path, self.repos_path):
629 if is_valid_repo_group(repo_path, self.repos_path):
630 raise Exception('This path %s is a valid group' % repo_path)
630 raise Exception('This path %s is a valid group' % repo_path)
631
631
632 log.info('creating repo %s in %s from url: `%s`',
632 log.info('creating repo %s in %s from url: `%s`',
633 repo_name, repo_path,
633 repo_name, repo_path,
634 obfuscate_url_pw(clone_uri))
634 obfuscate_url_pw(clone_uri))
635
635
636 backend = get_backend(repo_type)
636 backend = get_backend(repo_type)
637
637
638 if repo_type == 'hg':
638 if repo_type == 'hg':
639 baseui = make_ui()
639 baseui = make_ui()
640 # patch and reset hooks section of UI config to not run any
640 # patch and reset hooks section of UI config to not run any
641 # hooks on creating remote repo
641 # hooks on creating remote repo
642 for k, v in baseui.configitems('hooks'):
642 for k, v in baseui.configitems('hooks'):
643 baseui.setconfig('hooks', k, None)
643 baseui.setconfig('hooks', k, None)
644
644
645 repo = backend(repo_path, create=True, src_url=clone_uri, baseui=baseui)
645 repo = backend(repo_path, create=True, src_url=clone_uri, baseui=baseui)
646 elif repo_type == 'git':
646 elif repo_type == 'git':
647 repo = backend(repo_path, create=True, src_url=clone_uri, bare=True)
647 repo = backend(repo_path, create=True, src_url=clone_uri, bare=True)
648 # add kallithea hook into this repo
648 # add kallithea hook into this repo
649 scm.ScmModel().install_git_hooks(repo)
649 scm.ScmModel().install_git_hooks(repo)
650 else:
650 else:
651 raise Exception('Not supported repo_type %s expected hg/git' % repo_type)
651 raise Exception('Not supported repo_type %s expected hg/git' % repo_type)
652
652
653 log.debug('Created repo %s with %s backend',
653 log.debug('Created repo %s with %s backend',
654 repo_name, repo_type)
654 repo_name, repo_type)
655 return repo
655 return repo
656
656
657 def _rename_filesystem_repo(self, old, new):
657 def _rename_filesystem_repo(self, old, new):
658 """
658 """
659 renames repository on filesystem
659 renames repository on filesystem
660
660
661 :param old: old name
661 :param old: old name
662 :param new: new name
662 :param new: new name
663 """
663 """
664 log.info('renaming repo from %s to %s', old, new)
664 log.info('renaming repo from %s to %s', old, new)
665
665
666 old_path = os.path.join(self.repos_path, old)
666 old_path = os.path.join(self.repos_path, old)
667 new_path = os.path.join(self.repos_path, new)
667 new_path = os.path.join(self.repos_path, new)
668 if os.path.isdir(new_path):
668 if os.path.isdir(new_path):
669 raise Exception(
669 raise Exception(
670 'Was trying to rename to already existing dir %s' % new_path
670 'Was trying to rename to already existing dir %s' % new_path
671 )
671 )
672 shutil.move(old_path, new_path)
672 shutil.move(old_path, new_path)
673
673
674 def _delete_filesystem_repo(self, repo):
674 def _delete_filesystem_repo(self, repo):
675 """
675 """
676 removes repo from filesystem, the removal is actually done by
676 removes repo from filesystem, the removal is actually done by
677 renaming dir to a 'rm__*' prefix which Kallithea will skip.
677 renaming dir to a 'rm__*' prefix which Kallithea will skip.
678 It can be undeleted later by reverting the rename.
678 It can be undeleted later by reverting the rename.
679
679
680 :param repo: repo object
680 :param repo: repo object
681 """
681 """
682 rm_path = os.path.join(self.repos_path, repo.repo_name)
682 rm_path = os.path.join(self.repos_path, repo.repo_name)
683 log.info("Removing %s", rm_path)
683 log.info("Removing %s", rm_path)
684
684
685 _now = datetime.now()
685 _now = datetime.now()
686 _ms = str(_now.microsecond).rjust(6, '0')
686 _ms = str(_now.microsecond).rjust(6, '0')
687 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
687 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
688 repo.just_name)
688 repo.just_name)
689 if repo.group:
689 if repo.group:
690 args = repo.group.full_path_splitted + [_d]
690 args = repo.group.full_path_splitted + [_d]
691 _d = os.path.join(*args)
691 _d = os.path.join(*args)
692 if os.path.exists(rm_path):
692 if os.path.exists(rm_path):
693 shutil.move(rm_path, os.path.join(self.repos_path, _d))
693 shutil.move(rm_path, os.path.join(self.repos_path, _d))
694 else:
694 else:
695 log.error("Can't find repo to delete in %r", rm_path)
695 log.error("Can't find repo to delete in %r", rm_path)
696
696
697
697
698 @celerylib.task
698 @celerylib.task
699 def create_repo(form_data, cur_user):
699 def create_repo(form_data, cur_user):
700 cur_user = db.User.guess_instance(cur_user)
700 cur_user = db.User.guess_instance(cur_user)
701
701
702 owner = cur_user
702 owner = cur_user
703 repo_name = form_data['repo_name']
703 repo_name = form_data['repo_name']
704 repo_name_full = form_data['repo_name_full']
704 repo_name_full = form_data['repo_name_full']
705 repo_type = form_data['repo_type']
705 repo_type = form_data['repo_type']
706 description = form_data['repo_description']
706 description = form_data['repo_description']
707 private = form_data['repo_private']
707 private = form_data['repo_private']
708 clone_uri = form_data.get('clone_uri')
708 clone_uri = form_data.get('clone_uri')
709 repo_group = form_data['repo_group']
709 repo_group = form_data['repo_group']
710 landing_rev = form_data['repo_landing_rev']
710 landing_rev = form_data['repo_landing_rev']
711 copy_fork_permissions = form_data.get('copy_permissions')
711 copy_fork_permissions = form_data.get('copy_permissions')
712 copy_group_permissions = form_data.get('repo_copy_permissions')
712 copy_group_permissions = form_data.get('repo_copy_permissions')
713 fork_of = form_data.get('fork_parent_id')
713 fork_of = form_data.get('fork_parent_id')
714 enable_statistics = form_data['repo_enable_statistics']
715 enable_downloads = form_data['repo_enable_downloads']
714 state = form_data.get('repo_state', db.Repository.STATE_PENDING)
716 state = form_data.get('repo_state', db.Repository.STATE_PENDING)
715
717
716 # repo creation defaults, private and repo_type are filled in form
717 defs = db.Setting.get_default_repo_settings(strip_prefix=True)
718 enable_statistics = defs.get('repo_enable_statistics')
719 enable_downloads = defs.get('repo_enable_downloads')
720
721 try:
718 try:
722 db_repo = RepoModel()._create_repo(
719 db_repo = RepoModel()._create_repo(
723 repo_name=repo_name_full,
720 repo_name=repo_name_full,
724 repo_type=repo_type,
721 repo_type=repo_type,
725 description=description,
722 description=description,
726 owner=owner,
723 owner=owner,
727 private=private,
724 private=private,
728 clone_uri=clone_uri,
725 clone_uri=clone_uri,
729 repo_group=repo_group,
726 repo_group=repo_group,
730 landing_rev=landing_rev,
727 landing_rev=landing_rev,
731 fork_of=fork_of,
728 fork_of=fork_of,
732 copy_fork_permissions=copy_fork_permissions,
729 copy_fork_permissions=copy_fork_permissions,
733 copy_group_permissions=copy_group_permissions,
730 copy_group_permissions=copy_group_permissions,
734 enable_statistics=enable_statistics,
731 enable_statistics=enable_statistics,
735 enable_downloads=enable_downloads,
732 enable_downloads=enable_downloads,
736 state=state
733 state=state
737 )
734 )
738
735
739 userlog.action_logger(cur_user, 'user_created_repo',
736 userlog.action_logger(cur_user, 'user_created_repo',
740 form_data['repo_name_full'], '')
737 form_data['repo_name_full'], '')
741
738
742 meta.Session().commit()
739 meta.Session().commit()
743 # now create this repo on Filesystem
740 # now create this repo on Filesystem
744 RepoModel()._create_filesystem_repo(
741 RepoModel()._create_filesystem_repo(
745 repo_name=repo_name,
742 repo_name=repo_name,
746 repo_type=repo_type,
743 repo_type=repo_type,
747 repo_group=db.RepoGroup.guess_instance(repo_group),
744 repo_group=db.RepoGroup.guess_instance(repo_group),
748 clone_uri=clone_uri,
745 clone_uri=clone_uri,
749 )
746 )
750 db_repo = db.Repository.get_by_repo_name(repo_name_full)
747 db_repo = db.Repository.get_by_repo_name(repo_name_full)
751 hooks.log_create_repository(db_repo.get_dict(), created_by=owner.username)
748 hooks.log_create_repository(db_repo.get_dict(), created_by=owner.username)
752
749
753 # update repo changeset caches initially
750 # update repo changeset caches initially
754 db_repo.update_changeset_cache()
751 db_repo.update_changeset_cache()
755
752
756 # set new created state
753 # set new created state
757 db_repo.set_state(db.Repository.STATE_CREATED)
754 db_repo.set_state(db.Repository.STATE_CREATED)
758 meta.Session().commit()
755 meta.Session().commit()
759 except Exception as e:
756 except Exception as e:
760 log.warning('Exception %s occurred when forking repository, '
757 log.warning('Exception %s occurred when forking repository, '
761 'doing cleanup...' % e)
758 'doing cleanup...' % e)
762 # rollback things manually !
759 # rollback things manually !
763 db_repo = db.Repository.get_by_repo_name(repo_name_full)
760 db_repo = db.Repository.get_by_repo_name(repo_name_full)
764 if db_repo:
761 if db_repo:
765 db.Repository.delete(db_repo.repo_id)
762 db.Repository.delete(db_repo.repo_id)
766 meta.Session().commit()
763 meta.Session().commit()
767 RepoModel()._delete_filesystem_repo(db_repo)
764 RepoModel()._delete_filesystem_repo(db_repo)
768 raise
765 raise
769
766
770
767
771 @celerylib.task
768 @celerylib.task
772 def create_repo_fork(form_data, cur_user):
769 def create_repo_fork(form_data, cur_user):
773 """
770 """
774 Creates a fork of repository using interval VCS methods
771 Creates a fork of repository using interval VCS methods
775
772
776 :param form_data:
773 :param form_data:
777 :param cur_user:
774 :param cur_user:
778 """
775 """
779 base_path = kallithea.CONFIG['base_path']
776 base_path = kallithea.CONFIG['base_path']
780 cur_user = db.User.guess_instance(cur_user)
777 cur_user = db.User.guess_instance(cur_user)
781
778
782 repo_name = form_data['repo_name'] # fork in this case
779 repo_name = form_data['repo_name'] # fork in this case
783 repo_name_full = form_data['repo_name_full']
780 repo_name_full = form_data['repo_name_full']
784
781
785 repo_type = form_data['repo_type']
782 repo_type = form_data['repo_type']
786 owner = cur_user
783 owner = cur_user
787 private = form_data['private']
784 private = form_data['private']
788 clone_uri = form_data.get('clone_uri')
785 clone_uri = form_data.get('clone_uri')
789 repo_group = form_data['repo_group']
786 repo_group = form_data['repo_group']
790 landing_rev = form_data['landing_rev']
787 landing_rev = form_data['landing_rev']
791 copy_fork_permissions = form_data.get('copy_permissions')
788 copy_fork_permissions = form_data.get('copy_permissions')
792
789
793 try:
790 try:
794 fork_of = db.Repository.guess_instance(form_data.get('fork_parent_id'))
791 fork_of = db.Repository.guess_instance(form_data.get('fork_parent_id'))
795
792
796 RepoModel()._create_repo(
793 RepoModel()._create_repo(
797 repo_name=repo_name_full,
794 repo_name=repo_name_full,
798 repo_type=repo_type,
795 repo_type=repo_type,
799 description=form_data['description'],
796 description=form_data['description'],
800 owner=owner,
797 owner=owner,
801 private=private,
798 private=private,
802 clone_uri=clone_uri,
799 clone_uri=clone_uri,
803 repo_group=repo_group,
800 repo_group=repo_group,
804 landing_rev=landing_rev,
801 landing_rev=landing_rev,
805 fork_of=fork_of,
802 fork_of=fork_of,
806 copy_fork_permissions=copy_fork_permissions
803 copy_fork_permissions=copy_fork_permissions
807 )
804 )
808 userlog.action_logger(cur_user, 'user_forked_repo:%s' % repo_name_full,
805 userlog.action_logger(cur_user, 'user_forked_repo:%s' % repo_name_full,
809 fork_of.repo_name, '')
806 fork_of.repo_name, '')
810 meta.Session().commit()
807 meta.Session().commit()
811
808
812 source_repo_path = os.path.join(base_path, fork_of.repo_name)
809 source_repo_path = os.path.join(base_path, fork_of.repo_name)
813
810
814 # now create this repo on Filesystem
811 # now create this repo on Filesystem
815 RepoModel()._create_filesystem_repo(
812 RepoModel()._create_filesystem_repo(
816 repo_name=repo_name,
813 repo_name=repo_name,
817 repo_type=repo_type,
814 repo_type=repo_type,
818 repo_group=db.RepoGroup.guess_instance(repo_group),
815 repo_group=db.RepoGroup.guess_instance(repo_group),
819 clone_uri=source_repo_path,
816 clone_uri=source_repo_path,
820 )
817 )
821 db_repo = db.Repository.get_by_repo_name(repo_name_full)
818 db_repo = db.Repository.get_by_repo_name(repo_name_full)
822 hooks.log_create_repository(db_repo.get_dict(), created_by=owner.username)
819 hooks.log_create_repository(db_repo.get_dict(), created_by=owner.username)
823
820
824 # update repo changeset caches initially
821 # update repo changeset caches initially
825 db_repo.update_changeset_cache()
822 db_repo.update_changeset_cache()
826
823
827 # set new created state
824 # set new created state
828 db_repo.set_state(db.Repository.STATE_CREATED)
825 db_repo.set_state(db.Repository.STATE_CREATED)
829 meta.Session().commit()
826 meta.Session().commit()
830 except Exception as e:
827 except Exception as e:
831 log.warning('Exception %s occurred when forking repository, '
828 log.warning('Exception %s occurred when forking repository, '
832 'doing cleanup...' % e)
829 'doing cleanup...' % e)
833 # rollback things manually !
830 # rollback things manually !
834 db_repo = db.Repository.get_by_repo_name(repo_name_full)
831 db_repo = db.Repository.get_by_repo_name(repo_name_full)
835 if db_repo:
832 if db_repo:
836 db.Repository.delete(db_repo.repo_id)
833 db.Repository.delete(db_repo.repo_id)
837 meta.Session().commit()
834 meta.Session().commit()
838 RepoModel()._delete_filesystem_repo(db_repo)
835 RepoModel()._delete_filesystem_repo(db_repo)
839 raise
836 raise
@@ -1,97 +1,111 b''
1 ${h.form(url('repos'))}
1 ${h.form(url('repos'))}
2 <div class="form">
2 <div class="form">
3 <div class="form-group">
3 <div class="form-group">
4 <label class="control-label" for="repo_name">${_('Name')}:</label>
4 <label class="control-label" for="repo_name">${_('Name')}:</label>
5 <div>
5 <div>
6 ${h.text('repo_name',class_='form-control')}
6 ${h.text('repo_name',class_='form-control')}
7 </div>
7 </div>
8 </div>
8 </div>
9 <div class="form-group">
9 <div class="form-group">
10 <label class="control-label" for="repo_type">${_('Type')}:</label>
10 <label class="control-label" for="repo_type">${_('Type')}:</label>
11 <div>
11 <div>
12 ${h.select('repo_type','hg',c.backends,class_='form-control')}
12 ${h.select('repo_type','hg',c.backends,class_='form-control')}
13 <span class="help-block">${_('Type of repository to create.')}</span>
13 <span class="help-block">${_('Type of repository to create.')}</span>
14 </div>
14 </div>
15 </div>
15 </div>
16 <div class="form-group">
16 <div class="form-group">
17 <label class="control-label" for="clone_uri">${_('Clone remote repository')}:</label>
17 <label class="control-label" for="clone_uri">${_('Clone remote repository')}:</label>
18 <div>
18 <div>
19 ${h.text('clone_uri',class_='form-control', placeholder=_('Repository URL'))}
19 ${h.text('clone_uri',class_='form-control', placeholder=_('Repository URL'))}
20 <span class="help-block">
20 <span class="help-block">
21 ${_('Optional: URL of a remote repository. If set, the repository will be created as a clone from this URL.')}
21 ${_('Optional: URL of a remote repository. If set, the repository will be created as a clone from this URL.')}
22 </span>
22 </span>
23 </div>
23 </div>
24 </div>
24 </div>
25 <div class="form-group">
25 <div class="form-group">
26 <label class="control-label" for="repo_description">${_('Description')}:</label>
26 <label class="control-label" for="repo_description">${_('Description')}:</label>
27 <div>
27 <div>
28 ${h.textarea('repo_description',class_='form-control')}
28 ${h.textarea('repo_description',class_='form-control')}
29 <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
29 <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
30 </div>
30 </div>
31 </div>
31 </div>
32 <div class="form-group">
32 <div class="form-group">
33 <label class="control-label" for="repo_group">${_('Repository group')}:</label>
33 <label class="control-label" for="repo_group">${_('Repository group')}:</label>
34 <div>
34 <div>
35 ${h.select('repo_group',None,c.repo_groups,class_='form-control')}
35 ${h.select('repo_group',None,c.repo_groups,class_='form-control')}
36 <span class="help-block">${_('Optionally select a group to put this repository into.')}</span>
36 <span class="help-block">${_('Optionally select a group to put this repository into.')}</span>
37 </div>
37 </div>
38 </div>
38 </div>
39 <div id="copy_perms" class="form-group">
39 <div id="copy_perms" class="form-group">
40 <label class="control-label" for="repo_copy_permissions">${_('Copy parent group permissions')}:</label>
40 <label class="control-label" for="repo_copy_permissions">${_('Copy parent group permissions')}:</label>
41 <div>
41 <div>
42 ${h.checkbox('repo_copy_permissions',value="True")}
42 ${h.checkbox('repo_copy_permissions',value="True")}
43 <span class="help-block">${_('Copy permission set from parent repository group.')}</span>
43 <span class="help-block">${_('Copy permission set from parent repository group.')}</span>
44 </div>
44 </div>
45 </div>
45 </div>
46 <div class="form-group">
46 <div class="form-group">
47 <label class="control-label" for="repo_landing_rev">${_('Landing revision')}:</label>
47 <label class="control-label" for="repo_landing_rev">${_('Landing revision')}:</label>
48 <div>
48 <div>
49 ${h.select('repo_landing_rev',None,c.landing_revs,class_='form-control')}
49 ${h.select('repo_landing_rev',None,c.landing_revs,class_='form-control')}
50 <span class="help-block">${_('Default revision for files page, downloads, full text search index and readme generation')}</span>
50 <span class="help-block">${_('Default revision for files page, downloads, full text search index and readme generation')}</span>
51 </div>
51 </div>
52 </div>
52 </div>
53 <div class="form-group">
53 <div class="form-group">
54 <label class="control-label" for="repo_private">${_('Private repository')}:</label>
54 <label class="control-label" for="repo_private">${_('Private repository')}:</label>
55 <div>
55 <div>
56 ${h.checkbox('repo_private',value="True")}
56 ${h.checkbox('repo_private',value="True")}
57 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
57 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
58 </div>
58 </div>
59 </div>
59 </div>
60 <div class="form-group">
60 <div class="form-group">
61 <label class="control-label" for="repo_enable_statistics">${_('Enable statistics')}:</label>
62 <div>
63 ${h.checkbox('repo_enable_statistics',value="True")}
64 <span class="help-block">${_('Enable statistics window on summary page.')}</span>
65 </div>
66 </div>
67 <div class="form-group">
68 <label class="control-label" for="repo_enable_downloads">${_('Enable downloads')}:</label>
69 <div>
70 ${h.checkbox('repo_enable_downloads',value="True")}
71 <span class="help-block">${_('Enable download menu on summary page.')}</span>
72 </div>
73 </div>
74 <div class="form-group">
61 <div class="buttons">
75 <div class="buttons">
62 ${h.submit('add',_('Add'),class_="btn btn-default")}
76 ${h.submit('add',_('Add'),class_="btn btn-default")}
63 </div>
77 </div>
64 </div>
78 </div>
65 </div>
79 </div>
66 ${h.end_form()}
80 ${h.end_form()}
67
81
68 <script>
82 <script>
69 'use strict';
83 'use strict';
70 $(document).ready(function(){
84 $(document).ready(function(){
71 $('#repo_type').select2({
85 $('#repo_type').select2({
72 'minimumResultsForSearch': -1
86 'minimumResultsForSearch': -1
73 });
87 });
74 $('#repo_group').select2({
88 $('#repo_group').select2({
75 'dropdownAutoWidth': true
89 'dropdownAutoWidth': true
76 });
90 });
77
91
78 function setCopyPermsOption(group_val){
92 function setCopyPermsOption(group_val){
79 if(group_val != "-1"){
93 if(group_val != "-1"){
80 $('#copy_perms').show();
94 $('#copy_perms').show();
81 }
95 }
82 else{
96 else{
83 $('#copy_perms').hide();
97 $('#copy_perms').hide();
84 }
98 }
85 }
99 }
86
100
87 setCopyPermsOption($('#repo_group').val());
101 setCopyPermsOption($('#repo_group').val());
88 $('#repo_group').on("change", function(e) {
102 $('#repo_group').on("change", function(e) {
89 setCopyPermsOption(e.val);
103 setCopyPermsOption(e.val);
90 });
104 });
91
105
92 $('#repo_landing_rev').select2({
106 $('#repo_landing_rev').select2({
93 'minimumResultsForSearch': -1
107 'minimumResultsForSearch': -1
94 });
108 });
95 $('#repo_name').focus();
109 $('#repo_name').focus();
96 });
110 });
97 </script>
111 </script>
@@ -1,2880 +1,2880 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}), # currently broken
795 ('enable_statistics', {'enable_statistics': True}),
796 #('enable_downloads', {'enable_downloads': True}), # currently broken
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('name,perm,apply_to_children', [
1853 @base.parametrize('name,perm,apply_to_children', [
1854 ('none', 'group.none', 'none'),
1854 ('none', 'group.none', 'none'),
1855 ('read', 'group.read', 'none'),
1855 ('read', 'group.read', 'none'),
1856 ('write', 'group.write', 'none'),
1856 ('write', 'group.write', 'none'),
1857 ('admin', 'group.admin', 'none'),
1857 ('admin', 'group.admin', 'none'),
1858
1858
1859 ('none', 'group.none', 'all'),
1859 ('none', 'group.none', 'all'),
1860 ('read', 'group.read', 'all'),
1860 ('read', 'group.read', 'all'),
1861 ('write', 'group.write', 'all'),
1861 ('write', 'group.write', 'all'),
1862 ('admin', 'group.admin', 'all'),
1862 ('admin', 'group.admin', 'all'),
1863
1863
1864 ('none', 'group.none', 'repos'),
1864 ('none', 'group.none', 'repos'),
1865 ('read', 'group.read', 'repos'),
1865 ('read', 'group.read', 'repos'),
1866 ('write', 'group.write', 'repos'),
1866 ('write', 'group.write', 'repos'),
1867 ('admin', 'group.admin', 'repos'),
1867 ('admin', 'group.admin', 'repos'),
1868
1868
1869 ('none', 'group.none', 'groups'),
1869 ('none', 'group.none', 'groups'),
1870 ('read', 'group.read', 'groups'),
1870 ('read', 'group.read', 'groups'),
1871 ('write', 'group.write', 'groups'),
1871 ('write', 'group.write', 'groups'),
1872 ('admin', 'group.admin', 'groups'),
1872 ('admin', 'group.admin', 'groups'),
1873 ])
1873 ])
1874 def test_api_grant_user_permission_to_repo_group(self, name, perm, apply_to_children):
1874 def test_api_grant_user_permission_to_repo_group(self, name, perm, apply_to_children):
1875 id_, params = _build_data(self.apikey,
1875 id_, params = _build_data(self.apikey,
1876 'grant_user_permission_to_repo_group',
1876 'grant_user_permission_to_repo_group',
1877 repogroupid=TEST_REPO_GROUP,
1877 repogroupid=TEST_REPO_GROUP,
1878 userid=base.TEST_USER_ADMIN_LOGIN,
1878 userid=base.TEST_USER_ADMIN_LOGIN,
1879 perm=perm, apply_to_children=apply_to_children)
1879 perm=perm, apply_to_children=apply_to_children)
1880 response = api_call(self, params)
1880 response = api_call(self, params)
1881
1881
1882 ret = {
1882 ret = {
1883 'msg': 'Granted perm: `%s` (recursive:%s) for user: `%s` in repo group: `%s`' % (
1883 'msg': 'Granted perm: `%s` (recursive:%s) for user: `%s` in repo group: `%s`' % (
1884 perm, apply_to_children, base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
1884 perm, apply_to_children, base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
1885 ),
1885 ),
1886 'success': True
1886 'success': True
1887 }
1887 }
1888 expected = ret
1888 expected = ret
1889 self._compare_ok(id_, expected, given=response.body)
1889 self._compare_ok(id_, expected, given=response.body)
1890
1890
1891 @base.parametrize('name,perm,apply_to_children,grant_admin,access_ok', [
1891 @base.parametrize('name,perm,apply_to_children,grant_admin,access_ok', [
1892 ('none_fails', 'group.none', 'none', False, False),
1892 ('none_fails', 'group.none', 'none', False, False),
1893 ('read_fails', 'group.read', 'none', False, False),
1893 ('read_fails', 'group.read', 'none', False, False),
1894 ('write_fails', 'group.write', 'none', False, False),
1894 ('write_fails', 'group.write', 'none', False, False),
1895 ('admin_fails', 'group.admin', 'none', False, False),
1895 ('admin_fails', 'group.admin', 'none', False, False),
1896
1896
1897 # with granted perms
1897 # with granted perms
1898 ('none_ok', 'group.none', 'none', True, True),
1898 ('none_ok', 'group.none', 'none', True, True),
1899 ('read_ok', 'group.read', 'none', True, True),
1899 ('read_ok', 'group.read', 'none', True, True),
1900 ('write_ok', 'group.write', 'none', True, True),
1900 ('write_ok', 'group.write', 'none', True, True),
1901 ('admin_ok', 'group.admin', 'none', True, True),
1901 ('admin_ok', 'group.admin', 'none', True, True),
1902 ])
1902 ])
1903 def test_api_grant_user_permission_to_repo_group_by_regular_user(
1903 def test_api_grant_user_permission_to_repo_group_by_regular_user(
1904 self, name, perm, apply_to_children, grant_admin, access_ok):
1904 self, name, perm, apply_to_children, grant_admin, access_ok):
1905 if grant_admin:
1905 if grant_admin:
1906 RepoGroupModel().grant_user_permission(TEST_REPO_GROUP,
1906 RepoGroupModel().grant_user_permission(TEST_REPO_GROUP,
1907 self.TEST_USER_LOGIN,
1907 self.TEST_USER_LOGIN,
1908 'group.admin')
1908 'group.admin')
1909 meta.Session().commit()
1909 meta.Session().commit()
1910
1910
1911 id_, params = _build_data(self.apikey_regular,
1911 id_, params = _build_data(self.apikey_regular,
1912 'grant_user_permission_to_repo_group',
1912 'grant_user_permission_to_repo_group',
1913 repogroupid=TEST_REPO_GROUP,
1913 repogroupid=TEST_REPO_GROUP,
1914 userid=base.TEST_USER_ADMIN_LOGIN,
1914 userid=base.TEST_USER_ADMIN_LOGIN,
1915 perm=perm, apply_to_children=apply_to_children)
1915 perm=perm, apply_to_children=apply_to_children)
1916 response = api_call(self, params)
1916 response = api_call(self, params)
1917 if access_ok:
1917 if access_ok:
1918 ret = {
1918 ret = {
1919 'msg': 'Granted perm: `%s` (recursive:%s) for user: `%s` in repo group: `%s`' % (
1919 'msg': 'Granted perm: `%s` (recursive:%s) for user: `%s` in repo group: `%s`' % (
1920 perm, apply_to_children, base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
1920 perm, apply_to_children, base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
1921 ),
1921 ),
1922 'success': True
1922 'success': True
1923 }
1923 }
1924 expected = ret
1924 expected = ret
1925 self._compare_ok(id_, expected, given=response.body)
1925 self._compare_ok(id_, expected, given=response.body)
1926 else:
1926 else:
1927 expected = 'repository group `%s` does not exist' % TEST_REPO_GROUP
1927 expected = 'repository group `%s` does not exist' % TEST_REPO_GROUP
1928 self._compare_error(id_, expected, given=response.body)
1928 self._compare_error(id_, expected, given=response.body)
1929
1929
1930 def test_api_grant_user_permission_to_repo_group_wrong_permission(self):
1930 def test_api_grant_user_permission_to_repo_group_wrong_permission(self):
1931 perm = 'haha.no.permission'
1931 perm = 'haha.no.permission'
1932 id_, params = _build_data(self.apikey,
1932 id_, params = _build_data(self.apikey,
1933 'grant_user_permission_to_repo_group',
1933 'grant_user_permission_to_repo_group',
1934 repogroupid=TEST_REPO_GROUP,
1934 repogroupid=TEST_REPO_GROUP,
1935 userid=base.TEST_USER_ADMIN_LOGIN,
1935 userid=base.TEST_USER_ADMIN_LOGIN,
1936 perm=perm)
1936 perm=perm)
1937 response = api_call(self, params)
1937 response = api_call(self, params)
1938
1938
1939 expected = 'permission `%s` does not exist' % perm
1939 expected = 'permission `%s` does not exist' % perm
1940 self._compare_error(id_, expected, given=response.body)
1940 self._compare_error(id_, expected, given=response.body)
1941
1941
1942 @mock.patch.object(RepoGroupModel, 'grant_user_permission', raise_exception)
1942 @mock.patch.object(RepoGroupModel, 'grant_user_permission', raise_exception)
1943 def test_api_grant_user_permission_to_repo_group_exception_when_adding(self):
1943 def test_api_grant_user_permission_to_repo_group_exception_when_adding(self):
1944 perm = 'group.read'
1944 perm = 'group.read'
1945 id_, params = _build_data(self.apikey,
1945 id_, params = _build_data(self.apikey,
1946 'grant_user_permission_to_repo_group',
1946 'grant_user_permission_to_repo_group',
1947 repogroupid=TEST_REPO_GROUP,
1947 repogroupid=TEST_REPO_GROUP,
1948 userid=base.TEST_USER_ADMIN_LOGIN,
1948 userid=base.TEST_USER_ADMIN_LOGIN,
1949 perm=perm)
1949 perm=perm)
1950 response = api_call(self, params)
1950 response = api_call(self, params)
1951
1951
1952 expected = 'failed to edit permission for user: `%s` in repo group: `%s`' % (
1952 expected = 'failed to edit permission for user: `%s` in repo group: `%s`' % (
1953 base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
1953 base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
1954 )
1954 )
1955 self._compare_error(id_, expected, given=response.body)
1955 self._compare_error(id_, expected, given=response.body)
1956
1956
1957 @base.parametrize('name,apply_to_children', [
1957 @base.parametrize('name,apply_to_children', [
1958 ('none', 'none'),
1958 ('none', 'none'),
1959 ('all', 'all'),
1959 ('all', 'all'),
1960 ('repos', 'repos'),
1960 ('repos', 'repos'),
1961 ('groups', 'groups'),
1961 ('groups', 'groups'),
1962 ])
1962 ])
1963 def test_api_revoke_user_permission_from_repo_group(self, name, apply_to_children):
1963 def test_api_revoke_user_permission_from_repo_group(self, name, apply_to_children):
1964 RepoGroupModel().grant_user_permission(repo_group=TEST_REPO_GROUP,
1964 RepoGroupModel().grant_user_permission(repo_group=TEST_REPO_GROUP,
1965 user=base.TEST_USER_ADMIN_LOGIN,
1965 user=base.TEST_USER_ADMIN_LOGIN,
1966 perm='group.read',)
1966 perm='group.read',)
1967 meta.Session().commit()
1967 meta.Session().commit()
1968
1968
1969 id_, params = _build_data(self.apikey,
1969 id_, params = _build_data(self.apikey,
1970 'revoke_user_permission_from_repo_group',
1970 'revoke_user_permission_from_repo_group',
1971 repogroupid=TEST_REPO_GROUP,
1971 repogroupid=TEST_REPO_GROUP,
1972 userid=base.TEST_USER_ADMIN_LOGIN,
1972 userid=base.TEST_USER_ADMIN_LOGIN,
1973 apply_to_children=apply_to_children,)
1973 apply_to_children=apply_to_children,)
1974 response = api_call(self, params)
1974 response = api_call(self, params)
1975
1975
1976 expected = {
1976 expected = {
1977 'msg': 'Revoked perm (recursive:%s) for user: `%s` in repo group: `%s`' % (
1977 'msg': 'Revoked perm (recursive:%s) for user: `%s` in repo group: `%s`' % (
1978 apply_to_children, base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
1978 apply_to_children, base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
1979 ),
1979 ),
1980 'success': True
1980 'success': True
1981 }
1981 }
1982 self._compare_ok(id_, expected, given=response.body)
1982 self._compare_ok(id_, expected, given=response.body)
1983
1983
1984 @base.parametrize('name,apply_to_children,grant_admin,access_ok', [
1984 @base.parametrize('name,apply_to_children,grant_admin,access_ok', [
1985 ('none', 'none', False, False),
1985 ('none', 'none', False, False),
1986 ('all', 'all', False, False),
1986 ('all', 'all', False, False),
1987 ('repos', 'repos', False, False),
1987 ('repos', 'repos', False, False),
1988 ('groups', 'groups', False, False),
1988 ('groups', 'groups', False, False),
1989
1989
1990 # after granting admin rights
1990 # after granting admin rights
1991 ('none', 'none', False, False),
1991 ('none', 'none', False, False),
1992 ('all', 'all', False, False),
1992 ('all', 'all', False, False),
1993 ('repos', 'repos', False, False),
1993 ('repos', 'repos', False, False),
1994 ('groups', 'groups', False, False),
1994 ('groups', 'groups', False, False),
1995 ])
1995 ])
1996 def test_api_revoke_user_permission_from_repo_group_by_regular_user(
1996 def test_api_revoke_user_permission_from_repo_group_by_regular_user(
1997 self, name, apply_to_children, grant_admin, access_ok):
1997 self, name, apply_to_children, grant_admin, access_ok):
1998 RepoGroupModel().grant_user_permission(repo_group=TEST_REPO_GROUP,
1998 RepoGroupModel().grant_user_permission(repo_group=TEST_REPO_GROUP,
1999 user=base.TEST_USER_ADMIN_LOGIN,
1999 user=base.TEST_USER_ADMIN_LOGIN,
2000 perm='group.read',)
2000 perm='group.read',)
2001 meta.Session().commit()
2001 meta.Session().commit()
2002
2002
2003 if grant_admin:
2003 if grant_admin:
2004 RepoGroupModel().grant_user_permission(TEST_REPO_GROUP,
2004 RepoGroupModel().grant_user_permission(TEST_REPO_GROUP,
2005 self.TEST_USER_LOGIN,
2005 self.TEST_USER_LOGIN,
2006 'group.admin')
2006 'group.admin')
2007 meta.Session().commit()
2007 meta.Session().commit()
2008
2008
2009 id_, params = _build_data(self.apikey_regular,
2009 id_, params = _build_data(self.apikey_regular,
2010 'revoke_user_permission_from_repo_group',
2010 'revoke_user_permission_from_repo_group',
2011 repogroupid=TEST_REPO_GROUP,
2011 repogroupid=TEST_REPO_GROUP,
2012 userid=base.TEST_USER_ADMIN_LOGIN,
2012 userid=base.TEST_USER_ADMIN_LOGIN,
2013 apply_to_children=apply_to_children,)
2013 apply_to_children=apply_to_children,)
2014 response = api_call(self, params)
2014 response = api_call(self, params)
2015 if access_ok:
2015 if access_ok:
2016 expected = {
2016 expected = {
2017 'msg': 'Revoked perm (recursive:%s) for user: `%s` in repo group: `%s`' % (
2017 'msg': 'Revoked perm (recursive:%s) for user: `%s` in repo group: `%s`' % (
2018 apply_to_children, base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
2018 apply_to_children, base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
2019 ),
2019 ),
2020 'success': True
2020 'success': True
2021 }
2021 }
2022 self._compare_ok(id_, expected, given=response.body)
2022 self._compare_ok(id_, expected, given=response.body)
2023 else:
2023 else:
2024 expected = 'repository group `%s` does not exist' % TEST_REPO_GROUP
2024 expected = 'repository group `%s` does not exist' % TEST_REPO_GROUP
2025 self._compare_error(id_, expected, given=response.body)
2025 self._compare_error(id_, expected, given=response.body)
2026
2026
2027 @mock.patch.object(RepoGroupModel, 'revoke_user_permission', raise_exception)
2027 @mock.patch.object(RepoGroupModel, 'revoke_user_permission', raise_exception)
2028 def test_api_revoke_user_permission_from_repo_group_exception_when_adding(self):
2028 def test_api_revoke_user_permission_from_repo_group_exception_when_adding(self):
2029 id_, params = _build_data(self.apikey,
2029 id_, params = _build_data(self.apikey,
2030 'revoke_user_permission_from_repo_group',
2030 'revoke_user_permission_from_repo_group',
2031 repogroupid=TEST_REPO_GROUP,
2031 repogroupid=TEST_REPO_GROUP,
2032 userid=base.TEST_USER_ADMIN_LOGIN, )
2032 userid=base.TEST_USER_ADMIN_LOGIN, )
2033 response = api_call(self, params)
2033 response = api_call(self, params)
2034
2034
2035 expected = 'failed to edit permission for user: `%s` in repo group: `%s`' % (
2035 expected = 'failed to edit permission for user: `%s` in repo group: `%s`' % (
2036 base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
2036 base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
2037 )
2037 )
2038 self._compare_error(id_, expected, given=response.body)
2038 self._compare_error(id_, expected, given=response.body)
2039
2039
2040 @base.parametrize('name,perm,apply_to_children', [
2040 @base.parametrize('name,perm,apply_to_children', [
2041 ('none', 'group.none', 'none'),
2041 ('none', 'group.none', 'none'),
2042 ('read', 'group.read', 'none'),
2042 ('read', 'group.read', 'none'),
2043 ('write', 'group.write', 'none'),
2043 ('write', 'group.write', 'none'),
2044 ('admin', 'group.admin', 'none'),
2044 ('admin', 'group.admin', 'none'),
2045
2045
2046 ('none', 'group.none', 'all'),
2046 ('none', 'group.none', 'all'),
2047 ('read', 'group.read', 'all'),
2047 ('read', 'group.read', 'all'),
2048 ('write', 'group.write', 'all'),
2048 ('write', 'group.write', 'all'),
2049 ('admin', 'group.admin', 'all'),
2049 ('admin', 'group.admin', 'all'),
2050
2050
2051 ('none', 'group.none', 'repos'),
2051 ('none', 'group.none', 'repos'),
2052 ('read', 'group.read', 'repos'),
2052 ('read', 'group.read', 'repos'),
2053 ('write', 'group.write', 'repos'),
2053 ('write', 'group.write', 'repos'),
2054 ('admin', 'group.admin', 'repos'),
2054 ('admin', 'group.admin', 'repos'),
2055
2055
2056 ('none', 'group.none', 'groups'),
2056 ('none', 'group.none', 'groups'),
2057 ('read', 'group.read', 'groups'),
2057 ('read', 'group.read', 'groups'),
2058 ('write', 'group.write', 'groups'),
2058 ('write', 'group.write', 'groups'),
2059 ('admin', 'group.admin', 'groups'),
2059 ('admin', 'group.admin', 'groups'),
2060 ])
2060 ])
2061 def test_api_grant_user_group_permission_to_repo_group(self, name, perm, apply_to_children):
2061 def test_api_grant_user_group_permission_to_repo_group(self, name, perm, apply_to_children):
2062 id_, params = _build_data(self.apikey,
2062 id_, params = _build_data(self.apikey,
2063 'grant_user_group_permission_to_repo_group',
2063 'grant_user_group_permission_to_repo_group',
2064 repogroupid=TEST_REPO_GROUP,
2064 repogroupid=TEST_REPO_GROUP,
2065 usergroupid=TEST_USER_GROUP,
2065 usergroupid=TEST_USER_GROUP,
2066 perm=perm,
2066 perm=perm,
2067 apply_to_children=apply_to_children,)
2067 apply_to_children=apply_to_children,)
2068 response = api_call(self, params)
2068 response = api_call(self, params)
2069
2069
2070 ret = {
2070 ret = {
2071 'msg': 'Granted perm: `%s` (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2071 'msg': 'Granted perm: `%s` (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2072 perm, apply_to_children, TEST_USER_GROUP, TEST_REPO_GROUP
2072 perm, apply_to_children, TEST_USER_GROUP, TEST_REPO_GROUP
2073 ),
2073 ),
2074 'success': True
2074 'success': True
2075 }
2075 }
2076 expected = ret
2076 expected = ret
2077 self._compare_ok(id_, expected, given=response.body)
2077 self._compare_ok(id_, expected, given=response.body)
2078
2078
2079 @base.parametrize('name,perm,apply_to_children,grant_admin,access_ok', [
2079 @base.parametrize('name,perm,apply_to_children,grant_admin,access_ok', [
2080 ('none_fails', 'group.none', 'none', False, False),
2080 ('none_fails', 'group.none', 'none', False, False),
2081 ('read_fails', 'group.read', 'none', False, False),
2081 ('read_fails', 'group.read', 'none', False, False),
2082 ('write_fails', 'group.write', 'none', False, False),
2082 ('write_fails', 'group.write', 'none', False, False),
2083 ('admin_fails', 'group.admin', 'none', False, False),
2083 ('admin_fails', 'group.admin', 'none', False, False),
2084
2084
2085 # with granted perms
2085 # with granted perms
2086 ('none_ok', 'group.none', 'none', True, True),
2086 ('none_ok', 'group.none', 'none', True, True),
2087 ('read_ok', 'group.read', 'none', True, True),
2087 ('read_ok', 'group.read', 'none', True, True),
2088 ('write_ok', 'group.write', 'none', True, True),
2088 ('write_ok', 'group.write', 'none', True, True),
2089 ('admin_ok', 'group.admin', 'none', True, True),
2089 ('admin_ok', 'group.admin', 'none', True, True),
2090 ])
2090 ])
2091 def test_api_grant_user_group_permission_to_repo_group_by_regular_user(
2091 def test_api_grant_user_group_permission_to_repo_group_by_regular_user(
2092 self, name, perm, apply_to_children, grant_admin, access_ok):
2092 self, name, perm, apply_to_children, grant_admin, access_ok):
2093 if grant_admin:
2093 if grant_admin:
2094 RepoGroupModel().grant_user_permission(TEST_REPO_GROUP,
2094 RepoGroupModel().grant_user_permission(TEST_REPO_GROUP,
2095 self.TEST_USER_LOGIN,
2095 self.TEST_USER_LOGIN,
2096 'group.admin')
2096 'group.admin')
2097 meta.Session().commit()
2097 meta.Session().commit()
2098
2098
2099 id_, params = _build_data(self.apikey_regular,
2099 id_, params = _build_data(self.apikey_regular,
2100 'grant_user_group_permission_to_repo_group',
2100 'grant_user_group_permission_to_repo_group',
2101 repogroupid=TEST_REPO_GROUP,
2101 repogroupid=TEST_REPO_GROUP,
2102 usergroupid=TEST_USER_GROUP,
2102 usergroupid=TEST_USER_GROUP,
2103 perm=perm,
2103 perm=perm,
2104 apply_to_children=apply_to_children,)
2104 apply_to_children=apply_to_children,)
2105 response = api_call(self, params)
2105 response = api_call(self, params)
2106 if access_ok:
2106 if access_ok:
2107 ret = {
2107 ret = {
2108 'msg': 'Granted perm: `%s` (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2108 'msg': 'Granted perm: `%s` (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2109 perm, apply_to_children, TEST_USER_GROUP, TEST_REPO_GROUP
2109 perm, apply_to_children, TEST_USER_GROUP, TEST_REPO_GROUP
2110 ),
2110 ),
2111 'success': True
2111 'success': True
2112 }
2112 }
2113 expected = ret
2113 expected = ret
2114 self._compare_ok(id_, expected, given=response.body)
2114 self._compare_ok(id_, expected, given=response.body)
2115 else:
2115 else:
2116 expected = 'repository group `%s` does not exist' % TEST_REPO_GROUP
2116 expected = 'repository group `%s` does not exist' % TEST_REPO_GROUP
2117 self._compare_error(id_, expected, given=response.body)
2117 self._compare_error(id_, expected, given=response.body)
2118
2118
2119 def test_api_grant_user_group_permission_to_repo_group_wrong_permission(self):
2119 def test_api_grant_user_group_permission_to_repo_group_wrong_permission(self):
2120 perm = 'haha.no.permission'
2120 perm = 'haha.no.permission'
2121 id_, params = _build_data(self.apikey,
2121 id_, params = _build_data(self.apikey,
2122 'grant_user_group_permission_to_repo_group',
2122 'grant_user_group_permission_to_repo_group',
2123 repogroupid=TEST_REPO_GROUP,
2123 repogroupid=TEST_REPO_GROUP,
2124 usergroupid=TEST_USER_GROUP,
2124 usergroupid=TEST_USER_GROUP,
2125 perm=perm)
2125 perm=perm)
2126 response = api_call(self, params)
2126 response = api_call(self, params)
2127
2127
2128 expected = 'permission `%s` does not exist' % perm
2128 expected = 'permission `%s` does not exist' % perm
2129 self._compare_error(id_, expected, given=response.body)
2129 self._compare_error(id_, expected, given=response.body)
2130
2130
2131 @mock.patch.object(RepoGroupModel, 'grant_user_group_permission', raise_exception)
2131 @mock.patch.object(RepoGroupModel, 'grant_user_group_permission', raise_exception)
2132 def test_api_grant_user_group_permission_exception_when_adding_to_repo_group(self):
2132 def test_api_grant_user_group_permission_exception_when_adding_to_repo_group(self):
2133 perm = 'group.read'
2133 perm = 'group.read'
2134 id_, params = _build_data(self.apikey,
2134 id_, params = _build_data(self.apikey,
2135 'grant_user_group_permission_to_repo_group',
2135 'grant_user_group_permission_to_repo_group',
2136 repogroupid=TEST_REPO_GROUP,
2136 repogroupid=TEST_REPO_GROUP,
2137 usergroupid=TEST_USER_GROUP,
2137 usergroupid=TEST_USER_GROUP,
2138 perm=perm)
2138 perm=perm)
2139 response = api_call(self, params)
2139 response = api_call(self, params)
2140
2140
2141 expected = 'failed to edit permission for user group: `%s` in repo group: `%s`' % (
2141 expected = 'failed to edit permission for user group: `%s` in repo group: `%s`' % (
2142 TEST_USER_GROUP, TEST_REPO_GROUP
2142 TEST_USER_GROUP, TEST_REPO_GROUP
2143 )
2143 )
2144 self._compare_error(id_, expected, given=response.body)
2144 self._compare_error(id_, expected, given=response.body)
2145
2145
2146 @base.parametrize('name,apply_to_children', [
2146 @base.parametrize('name,apply_to_children', [
2147 ('none', 'none'),
2147 ('none', 'none'),
2148 ('all', 'all'),
2148 ('all', 'all'),
2149 ('repos', 'repos'),
2149 ('repos', 'repos'),
2150 ('groups', 'groups'),
2150 ('groups', 'groups'),
2151 ])
2151 ])
2152 def test_api_revoke_user_group_permission_from_repo_group(self, name, apply_to_children):
2152 def test_api_revoke_user_group_permission_from_repo_group(self, name, apply_to_children):
2153 RepoGroupModel().grant_user_group_permission(repo_group=TEST_REPO_GROUP,
2153 RepoGroupModel().grant_user_group_permission(repo_group=TEST_REPO_GROUP,
2154 group_name=TEST_USER_GROUP,
2154 group_name=TEST_USER_GROUP,
2155 perm='group.read',)
2155 perm='group.read',)
2156 meta.Session().commit()
2156 meta.Session().commit()
2157 id_, params = _build_data(self.apikey,
2157 id_, params = _build_data(self.apikey,
2158 'revoke_user_group_permission_from_repo_group',
2158 'revoke_user_group_permission_from_repo_group',
2159 repogroupid=TEST_REPO_GROUP,
2159 repogroupid=TEST_REPO_GROUP,
2160 usergroupid=TEST_USER_GROUP,
2160 usergroupid=TEST_USER_GROUP,
2161 apply_to_children=apply_to_children,)
2161 apply_to_children=apply_to_children,)
2162 response = api_call(self, params)
2162 response = api_call(self, params)
2163
2163
2164 expected = {
2164 expected = {
2165 'msg': 'Revoked perm (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2165 'msg': 'Revoked perm (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2166 apply_to_children, TEST_USER_GROUP, TEST_REPO_GROUP
2166 apply_to_children, TEST_USER_GROUP, TEST_REPO_GROUP
2167 ),
2167 ),
2168 'success': True
2168 'success': True
2169 }
2169 }
2170 self._compare_ok(id_, expected, given=response.body)
2170 self._compare_ok(id_, expected, given=response.body)
2171
2171
2172 @base.parametrize('name,apply_to_children,grant_admin,access_ok', [
2172 @base.parametrize('name,apply_to_children,grant_admin,access_ok', [
2173 ('none', 'none', False, False),
2173 ('none', 'none', False, False),
2174 ('all', 'all', False, False),
2174 ('all', 'all', False, False),
2175 ('repos', 'repos', False, False),
2175 ('repos', 'repos', False, False),
2176 ('groups', 'groups', False, False),
2176 ('groups', 'groups', False, False),
2177
2177
2178 # after granting admin rights
2178 # after granting admin rights
2179 ('none', 'none', False, False),
2179 ('none', 'none', False, False),
2180 ('all', 'all', False, False),
2180 ('all', 'all', False, False),
2181 ('repos', 'repos', False, False),
2181 ('repos', 'repos', False, False),
2182 ('groups', 'groups', False, False),
2182 ('groups', 'groups', False, False),
2183 ])
2183 ])
2184 def test_api_revoke_user_group_permission_from_repo_group_by_regular_user(
2184 def test_api_revoke_user_group_permission_from_repo_group_by_regular_user(
2185 self, name, apply_to_children, grant_admin, access_ok):
2185 self, name, apply_to_children, grant_admin, access_ok):
2186 RepoGroupModel().grant_user_permission(repo_group=TEST_REPO_GROUP,
2186 RepoGroupModel().grant_user_permission(repo_group=TEST_REPO_GROUP,
2187 user=base.TEST_USER_ADMIN_LOGIN,
2187 user=base.TEST_USER_ADMIN_LOGIN,
2188 perm='group.read',)
2188 perm='group.read',)
2189 meta.Session().commit()
2189 meta.Session().commit()
2190
2190
2191 if grant_admin:
2191 if grant_admin:
2192 RepoGroupModel().grant_user_permission(TEST_REPO_GROUP,
2192 RepoGroupModel().grant_user_permission(TEST_REPO_GROUP,
2193 self.TEST_USER_LOGIN,
2193 self.TEST_USER_LOGIN,
2194 'group.admin')
2194 'group.admin')
2195 meta.Session().commit()
2195 meta.Session().commit()
2196
2196
2197 id_, params = _build_data(self.apikey_regular,
2197 id_, params = _build_data(self.apikey_regular,
2198 'revoke_user_group_permission_from_repo_group',
2198 'revoke_user_group_permission_from_repo_group',
2199 repogroupid=TEST_REPO_GROUP,
2199 repogroupid=TEST_REPO_GROUP,
2200 usergroupid=TEST_USER_GROUP,
2200 usergroupid=TEST_USER_GROUP,
2201 apply_to_children=apply_to_children,)
2201 apply_to_children=apply_to_children,)
2202 response = api_call(self, params)
2202 response = api_call(self, params)
2203 if access_ok:
2203 if access_ok:
2204 expected = {
2204 expected = {
2205 'msg': 'Revoked perm (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2205 'msg': 'Revoked perm (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2206 apply_to_children, base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
2206 apply_to_children, base.TEST_USER_ADMIN_LOGIN, TEST_REPO_GROUP
2207 ),
2207 ),
2208 'success': True
2208 'success': True
2209 }
2209 }
2210 self._compare_ok(id_, expected, given=response.body)
2210 self._compare_ok(id_, expected, given=response.body)
2211 else:
2211 else:
2212 expected = 'repository group `%s` does not exist' % TEST_REPO_GROUP
2212 expected = 'repository group `%s` does not exist' % TEST_REPO_GROUP
2213 self._compare_error(id_, expected, given=response.body)
2213 self._compare_error(id_, expected, given=response.body)
2214
2214
2215 @mock.patch.object(RepoGroupModel, 'revoke_user_group_permission', raise_exception)
2215 @mock.patch.object(RepoGroupModel, 'revoke_user_group_permission', raise_exception)
2216 def test_api_revoke_user_group_permission_from_repo_group_exception_when_adding(self):
2216 def test_api_revoke_user_group_permission_from_repo_group_exception_when_adding(self):
2217 id_, params = _build_data(self.apikey, 'revoke_user_group_permission_from_repo_group',
2217 id_, params = _build_data(self.apikey, 'revoke_user_group_permission_from_repo_group',
2218 repogroupid=TEST_REPO_GROUP,
2218 repogroupid=TEST_REPO_GROUP,
2219 usergroupid=TEST_USER_GROUP,)
2219 usergroupid=TEST_USER_GROUP,)
2220 response = api_call(self, params)
2220 response = api_call(self, params)
2221
2221
2222 expected = 'failed to edit permission for user group: `%s` in repo group: `%s`' % (
2222 expected = 'failed to edit permission for user group: `%s` in repo group: `%s`' % (
2223 TEST_USER_GROUP, TEST_REPO_GROUP
2223 TEST_USER_GROUP, TEST_REPO_GROUP
2224 )
2224 )
2225 self._compare_error(id_, expected, given=response.body)
2225 self._compare_error(id_, expected, given=response.body)
2226
2226
2227 def test_api_get_gist(self):
2227 def test_api_get_gist(self):
2228 gist = fixture.create_gist()
2228 gist = fixture.create_gist()
2229 gist_id = gist.gist_access_id
2229 gist_id = gist.gist_access_id
2230 gist_created_on = gist.created_on
2230 gist_created_on = gist.created_on
2231 id_, params = _build_data(self.apikey, 'get_gist',
2231 id_, params = _build_data(self.apikey, 'get_gist',
2232 gistid=gist_id, )
2232 gistid=gist_id, )
2233 response = api_call(self, params)
2233 response = api_call(self, params)
2234
2234
2235 expected = {
2235 expected = {
2236 'access_id': gist_id,
2236 'access_id': gist_id,
2237 'created_on': gist_created_on,
2237 'created_on': gist_created_on,
2238 'description': 'new-gist',
2238 'description': 'new-gist',
2239 'expires': -1.0,
2239 'expires': -1.0,
2240 'gist_id': int(gist_id),
2240 'gist_id': int(gist_id),
2241 'type': 'public',
2241 'type': 'public',
2242 'url': 'http://localhost:80/_admin/gists/%s' % gist_id
2242 'url': 'http://localhost:80/_admin/gists/%s' % gist_id
2243 }
2243 }
2244
2244
2245 self._compare_ok(id_, expected, given=response.body)
2245 self._compare_ok(id_, expected, given=response.body)
2246
2246
2247 def test_api_get_gist_that_does_not_exist(self):
2247 def test_api_get_gist_that_does_not_exist(self):
2248 id_, params = _build_data(self.apikey_regular, 'get_gist',
2248 id_, params = _build_data(self.apikey_regular, 'get_gist',
2249 gistid='12345', )
2249 gistid='12345', )
2250 response = api_call(self, params)
2250 response = api_call(self, params)
2251 expected = 'gist `%s` does not exist' % ('12345',)
2251 expected = 'gist `%s` does not exist' % ('12345',)
2252 self._compare_error(id_, expected, given=response.body)
2252 self._compare_error(id_, expected, given=response.body)
2253
2253
2254 def test_api_get_gist_private_gist_without_permission(self):
2254 def test_api_get_gist_private_gist_without_permission(self):
2255 gist = fixture.create_gist()
2255 gist = fixture.create_gist()
2256 gist_id = gist.gist_access_id
2256 gist_id = gist.gist_access_id
2257 gist_created_on = gist.created_on
2257 gist_created_on = gist.created_on
2258 id_, params = _build_data(self.apikey_regular, 'get_gist',
2258 id_, params = _build_data(self.apikey_regular, 'get_gist',
2259 gistid=gist_id, )
2259 gistid=gist_id, )
2260 response = api_call(self, params)
2260 response = api_call(self, params)
2261
2261
2262 expected = 'gist `%s` does not exist' % gist_id
2262 expected = 'gist `%s` does not exist' % gist_id
2263 self._compare_error(id_, expected, given=response.body)
2263 self._compare_error(id_, expected, given=response.body)
2264
2264
2265 def test_api_get_gists(self):
2265 def test_api_get_gists(self):
2266 fixture.create_gist()
2266 fixture.create_gist()
2267 fixture.create_gist()
2267 fixture.create_gist()
2268
2268
2269 id_, params = _build_data(self.apikey, 'get_gists')
2269 id_, params = _build_data(self.apikey, 'get_gists')
2270 response = api_call(self, params)
2270 response = api_call(self, params)
2271 expected = response.json
2271 expected = response.json
2272 assert len(response.json['result']) == 2
2272 assert len(response.json['result']) == 2
2273 #self._compare_ok(id_, expected, given=response.body)
2273 #self._compare_ok(id_, expected, given=response.body)
2274
2274
2275 def test_api_get_gists_regular_user(self):
2275 def test_api_get_gists_regular_user(self):
2276 # by admin
2276 # by admin
2277 fixture.create_gist()
2277 fixture.create_gist()
2278 fixture.create_gist()
2278 fixture.create_gist()
2279
2279
2280 # by reg user
2280 # by reg user
2281 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2281 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2282 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2282 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2283 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2283 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2284
2284
2285 id_, params = _build_data(self.apikey_regular, 'get_gists')
2285 id_, params = _build_data(self.apikey_regular, 'get_gists')
2286 response = api_call(self, params)
2286 response = api_call(self, params)
2287 expected = response.json
2287 expected = response.json
2288 assert len(response.json['result']) == 3
2288 assert len(response.json['result']) == 3
2289 #self._compare_ok(id_, expected, given=response.body)
2289 #self._compare_ok(id_, expected, given=response.body)
2290
2290
2291 def test_api_get_gists_only_for_regular_user(self):
2291 def test_api_get_gists_only_for_regular_user(self):
2292 # by admin
2292 # by admin
2293 fixture.create_gist()
2293 fixture.create_gist()
2294 fixture.create_gist()
2294 fixture.create_gist()
2295
2295
2296 # by reg user
2296 # by reg user
2297 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2297 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2298 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2298 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2299 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2299 fixture.create_gist(owner=self.TEST_USER_LOGIN)
2300
2300
2301 id_, params = _build_data(self.apikey, 'get_gists',
2301 id_, params = _build_data(self.apikey, 'get_gists',
2302 userid=self.TEST_USER_LOGIN)
2302 userid=self.TEST_USER_LOGIN)
2303 response = api_call(self, params)
2303 response = api_call(self, params)
2304 expected = response.json
2304 expected = response.json
2305 assert len(response.json['result']) == 3
2305 assert len(response.json['result']) == 3
2306 #self._compare_ok(id_, expected, given=response.body)
2306 #self._compare_ok(id_, expected, given=response.body)
2307
2307
2308 def test_api_get_gists_regular_user_with_different_userid(self):
2308 def test_api_get_gists_regular_user_with_different_userid(self):
2309 id_, params = _build_data(self.apikey_regular, 'get_gists',
2309 id_, params = _build_data(self.apikey_regular, 'get_gists',
2310 userid=base.TEST_USER_ADMIN_LOGIN)
2310 userid=base.TEST_USER_ADMIN_LOGIN)
2311 response = api_call(self, params)
2311 response = api_call(self, params)
2312 expected = 'userid is not the same as your user'
2312 expected = 'userid is not the same as your user'
2313 self._compare_error(id_, expected, given=response.body)
2313 self._compare_error(id_, expected, given=response.body)
2314
2314
2315 def test_api_create_gist(self):
2315 def test_api_create_gist(self):
2316 id_, params = _build_data(self.apikey_regular, 'create_gist',
2316 id_, params = _build_data(self.apikey_regular, 'create_gist',
2317 lifetime=10,
2317 lifetime=10,
2318 description='foobar-gist',
2318 description='foobar-gist',
2319 gist_type='public',
2319 gist_type='public',
2320 files={'foobar': {'content': 'foo'}})
2320 files={'foobar': {'content': 'foo'}})
2321 response = api_call(self, params)
2321 response = api_call(self, params)
2322 expected = {
2322 expected = {
2323 'gist': {
2323 'gist': {
2324 'access_id': response.json['result']['gist']['access_id'],
2324 'access_id': response.json['result']['gist']['access_id'],
2325 'created_on': response.json['result']['gist']['created_on'],
2325 'created_on': response.json['result']['gist']['created_on'],
2326 'description': 'foobar-gist',
2326 'description': 'foobar-gist',
2327 'expires': response.json['result']['gist']['expires'],
2327 'expires': response.json['result']['gist']['expires'],
2328 'gist_id': response.json['result']['gist']['gist_id'],
2328 'gist_id': response.json['result']['gist']['gist_id'],
2329 'type': 'public',
2329 'type': 'public',
2330 'url': response.json['result']['gist']['url']
2330 'url': response.json['result']['gist']['url']
2331 },
2331 },
2332 'msg': 'created new gist'
2332 'msg': 'created new gist'
2333 }
2333 }
2334 self._compare_ok(id_, expected, given=response.body)
2334 self._compare_ok(id_, expected, given=response.body)
2335
2335
2336 @mock.patch.object(GistModel, 'create', raise_exception)
2336 @mock.patch.object(GistModel, 'create', raise_exception)
2337 def test_api_create_gist_exception_occurred(self):
2337 def test_api_create_gist_exception_occurred(self):
2338 id_, params = _build_data(self.apikey_regular, 'create_gist',
2338 id_, params = _build_data(self.apikey_regular, 'create_gist',
2339 files={})
2339 files={})
2340 response = api_call(self, params)
2340 response = api_call(self, params)
2341 expected = 'failed to create gist'
2341 expected = 'failed to create gist'
2342 self._compare_error(id_, expected, given=response.body)
2342 self._compare_error(id_, expected, given=response.body)
2343
2343
2344 def test_api_delete_gist(self):
2344 def test_api_delete_gist(self):
2345 gist_id = fixture.create_gist().gist_access_id
2345 gist_id = fixture.create_gist().gist_access_id
2346 id_, params = _build_data(self.apikey, 'delete_gist',
2346 id_, params = _build_data(self.apikey, 'delete_gist',
2347 gistid=gist_id)
2347 gistid=gist_id)
2348 response = api_call(self, params)
2348 response = api_call(self, params)
2349 expected = {'gist': None, 'msg': 'deleted gist ID:%s' % gist_id}
2349 expected = {'gist': None, 'msg': 'deleted gist ID:%s' % gist_id}
2350 self._compare_ok(id_, expected, given=response.body)
2350 self._compare_ok(id_, expected, given=response.body)
2351
2351
2352 def test_api_delete_gist_regular_user(self):
2352 def test_api_delete_gist_regular_user(self):
2353 gist_id = fixture.create_gist(owner=self.TEST_USER_LOGIN).gist_access_id
2353 gist_id = fixture.create_gist(owner=self.TEST_USER_LOGIN).gist_access_id
2354 id_, params = _build_data(self.apikey_regular, 'delete_gist',
2354 id_, params = _build_data(self.apikey_regular, 'delete_gist',
2355 gistid=gist_id)
2355 gistid=gist_id)
2356 response = api_call(self, params)
2356 response = api_call(self, params)
2357 expected = {'gist': None, 'msg': 'deleted gist ID:%s' % gist_id}
2357 expected = {'gist': None, 'msg': 'deleted gist ID:%s' % gist_id}
2358 self._compare_ok(id_, expected, given=response.body)
2358 self._compare_ok(id_, expected, given=response.body)
2359
2359
2360 def test_api_delete_gist_regular_user_no_permission(self):
2360 def test_api_delete_gist_regular_user_no_permission(self):
2361 gist_id = fixture.create_gist().gist_access_id
2361 gist_id = fixture.create_gist().gist_access_id
2362 id_, params = _build_data(self.apikey_regular, 'delete_gist',
2362 id_, params = _build_data(self.apikey_regular, 'delete_gist',
2363 gistid=gist_id)
2363 gistid=gist_id)
2364 response = api_call(self, params)
2364 response = api_call(self, params)
2365 expected = 'gist `%s` does not exist' % (gist_id,)
2365 expected = 'gist `%s` does not exist' % (gist_id,)
2366 self._compare_error(id_, expected, given=response.body)
2366 self._compare_error(id_, expected, given=response.body)
2367
2367
2368 @mock.patch.object(GistModel, 'delete', raise_exception)
2368 @mock.patch.object(GistModel, 'delete', raise_exception)
2369 def test_api_delete_gist_exception_occurred(self):
2369 def test_api_delete_gist_exception_occurred(self):
2370 gist_id = fixture.create_gist().gist_access_id
2370 gist_id = fixture.create_gist().gist_access_id
2371 id_, params = _build_data(self.apikey, 'delete_gist',
2371 id_, params = _build_data(self.apikey, 'delete_gist',
2372 gistid=gist_id)
2372 gistid=gist_id)
2373 response = api_call(self, params)
2373 response = api_call(self, params)
2374 expected = 'failed to delete gist ID:%s' % (gist_id,)
2374 expected = 'failed to delete gist ID:%s' % (gist_id,)
2375 self._compare_error(id_, expected, given=response.body)
2375 self._compare_error(id_, expected, given=response.body)
2376
2376
2377 def test_api_get_ip(self):
2377 def test_api_get_ip(self):
2378 id_, params = _build_data(self.apikey, 'get_ip')
2378 id_, params = _build_data(self.apikey, 'get_ip')
2379 response = api_call(self, params)
2379 response = api_call(self, params)
2380 expected = {
2380 expected = {
2381 'server_ip_addr': '0.0.0.0',
2381 'server_ip_addr': '0.0.0.0',
2382 'user_ips': []
2382 'user_ips': []
2383 }
2383 }
2384 self._compare_ok(id_, expected, given=response.body)
2384 self._compare_ok(id_, expected, given=response.body)
2385
2385
2386 def test_api_get_server_info(self):
2386 def test_api_get_server_info(self):
2387 id_, params = _build_data(self.apikey, 'get_server_info')
2387 id_, params = _build_data(self.apikey, 'get_server_info')
2388 response = api_call(self, params)
2388 response = api_call(self, params)
2389 expected = db.Setting.get_server_info()
2389 expected = db.Setting.get_server_info()
2390 self._compare_ok(id_, expected, given=response.body)
2390 self._compare_ok(id_, expected, given=response.body)
2391
2391
2392 def test_api_get_changesets(self):
2392 def test_api_get_changesets(self):
2393 id_, params = _build_data(self.apikey, 'get_changesets',
2393 id_, params = _build_data(self.apikey, 'get_changesets',
2394 repoid=self.REPO, start=0, end=2)
2394 repoid=self.REPO, start=0, end=2)
2395 response = api_call(self, params)
2395 response = api_call(self, params)
2396 result = ext_json.loads(response.body)["result"]
2396 result = ext_json.loads(response.body)["result"]
2397 assert len(result) == 3
2397 assert len(result) == 3
2398 assert 'message' in result[0]
2398 assert 'message' in result[0]
2399 assert 'added' not in result[0]
2399 assert 'added' not in result[0]
2400
2400
2401 def test_api_get_changesets_with_max_revisions(self):
2401 def test_api_get_changesets_with_max_revisions(self):
2402 id_, params = _build_data(self.apikey, 'get_changesets',
2402 id_, params = _build_data(self.apikey, 'get_changesets',
2403 repoid=self.REPO, start_date="2011-02-24T00:00:00", max_revisions=10)
2403 repoid=self.REPO, start_date="2011-02-24T00:00:00", max_revisions=10)
2404 response = api_call(self, params)
2404 response = api_call(self, params)
2405 result = ext_json.loads(response.body)["result"]
2405 result = ext_json.loads(response.body)["result"]
2406 assert len(result) == 10
2406 assert len(result) == 10
2407 assert 'message' in result[0]
2407 assert 'message' in result[0]
2408 assert 'added' not in result[0]
2408 assert 'added' not in result[0]
2409
2409
2410 def test_api_get_changesets_with_branch(self):
2410 def test_api_get_changesets_with_branch(self):
2411 if self.REPO == 'vcs_test_hg':
2411 if self.REPO == 'vcs_test_hg':
2412 branch = 'stable'
2412 branch = 'stable'
2413 else:
2413 else:
2414 pytest.skip("skipping due to missing branches in git test repo")
2414 pytest.skip("skipping due to missing branches in git test repo")
2415 id_, params = _build_data(self.apikey, 'get_changesets',
2415 id_, params = _build_data(self.apikey, 'get_changesets',
2416 repoid=self.REPO, branch_name=branch, start_date="2011-02-24T00:00:00")
2416 repoid=self.REPO, branch_name=branch, start_date="2011-02-24T00:00:00")
2417 response = api_call(self, params)
2417 response = api_call(self, params)
2418 result = ext_json.loads(response.body)["result"]
2418 result = ext_json.loads(response.body)["result"]
2419 assert len(result) == 5
2419 assert len(result) == 5
2420 assert 'message' in result[0]
2420 assert 'message' in result[0]
2421 assert 'added' not in result[0]
2421 assert 'added' not in result[0]
2422
2422
2423 def test_api_get_changesets_with_file_list(self):
2423 def test_api_get_changesets_with_file_list(self):
2424 id_, params = _build_data(self.apikey, 'get_changesets',
2424 id_, params = _build_data(self.apikey, 'get_changesets',
2425 repoid=self.REPO, start_date="2010-04-07T23:30:30", end_date="2010-04-08T00:31:14", with_file_list=True)
2425 repoid=self.REPO, start_date="2010-04-07T23:30:30", end_date="2010-04-08T00:31:14", with_file_list=True)
2426 response = api_call(self, params)
2426 response = api_call(self, params)
2427 result = ext_json.loads(response.body)["result"]
2427 result = ext_json.loads(response.body)["result"]
2428 assert len(result) == 3
2428 assert len(result) == 3
2429 assert 'message' in result[0]
2429 assert 'message' in result[0]
2430 assert 'added' in result[0]
2430 assert 'added' in result[0]
2431
2431
2432 def test_api_get_changeset(self):
2432 def test_api_get_changeset(self):
2433 review = fixture.review_changeset(self.REPO, self.TEST_REVISION, "approved")
2433 review = fixture.review_changeset(self.REPO, self.TEST_REVISION, "approved")
2434 id_, params = _build_data(self.apikey, 'get_changeset',
2434 id_, params = _build_data(self.apikey, 'get_changeset',
2435 repoid=self.REPO, raw_id=self.TEST_REVISION)
2435 repoid=self.REPO, raw_id=self.TEST_REVISION)
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 result["raw_id"] == self.TEST_REVISION
2438 assert result["raw_id"] == self.TEST_REVISION
2439 assert "reviews" not in result
2439 assert "reviews" not in result
2440
2440
2441 def test_api_get_changeset_with_reviews(self):
2441 def test_api_get_changeset_with_reviews(self):
2442 reviewobjs = fixture.review_changeset(self.REPO, self.TEST_REVISION, "approved")
2442 reviewobjs = fixture.review_changeset(self.REPO, self.TEST_REVISION, "approved")
2443 id_, params = _build_data(self.apikey, 'get_changeset',
2443 id_, params = _build_data(self.apikey, 'get_changeset',
2444 repoid=self.REPO, raw_id=self.TEST_REVISION,
2444 repoid=self.REPO, raw_id=self.TEST_REVISION,
2445 with_reviews=True)
2445 with_reviews=True)
2446 response = api_call(self, params)
2446 response = api_call(self, params)
2447 result = ext_json.loads(response.body)["result"]
2447 result = ext_json.loads(response.body)["result"]
2448 assert result["raw_id"] == self.TEST_REVISION
2448 assert result["raw_id"] == self.TEST_REVISION
2449 assert "reviews" in result
2449 assert "reviews" in result
2450 assert len(result["reviews"]) == 1
2450 assert len(result["reviews"]) == 1
2451 review = result["reviews"][0]
2451 review = result["reviews"][0]
2452 expected = {
2452 expected = {
2453 'status': 'approved',
2453 'status': 'approved',
2454 'modified_at': reviewobjs[0].modified_at.replace(microsecond=0).isoformat(),
2454 'modified_at': reviewobjs[0].modified_at.replace(microsecond=0).isoformat(),
2455 'reviewer': 'test_admin',
2455 'reviewer': 'test_admin',
2456 }
2456 }
2457 assert review == expected
2457 assert review == expected
2458
2458
2459 def test_api_get_changeset_that_does_not_exist(self):
2459 def test_api_get_changeset_that_does_not_exist(self):
2460 """ Fetch changeset status for non-existant changeset.
2460 """ Fetch changeset status for non-existant changeset.
2461 revision id is the above git hash used in the test above with the
2461 revision id is the above git hash used in the test above with the
2462 last 3 nibbles replaced with 0xf. Should not exist for git _or_ hg.
2462 last 3 nibbles replaced with 0xf. Should not exist for git _or_ hg.
2463 """
2463 """
2464 id_, params = _build_data(self.apikey, 'get_changeset',
2464 id_, params = _build_data(self.apikey, 'get_changeset',
2465 repoid=self.REPO, raw_id = '7ab37bc680b4aa72c34d07b230c866c28e9fcfff')
2465 repoid=self.REPO, raw_id = '7ab37bc680b4aa72c34d07b230c866c28e9fcfff')
2466 response = api_call(self, params)
2466 response = api_call(self, params)
2467 expected = 'Changeset %s does not exist' % ('7ab37bc680b4aa72c34d07b230c866c28e9fcfff',)
2467 expected = 'Changeset %s does not exist' % ('7ab37bc680b4aa72c34d07b230c866c28e9fcfff',)
2468 self._compare_error(id_, expected, given=response.body)
2468 self._compare_error(id_, expected, given=response.body)
2469
2469
2470 def test_api_get_changeset_without_permission(self):
2470 def test_api_get_changeset_without_permission(self):
2471 review = fixture.review_changeset(self.REPO, self.TEST_REVISION, "approved")
2471 review = fixture.review_changeset(self.REPO, self.TEST_REVISION, "approved")
2472 RepoModel().revoke_user_permission(repo=self.REPO, user=self.TEST_USER_LOGIN)
2472 RepoModel().revoke_user_permission(repo=self.REPO, user=self.TEST_USER_LOGIN)
2473 RepoModel().revoke_user_permission(repo=self.REPO, user="default")
2473 RepoModel().revoke_user_permission(repo=self.REPO, user="default")
2474 id_, params = _build_data(self.apikey_regular, 'get_changeset',
2474 id_, params = _build_data(self.apikey_regular, 'get_changeset',
2475 repoid=self.REPO, raw_id=self.TEST_REVISION)
2475 repoid=self.REPO, raw_id=self.TEST_REVISION)
2476 response = api_call(self, params)
2476 response = api_call(self, params)
2477 expected = 'Access denied to repo %s' % self.REPO
2477 expected = 'Access denied to repo %s' % self.REPO
2478 self._compare_error(id_, expected, given=response.body)
2478 self._compare_error(id_, expected, given=response.body)
2479
2479
2480 def test_api_get_pullrequest(self):
2480 def test_api_get_pullrequest(self):
2481 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'get test')
2481 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'get test')
2482 random_id = random.randrange(1, 9999)
2482 random_id = random.randrange(1, 9999)
2483 params = ascii_bytes(ext_json.dumps({
2483 params = ascii_bytes(ext_json.dumps({
2484 "id": random_id,
2484 "id": random_id,
2485 "api_key": self.apikey,
2485 "api_key": self.apikey,
2486 "method": 'get_pullrequest',
2486 "method": 'get_pullrequest',
2487 "args": {"pullrequest_id": pull_request_id},
2487 "args": {"pullrequest_id": pull_request_id},
2488 }))
2488 }))
2489 response = api_call(self, params)
2489 response = api_call(self, params)
2490 pullrequest = db.PullRequest().get(pull_request_id)
2490 pullrequest = db.PullRequest().get(pull_request_id)
2491 expected = {
2491 expected = {
2492 "status": "new",
2492 "status": "new",
2493 "pull_request_id": pull_request_id,
2493 "pull_request_id": pull_request_id,
2494 "description": "No description",
2494 "description": "No description",
2495 "url": "/%s/pull-request/%s/_/%s" % (self.REPO, pull_request_id, "stable"),
2495 "url": "/%s/pull-request/%s/_/%s" % (self.REPO, pull_request_id, "stable"),
2496 "reviewers": [{"username": "test_regular"}],
2496 "reviewers": [{"username": "test_regular"}],
2497 "org_repo_url": "http://localhost:80/%s" % self.REPO,
2497 "org_repo_url": "http://localhost:80/%s" % self.REPO,
2498 "org_ref_parts": ["branch", "stable", self.TEST_PR_SRC],
2498 "org_ref_parts": ["branch", "stable", self.TEST_PR_SRC],
2499 "other_ref_parts": ["branch", "default", self.TEST_PR_DST],
2499 "other_ref_parts": ["branch", "default", self.TEST_PR_DST],
2500 "comments": [{"username": base.TEST_USER_ADMIN_LOGIN, "text": "",
2500 "comments": [{"username": base.TEST_USER_ADMIN_LOGIN, "text": "",
2501 "comment_id": pullrequest.comments[0].comment_id}],
2501 "comment_id": pullrequest.comments[0].comment_id}],
2502 "owner": base.TEST_USER_ADMIN_LOGIN,
2502 "owner": base.TEST_USER_ADMIN_LOGIN,
2503 "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))],
2503 "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))],
2504 "title": "get test",
2504 "title": "get test",
2505 "revisions": self.TEST_PR_REVISIONS,
2505 "revisions": self.TEST_PR_REVISIONS,
2506 "created_on": "2000-01-01T00:00:00",
2506 "created_on": "2000-01-01T00:00:00",
2507 "updated_on": "2000-01-01T00:00:00",
2507 "updated_on": "2000-01-01T00:00:00",
2508 }
2508 }
2509 self._compare_ok(random_id, expected,
2509 self._compare_ok(random_id, expected,
2510 given=re.sub(br"\d\d\d\d\-\d\d\-\d\dT\d\d\:\d\d\:\d\d",
2510 given=re.sub(br"\d\d\d\d\-\d\d\-\d\dT\d\d\:\d\d\:\d\d",
2511 b"2000-01-01T00:00:00", response.body))
2511 b"2000-01-01T00:00:00", response.body))
2512
2512
2513 def test_api_close_pullrequest(self):
2513 def test_api_close_pullrequest(self):
2514 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'close test')
2514 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'close test')
2515 random_id = random.randrange(1, 9999)
2515 random_id = random.randrange(1, 9999)
2516 params = ascii_bytes(ext_json.dumps({
2516 params = ascii_bytes(ext_json.dumps({
2517 "id": random_id,
2517 "id": random_id,
2518 "api_key": self.apikey,
2518 "api_key": self.apikey,
2519 "method": "comment_pullrequest",
2519 "method": "comment_pullrequest",
2520 "args": {"pull_request_id": pull_request_id, "close_pr": True},
2520 "args": {"pull_request_id": pull_request_id, "close_pr": True},
2521 }))
2521 }))
2522 response = api_call(self, params)
2522 response = api_call(self, params)
2523 self._compare_ok(random_id, True, given=response.body)
2523 self._compare_ok(random_id, True, given=response.body)
2524 pullrequest = db.PullRequest().get(pull_request_id)
2524 pullrequest = db.PullRequest().get(pull_request_id)
2525 assert pullrequest.comments[-1].text == ''
2525 assert pullrequest.comments[-1].text == ''
2526 assert pullrequest.status == db.PullRequest.STATUS_CLOSED
2526 assert pullrequest.status == db.PullRequest.STATUS_CLOSED
2527 assert pullrequest.is_closed() == True
2527 assert pullrequest.is_closed() == True
2528
2528
2529 def test_api_status_pullrequest(self):
2529 def test_api_status_pullrequest(self):
2530 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, "status test")
2530 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, "status test")
2531
2531
2532 random_id = random.randrange(1, 9999)
2532 random_id = random.randrange(1, 9999)
2533 params = ascii_bytes(ext_json.dumps({
2533 params = ascii_bytes(ext_json.dumps({
2534 "id": random_id,
2534 "id": random_id,
2535 "api_key": db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN).api_key,
2535 "api_key": db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN).api_key,
2536 "method": "comment_pullrequest",
2536 "method": "comment_pullrequest",
2537 "args": {"pull_request_id": pull_request_id, "status": db.ChangesetStatus.STATUS_APPROVED},
2537 "args": {"pull_request_id": pull_request_id, "status": db.ChangesetStatus.STATUS_APPROVED},
2538 }))
2538 }))
2539 response = api_call(self, params)
2539 response = api_call(self, params)
2540 pullrequest = db.PullRequest().get(pull_request_id)
2540 pullrequest = db.PullRequest().get(pull_request_id)
2541 self._compare_error(random_id, "No permission to change pull request status. User needs to be admin, owner or reviewer.", given=response.body)
2541 self._compare_error(random_id, "No permission to change pull request status. User needs to be admin, owner or reviewer.", given=response.body)
2542 assert db.ChangesetStatus.STATUS_UNDER_REVIEW == ChangesetStatusModel().calculate_pull_request_result(pullrequest)[2]
2542 assert db.ChangesetStatus.STATUS_UNDER_REVIEW == ChangesetStatusModel().calculate_pull_request_result(pullrequest)[2]
2543 params = ascii_bytes(ext_json.dumps({
2543 params = ascii_bytes(ext_json.dumps({
2544 "id": random_id,
2544 "id": random_id,
2545 "api_key": db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN).api_key,
2545 "api_key": db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN).api_key,
2546 "method": "comment_pullrequest",
2546 "method": "comment_pullrequest",
2547 "args": {"pull_request_id": pull_request_id, "status": db.ChangesetStatus.STATUS_APPROVED},
2547 "args": {"pull_request_id": pull_request_id, "status": db.ChangesetStatus.STATUS_APPROVED},
2548 }))
2548 }))
2549 response = api_call(self, params)
2549 response = api_call(self, params)
2550 self._compare_ok(random_id, True, given=response.body)
2550 self._compare_ok(random_id, True, given=response.body)
2551 pullrequest = db.PullRequest().get(pull_request_id)
2551 pullrequest = db.PullRequest().get(pull_request_id)
2552 assert db.ChangesetStatus.STATUS_APPROVED == ChangesetStatusModel().calculate_pull_request_result(pullrequest)[2]
2552 assert db.ChangesetStatus.STATUS_APPROVED == ChangesetStatusModel().calculate_pull_request_result(pullrequest)[2]
2553
2553
2554 def test_api_comment_pullrequest(self):
2554 def test_api_comment_pullrequest(self):
2555 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, "comment test")
2555 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, "comment 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, "comment_msg": "Looks good to me"},
2561 "args": {"pull_request_id": pull_request_id, "comment_msg": "Looks good to me"},
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 == 'Looks good to me'
2566 assert pullrequest.comments[-1].text == 'Looks good to me'
2567
2567
2568 def test_api_edit_reviewers_add_single(self):
2568 def test_api_edit_reviewers_add_single(self):
2569 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2569 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2570 pullrequest = db.PullRequest().get(pull_request_id)
2570 pullrequest = db.PullRequest().get(pull_request_id)
2571 pullrequest.owner = self.test_user
2571 pullrequest.owner = self.test_user
2572 random_id = random.randrange(1, 9999)
2572 random_id = random.randrange(1, 9999)
2573 params = ascii_bytes(ext_json.dumps({
2573 params = ascii_bytes(ext_json.dumps({
2574 "id": random_id,
2574 "id": random_id,
2575 "api_key": self.apikey_regular,
2575 "api_key": self.apikey_regular,
2576 "method": "edit_reviewers",
2576 "method": "edit_reviewers",
2577 "args": {"pull_request_id": pull_request_id, "add": base.TEST_USER_REGULAR2_LOGIN},
2577 "args": {"pull_request_id": pull_request_id, "add": base.TEST_USER_REGULAR2_LOGIN},
2578 }))
2578 }))
2579 response = api_call(self, params)
2579 response = api_call(self, params)
2580 expected = { 'added': [base.TEST_USER_REGULAR2_LOGIN], 'already_present': [], 'removed': [] }
2580 expected = { 'added': [base.TEST_USER_REGULAR2_LOGIN], 'already_present': [], 'removed': [] }
2581
2581
2582 self._compare_ok(random_id, expected, given=response.body)
2582 self._compare_ok(random_id, expected, given=response.body)
2583 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2583 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2584
2584
2585 def test_api_edit_reviewers_add_nonexistent(self):
2585 def test_api_edit_reviewers_add_nonexistent(self):
2586 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2586 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2587 pullrequest = db.PullRequest().get(pull_request_id)
2587 pullrequest = db.PullRequest().get(pull_request_id)
2588 pullrequest.owner = self.test_user
2588 pullrequest.owner = self.test_user
2589 random_id = random.randrange(1, 9999)
2589 random_id = random.randrange(1, 9999)
2590 params = ascii_bytes(ext_json.dumps({
2590 params = ascii_bytes(ext_json.dumps({
2591 "id": random_id,
2591 "id": random_id,
2592 "api_key": self.apikey_regular,
2592 "api_key": self.apikey_regular,
2593 "method": "edit_reviewers",
2593 "method": "edit_reviewers",
2594 "args": {"pull_request_id": pull_request_id, "add": 999},
2594 "args": {"pull_request_id": pull_request_id, "add": 999},
2595 }))
2595 }))
2596 response = api_call(self, params)
2596 response = api_call(self, params)
2597
2597
2598 self._compare_error(random_id, "user `999` does not exist", given=response.body)
2598 self._compare_error(random_id, "user `999` does not exist", given=response.body)
2599
2599
2600 def test_api_edit_reviewers_add_multiple(self):
2600 def test_api_edit_reviewers_add_multiple(self):
2601 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2601 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2602 pullrequest = db.PullRequest().get(pull_request_id)
2602 pullrequest = db.PullRequest().get(pull_request_id)
2603 pullrequest.owner = self.test_user
2603 pullrequest.owner = self.test_user
2604 random_id = random.randrange(1, 9999)
2604 random_id = random.randrange(1, 9999)
2605 params = ascii_bytes(ext_json.dumps({
2605 params = ascii_bytes(ext_json.dumps({
2606 "id": random_id,
2606 "id": random_id,
2607 "api_key": self.apikey_regular,
2607 "api_key": self.apikey_regular,
2608 "method": "edit_reviewers",
2608 "method": "edit_reviewers",
2609 "args": {
2609 "args": {
2610 "pull_request_id": pull_request_id,
2610 "pull_request_id": pull_request_id,
2611 "add": [ self.TEST_USER_LOGIN, base.TEST_USER_REGULAR2_LOGIN ]
2611 "add": [ self.TEST_USER_LOGIN, base.TEST_USER_REGULAR2_LOGIN ]
2612 },
2612 },
2613 }))
2613 }))
2614 response = api_call(self, params)
2614 response = api_call(self, params)
2615 # list order depends on python sorting hash, which is randomized
2615 # list order depends on python sorting hash, which is randomized
2616 assert set(ext_json.loads(response.body)['result']['added']) == set([base.TEST_USER_REGULAR2_LOGIN, self.TEST_USER_LOGIN])
2616 assert set(ext_json.loads(response.body)['result']['added']) == set([base.TEST_USER_REGULAR2_LOGIN, self.TEST_USER_LOGIN])
2617 assert set(ext_json.loads(response.body)['result']['already_present']) == set()
2617 assert set(ext_json.loads(response.body)['result']['already_present']) == set()
2618 assert set(ext_json.loads(response.body)['result']['removed']) == set()
2618 assert set(ext_json.loads(response.body)['result']['removed']) == set()
2619
2619
2620 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2620 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2621 assert db.User.get_by_username(self.TEST_USER_LOGIN) in pullrequest.get_reviewer_users()
2621 assert db.User.get_by_username(self.TEST_USER_LOGIN) in pullrequest.get_reviewer_users()
2622
2622
2623 def test_api_edit_reviewers_add_already_present(self):
2623 def test_api_edit_reviewers_add_already_present(self):
2624 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2624 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2625 pullrequest = db.PullRequest().get(pull_request_id)
2625 pullrequest = db.PullRequest().get(pull_request_id)
2626 pullrequest.owner = self.test_user
2626 pullrequest.owner = self.test_user
2627 random_id = random.randrange(1, 9999)
2627 random_id = random.randrange(1, 9999)
2628 params = ascii_bytes(ext_json.dumps({
2628 params = ascii_bytes(ext_json.dumps({
2629 "id": random_id,
2629 "id": random_id,
2630 "api_key": self.apikey_regular,
2630 "api_key": self.apikey_regular,
2631 "method": "edit_reviewers",
2631 "method": "edit_reviewers",
2632 "args": {
2632 "args": {
2633 "pull_request_id": pull_request_id,
2633 "pull_request_id": pull_request_id,
2634 "add": [ base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_REGULAR2_LOGIN ]
2634 "add": [ base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_REGULAR2_LOGIN ]
2635 },
2635 },
2636 }))
2636 }))
2637 response = api_call(self, params)
2637 response = api_call(self, params)
2638 expected = { 'added': [base.TEST_USER_REGULAR2_LOGIN],
2638 expected = { 'added': [base.TEST_USER_REGULAR2_LOGIN],
2639 'already_present': [base.TEST_USER_REGULAR_LOGIN],
2639 'already_present': [base.TEST_USER_REGULAR_LOGIN],
2640 'removed': [],
2640 'removed': [],
2641 }
2641 }
2642
2642
2643 self._compare_ok(random_id, expected, given=response.body)
2643 self._compare_ok(random_id, expected, given=response.body)
2644 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2644 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2645 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2645 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2646
2646
2647 def test_api_edit_reviewers_add_closed(self):
2647 def test_api_edit_reviewers_add_closed(self):
2648 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2648 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2649 pullrequest = db.PullRequest().get(pull_request_id)
2649 pullrequest = db.PullRequest().get(pull_request_id)
2650 pullrequest.owner = self.test_user
2650 pullrequest.owner = self.test_user
2651 PullRequestModel().close_pull_request(pull_request_id)
2651 PullRequestModel().close_pull_request(pull_request_id)
2652 random_id = random.randrange(1, 9999)
2652 random_id = random.randrange(1, 9999)
2653 params = ascii_bytes(ext_json.dumps({
2653 params = ascii_bytes(ext_json.dumps({
2654 "id": random_id,
2654 "id": random_id,
2655 "api_key": self.apikey_regular,
2655 "api_key": self.apikey_regular,
2656 "method": "edit_reviewers",
2656 "method": "edit_reviewers",
2657 "args": {"pull_request_id": pull_request_id, "add": base.TEST_USER_REGULAR2_LOGIN},
2657 "args": {"pull_request_id": pull_request_id, "add": base.TEST_USER_REGULAR2_LOGIN},
2658 }))
2658 }))
2659 response = api_call(self, params)
2659 response = api_call(self, params)
2660 self._compare_error(random_id, "Cannot edit reviewers of a closed pull request.", given=response.body)
2660 self._compare_error(random_id, "Cannot edit reviewers of a closed pull request.", given=response.body)
2661
2661
2662 def test_api_edit_reviewers_add_not_owner(self):
2662 def test_api_edit_reviewers_add_not_owner(self):
2663 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2663 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2664 pullrequest = db.PullRequest().get(pull_request_id)
2664 pullrequest = db.PullRequest().get(pull_request_id)
2665 pullrequest.owner = db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN)
2665 pullrequest.owner = db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN)
2666 random_id = random.randrange(1, 9999)
2666 random_id = random.randrange(1, 9999)
2667 params = ascii_bytes(ext_json.dumps({
2667 params = ascii_bytes(ext_json.dumps({
2668 "id": random_id,
2668 "id": random_id,
2669 "api_key": self.apikey_regular,
2669 "api_key": self.apikey_regular,
2670 "method": "edit_reviewers",
2670 "method": "edit_reviewers",
2671 "args": {"pull_request_id": pull_request_id, "add": base.TEST_USER_REGULAR2_LOGIN},
2671 "args": {"pull_request_id": pull_request_id, "add": base.TEST_USER_REGULAR2_LOGIN},
2672 }))
2672 }))
2673 response = api_call(self, params)
2673 response = api_call(self, params)
2674 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)
2674 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)
2675
2675
2676
2676
2677 def test_api_edit_reviewers_remove_single(self):
2677 def test_api_edit_reviewers_remove_single(self):
2678 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2678 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2679 pullrequest = db.PullRequest().get(pull_request_id)
2679 pullrequest = db.PullRequest().get(pull_request_id)
2680 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2680 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2681
2681
2682 pullrequest.owner = self.test_user
2682 pullrequest.owner = self.test_user
2683 random_id = random.randrange(1, 9999)
2683 random_id = random.randrange(1, 9999)
2684 params = ascii_bytes(ext_json.dumps({
2684 params = ascii_bytes(ext_json.dumps({
2685 "id": random_id,
2685 "id": random_id,
2686 "api_key": self.apikey_regular,
2686 "api_key": self.apikey_regular,
2687 "method": "edit_reviewers",
2687 "method": "edit_reviewers",
2688 "args": {"pull_request_id": pull_request_id, "remove": base.TEST_USER_REGULAR_LOGIN},
2688 "args": {"pull_request_id": pull_request_id, "remove": base.TEST_USER_REGULAR_LOGIN},
2689 }))
2689 }))
2690 response = api_call(self, params)
2690 response = api_call(self, params)
2691
2691
2692 expected = { 'added': [],
2692 expected = { 'added': [],
2693 'already_present': [],
2693 'already_present': [],
2694 'removed': [base.TEST_USER_REGULAR_LOGIN],
2694 'removed': [base.TEST_USER_REGULAR_LOGIN],
2695 }
2695 }
2696 self._compare_ok(random_id, expected, given=response.body)
2696 self._compare_ok(random_id, expected, given=response.body)
2697 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) not in pullrequest.get_reviewer_users()
2697 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) not in pullrequest.get_reviewer_users()
2698
2698
2699 def test_api_edit_reviewers_remove_nonexistent(self):
2699 def test_api_edit_reviewers_remove_nonexistent(self):
2700 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2700 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2701 pullrequest = db.PullRequest().get(pull_request_id)
2701 pullrequest = db.PullRequest().get(pull_request_id)
2702 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2702 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2703
2703
2704 pullrequest.owner = self.test_user
2704 pullrequest.owner = self.test_user
2705 random_id = random.randrange(1, 9999)
2705 random_id = random.randrange(1, 9999)
2706 params = ascii_bytes(ext_json.dumps({
2706 params = ascii_bytes(ext_json.dumps({
2707 "id": random_id,
2707 "id": random_id,
2708 "api_key": self.apikey_regular,
2708 "api_key": self.apikey_regular,
2709 "method": "edit_reviewers",
2709 "method": "edit_reviewers",
2710 "args": {"pull_request_id": pull_request_id, "remove": 999},
2710 "args": {"pull_request_id": pull_request_id, "remove": 999},
2711 }))
2711 }))
2712 response = api_call(self, params)
2712 response = api_call(self, params)
2713
2713
2714 self._compare_error(random_id, "user `999` does not exist", given=response.body)
2714 self._compare_error(random_id, "user `999` does not exist", given=response.body)
2715 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2715 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2716
2716
2717 def test_api_edit_reviewers_remove_nonpresent(self):
2717 def test_api_edit_reviewers_remove_nonpresent(self):
2718 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2718 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2719 pullrequest = db.PullRequest().get(pull_request_id)
2719 pullrequest = db.PullRequest().get(pull_request_id)
2720 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2720 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_REGULAR2_LOGIN) not in pullrequest.get_reviewer_users()
2721 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) not 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_REGULAR2_LOGIN},
2729 "args": {"pull_request_id": pull_request_id, "remove": base.TEST_USER_REGULAR2_LOGIN},
2730 }))
2730 }))
2731 response = api_call(self, params)
2731 response = api_call(self, params)
2732
2732
2733 # NOTE: no explicit indication that removed user was not even a reviewer
2733 # NOTE: no explicit indication that removed user was not even a reviewer
2734 expected = { 'added': [],
2734 expected = { 'added': [],
2735 'already_present': [],
2735 'already_present': [],
2736 'removed': [base.TEST_USER_REGULAR2_LOGIN],
2736 'removed': [base.TEST_USER_REGULAR2_LOGIN],
2737 }
2737 }
2738 self._compare_ok(random_id, expected, given=response.body)
2738 self._compare_ok(random_id, expected, given=response.body)
2739 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2739 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2740 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) not in pullrequest.get_reviewer_users()
2740 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) not in pullrequest.get_reviewer_users()
2741
2741
2742 def test_api_edit_reviewers_remove_multiple(self):
2742 def test_api_edit_reviewers_remove_multiple(self):
2743 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2743 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2744 pullrequest = db.PullRequest().get(pull_request_id)
2744 pullrequest = db.PullRequest().get(pull_request_id)
2745 prr = db.PullRequestReviewer(db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN), pullrequest)
2745 prr = db.PullRequestReviewer(db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN), pullrequest)
2746 meta.Session().add(prr)
2746 meta.Session().add(prr)
2747 meta.Session().commit()
2747 meta.Session().commit()
2748
2748
2749 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2749 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2750 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2750 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2751
2751
2752 pullrequest.owner = self.test_user
2752 pullrequest.owner = self.test_user
2753 random_id = random.randrange(1, 9999)
2753 random_id = random.randrange(1, 9999)
2754 params = ascii_bytes(ext_json.dumps({
2754 params = ascii_bytes(ext_json.dumps({
2755 "id": random_id,
2755 "id": random_id,
2756 "api_key": self.apikey_regular,
2756 "api_key": self.apikey_regular,
2757 "method": "edit_reviewers",
2757 "method": "edit_reviewers",
2758 "args": {"pull_request_id": pull_request_id, "remove": [ base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_REGULAR2_LOGIN ] },
2758 "args": {"pull_request_id": pull_request_id, "remove": [ base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_REGULAR2_LOGIN ] },
2759 }))
2759 }))
2760 response = api_call(self, params)
2760 response = api_call(self, params)
2761
2761
2762 # list order depends on python sorting hash, which is randomized
2762 # list order depends on python sorting hash, which is randomized
2763 assert set(ext_json.loads(response.body)['result']['added']) == set()
2763 assert set(ext_json.loads(response.body)['result']['added']) == set()
2764 assert set(ext_json.loads(response.body)['result']['already_present']) == set()
2764 assert set(ext_json.loads(response.body)['result']['already_present']) == set()
2765 assert set(ext_json.loads(response.body)['result']['removed']) == set([base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_REGULAR2_LOGIN])
2765 assert set(ext_json.loads(response.body)['result']['removed']) == set([base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_REGULAR2_LOGIN])
2766 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) not in pullrequest.get_reviewer_users()
2766 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) not in pullrequest.get_reviewer_users()
2767 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) not in pullrequest.get_reviewer_users()
2767 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) not in pullrequest.get_reviewer_users()
2768
2768
2769 def test_api_edit_reviewers_remove_closed(self):
2769 def test_api_edit_reviewers_remove_closed(self):
2770 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2770 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2771 pullrequest = db.PullRequest().get(pull_request_id)
2771 pullrequest = db.PullRequest().get(pull_request_id)
2772 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2772 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2773 PullRequestModel().close_pull_request(pull_request_id)
2773 PullRequestModel().close_pull_request(pull_request_id)
2774
2774
2775 pullrequest.owner = self.test_user
2775 pullrequest.owner = self.test_user
2776 random_id = random.randrange(1, 9999)
2776 random_id = random.randrange(1, 9999)
2777 params = ascii_bytes(ext_json.dumps({
2777 params = ascii_bytes(ext_json.dumps({
2778 "id": random_id,
2778 "id": random_id,
2779 "api_key": self.apikey_regular,
2779 "api_key": self.apikey_regular,
2780 "method": "edit_reviewers",
2780 "method": "edit_reviewers",
2781 "args": {"pull_request_id": pull_request_id, "remove": base.TEST_USER_REGULAR_LOGIN},
2781 "args": {"pull_request_id": pull_request_id, "remove": base.TEST_USER_REGULAR_LOGIN},
2782 }))
2782 }))
2783 response = api_call(self, params)
2783 response = api_call(self, params)
2784
2784
2785 self._compare_error(random_id, "Cannot edit reviewers of a closed pull request.", given=response.body)
2785 self._compare_error(random_id, "Cannot edit reviewers of a closed pull request.", given=response.body)
2786 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2786 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2787
2787
2788 def test_api_edit_reviewers_remove_not_owner(self):
2788 def test_api_edit_reviewers_remove_not_owner(self):
2789 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2789 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2790 pullrequest = db.PullRequest().get(pull_request_id)
2790 pullrequest = db.PullRequest().get(pull_request_id)
2791 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_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2792
2792
2793 pullrequest.owner = db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN)
2793 pullrequest.owner = db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN)
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},
2799 "args": {"pull_request_id": pull_request_id, "remove": base.TEST_USER_REGULAR_LOGIN},
2800 }))
2800 }))
2801 response = api_call(self, params)
2801 response = api_call(self, params)
2802
2802
2803 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)
2803 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)
2804 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2804 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2805
2805
2806 def test_api_edit_reviewers_add_remove_single(self):
2806 def test_api_edit_reviewers_add_remove_single(self):
2807 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2807 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2808 pullrequest = db.PullRequest().get(pull_request_id)
2808 pullrequest = db.PullRequest().get(pull_request_id)
2809 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2809 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2810 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) not in pullrequest.get_reviewer_users()
2810 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) not in pullrequest.get_reviewer_users()
2811
2811
2812 pullrequest.owner = self.test_user
2812 pullrequest.owner = self.test_user
2813 random_id = random.randrange(1, 9999)
2813 random_id = random.randrange(1, 9999)
2814 params = ascii_bytes(ext_json.dumps({
2814 params = ascii_bytes(ext_json.dumps({
2815 "id": random_id,
2815 "id": random_id,
2816 "api_key": self.apikey_regular,
2816 "api_key": self.apikey_regular,
2817 "method": "edit_reviewers",
2817 "method": "edit_reviewers",
2818 "args": {"pull_request_id": pull_request_id,
2818 "args": {"pull_request_id": pull_request_id,
2819 "add": base.TEST_USER_REGULAR2_LOGIN,
2819 "add": base.TEST_USER_REGULAR2_LOGIN,
2820 "remove": base.TEST_USER_REGULAR_LOGIN
2820 "remove": base.TEST_USER_REGULAR_LOGIN
2821 },
2821 },
2822 }))
2822 }))
2823 response = api_call(self, params)
2823 response = api_call(self, params)
2824
2824
2825 expected = { 'added': [base.TEST_USER_REGULAR2_LOGIN],
2825 expected = { 'added': [base.TEST_USER_REGULAR2_LOGIN],
2826 'already_present': [],
2826 'already_present': [],
2827 'removed': [base.TEST_USER_REGULAR_LOGIN],
2827 'removed': [base.TEST_USER_REGULAR_LOGIN],
2828 }
2828 }
2829 self._compare_ok(random_id, expected, given=response.body)
2829 self._compare_ok(random_id, expected, given=response.body)
2830 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) not in pullrequest.get_reviewer_users()
2830 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) not in pullrequest.get_reviewer_users()
2831 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2831 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2832
2832
2833 def test_api_edit_reviewers_add_remove_multiple(self):
2833 def test_api_edit_reviewers_add_remove_multiple(self):
2834 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2834 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2835 pullrequest = db.PullRequest().get(pull_request_id)
2835 pullrequest = db.PullRequest().get(pull_request_id)
2836 prr = db.PullRequestReviewer(db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN), pullrequest)
2836 prr = db.PullRequestReviewer(db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN), pullrequest)
2837 meta.Session().add(prr)
2837 meta.Session().add(prr)
2838 meta.Session().commit()
2838 meta.Session().commit()
2839 assert db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN) in pullrequest.get_reviewer_users()
2839 assert db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN) in pullrequest.get_reviewer_users()
2840 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2840 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2841 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) not in pullrequest.get_reviewer_users()
2841 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) not in pullrequest.get_reviewer_users()
2842
2842
2843 pullrequest.owner = self.test_user
2843 pullrequest.owner = self.test_user
2844 random_id = random.randrange(1, 9999)
2844 random_id = random.randrange(1, 9999)
2845 params = ascii_bytes(ext_json.dumps({
2845 params = ascii_bytes(ext_json.dumps({
2846 "id": random_id,
2846 "id": random_id,
2847 "api_key": self.apikey_regular,
2847 "api_key": self.apikey_regular,
2848 "method": "edit_reviewers",
2848 "method": "edit_reviewers",
2849 "args": {"pull_request_id": pull_request_id,
2849 "args": {"pull_request_id": pull_request_id,
2850 "add": [ base.TEST_USER_REGULAR2_LOGIN ],
2850 "add": [ base.TEST_USER_REGULAR2_LOGIN ],
2851 "remove": [ base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_ADMIN_LOGIN ],
2851 "remove": [ base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_ADMIN_LOGIN ],
2852 },
2852 },
2853 }))
2853 }))
2854 response = api_call(self, params)
2854 response = api_call(self, params)
2855
2855
2856 # list order depends on python sorting hash, which is randomized
2856 # list order depends on python sorting hash, which is randomized
2857 assert set(ext_json.loads(response.body)['result']['added']) == set([base.TEST_USER_REGULAR2_LOGIN])
2857 assert set(ext_json.loads(response.body)['result']['added']) == set([base.TEST_USER_REGULAR2_LOGIN])
2858 assert set(ext_json.loads(response.body)['result']['already_present']) == set()
2858 assert set(ext_json.loads(response.body)['result']['already_present']) == set()
2859 assert set(ext_json.loads(response.body)['result']['removed']) == set([base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_ADMIN_LOGIN])
2859 assert set(ext_json.loads(response.body)['result']['removed']) == set([base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_ADMIN_LOGIN])
2860 assert db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN) not in pullrequest.get_reviewer_users()
2860 assert db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN) not in pullrequest.get_reviewer_users()
2861 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) not in pullrequest.get_reviewer_users()
2861 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) not in pullrequest.get_reviewer_users()
2862 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2862 assert db.User.get_by_username(base.TEST_USER_REGULAR2_LOGIN) in pullrequest.get_reviewer_users()
2863
2863
2864 def test_api_edit_reviewers_invalid_params(self):
2864 def test_api_edit_reviewers_invalid_params(self):
2865 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2865 pull_request_id = fixture.create_pullrequest(self, self.REPO, self.TEST_PR_SRC, self.TEST_PR_DST, 'edit reviewer test')
2866 pullrequest = db.PullRequest().get(pull_request_id)
2866 pullrequest = db.PullRequest().get(pull_request_id)
2867 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2867 assert db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN) in pullrequest.get_reviewer_users()
2868
2868
2869 pullrequest.owner = db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN)
2869 pullrequest.owner = db.User.get_by_username(base.TEST_USER_REGULAR_LOGIN)
2870 random_id = random.randrange(1, 9999)
2870 random_id = random.randrange(1, 9999)
2871 params = ascii_bytes(ext_json.dumps({
2871 params = ascii_bytes(ext_json.dumps({
2872 "id": random_id,
2872 "id": random_id,
2873 "api_key": self.apikey_regular,
2873 "api_key": self.apikey_regular,
2874 "method": "edit_reviewers",
2874 "method": "edit_reviewers",
2875 "args": {"pull_request_id": pull_request_id},
2875 "args": {"pull_request_id": pull_request_id},
2876 }))
2876 }))
2877 response = api_call(self, params)
2877 response = api_call(self, params)
2878
2878
2879 self._compare_error(random_id, "Invalid request. Neither 'add' nor 'remove' is specified.", given=response.body)
2879 self._compare_error(random_id, "Invalid request. Neither 'add' nor 'remove' is specified.", given=response.body)
2880 assert ext_json.loads(response.body)['result'] is None
2880 assert ext_json.loads(response.body)['result'] is None
@@ -1,434 +1,436 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 Helpers for fixture generation
16 Helpers for fixture generation
17 """
17 """
18
18
19 import logging
19 import logging
20 import os
20 import os
21 import shutil
21 import shutil
22 import tarfile
22 import tarfile
23 from os.path import dirname
23 from os.path import dirname
24
24
25 from tg import request
25 from tg import request
26 from tg.util.webtest import test_context
26 from tg.util.webtest import test_context
27
27
28 from kallithea.lib.auth import AuthUser
28 from kallithea.lib.auth import AuthUser
29 from kallithea.lib.db_manage import DbManage
29 from kallithea.lib.db_manage import DbManage
30 from kallithea.lib.indexers.daemon import WhooshIndexingDaemon
30 from kallithea.lib.indexers.daemon import WhooshIndexingDaemon
31 from kallithea.lib.pidlock import DaemonLock
31 from kallithea.lib.pidlock import DaemonLock
32 from kallithea.lib.vcs.backends.base import EmptyChangeset
32 from kallithea.lib.vcs.backends.base import EmptyChangeset
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.comment import ChangesetCommentsModel
35 from kallithea.model.comment import ChangesetCommentsModel
36 from kallithea.model.gist import GistModel
36 from kallithea.model.gist import GistModel
37 from kallithea.model.pull_request import CreatePullRequestAction # , CreatePullRequestIterationAction, PullRequestModel
37 from kallithea.model.pull_request import CreatePullRequestAction # , CreatePullRequestIterationAction, PullRequestModel
38 from kallithea.model.repo import RepoModel
38 from kallithea.model.repo import RepoModel
39 from kallithea.model.repo_group import RepoGroupModel
39 from kallithea.model.repo_group import RepoGroupModel
40 from kallithea.model.scm import ScmModel
40 from kallithea.model.scm import ScmModel
41 from kallithea.model.user import UserModel
41 from kallithea.model.user import UserModel
42 from kallithea.model.user_group import UserGroupModel
42 from kallithea.model.user_group import UserGroupModel
43 from kallithea.tests.base import (GIT_REPO, HG_REPO, IP_ADDR, TEST_USER_ADMIN_EMAIL, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS, TEST_USER_REGULAR2_EMAIL,
43 from kallithea.tests.base import (GIT_REPO, HG_REPO, IP_ADDR, TEST_USER_ADMIN_EMAIL, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS, TEST_USER_REGULAR2_EMAIL,
44 TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
44 TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
45 TESTS_TMP_PATH, invalidate_all_caches)
45 TESTS_TMP_PATH, invalidate_all_caches)
46
46
47
47
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50 FIXTURES = os.path.join(dirname(dirname(os.path.abspath(__file__))), 'tests', 'fixtures')
50 FIXTURES = os.path.join(dirname(dirname(os.path.abspath(__file__))), 'tests', 'fixtures')
51
51
52
52
53 def raise_exception(*args, **kwargs):
53 def raise_exception(*args, **kwargs):
54 raise Exception('raise_exception raised exception')
54 raise Exception('raise_exception raised exception')
55
55
56
56
57 class Fixture(object):
57 class Fixture(object):
58
58
59 def __init__(self):
59 def __init__(self):
60 pass
60 pass
61
61
62 def anon_access(self, status):
62 def anon_access(self, status):
63 """
63 """
64 Context manager for controlling anonymous access.
64 Context manager for controlling anonymous access.
65 Anon access will be set and committed, but restored again when exiting the block.
65 Anon access will be set and committed, but restored again when exiting the block.
66
66
67 Usage:
67 Usage:
68
68
69 fixture = Fixture()
69 fixture = Fixture()
70 with fixture.anon_access(False):
70 with fixture.anon_access(False):
71 stuff
71 stuff
72 """
72 """
73
73
74 class context(object):
74 class context(object):
75 def __enter__(self):
75 def __enter__(self):
76 anon = db.User.get_default_user()
76 anon = db.User.get_default_user()
77 self._before = anon.active
77 self._before = anon.active
78 anon.active = status
78 anon.active = status
79 meta.Session().commit()
79 meta.Session().commit()
80 invalidate_all_caches()
80 invalidate_all_caches()
81
81
82 def __exit__(self, exc_type, exc_val, exc_tb):
82 def __exit__(self, exc_type, exc_val, exc_tb):
83 anon = db.User.get_default_user()
83 anon = db.User.get_default_user()
84 anon.active = self._before
84 anon.active = self._before
85 meta.Session().commit()
85 meta.Session().commit()
86
86
87 return context()
87 return context()
88
88
89 def _get_repo_create_params(self, **custom):
89 def _get_repo_create_params(self, **custom):
90 """Return form values to be validated through RepoForm"""
90 """Return form values to be validated through RepoForm"""
91 defs = dict(
91 defs = dict(
92 repo_name=None,
92 repo_name=None,
93 repo_type='hg',
93 repo_type='hg',
94 clone_uri='',
94 clone_uri='',
95 repo_group='-1',
95 repo_group='-1',
96 repo_description='DESC',
96 repo_description='DESC',
97 repo_private=False,
97 repo_private=False,
98 repo_enable_statistics=False,
99 repo_enable_downloads=False,
98 repo_landing_rev='rev:tip',
100 repo_landing_rev='rev:tip',
99 repo_copy_permissions=False,
101 repo_copy_permissions=False,
100 repo_state=db.Repository.STATE_CREATED,
102 repo_state=db.Repository.STATE_CREATED,
101 )
103 )
102 defs.update(custom)
104 defs.update(custom)
103 if 'repo_name_full' not in custom:
105 if 'repo_name_full' not in custom:
104 defs.update({'repo_name_full': defs['repo_name']})
106 defs.update({'repo_name_full': defs['repo_name']})
105
107
106 # fix the repo name if passed as repo_name_full
108 # fix the repo name if passed as repo_name_full
107 if defs['repo_name']:
109 if defs['repo_name']:
108 defs['repo_name'] = defs['repo_name'].split('/')[-1]
110 defs['repo_name'] = defs['repo_name'].split('/')[-1]
109
111
110 return defs
112 return defs
111
113
112 def _get_repo_group_create_params(self, **custom):
114 def _get_repo_group_create_params(self, **custom):
113 """Return form values to be validated through RepoGroupForm"""
115 """Return form values to be validated through RepoGroupForm"""
114 defs = dict(
116 defs = dict(
115 group_name=None,
117 group_name=None,
116 group_description='DESC',
118 group_description='DESC',
117 parent_group_id='-1',
119 parent_group_id='-1',
118 perms_updates=[],
120 perms_updates=[],
119 perms_new=[],
121 perms_new=[],
120 recursive=False
122 recursive=False
121 )
123 )
122 defs.update(custom)
124 defs.update(custom)
123
125
124 return defs
126 return defs
125
127
126 def _get_user_create_params(self, name, **custom):
128 def _get_user_create_params(self, name, **custom):
127 defs = dict(
129 defs = dict(
128 username=name,
130 username=name,
129 password='qweqwe',
131 password='qweqwe',
130 email='%s+test@example.com' % name,
132 email='%s+test@example.com' % name,
131 firstname='TestUser',
133 firstname='TestUser',
132 lastname='Test',
134 lastname='Test',
133 active=True,
135 active=True,
134 admin=False,
136 admin=False,
135 extern_type='internal',
137 extern_type='internal',
136 extern_name=None
138 extern_name=None
137 )
139 )
138 defs.update(custom)
140 defs.update(custom)
139
141
140 return defs
142 return defs
141
143
142 def _get_user_group_create_params(self, name, **custom):
144 def _get_user_group_create_params(self, name, **custom):
143 defs = dict(
145 defs = dict(
144 users_group_name=name,
146 users_group_name=name,
145 user_group_description='DESC',
147 user_group_description='DESC',
146 users_group_active=True,
148 users_group_active=True,
147 user_group_data={},
149 user_group_data={},
148 )
150 )
149 defs.update(custom)
151 defs.update(custom)
150
152
151 return defs
153 return defs
152
154
153 def create_repo(self, name, repo_group=None, cur_user=TEST_USER_ADMIN_LOGIN, **kwargs):
155 def create_repo(self, name, repo_group=None, cur_user=TEST_USER_ADMIN_LOGIN, **kwargs):
154 if 'skip_if_exists' in kwargs:
156 if 'skip_if_exists' in kwargs:
155 del kwargs['skip_if_exists']
157 del kwargs['skip_if_exists']
156 r = db.Repository.get_by_repo_name(name)
158 r = db.Repository.get_by_repo_name(name)
157 if r:
159 if r:
158 return r
160 return r
159
161
160 if isinstance(repo_group, db.RepoGroup):
162 if isinstance(repo_group, db.RepoGroup):
161 repo_group = repo_group.group_id
163 repo_group = repo_group.group_id
162
164
163 form_data = self._get_repo_create_params(repo_name=name, **kwargs)
165 form_data = self._get_repo_create_params(repo_name=name, **kwargs)
164 form_data['repo_group'] = repo_group # patch form dict so it can be used directly by model
166 form_data['repo_group'] = repo_group # patch form dict so it can be used directly by model
165 RepoModel().create(form_data, cur_user=cur_user)
167 RepoModel().create(form_data, cur_user=cur_user)
166 meta.Session().commit()
168 meta.Session().commit()
167 ScmModel().mark_for_invalidation(name)
169 ScmModel().mark_for_invalidation(name)
168 return db.Repository.get_by_repo_name(name)
170 return db.Repository.get_by_repo_name(name)
169
171
170 def create_fork(self, repo_to_fork, fork_name, cur_user=TEST_USER_ADMIN_LOGIN, **kwargs):
172 def create_fork(self, repo_to_fork, fork_name, cur_user=TEST_USER_ADMIN_LOGIN, **kwargs):
171 repo_to_fork = db.Repository.get_by_repo_name(repo_to_fork)
173 repo_to_fork = db.Repository.get_by_repo_name(repo_to_fork)
172
174
173 form_data = self._get_repo_create_params(repo_name=fork_name,
175 form_data = self._get_repo_create_params(repo_name=fork_name,
174 fork_parent_id=repo_to_fork.repo_id,
176 fork_parent_id=repo_to_fork.repo_id,
175 repo_type=repo_to_fork.repo_type,
177 repo_type=repo_to_fork.repo_type,
176 **kwargs)
178 **kwargs)
177 # patch form dict so it can be used directly by model
179 # patch form dict so it can be used directly by model
178 form_data['description'] = form_data['repo_description']
180 form_data['description'] = form_data['repo_description']
179 form_data['private'] = form_data['repo_private']
181 form_data['private'] = form_data['repo_private']
180 form_data['landing_rev'] = form_data['repo_landing_rev']
182 form_data['landing_rev'] = form_data['repo_landing_rev']
181
183
182 RepoModel().create_fork(form_data, cur_user=cur_user)
184 RepoModel().create_fork(form_data, cur_user=cur_user)
183 meta.Session().commit()
185 meta.Session().commit()
184 ScmModel().mark_for_invalidation(fork_name)
186 ScmModel().mark_for_invalidation(fork_name)
185 r = db.Repository.get_by_repo_name(fork_name)
187 r = db.Repository.get_by_repo_name(fork_name)
186 assert r
188 assert r
187 return r
189 return r
188
190
189 def destroy_repo(self, repo_name, **kwargs):
191 def destroy_repo(self, repo_name, **kwargs):
190 RepoModel().delete(repo_name, **kwargs)
192 RepoModel().delete(repo_name, **kwargs)
191 meta.Session().commit()
193 meta.Session().commit()
192
194
193 def create_repo_group(self, name, parent_group_id=None, cur_user=TEST_USER_ADMIN_LOGIN, **kwargs):
195 def create_repo_group(self, name, parent_group_id=None, cur_user=TEST_USER_ADMIN_LOGIN, **kwargs):
194 assert '/' not in name, (name, kwargs) # use group_parent_id to make nested groups
196 assert '/' not in name, (name, kwargs) # use group_parent_id to make nested groups
195 if 'skip_if_exists' in kwargs:
197 if 'skip_if_exists' in kwargs:
196 del kwargs['skip_if_exists']
198 del kwargs['skip_if_exists']
197 gr = db.RepoGroup.get_by_group_name(group_name=name)
199 gr = db.RepoGroup.get_by_group_name(group_name=name)
198 if gr:
200 if gr:
199 return gr
201 return gr
200 form_data = self._get_repo_group_create_params(group_name=name, **kwargs)
202 form_data = self._get_repo_group_create_params(group_name=name, **kwargs)
201 gr = RepoGroupModel().create(
203 gr = RepoGroupModel().create(
202 group_name=form_data['group_name'],
204 group_name=form_data['group_name'],
203 group_description=form_data['group_name'],
205 group_description=form_data['group_name'],
204 parent=parent_group_id,
206 parent=parent_group_id,
205 owner=cur_user,
207 owner=cur_user,
206 )
208 )
207 meta.Session().commit()
209 meta.Session().commit()
208 gr = db.RepoGroup.get_by_group_name(gr.group_name)
210 gr = db.RepoGroup.get_by_group_name(gr.group_name)
209 return gr
211 return gr
210
212
211 def destroy_repo_group(self, repogroupid):
213 def destroy_repo_group(self, repogroupid):
212 RepoGroupModel().delete(repogroupid)
214 RepoGroupModel().delete(repogroupid)
213 meta.Session().commit()
215 meta.Session().commit()
214
216
215 def create_user(self, name, **kwargs):
217 def create_user(self, name, **kwargs):
216 if 'skip_if_exists' in kwargs:
218 if 'skip_if_exists' in kwargs:
217 del kwargs['skip_if_exists']
219 del kwargs['skip_if_exists']
218 user = db.User.get_by_username(name)
220 user = db.User.get_by_username(name)
219 if user:
221 if user:
220 return user
222 return user
221 form_data = self._get_user_create_params(name, **kwargs)
223 form_data = self._get_user_create_params(name, **kwargs)
222 user = UserModel().create(form_data)
224 user = UserModel().create(form_data)
223 meta.Session().commit()
225 meta.Session().commit()
224 user = db.User.get_by_username(user.username)
226 user = db.User.get_by_username(user.username)
225 return user
227 return user
226
228
227 def destroy_user(self, userid):
229 def destroy_user(self, userid):
228 UserModel().delete(userid)
230 UserModel().delete(userid)
229 meta.Session().commit()
231 meta.Session().commit()
230
232
231 def create_user_group(self, name, cur_user=TEST_USER_ADMIN_LOGIN, **kwargs):
233 def create_user_group(self, name, cur_user=TEST_USER_ADMIN_LOGIN, **kwargs):
232 if 'skip_if_exists' in kwargs:
234 if 'skip_if_exists' in kwargs:
233 del kwargs['skip_if_exists']
235 del kwargs['skip_if_exists']
234 gr = db.UserGroup.get_by_group_name(group_name=name)
236 gr = db.UserGroup.get_by_group_name(group_name=name)
235 if gr:
237 if gr:
236 return gr
238 return gr
237 form_data = self._get_user_group_create_params(name, **kwargs)
239 form_data = self._get_user_group_create_params(name, **kwargs)
238 user_group = UserGroupModel().create(
240 user_group = UserGroupModel().create(
239 name=form_data['users_group_name'],
241 name=form_data['users_group_name'],
240 description=form_data['user_group_description'],
242 description=form_data['user_group_description'],
241 owner=cur_user,
243 owner=cur_user,
242 active=form_data['users_group_active'],
244 active=form_data['users_group_active'],
243 group_data=form_data['user_group_data'])
245 group_data=form_data['user_group_data'])
244 meta.Session().commit()
246 meta.Session().commit()
245 user_group = db.UserGroup.get_by_group_name(user_group.users_group_name)
247 user_group = db.UserGroup.get_by_group_name(user_group.users_group_name)
246 return user_group
248 return user_group
247
249
248 def destroy_user_group(self, usergroupid):
250 def destroy_user_group(self, usergroupid):
249 UserGroupModel().delete(user_group=usergroupid, force=True)
251 UserGroupModel().delete(user_group=usergroupid, force=True)
250 meta.Session().commit()
252 meta.Session().commit()
251
253
252 def create_gist(self, **kwargs):
254 def create_gist(self, **kwargs):
253 form_data = {
255 form_data = {
254 'description': 'new-gist',
256 'description': 'new-gist',
255 'owner': TEST_USER_ADMIN_LOGIN,
257 'owner': TEST_USER_ADMIN_LOGIN,
256 'gist_type': db.Gist.GIST_PUBLIC,
258 'gist_type': db.Gist.GIST_PUBLIC,
257 'lifetime': -1,
259 'lifetime': -1,
258 'gist_mapping': {'filename1.txt': {'content': 'hello world'}}
260 'gist_mapping': {'filename1.txt': {'content': 'hello world'}}
259 }
261 }
260 form_data.update(kwargs)
262 form_data.update(kwargs)
261 gist = GistModel().create(
263 gist = GistModel().create(
262 description=form_data['description'], owner=form_data['owner'], ip_addr=IP_ADDR,
264 description=form_data['description'], owner=form_data['owner'], ip_addr=IP_ADDR,
263 gist_mapping=form_data['gist_mapping'], gist_type=form_data['gist_type'],
265 gist_mapping=form_data['gist_mapping'], gist_type=form_data['gist_type'],
264 lifetime=form_data['lifetime']
266 lifetime=form_data['lifetime']
265 )
267 )
266 meta.Session().commit()
268 meta.Session().commit()
267
269
268 return gist
270 return gist
269
271
270 def destroy_gists(self, gistid=None):
272 def destroy_gists(self, gistid=None):
271 for g in db.Gist.query():
273 for g in db.Gist.query():
272 if gistid:
274 if gistid:
273 if gistid == g.gist_access_id:
275 if gistid == g.gist_access_id:
274 GistModel().delete(g)
276 GistModel().delete(g)
275 else:
277 else:
276 GistModel().delete(g)
278 GistModel().delete(g)
277 meta.Session().commit()
279 meta.Session().commit()
278
280
279 def load_resource(self, resource_name, strip=True):
281 def load_resource(self, resource_name, strip=True):
280 with open(os.path.join(FIXTURES, resource_name), 'rb') as f:
282 with open(os.path.join(FIXTURES, resource_name), 'rb') as f:
281 source = f.read()
283 source = f.read()
282 if strip:
284 if strip:
283 source = source.strip()
285 source = source.strip()
284
286
285 return source
287 return source
286
288
287 def commit_change(self, repo, filename, content, message, vcs_type,
289 def commit_change(self, repo, filename, content, message, vcs_type,
288 parent=None, newfile=False, author=None):
290 parent=None, newfile=False, author=None):
289 repo = db.Repository.get_by_repo_name(repo)
291 repo = db.Repository.get_by_repo_name(repo)
290 _cs = parent
292 _cs = parent
291 if parent is None:
293 if parent is None:
292 _cs = EmptyChangeset(alias=vcs_type)
294 _cs = EmptyChangeset(alias=vcs_type)
293 if author is None:
295 if author is None:
294 author = '%s <%s>' % (TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_EMAIL)
296 author = '%s <%s>' % (TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_EMAIL)
295
297
296 if newfile:
298 if newfile:
297 nodes = {
299 nodes = {
298 filename: {
300 filename: {
299 'content': content
301 'content': content
300 }
302 }
301 }
303 }
302 cs = ScmModel().create_nodes(
304 cs = ScmModel().create_nodes(
303 user=TEST_USER_ADMIN_LOGIN,
305 user=TEST_USER_ADMIN_LOGIN,
304 ip_addr=IP_ADDR,
306 ip_addr=IP_ADDR,
305 repo=repo,
307 repo=repo,
306 message=message,
308 message=message,
307 nodes=nodes,
309 nodes=nodes,
308 parent_cs=_cs,
310 parent_cs=_cs,
309 author=author,
311 author=author,
310 )
312 )
311 else:
313 else:
312 cs = ScmModel().commit_change(
314 cs = ScmModel().commit_change(
313 repo=repo.scm_instance, repo_name=repo.repo_name,
315 repo=repo.scm_instance, repo_name=repo.repo_name,
314 cs=parent,
316 cs=parent,
315 user=TEST_USER_ADMIN_LOGIN,
317 user=TEST_USER_ADMIN_LOGIN,
316 ip_addr=IP_ADDR,
318 ip_addr=IP_ADDR,
317 author=author,
319 author=author,
318 message=message,
320 message=message,
319 content=content,
321 content=content,
320 f_path=filename
322 f_path=filename
321 )
323 )
322 return cs
324 return cs
323
325
324 def review_changeset(self, repo, revision, status, author=TEST_USER_ADMIN_LOGIN):
326 def review_changeset(self, repo, revision, status, author=TEST_USER_ADMIN_LOGIN):
325 comment = ChangesetCommentsModel().create("review comment", repo, author, revision=revision, send_email=False)
327 comment = ChangesetCommentsModel().create("review comment", repo, author, revision=revision, send_email=False)
326 csm = ChangesetStatusModel().set_status(repo, db.ChangesetStatus.STATUS_APPROVED, author, comment, revision=revision)
328 csm = ChangesetStatusModel().set_status(repo, db.ChangesetStatus.STATUS_APPROVED, author, comment, revision=revision)
327 meta.Session().commit()
329 meta.Session().commit()
328 return csm
330 return csm
329
331
330 def create_pullrequest(self, testcontroller, repo_name, pr_src_rev, pr_dst_rev, title='title'):
332 def create_pullrequest(self, testcontroller, repo_name, pr_src_rev, pr_dst_rev, title='title'):
331 org_ref = 'branch:stable:%s' % pr_src_rev
333 org_ref = 'branch:stable:%s' % pr_src_rev
332 other_ref = 'branch:default:%s' % pr_dst_rev
334 other_ref = 'branch:default:%s' % pr_dst_rev
333 with test_context(testcontroller.app): # needed to be able to mock request user and routes.url
335 with test_context(testcontroller.app): # needed to be able to mock request user and routes.url
334 org_repo = other_repo = db.Repository.get_by_repo_name(repo_name)
336 org_repo = other_repo = db.Repository.get_by_repo_name(repo_name)
335 owner_user = db.User.get_by_username(TEST_USER_ADMIN_LOGIN)
337 owner_user = db.User.get_by_username(TEST_USER_ADMIN_LOGIN)
336 reviewers = [db.User.get_by_username(TEST_USER_REGULAR_LOGIN)]
338 reviewers = [db.User.get_by_username(TEST_USER_REGULAR_LOGIN)]
337 request.authuser = AuthUser(dbuser=owner_user)
339 request.authuser = AuthUser(dbuser=owner_user)
338 # creating a PR sends a message with an absolute URL - without routing that requires mocking
340 # creating a PR sends a message with an absolute URL - without routing that requires mocking
339 request.environ['routes.url'] = lambda arg, qualified=False, **kwargs: ('https://localhost' if qualified else '') + '/fake/' + arg
341 request.environ['routes.url'] = lambda arg, qualified=False, **kwargs: ('https://localhost' if qualified else '') + '/fake/' + arg
340 cmd = CreatePullRequestAction(org_repo, other_repo, org_ref, other_ref, title, 'No description', owner_user, reviewers)
342 cmd = CreatePullRequestAction(org_repo, other_repo, org_ref, other_ref, title, 'No description', owner_user, reviewers)
341 pull_request = cmd.execute()
343 pull_request = cmd.execute()
342 meta.Session().commit()
344 meta.Session().commit()
343 return pull_request.pull_request_id
345 return pull_request.pull_request_id
344
346
345
347
346 #==============================================================================
348 #==============================================================================
347 # Global test environment setup
349 # Global test environment setup
348 #==============================================================================
350 #==============================================================================
349
351
350 def create_test_env(repos_test_path, config, reuse_database):
352 def create_test_env(repos_test_path, config, reuse_database):
351 """
353 """
352 Makes a fresh database and
354 Makes a fresh database and
353 install test repository into tmp dir
355 install test repository into tmp dir
354 """
356 """
355
357
356 # PART ONE create db
358 # PART ONE create db
357 dbconf = config['sqlalchemy.url']
359 dbconf = config['sqlalchemy.url']
358 log.debug('making test db %s', dbconf)
360 log.debug('making test db %s', dbconf)
359
361
360 # create test dir if it doesn't exist
362 # create test dir if it doesn't exist
361 if not os.path.isdir(repos_test_path):
363 if not os.path.isdir(repos_test_path):
362 log.debug('Creating testdir %s', repos_test_path)
364 log.debug('Creating testdir %s', repos_test_path)
363 os.makedirs(repos_test_path)
365 os.makedirs(repos_test_path)
364
366
365 dbmanage = DbManage(dbconf=dbconf, root=config['here'],
367 dbmanage = DbManage(dbconf=dbconf, root=config['here'],
366 cli_args={
368 cli_args={
367 'force_ask': True,
369 'force_ask': True,
368 'username': TEST_USER_ADMIN_LOGIN,
370 'username': TEST_USER_ADMIN_LOGIN,
369 'password': TEST_USER_ADMIN_PASS,
371 'password': TEST_USER_ADMIN_PASS,
370 'email': TEST_USER_ADMIN_EMAIL,
372 'email': TEST_USER_ADMIN_EMAIL,
371 })
373 })
372 dbmanage.create_tables(reuse_database=reuse_database)
374 dbmanage.create_tables(reuse_database=reuse_database)
373 # for tests dynamically set new root paths based on generated content
375 # for tests dynamically set new root paths based on generated content
374 dbmanage.create_settings(dbmanage.prompt_repo_root_path(repos_test_path))
376 dbmanage.create_settings(dbmanage.prompt_repo_root_path(repos_test_path))
375 dbmanage.create_default_user()
377 dbmanage.create_default_user()
376 dbmanage.create_admin_user()
378 dbmanage.create_admin_user()
377 dbmanage.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, TEST_USER_REGULAR_EMAIL, False)
379 dbmanage.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, TEST_USER_REGULAR_EMAIL, False)
378 dbmanage.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL, False)
380 dbmanage.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL, False)
379 dbmanage.create_permissions()
381 dbmanage.create_permissions()
380 dbmanage.populate_default_permissions()
382 dbmanage.populate_default_permissions()
381 meta.Session().commit()
383 meta.Session().commit()
382 # PART TWO make test repo
384 # PART TWO make test repo
383 log.debug('making test vcs repositories')
385 log.debug('making test vcs repositories')
384
386
385 idx_path = config['index_dir']
387 idx_path = config['index_dir']
386 data_path = config['cache_dir']
388 data_path = config['cache_dir']
387
389
388 # clean index and data
390 # clean index and data
389 if idx_path and os.path.exists(idx_path):
391 if idx_path and os.path.exists(idx_path):
390 log.debug('remove %s', idx_path)
392 log.debug('remove %s', idx_path)
391 shutil.rmtree(idx_path)
393 shutil.rmtree(idx_path)
392
394
393 if data_path and os.path.exists(data_path):
395 if data_path and os.path.exists(data_path):
394 log.debug('remove %s', data_path)
396 log.debug('remove %s', data_path)
395 shutil.rmtree(data_path)
397 shutil.rmtree(data_path)
396
398
397 # CREATE DEFAULT TEST REPOS
399 # CREATE DEFAULT TEST REPOS
398 tar = tarfile.open(os.path.join(FIXTURES, 'vcs_test_hg.tar.gz'))
400 tar = tarfile.open(os.path.join(FIXTURES, 'vcs_test_hg.tar.gz'))
399 tar.extractall(os.path.join(TESTS_TMP_PATH, HG_REPO))
401 tar.extractall(os.path.join(TESTS_TMP_PATH, HG_REPO))
400 tar.close()
402 tar.close()
401
403
402 tar = tarfile.open(os.path.join(FIXTURES, 'vcs_test_git.tar.gz'))
404 tar = tarfile.open(os.path.join(FIXTURES, 'vcs_test_git.tar.gz'))
403 tar.extractall(os.path.join(TESTS_TMP_PATH, GIT_REPO))
405 tar.extractall(os.path.join(TESTS_TMP_PATH, GIT_REPO))
404 tar.close()
406 tar.close()
405
407
406
408
407 def create_test_index(repo_location, config, full_index):
409 def create_test_index(repo_location, config, full_index):
408 """
410 """
409 Makes default test index
411 Makes default test index
410 """
412 """
411
413
412 index_location = os.path.join(config['index_dir'])
414 index_location = os.path.join(config['index_dir'])
413 if not os.path.exists(index_location):
415 if not os.path.exists(index_location):
414 os.makedirs(index_location)
416 os.makedirs(index_location)
415
417
416 l = DaemonLock(os.path.join(index_location, 'make_index.lock'))
418 l = DaemonLock(os.path.join(index_location, 'make_index.lock'))
417 WhooshIndexingDaemon(index_location=index_location,
419 WhooshIndexingDaemon(index_location=index_location,
418 repo_location=repo_location) \
420 repo_location=repo_location) \
419 .run(full_index=full_index)
421 .run(full_index=full_index)
420 l.release()
422 l.release()
421
423
422
424
423 def failing_test_hook(ui, repo, **kwargs):
425 def failing_test_hook(ui, repo, **kwargs):
424 ui.write(b"failing_test_hook failed\n")
426 ui.write(b"failing_test_hook failed\n")
425 return 1
427 return 1
426
428
427
429
428 def exception_test_hook(ui, repo, **kwargs):
430 def exception_test_hook(ui, repo, **kwargs):
429 raise Exception("exception_test_hook threw an exception")
431 raise Exception("exception_test_hook threw an exception")
430
432
431
433
432 def passing_test_hook(ui, repo, **kwargs):
434 def passing_test_hook(ui, repo, **kwargs):
433 ui.write(b"passing_test_hook succeeded\n")
435 ui.write(b"passing_test_hook succeeded\n")
434 return 0
436 return 0
General Comments 0
You need to be logged in to leave comments. Login now