##// END OF EJS Templates
get_locks API function draft
marcink -
r3502:7cde75ea beta
parent child Browse files
Show More
@@ -1,978 +1,1017 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.api
3 rhodecode.controllers.api
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 API controller for RhodeCode
6 API controller for RhodeCode
7
7
8 :created_on: Aug 20, 2011
8 :created_on: Aug 20, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import traceback
28 import traceback
29 import logging
29 import logging
30
30
31 from rhodecode.controllers.api import JSONRPCController, JSONRPCError
31 from rhodecode.controllers.api import JSONRPCController, JSONRPCError
32 from rhodecode.lib.auth import PasswordGenerator, AuthUser, \
32 from rhodecode.lib.auth import PasswordGenerator, AuthUser, \
33 HasPermissionAllDecorator, HasPermissionAnyDecorator, \
33 HasPermissionAllDecorator, HasPermissionAnyDecorator, \
34 HasPermissionAnyApi, HasRepoPermissionAnyApi
34 HasPermissionAnyApi, HasRepoPermissionAnyApi
35 from rhodecode.lib.utils import map_groups, repo2db_mapper
35 from rhodecode.lib.utils import map_groups, repo2db_mapper
36 from rhodecode.lib.utils2 import str2bool, time_to_datetime
36 from rhodecode.lib.utils2 import str2bool, time_to_datetime, safe_int
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38 from rhodecode.model.meta import Session
38 from rhodecode.model.meta import Session
39 from rhodecode.model.scm import ScmModel
39 from rhodecode.model.scm import ScmModel
40 from rhodecode.model.repo import RepoModel
40 from rhodecode.model.repo import RepoModel
41 from rhodecode.model.user import UserModel
41 from rhodecode.model.user import UserModel
42 from rhodecode.model.users_group import UserGroupModel
42 from rhodecode.model.users_group import UserGroupModel
43 from rhodecode.model.permission import PermissionModel
43 from rhodecode.model.permission import PermissionModel
44 from rhodecode.model.db import Repository, RhodeCodeSetting, UserIpMap
44 from rhodecode.model.db import Repository, RhodeCodeSetting, UserIpMap
45 from rhodecode.lib.compat import json
45
46
46 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
47
48
48
49
49 class OptionalAttr(object):
50 class OptionalAttr(object):
50 """
51 """
51 Special Optional Option that defines other attribute
52 Special Optional Option that defines other attribute
52 """
53 """
53 def __init__(self, attr_name):
54 def __init__(self, attr_name):
54 self.attr_name = attr_name
55 self.attr_name = attr_name
55
56
56 def __repr__(self):
57 def __repr__(self):
57 return '<OptionalAttr:%s>' % self.attr_name
58 return '<OptionalAttr:%s>' % self.attr_name
58
59
59 def __call__(self):
60 def __call__(self):
60 return self
61 return self
61 #alias
62 #alias
62 OAttr = OptionalAttr
63 OAttr = OptionalAttr
63
64
64
65
65 class Optional(object):
66 class Optional(object):
66 """
67 """
67 Defines an optional parameter::
68 Defines an optional parameter::
68
69
69 param = param.getval() if isinstance(param, Optional) else param
70 param = param.getval() if isinstance(param, Optional) else param
70 param = param() if isinstance(param, Optional) else param
71 param = param() if isinstance(param, Optional) else param
71
72
72 is equivalent of::
73 is equivalent of::
73
74
74 param = Optional.extract(param)
75 param = Optional.extract(param)
75
76
76 """
77 """
77 def __init__(self, type_):
78 def __init__(self, type_):
78 self.type_ = type_
79 self.type_ = type_
79
80
80 def __repr__(self):
81 def __repr__(self):
81 return '<Optional:%s>' % self.type_.__repr__()
82 return '<Optional:%s>' % self.type_.__repr__()
82
83
83 def __call__(self):
84 def __call__(self):
84 return self.getval()
85 return self.getval()
85
86
86 def getval(self):
87 def getval(self):
87 """
88 """
88 returns value from this Optional instance
89 returns value from this Optional instance
89 """
90 """
90 return self.type_
91 return self.type_
91
92
92 @classmethod
93 @classmethod
93 def extract(cls, val):
94 def extract(cls, val):
94 if isinstance(val, cls):
95 if isinstance(val, cls):
95 return val.getval()
96 return val.getval()
96 return val
97 return val
97
98
98
99
99 def get_user_or_error(userid):
100 def get_user_or_error(userid):
100 """
101 """
101 Get user by id or name or return JsonRPCError if not found
102 Get user by id or name or return JsonRPCError if not found
102
103
103 :param userid:
104 :param userid:
104 """
105 """
105 user = UserModel().get_user(userid)
106 user = UserModel().get_user(userid)
106 if user is None:
107 if user is None:
107 raise JSONRPCError("user `%s` does not exist" % userid)
108 raise JSONRPCError("user `%s` does not exist" % userid)
108 return user
109 return user
109
110
110
111
111 def get_repo_or_error(repoid):
112 def get_repo_or_error(repoid):
112 """
113 """
113 Get repo by id or name or return JsonRPCError if not found
114 Get repo by id or name or return JsonRPCError if not found
114
115
115 :param userid:
116 :param userid:
116 """
117 """
117 repo = RepoModel().get_repo(repoid)
118 repo = RepoModel().get_repo(repoid)
118 if repo is None:
119 if repo is None:
119 raise JSONRPCError('repository `%s` does not exist' % (repoid))
120 raise JSONRPCError('repository `%s` does not exist' % (repoid))
120 return repo
121 return repo
121
122
122
123
123 def get_users_group_or_error(usersgroupid):
124 def get_users_group_or_error(usersgroupid):
124 """
125 """
125 Get user group by id or name or return JsonRPCError if not found
126 Get user group by id or name or return JsonRPCError if not found
126
127
127 :param userid:
128 :param userid:
128 """
129 """
129 users_group = UserGroupModel().get_group(usersgroupid)
130 users_group = UserGroupModel().get_group(usersgroupid)
130 if users_group is None:
131 if users_group is None:
131 raise JSONRPCError('user group `%s` does not exist' % usersgroupid)
132 raise JSONRPCError('user group `%s` does not exist' % usersgroupid)
132 return users_group
133 return users_group
133
134
134
135
135 def get_perm_or_error(permid):
136 def get_perm_or_error(permid):
136 """
137 """
137 Get permission by id or name or return JsonRPCError if not found
138 Get permission by id or name or return JsonRPCError if not found
138
139
139 :param userid:
140 :param userid:
140 """
141 """
141 perm = PermissionModel().get_permission_by_name(permid)
142 perm = PermissionModel().get_permission_by_name(permid)
142 if perm is None:
143 if perm is None:
143 raise JSONRPCError('permission `%s` does not exist' % (permid))
144 raise JSONRPCError('permission `%s` does not exist' % (permid))
144 return perm
145 return perm
145
146
146
147
147 class ApiController(JSONRPCController):
148 class ApiController(JSONRPCController):
148 """
149 """
149 API Controller
150 API Controller
150
151
151
152
152 Each method needs to have USER as argument this is then based on given
153 Each method needs to have USER as argument this is then based on given
153 API_KEY propagated as instance of user object
154 API_KEY propagated as instance of user object
154
155
155 Preferably this should be first argument also
156 Preferably this should be first argument also
156
157
157
158
158 Each function should also **raise** JSONRPCError for any
159 Each function should also **raise** JSONRPCError for any
159 errors that happens
160 errors that happens
160
161
161 """
162 """
162
163
163 @HasPermissionAllDecorator('hg.admin')
164 @HasPermissionAllDecorator('hg.admin')
164 def pull(self, apiuser, repoid):
165 def pull(self, apiuser, repoid):
165 """
166 """
166 Dispatch pull action on given repo
167 Dispatch pull action on given repo
167
168
168 :param apiuser:
169 :param apiuser:
169 :param repoid:
170 :param repoid:
170 """
171 """
171
172
172 repo = get_repo_or_error(repoid)
173 repo = get_repo_or_error(repoid)
173
174
174 try:
175 try:
175 ScmModel().pull_changes(repo.repo_name,
176 ScmModel().pull_changes(repo.repo_name,
176 self.rhodecode_user.username)
177 self.rhodecode_user.username)
177 return 'Pulled from `%s`' % repo.repo_name
178 return 'Pulled from `%s`' % repo.repo_name
178 except Exception:
179 except Exception:
179 log.error(traceback.format_exc())
180 log.error(traceback.format_exc())
180 raise JSONRPCError(
181 raise JSONRPCError(
181 'Unable to pull changes from `%s`' % repo.repo_name
182 'Unable to pull changes from `%s`' % repo.repo_name
182 )
183 )
183
184
184 @HasPermissionAllDecorator('hg.admin')
185 @HasPermissionAllDecorator('hg.admin')
185 def rescan_repos(self, apiuser, remove_obsolete=Optional(False)):
186 def rescan_repos(self, apiuser, remove_obsolete=Optional(False)):
186 """
187 """
187 Dispatch rescan repositories action. If remove_obsolete is set
188 Dispatch rescan repositories action. If remove_obsolete is set
188 than also delete repos that are in database but not in the filesystem.
189 than also delete repos that are in database but not in the filesystem.
189 aka "clean zombies"
190 aka "clean zombies"
190
191
191 :param apiuser:
192 :param apiuser:
192 :param remove_obsolete:
193 :param remove_obsolete:
193 """
194 """
194
195
195 try:
196 try:
196 rm_obsolete = Optional.extract(remove_obsolete)
197 rm_obsolete = Optional.extract(remove_obsolete)
197 added, removed = repo2db_mapper(ScmModel().repo_scan(),
198 added, removed = repo2db_mapper(ScmModel().repo_scan(),
198 remove_obsolete=rm_obsolete)
199 remove_obsolete=rm_obsolete)
199 return {'added': added, 'removed': removed}
200 return {'added': added, 'removed': removed}
200 except Exception:
201 except Exception:
201 log.error(traceback.format_exc())
202 log.error(traceback.format_exc())
202 raise JSONRPCError(
203 raise JSONRPCError(
203 'Error occurred during rescan repositories action'
204 'Error occurred during rescan repositories action'
204 )
205 )
205
206
206 def invalidate_cache(self, apiuser, repoid):
207 def invalidate_cache(self, apiuser, repoid):
207 """
208 """
208 Dispatch cache invalidation action on given repo
209 Dispatch cache invalidation action on given repo
209
210
210 :param apiuser:
211 :param apiuser:
211 :param repoid:
212 :param repoid:
212 """
213 """
213 repo = get_repo_or_error(repoid)
214 repo = get_repo_or_error(repoid)
214 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
215 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
215 # check if we have admin permission for this repo !
216 # check if we have admin permission for this repo !
216 if HasRepoPermissionAnyApi('repository.admin',
217 if HasRepoPermissionAnyApi('repository.admin',
217 'repository.write')(user=apiuser,
218 'repository.write')(user=apiuser,
218 repo_name=repo.repo_name) is False:
219 repo_name=repo.repo_name) is False:
219 raise JSONRPCError('repository `%s` does not exist' % (repoid))
220 raise JSONRPCError('repository `%s` does not exist' % (repoid))
220
221
221 try:
222 try:
222 invalidated_keys = ScmModel().mark_for_invalidation(repo.repo_name)
223 invalidated_keys = ScmModel().mark_for_invalidation(repo.repo_name)
223 Session().commit()
224 Session().commit()
224 return ('Cache for repository `%s` was invalidated: '
225 return ('Cache for repository `%s` was invalidated: '
225 'invalidated cache keys: %s' % (repoid, invalidated_keys))
226 'invalidated cache keys: %s' % (repoid, invalidated_keys))
226 except Exception:
227 except Exception:
227 log.error(traceback.format_exc())
228 log.error(traceback.format_exc())
228 raise JSONRPCError(
229 raise JSONRPCError(
229 'Error occurred during cache invalidation action'
230 'Error occurred during cache invalidation action'
230 )
231 )
231
232
232 def lock(self, apiuser, repoid, locked=Optional(None),
233 def lock(self, apiuser, repoid, locked=Optional(None),
233 userid=Optional(OAttr('apiuser'))):
234 userid=Optional(OAttr('apiuser'))):
234 """
235 """
235 Set locking state on particular repository by given user, if
236 Set locking state on particular repository by given user, if
236 this command is runned by non-admin account userid is set to user
237 this command is runned by non-admin account userid is set to user
237 who is calling this method
238 who is calling this method
238
239
239 :param apiuser:
240 :param apiuser:
240 :param repoid:
241 :param repoid:
241 :param userid:
242 :param userid:
242 :param locked:
243 :param locked:
243 """
244 """
244 repo = get_repo_or_error(repoid)
245 repo = get_repo_or_error(repoid)
245 if HasPermissionAnyApi('hg.admin')(user=apiuser):
246 if HasPermissionAnyApi('hg.admin')(user=apiuser):
246 pass
247 pass
247 elif HasRepoPermissionAnyApi('repository.admin',
248 elif HasRepoPermissionAnyApi('repository.admin',
248 'repository.write')(user=apiuser,
249 'repository.write')(user=apiuser,
249 repo_name=repo.repo_name):
250 repo_name=repo.repo_name):
250 #make sure normal user does not pass someone else userid,
251 #make sure normal user does not pass someone else userid,
251 #he is not allowed to do that
252 #he is not allowed to do that
252 if not isinstance(userid, Optional) and userid != apiuser.user_id:
253 if not isinstance(userid, Optional) and userid != apiuser.user_id:
253 raise JSONRPCError(
254 raise JSONRPCError(
254 'userid is not the same as your user'
255 'userid is not the same as your user'
255 )
256 )
256 else:
257 else:
257 raise JSONRPCError('repository `%s` does not exist' % (repoid))
258 raise JSONRPCError('repository `%s` does not exist' % (repoid))
258
259
259 if isinstance(userid, Optional):
260 if isinstance(userid, Optional):
260 userid = apiuser.user_id
261 userid = apiuser.user_id
261
262
262 user = get_user_or_error(userid)
263 user = get_user_or_error(userid)
263
264
264 if isinstance(locked, Optional):
265 if isinstance(locked, Optional):
265 lockobj = Repository.getlock(repo)
266 lockobj = Repository.getlock(repo)
266
267
267 if lockobj[0] is None:
268 if lockobj[0] is None:
268 return ('Repo `%s` not locked. Locked=`False`.'
269 return ('Repo `%s` not locked. Locked=`False`.'
269 % (repo.repo_name))
270 % (repo.repo_name))
270 else:
271 else:
271 userid, time_ = lockobj
272 userid, time_ = lockobj
272 user = get_user_or_error(userid)
273 user = get_user_or_error(userid)
273
274
274 return ('Repo `%s` locked by `%s`. Locked=`True`. '
275 return ('Repo `%s` locked by `%s`. Locked=`True`. '
275 'Locked since: `%s`'
276 'Locked since: `%s`'
276 % (repo.repo_name, user.username,
277 % (repo.repo_name, user.username,
277 h.fmt_date(time_to_datetime(time_))))
278 json.dumps(time_to_datetime(time_))))
278
279
279 else:
280 else:
280 locked = str2bool(locked)
281 locked = str2bool(locked)
281 try:
282 try:
282 if locked:
283 if locked:
283 Repository.lock(repo, user.user_id)
284 Repository.lock(repo, user.user_id)
284 else:
285 else:
285 Repository.unlock(repo)
286 Repository.unlock(repo)
286
287
287 return ('User `%s` set lock state for repo `%s` to `%s`'
288 return ('User `%s` set lock state for repo `%s` to `%s`'
288 % (user.username, repo.repo_name, locked))
289 % (user.username, repo.repo_name, locked))
289 except Exception:
290 except Exception:
290 log.error(traceback.format_exc())
291 log.error(traceback.format_exc())
291 raise JSONRPCError(
292 raise JSONRPCError(
292 'Error occurred locking repository `%s`' % repo.repo_name
293 'Error occurred locking repository `%s`' % repo.repo_name
293 )
294 )
294
295
296 def get_locks(self, apiuser, userid=Optional(OAttr('apiuser'))):
297 """
298 Get all locks for given userid, if
299 this command is runned by non-admin account userid is set to user
300 who is calling this method, thus returning locks for himself
301
302 :param apiuser:
303 :param userid:
304 """
305 if HasPermissionAnyApi('hg.admin')(user=apiuser):
306 pass
307 else:
308 #make sure normal user does not pass someone else userid,
309 #he is not allowed to do that
310 if not isinstance(userid, Optional) and userid != apiuser.user_id:
311 raise JSONRPCError(
312 'userid is not the same as your user'
313 )
314 ret = []
315 if isinstance(userid, Optional):
316 user = None
317 else:
318 user = get_user_or_error(userid)
319
320 #show all locks
321 for r in Repository.getAll():
322 userid, time_ = r.locked
323 if time_:
324 _api_data = r.get_api_data()
325 # if we use userfilter just show the locks for this user
326 if user:
327 if safe_int(userid) == user.user_id:
328 ret.append(_api_data)
329 else:
330 ret.append(_api_data)
331
332 return ret
333
295 @HasPermissionAllDecorator('hg.admin')
334 @HasPermissionAllDecorator('hg.admin')
296 def show_ip(self, apiuser, userid):
335 def show_ip(self, apiuser, userid):
297 """
336 """
298 Shows IP address as seen from RhodeCode server, together with all
337 Shows IP address as seen from RhodeCode server, together with all
299 defined IP addresses for given user
338 defined IP addresses for given user
300
339
301 :param apiuser:
340 :param apiuser:
302 :param userid:
341 :param userid:
303 """
342 """
304 user = get_user_or_error(userid)
343 user = get_user_or_error(userid)
305 ips = UserIpMap.query().filter(UserIpMap.user == user).all()
344 ips = UserIpMap.query().filter(UserIpMap.user == user).all()
306 return dict(
345 return dict(
307 ip_addr_server=self.ip_addr,
346 ip_addr_server=self.ip_addr,
308 user_ips=ips
347 user_ips=ips
309 )
348 )
310
349
311 def get_user(self, apiuser, userid=Optional(OAttr('apiuser'))):
350 def get_user(self, apiuser, userid=Optional(OAttr('apiuser'))):
312 """"
351 """"
313 Get a user by username, or userid, if userid is given
352 Get a user by username, or userid, if userid is given
314
353
315 :param apiuser:
354 :param apiuser:
316 :param userid:
355 :param userid:
317 """
356 """
318 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
357 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
319 #make sure normal user does not pass someone else userid,
358 #make sure normal user does not pass someone else userid,
320 #he is not allowed to do that
359 #he is not allowed to do that
321 if not isinstance(userid, Optional) and userid != apiuser.user_id:
360 if not isinstance(userid, Optional) and userid != apiuser.user_id:
322 raise JSONRPCError(
361 raise JSONRPCError(
323 'userid is not the same as your user'
362 'userid is not the same as your user'
324 )
363 )
325
364
326 if isinstance(userid, Optional):
365 if isinstance(userid, Optional):
327 userid = apiuser.user_id
366 userid = apiuser.user_id
328
367
329 user = get_user_or_error(userid)
368 user = get_user_or_error(userid)
330 data = user.get_api_data()
369 data = user.get_api_data()
331 data['permissions'] = AuthUser(user_id=user.user_id).permissions
370 data['permissions'] = AuthUser(user_id=user.user_id).permissions
332 return data
371 return data
333
372
334 @HasPermissionAllDecorator('hg.admin')
373 @HasPermissionAllDecorator('hg.admin')
335 def get_users(self, apiuser):
374 def get_users(self, apiuser):
336 """"
375 """"
337 Get all users
376 Get all users
338
377
339 :param apiuser:
378 :param apiuser:
340 """
379 """
341
380
342 result = []
381 result = []
343 for user in UserModel().get_all():
382 for user in UserModel().get_all():
344 result.append(user.get_api_data())
383 result.append(user.get_api_data())
345 return result
384 return result
346
385
347 @HasPermissionAllDecorator('hg.admin')
386 @HasPermissionAllDecorator('hg.admin')
348 def create_user(self, apiuser, username, email, password,
387 def create_user(self, apiuser, username, email, password,
349 firstname=Optional(None), lastname=Optional(None),
388 firstname=Optional(None), lastname=Optional(None),
350 active=Optional(True), admin=Optional(False),
389 active=Optional(True), admin=Optional(False),
351 ldap_dn=Optional(None)):
390 ldap_dn=Optional(None)):
352 """
391 """
353 Create new user
392 Create new user
354
393
355 :param apiuser:
394 :param apiuser:
356 :param username:
395 :param username:
357 :param email:
396 :param email:
358 :param password:
397 :param password:
359 :param firstname:
398 :param firstname:
360 :param lastname:
399 :param lastname:
361 :param active:
400 :param active:
362 :param admin:
401 :param admin:
363 :param ldap_dn:
402 :param ldap_dn:
364 """
403 """
365
404
366 if UserModel().get_by_username(username):
405 if UserModel().get_by_username(username):
367 raise JSONRPCError("user `%s` already exist" % username)
406 raise JSONRPCError("user `%s` already exist" % username)
368
407
369 if UserModel().get_by_email(email, case_insensitive=True):
408 if UserModel().get_by_email(email, case_insensitive=True):
370 raise JSONRPCError("email `%s` already exist" % email)
409 raise JSONRPCError("email `%s` already exist" % email)
371
410
372 if Optional.extract(ldap_dn):
411 if Optional.extract(ldap_dn):
373 # generate temporary password if ldap_dn
412 # generate temporary password if ldap_dn
374 password = PasswordGenerator().gen_password(length=8)
413 password = PasswordGenerator().gen_password(length=8)
375
414
376 try:
415 try:
377 user = UserModel().create_or_update(
416 user = UserModel().create_or_update(
378 username=Optional.extract(username),
417 username=Optional.extract(username),
379 password=Optional.extract(password),
418 password=Optional.extract(password),
380 email=Optional.extract(email),
419 email=Optional.extract(email),
381 firstname=Optional.extract(firstname),
420 firstname=Optional.extract(firstname),
382 lastname=Optional.extract(lastname),
421 lastname=Optional.extract(lastname),
383 active=Optional.extract(active),
422 active=Optional.extract(active),
384 admin=Optional.extract(admin),
423 admin=Optional.extract(admin),
385 ldap_dn=Optional.extract(ldap_dn)
424 ldap_dn=Optional.extract(ldap_dn)
386 )
425 )
387 Session().commit()
426 Session().commit()
388 return dict(
427 return dict(
389 msg='created new user `%s`' % username,
428 msg='created new user `%s`' % username,
390 user=user.get_api_data()
429 user=user.get_api_data()
391 )
430 )
392 except Exception:
431 except Exception:
393 log.error(traceback.format_exc())
432 log.error(traceback.format_exc())
394 raise JSONRPCError('failed to create user `%s`' % username)
433 raise JSONRPCError('failed to create user `%s`' % username)
395
434
396 @HasPermissionAllDecorator('hg.admin')
435 @HasPermissionAllDecorator('hg.admin')
397 def update_user(self, apiuser, userid, username=Optional(None),
436 def update_user(self, apiuser, userid, username=Optional(None),
398 email=Optional(None), firstname=Optional(None),
437 email=Optional(None), firstname=Optional(None),
399 lastname=Optional(None), active=Optional(None),
438 lastname=Optional(None), active=Optional(None),
400 admin=Optional(None), ldap_dn=Optional(None),
439 admin=Optional(None), ldap_dn=Optional(None),
401 password=Optional(None)):
440 password=Optional(None)):
402 """
441 """
403 Updates given user
442 Updates given user
404
443
405 :param apiuser:
444 :param apiuser:
406 :param userid:
445 :param userid:
407 :param username:
446 :param username:
408 :param email:
447 :param email:
409 :param firstname:
448 :param firstname:
410 :param lastname:
449 :param lastname:
411 :param active:
450 :param active:
412 :param admin:
451 :param admin:
413 :param ldap_dn:
452 :param ldap_dn:
414 :param password:
453 :param password:
415 """
454 """
416
455
417 user = get_user_or_error(userid)
456 user = get_user_or_error(userid)
418
457
419 # call function and store only updated arguments
458 # call function and store only updated arguments
420 updates = {}
459 updates = {}
421
460
422 def store_update(attr, name):
461 def store_update(attr, name):
423 if not isinstance(attr, Optional):
462 if not isinstance(attr, Optional):
424 updates[name] = attr
463 updates[name] = attr
425
464
426 try:
465 try:
427
466
428 store_update(username, 'username')
467 store_update(username, 'username')
429 store_update(password, 'password')
468 store_update(password, 'password')
430 store_update(email, 'email')
469 store_update(email, 'email')
431 store_update(firstname, 'name')
470 store_update(firstname, 'name')
432 store_update(lastname, 'lastname')
471 store_update(lastname, 'lastname')
433 store_update(active, 'active')
472 store_update(active, 'active')
434 store_update(admin, 'admin')
473 store_update(admin, 'admin')
435 store_update(ldap_dn, 'ldap_dn')
474 store_update(ldap_dn, 'ldap_dn')
436
475
437 user = UserModel().update_user(user, **updates)
476 user = UserModel().update_user(user, **updates)
438 Session().commit()
477 Session().commit()
439 return dict(
478 return dict(
440 msg='updated user ID:%s %s' % (user.user_id, user.username),
479 msg='updated user ID:%s %s' % (user.user_id, user.username),
441 user=user.get_api_data()
480 user=user.get_api_data()
442 )
481 )
443 except Exception:
482 except Exception:
444 log.error(traceback.format_exc())
483 log.error(traceback.format_exc())
445 raise JSONRPCError('failed to update user `%s`' % userid)
484 raise JSONRPCError('failed to update user `%s`' % userid)
446
485
447 @HasPermissionAllDecorator('hg.admin')
486 @HasPermissionAllDecorator('hg.admin')
448 def delete_user(self, apiuser, userid):
487 def delete_user(self, apiuser, userid):
449 """"
488 """"
450 Deletes an user
489 Deletes an user
451
490
452 :param apiuser:
491 :param apiuser:
453 :param userid:
492 :param userid:
454 """
493 """
455 user = get_user_or_error(userid)
494 user = get_user_or_error(userid)
456
495
457 try:
496 try:
458 UserModel().delete(userid)
497 UserModel().delete(userid)
459 Session().commit()
498 Session().commit()
460 return dict(
499 return dict(
461 msg='deleted user ID:%s %s' % (user.user_id, user.username),
500 msg='deleted user ID:%s %s' % (user.user_id, user.username),
462 user=None
501 user=None
463 )
502 )
464 except Exception:
503 except Exception:
465 log.error(traceback.format_exc())
504 log.error(traceback.format_exc())
466 raise JSONRPCError('failed to delete ID:%s %s' % (user.user_id,
505 raise JSONRPCError('failed to delete ID:%s %s' % (user.user_id,
467 user.username))
506 user.username))
468
507
469 @HasPermissionAllDecorator('hg.admin')
508 @HasPermissionAllDecorator('hg.admin')
470 def get_users_group(self, apiuser, usersgroupid):
509 def get_users_group(self, apiuser, usersgroupid):
471 """"
510 """"
472 Get user group by name or id
511 Get user group by name or id
473
512
474 :param apiuser:
513 :param apiuser:
475 :param usersgroupid:
514 :param usersgroupid:
476 """
515 """
477 users_group = get_users_group_or_error(usersgroupid)
516 users_group = get_users_group_or_error(usersgroupid)
478
517
479 data = users_group.get_api_data()
518 data = users_group.get_api_data()
480
519
481 members = []
520 members = []
482 for user in users_group.members:
521 for user in users_group.members:
483 user = user.user
522 user = user.user
484 members.append(user.get_api_data())
523 members.append(user.get_api_data())
485 data['members'] = members
524 data['members'] = members
486 return data
525 return data
487
526
488 @HasPermissionAllDecorator('hg.admin')
527 @HasPermissionAllDecorator('hg.admin')
489 def get_users_groups(self, apiuser):
528 def get_users_groups(self, apiuser):
490 """"
529 """"
491 Get all user groups
530 Get all user groups
492
531
493 :param apiuser:
532 :param apiuser:
494 """
533 """
495
534
496 result = []
535 result = []
497 for users_group in UserGroupModel().get_all():
536 for users_group in UserGroupModel().get_all():
498 result.append(users_group.get_api_data())
537 result.append(users_group.get_api_data())
499 return result
538 return result
500
539
501 @HasPermissionAllDecorator('hg.admin')
540 @HasPermissionAllDecorator('hg.admin')
502 def create_users_group(self, apiuser, group_name, active=Optional(True)):
541 def create_users_group(self, apiuser, group_name, active=Optional(True)):
503 """
542 """
504 Creates an new usergroup
543 Creates an new usergroup
505
544
506 :param apiuser:
545 :param apiuser:
507 :param group_name:
546 :param group_name:
508 :param active:
547 :param active:
509 """
548 """
510
549
511 if UserGroupModel().get_by_name(group_name):
550 if UserGroupModel().get_by_name(group_name):
512 raise JSONRPCError("user group `%s` already exist" % group_name)
551 raise JSONRPCError("user group `%s` already exist" % group_name)
513
552
514 try:
553 try:
515 active = Optional.extract(active)
554 active = Optional.extract(active)
516 ug = UserGroupModel().create(name=group_name, active=active)
555 ug = UserGroupModel().create(name=group_name, active=active)
517 Session().commit()
556 Session().commit()
518 return dict(
557 return dict(
519 msg='created new user group `%s`' % group_name,
558 msg='created new user group `%s`' % group_name,
520 users_group=ug.get_api_data()
559 users_group=ug.get_api_data()
521 )
560 )
522 except Exception:
561 except Exception:
523 log.error(traceback.format_exc())
562 log.error(traceback.format_exc())
524 raise JSONRPCError('failed to create group `%s`' % group_name)
563 raise JSONRPCError('failed to create group `%s`' % group_name)
525
564
526 @HasPermissionAllDecorator('hg.admin')
565 @HasPermissionAllDecorator('hg.admin')
527 def add_user_to_users_group(self, apiuser, usersgroupid, userid):
566 def add_user_to_users_group(self, apiuser, usersgroupid, userid):
528 """"
567 """"
529 Add a user to a user group
568 Add a user to a user group
530
569
531 :param apiuser:
570 :param apiuser:
532 :param usersgroupid:
571 :param usersgroupid:
533 :param userid:
572 :param userid:
534 """
573 """
535 user = get_user_or_error(userid)
574 user = get_user_or_error(userid)
536 users_group = get_users_group_or_error(usersgroupid)
575 users_group = get_users_group_or_error(usersgroupid)
537
576
538 try:
577 try:
539 ugm = UserGroupModel().add_user_to_group(users_group, user)
578 ugm = UserGroupModel().add_user_to_group(users_group, user)
540 success = True if ugm != True else False
579 success = True if ugm != True else False
541 msg = 'added member `%s` to user group `%s`' % (
580 msg = 'added member `%s` to user group `%s`' % (
542 user.username, users_group.users_group_name
581 user.username, users_group.users_group_name
543 )
582 )
544 msg = msg if success else 'User is already in that group'
583 msg = msg if success else 'User is already in that group'
545 Session().commit()
584 Session().commit()
546
585
547 return dict(
586 return dict(
548 success=success,
587 success=success,
549 msg=msg
588 msg=msg
550 )
589 )
551 except Exception:
590 except Exception:
552 log.error(traceback.format_exc())
591 log.error(traceback.format_exc())
553 raise JSONRPCError(
592 raise JSONRPCError(
554 'failed to add member to user group `%s`' % (
593 'failed to add member to user group `%s`' % (
555 users_group.users_group_name
594 users_group.users_group_name
556 )
595 )
557 )
596 )
558
597
559 @HasPermissionAllDecorator('hg.admin')
598 @HasPermissionAllDecorator('hg.admin')
560 def remove_user_from_users_group(self, apiuser, usersgroupid, userid):
599 def remove_user_from_users_group(self, apiuser, usersgroupid, userid):
561 """
600 """
562 Remove user from a group
601 Remove user from a group
563
602
564 :param apiuser:
603 :param apiuser:
565 :param usersgroupid:
604 :param usersgroupid:
566 :param userid:
605 :param userid:
567 """
606 """
568 user = get_user_or_error(userid)
607 user = get_user_or_error(userid)
569 users_group = get_users_group_or_error(usersgroupid)
608 users_group = get_users_group_or_error(usersgroupid)
570
609
571 try:
610 try:
572 success = UserGroupModel().remove_user_from_group(users_group,
611 success = UserGroupModel().remove_user_from_group(users_group,
573 user)
612 user)
574 msg = 'removed member `%s` from user group `%s`' % (
613 msg = 'removed member `%s` from user group `%s`' % (
575 user.username, users_group.users_group_name
614 user.username, users_group.users_group_name
576 )
615 )
577 msg = msg if success else "User wasn't in group"
616 msg = msg if success else "User wasn't in group"
578 Session().commit()
617 Session().commit()
579 return dict(success=success, msg=msg)
618 return dict(success=success, msg=msg)
580 except Exception:
619 except Exception:
581 log.error(traceback.format_exc())
620 log.error(traceback.format_exc())
582 raise JSONRPCError(
621 raise JSONRPCError(
583 'failed to remove member from user group `%s`' % (
622 'failed to remove member from user group `%s`' % (
584 users_group.users_group_name
623 users_group.users_group_name
585 )
624 )
586 )
625 )
587
626
588 def get_repo(self, apiuser, repoid):
627 def get_repo(self, apiuser, repoid):
589 """"
628 """"
590 Get repository by name
629 Get repository by name
591
630
592 :param apiuser:
631 :param apiuser:
593 :param repoid:
632 :param repoid:
594 """
633 """
595 repo = get_repo_or_error(repoid)
634 repo = get_repo_or_error(repoid)
596
635
597 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
636 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
598 # check if we have admin permission for this repo !
637 # check if we have admin permission for this repo !
599 if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
638 if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
600 repo_name=repo.repo_name) is False:
639 repo_name=repo.repo_name) is False:
601 raise JSONRPCError('repository `%s` does not exist' % (repoid))
640 raise JSONRPCError('repository `%s` does not exist' % (repoid))
602
641
603 members = []
642 members = []
604 followers = []
643 followers = []
605 for user in repo.repo_to_perm:
644 for user in repo.repo_to_perm:
606 perm = user.permission.permission_name
645 perm = user.permission.permission_name
607 user = user.user
646 user = user.user
608 user_data = user.get_api_data()
647 user_data = user.get_api_data()
609 user_data['type'] = "user"
648 user_data['type'] = "user"
610 user_data['permission'] = perm
649 user_data['permission'] = perm
611 members.append(user_data)
650 members.append(user_data)
612
651
613 for users_group in repo.users_group_to_perm:
652 for users_group in repo.users_group_to_perm:
614 perm = users_group.permission.permission_name
653 perm = users_group.permission.permission_name
615 users_group = users_group.users_group
654 users_group = users_group.users_group
616 users_group_data = users_group.get_api_data()
655 users_group_data = users_group.get_api_data()
617 users_group_data['type'] = "users_group"
656 users_group_data['type'] = "users_group"
618 users_group_data['permission'] = perm
657 users_group_data['permission'] = perm
619 members.append(users_group_data)
658 members.append(users_group_data)
620
659
621 for user in repo.followers:
660 for user in repo.followers:
622 followers.append(user.user.get_api_data())
661 followers.append(user.user.get_api_data())
623
662
624 data = repo.get_api_data()
663 data = repo.get_api_data()
625 data['members'] = members
664 data['members'] = members
626 data['followers'] = followers
665 data['followers'] = followers
627 return data
666 return data
628
667
629 def get_repos(self, apiuser):
668 def get_repos(self, apiuser):
630 """"
669 """"
631 Get all repositories
670 Get all repositories
632
671
633 :param apiuser:
672 :param apiuser:
634 """
673 """
635 result = []
674 result = []
636 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
675 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
637 repos = RepoModel().get_all_user_repos(user=apiuser)
676 repos = RepoModel().get_all_user_repos(user=apiuser)
638 else:
677 else:
639 repos = RepoModel().get_all()
678 repos = RepoModel().get_all()
640
679
641 for repo in repos:
680 for repo in repos:
642 result.append(repo.get_api_data())
681 result.append(repo.get_api_data())
643 return result
682 return result
644
683
645 @HasPermissionAllDecorator('hg.admin')
684 @HasPermissionAllDecorator('hg.admin')
646 def get_repo_nodes(self, apiuser, repoid, revision, root_path,
685 def get_repo_nodes(self, apiuser, repoid, revision, root_path,
647 ret_type='all'):
686 ret_type='all'):
648 """
687 """
649 returns a list of nodes and it's children
688 returns a list of nodes and it's children
650 for a given path at given revision. It's possible to specify ret_type
689 for a given path at given revision. It's possible to specify ret_type
651 to show only files or dirs
690 to show only files or dirs
652
691
653 :param apiuser:
692 :param apiuser:
654 :param repoid: name or id of repository
693 :param repoid: name or id of repository
655 :param revision: revision for which listing should be done
694 :param revision: revision for which listing should be done
656 :param root_path: path from which start displaying
695 :param root_path: path from which start displaying
657 :param ret_type: return type 'all|files|dirs' nodes
696 :param ret_type: return type 'all|files|dirs' nodes
658 """
697 """
659 repo = get_repo_or_error(repoid)
698 repo = get_repo_or_error(repoid)
660 try:
699 try:
661 _d, _f = ScmModel().get_nodes(repo, revision, root_path,
700 _d, _f = ScmModel().get_nodes(repo, revision, root_path,
662 flat=False)
701 flat=False)
663 _map = {
702 _map = {
664 'all': _d + _f,
703 'all': _d + _f,
665 'files': _f,
704 'files': _f,
666 'dirs': _d,
705 'dirs': _d,
667 }
706 }
668 return _map[ret_type]
707 return _map[ret_type]
669 except KeyError:
708 except KeyError:
670 raise JSONRPCError('ret_type must be one of %s' % _map.keys())
709 raise JSONRPCError('ret_type must be one of %s' % _map.keys())
671 except Exception:
710 except Exception:
672 log.error(traceback.format_exc())
711 log.error(traceback.format_exc())
673 raise JSONRPCError(
712 raise JSONRPCError(
674 'failed to get repo: `%s` nodes' % repo.repo_name
713 'failed to get repo: `%s` nodes' % repo.repo_name
675 )
714 )
676
715
677 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
716 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
678 def create_repo(self, apiuser, repo_name, owner=Optional(OAttr('apiuser')),
717 def create_repo(self, apiuser, repo_name, owner=Optional(OAttr('apiuser')),
679 repo_type=Optional('hg'),
718 repo_type=Optional('hg'),
680 description=Optional(''), private=Optional(False),
719 description=Optional(''), private=Optional(False),
681 clone_uri=Optional(None), landing_rev=Optional('tip'),
720 clone_uri=Optional(None), landing_rev=Optional('tip'),
682 enable_statistics=Optional(False),
721 enable_statistics=Optional(False),
683 enable_locking=Optional(False),
722 enable_locking=Optional(False),
684 enable_downloads=Optional(False)):
723 enable_downloads=Optional(False)):
685 """
724 """
686 Create repository, if clone_url is given it makes a remote clone
725 Create repository, if clone_url is given it makes a remote clone
687 if repo_name is within a group name the groups will be created
726 if repo_name is within a group name the groups will be created
688 automatically if they aren't present
727 automatically if they aren't present
689
728
690 :param apiuser:
729 :param apiuser:
691 :param repo_name:
730 :param repo_name:
692 :param onwer:
731 :param onwer:
693 :param repo_type:
732 :param repo_type:
694 :param description:
733 :param description:
695 :param private:
734 :param private:
696 :param clone_uri:
735 :param clone_uri:
697 :param landing_rev:
736 :param landing_rev:
698 """
737 """
699 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
738 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
700 if not isinstance(owner, Optional):
739 if not isinstance(owner, Optional):
701 #forbid setting owner for non-admins
740 #forbid setting owner for non-admins
702 raise JSONRPCError(
741 raise JSONRPCError(
703 'Only RhodeCode admin can specify `owner` param'
742 'Only RhodeCode admin can specify `owner` param'
704 )
743 )
705 if isinstance(owner, Optional):
744 if isinstance(owner, Optional):
706 owner = apiuser.user_id
745 owner = apiuser.user_id
707
746
708 owner = get_user_or_error(owner)
747 owner = get_user_or_error(owner)
709
748
710 if RepoModel().get_by_repo_name(repo_name):
749 if RepoModel().get_by_repo_name(repo_name):
711 raise JSONRPCError("repo `%s` already exist" % repo_name)
750 raise JSONRPCError("repo `%s` already exist" % repo_name)
712
751
713 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
752 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
714 if isinstance(private, Optional):
753 if isinstance(private, Optional):
715 private = defs.get('repo_private') or Optional.extract(private)
754 private = defs.get('repo_private') or Optional.extract(private)
716 if isinstance(repo_type, Optional):
755 if isinstance(repo_type, Optional):
717 repo_type = defs.get('repo_type')
756 repo_type = defs.get('repo_type')
718 if isinstance(enable_statistics, Optional):
757 if isinstance(enable_statistics, Optional):
719 enable_statistics = defs.get('repo_enable_statistics')
758 enable_statistics = defs.get('repo_enable_statistics')
720 if isinstance(enable_locking, Optional):
759 if isinstance(enable_locking, Optional):
721 enable_locking = defs.get('repo_enable_locking')
760 enable_locking = defs.get('repo_enable_locking')
722 if isinstance(enable_downloads, Optional):
761 if isinstance(enable_downloads, Optional):
723 enable_downloads = defs.get('repo_enable_downloads')
762 enable_downloads = defs.get('repo_enable_downloads')
724
763
725 clone_uri = Optional.extract(clone_uri)
764 clone_uri = Optional.extract(clone_uri)
726 description = Optional.extract(description)
765 description = Optional.extract(description)
727 landing_rev = Optional.extract(landing_rev)
766 landing_rev = Optional.extract(landing_rev)
728
767
729 try:
768 try:
730 # create structure of groups and return the last group
769 # create structure of groups and return the last group
731 group = map_groups(repo_name)
770 group = map_groups(repo_name)
732
771
733 repo = RepoModel().create_repo(
772 repo = RepoModel().create_repo(
734 repo_name=repo_name,
773 repo_name=repo_name,
735 repo_type=repo_type,
774 repo_type=repo_type,
736 description=description,
775 description=description,
737 owner=owner,
776 owner=owner,
738 private=private,
777 private=private,
739 clone_uri=clone_uri,
778 clone_uri=clone_uri,
740 repos_group=group,
779 repos_group=group,
741 landing_rev=landing_rev,
780 landing_rev=landing_rev,
742 enable_statistics=enable_statistics,
781 enable_statistics=enable_statistics,
743 enable_downloads=enable_downloads,
782 enable_downloads=enable_downloads,
744 enable_locking=enable_locking
783 enable_locking=enable_locking
745 )
784 )
746
785
747 Session().commit()
786 Session().commit()
748 return dict(
787 return dict(
749 msg="Created new repository `%s`" % (repo.repo_name),
788 msg="Created new repository `%s`" % (repo.repo_name),
750 repo=repo.get_api_data()
789 repo=repo.get_api_data()
751 )
790 )
752 except Exception:
791 except Exception:
753 log.error(traceback.format_exc())
792 log.error(traceback.format_exc())
754 raise JSONRPCError('failed to create repository `%s`' % repo_name)
793 raise JSONRPCError('failed to create repository `%s`' % repo_name)
755
794
756 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
795 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
757 def fork_repo(self, apiuser, repoid, fork_name, owner=Optional(OAttr('apiuser')),
796 def fork_repo(self, apiuser, repoid, fork_name, owner=Optional(OAttr('apiuser')),
758 description=Optional(''), copy_permissions=Optional(False),
797 description=Optional(''), copy_permissions=Optional(False),
759 private=Optional(False), landing_rev=Optional('tip')):
798 private=Optional(False), landing_rev=Optional('tip')):
760 repo = get_repo_or_error(repoid)
799 repo = get_repo_or_error(repoid)
761 repo_name = repo.repo_name
800 repo_name = repo.repo_name
762
801
763 _repo = RepoModel().get_by_repo_name(fork_name)
802 _repo = RepoModel().get_by_repo_name(fork_name)
764 if _repo:
803 if _repo:
765 type_ = 'fork' if _repo.fork else 'repo'
804 type_ = 'fork' if _repo.fork else 'repo'
766 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
805 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
767
806
768 if HasPermissionAnyApi('hg.admin')(user=apiuser):
807 if HasPermissionAnyApi('hg.admin')(user=apiuser):
769 pass
808 pass
770 elif HasRepoPermissionAnyApi('repository.admin',
809 elif HasRepoPermissionAnyApi('repository.admin',
771 'repository.write',
810 'repository.write',
772 'repository.read')(user=apiuser,
811 'repository.read')(user=apiuser,
773 repo_name=repo.repo_name):
812 repo_name=repo.repo_name):
774 if not isinstance(owner, Optional):
813 if not isinstance(owner, Optional):
775 #forbid setting owner for non-admins
814 #forbid setting owner for non-admins
776 raise JSONRPCError(
815 raise JSONRPCError(
777 'Only RhodeCode admin can specify `owner` param'
816 'Only RhodeCode admin can specify `owner` param'
778 )
817 )
779 else:
818 else:
780 raise JSONRPCError('repository `%s` does not exist' % (repoid))
819 raise JSONRPCError('repository `%s` does not exist' % (repoid))
781
820
782 if isinstance(owner, Optional):
821 if isinstance(owner, Optional):
783 owner = apiuser.user_id
822 owner = apiuser.user_id
784
823
785 owner = get_user_or_error(owner)
824 owner = get_user_or_error(owner)
786
825
787 try:
826 try:
788 # create structure of groups and return the last group
827 # create structure of groups and return the last group
789 group = map_groups(fork_name)
828 group = map_groups(fork_name)
790
829
791 form_data = dict(
830 form_data = dict(
792 repo_name=fork_name,
831 repo_name=fork_name,
793 repo_name_full=fork_name,
832 repo_name_full=fork_name,
794 repo_group=group,
833 repo_group=group,
795 repo_type=repo.repo_type,
834 repo_type=repo.repo_type,
796 description=Optional.extract(description),
835 description=Optional.extract(description),
797 private=Optional.extract(private),
836 private=Optional.extract(private),
798 copy_permissions=Optional.extract(copy_permissions),
837 copy_permissions=Optional.extract(copy_permissions),
799 landing_rev=Optional.extract(landing_rev),
838 landing_rev=Optional.extract(landing_rev),
800 update_after_clone=False,
839 update_after_clone=False,
801 fork_parent_id=repo.repo_id,
840 fork_parent_id=repo.repo_id,
802 )
841 )
803 RepoModel().create_fork(form_data, cur_user=owner)
842 RepoModel().create_fork(form_data, cur_user=owner)
804 return dict(
843 return dict(
805 msg='Created fork of `%s` as `%s`' % (repo.repo_name,
844 msg='Created fork of `%s` as `%s`' % (repo.repo_name,
806 fork_name),
845 fork_name),
807 success=True # cannot return the repo data here since fork
846 success=True # cannot return the repo data here since fork
808 # cann be done async
847 # cann be done async
809 )
848 )
810 except Exception:
849 except Exception:
811 log.error(traceback.format_exc())
850 log.error(traceback.format_exc())
812 raise JSONRPCError(
851 raise JSONRPCError(
813 'failed to fork repository `%s` as `%s`' % (repo_name,
852 'failed to fork repository `%s` as `%s`' % (repo_name,
814 fork_name)
853 fork_name)
815 )
854 )
816
855
817 def delete_repo(self, apiuser, repoid):
856 def delete_repo(self, apiuser, repoid):
818 """
857 """
819 Deletes a given repository
858 Deletes a given repository
820
859
821 :param apiuser:
860 :param apiuser:
822 :param repoid:
861 :param repoid:
823 """
862 """
824 repo = get_repo_or_error(repoid)
863 repo = get_repo_or_error(repoid)
825
864
826 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
865 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
827 # check if we have admin permission for this repo !
866 # check if we have admin permission for this repo !
828 if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
867 if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
829 repo_name=repo.repo_name) is False:
868 repo_name=repo.repo_name) is False:
830 raise JSONRPCError('repository `%s` does not exist' % (repoid))
869 raise JSONRPCError('repository `%s` does not exist' % (repoid))
831
870
832 try:
871 try:
833 RepoModel().delete(repo)
872 RepoModel().delete(repo)
834 Session().commit()
873 Session().commit()
835 return dict(
874 return dict(
836 msg='Deleted repository `%s`' % repo.repo_name,
875 msg='Deleted repository `%s`' % repo.repo_name,
837 success=True
876 success=True
838 )
877 )
839 except Exception:
878 except Exception:
840 log.error(traceback.format_exc())
879 log.error(traceback.format_exc())
841 raise JSONRPCError(
880 raise JSONRPCError(
842 'failed to delete repository `%s`' % repo.repo_name
881 'failed to delete repository `%s`' % repo.repo_name
843 )
882 )
844
883
845 @HasPermissionAllDecorator('hg.admin')
884 @HasPermissionAllDecorator('hg.admin')
846 def grant_user_permission(self, apiuser, repoid, userid, perm):
885 def grant_user_permission(self, apiuser, repoid, userid, perm):
847 """
886 """
848 Grant permission for user on given repository, or update existing one
887 Grant permission for user on given repository, or update existing one
849 if found
888 if found
850
889
851 :param repoid:
890 :param repoid:
852 :param userid:
891 :param userid:
853 :param perm:
892 :param perm:
854 """
893 """
855 repo = get_repo_or_error(repoid)
894 repo = get_repo_or_error(repoid)
856 user = get_user_or_error(userid)
895 user = get_user_or_error(userid)
857 perm = get_perm_or_error(perm)
896 perm = get_perm_or_error(perm)
858
897
859 try:
898 try:
860
899
861 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
900 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
862
901
863 Session().commit()
902 Session().commit()
864 return dict(
903 return dict(
865 msg='Granted perm: `%s` for user: `%s` in repo: `%s`' % (
904 msg='Granted perm: `%s` for user: `%s` in repo: `%s`' % (
866 perm.permission_name, user.username, repo.repo_name
905 perm.permission_name, user.username, repo.repo_name
867 ),
906 ),
868 success=True
907 success=True
869 )
908 )
870 except Exception:
909 except Exception:
871 log.error(traceback.format_exc())
910 log.error(traceback.format_exc())
872 raise JSONRPCError(
911 raise JSONRPCError(
873 'failed to edit permission for user: `%s` in repo: `%s`' % (
912 'failed to edit permission for user: `%s` in repo: `%s`' % (
874 userid, repoid
913 userid, repoid
875 )
914 )
876 )
915 )
877
916
878 @HasPermissionAllDecorator('hg.admin')
917 @HasPermissionAllDecorator('hg.admin')
879 def revoke_user_permission(self, apiuser, repoid, userid):
918 def revoke_user_permission(self, apiuser, repoid, userid):
880 """
919 """
881 Revoke permission for user on given repository
920 Revoke permission for user on given repository
882
921
883 :param apiuser:
922 :param apiuser:
884 :param repoid:
923 :param repoid:
885 :param userid:
924 :param userid:
886 """
925 """
887
926
888 repo = get_repo_or_error(repoid)
927 repo = get_repo_or_error(repoid)
889 user = get_user_or_error(userid)
928 user = get_user_or_error(userid)
890 try:
929 try:
891
930
892 RepoModel().revoke_user_permission(repo=repo, user=user)
931 RepoModel().revoke_user_permission(repo=repo, user=user)
893
932
894 Session().commit()
933 Session().commit()
895 return dict(
934 return dict(
896 msg='Revoked perm for user: `%s` in repo: `%s`' % (
935 msg='Revoked perm for user: `%s` in repo: `%s`' % (
897 user.username, repo.repo_name
936 user.username, repo.repo_name
898 ),
937 ),
899 success=True
938 success=True
900 )
939 )
901 except Exception:
940 except Exception:
902 log.error(traceback.format_exc())
941 log.error(traceback.format_exc())
903 raise JSONRPCError(
942 raise JSONRPCError(
904 'failed to edit permission for user: `%s` in repo: `%s`' % (
943 'failed to edit permission for user: `%s` in repo: `%s`' % (
905 userid, repoid
944 userid, repoid
906 )
945 )
907 )
946 )
908
947
909 @HasPermissionAllDecorator('hg.admin')
948 @HasPermissionAllDecorator('hg.admin')
910 def grant_users_group_permission(self, apiuser, repoid, usersgroupid,
949 def grant_users_group_permission(self, apiuser, repoid, usersgroupid,
911 perm):
950 perm):
912 """
951 """
913 Grant permission for user group on given repository, or update
952 Grant permission for user group on given repository, or update
914 existing one if found
953 existing one if found
915
954
916 :param apiuser:
955 :param apiuser:
917 :param repoid:
956 :param repoid:
918 :param usersgroupid:
957 :param usersgroupid:
919 :param perm:
958 :param perm:
920 """
959 """
921 repo = get_repo_or_error(repoid)
960 repo = get_repo_or_error(repoid)
922 perm = get_perm_or_error(perm)
961 perm = get_perm_or_error(perm)
923 users_group = get_users_group_or_error(usersgroupid)
962 users_group = get_users_group_or_error(usersgroupid)
924
963
925 try:
964 try:
926 RepoModel().grant_users_group_permission(repo=repo,
965 RepoModel().grant_users_group_permission(repo=repo,
927 group_name=users_group,
966 group_name=users_group,
928 perm=perm)
967 perm=perm)
929
968
930 Session().commit()
969 Session().commit()
931 return dict(
970 return dict(
932 msg='Granted perm: `%s` for user group: `%s` in '
971 msg='Granted perm: `%s` for user group: `%s` in '
933 'repo: `%s`' % (
972 'repo: `%s`' % (
934 perm.permission_name, users_group.users_group_name,
973 perm.permission_name, users_group.users_group_name,
935 repo.repo_name
974 repo.repo_name
936 ),
975 ),
937 success=True
976 success=True
938 )
977 )
939 except Exception:
978 except Exception:
940 log.error(traceback.format_exc())
979 log.error(traceback.format_exc())
941 raise JSONRPCError(
980 raise JSONRPCError(
942 'failed to edit permission for user group: `%s` in '
981 'failed to edit permission for user group: `%s` in '
943 'repo: `%s`' % (
982 'repo: `%s`' % (
944 usersgroupid, repo.repo_name
983 usersgroupid, repo.repo_name
945 )
984 )
946 )
985 )
947
986
948 @HasPermissionAllDecorator('hg.admin')
987 @HasPermissionAllDecorator('hg.admin')
949 def revoke_users_group_permission(self, apiuser, repoid, usersgroupid):
988 def revoke_users_group_permission(self, apiuser, repoid, usersgroupid):
950 """
989 """
951 Revoke permission for user group on given repository
990 Revoke permission for user group on given repository
952
991
953 :param apiuser:
992 :param apiuser:
954 :param repoid:
993 :param repoid:
955 :param usersgroupid:
994 :param usersgroupid:
956 """
995 """
957 repo = get_repo_or_error(repoid)
996 repo = get_repo_or_error(repoid)
958 users_group = get_users_group_or_error(usersgroupid)
997 users_group = get_users_group_or_error(usersgroupid)
959
998
960 try:
999 try:
961 RepoModel().revoke_users_group_permission(repo=repo,
1000 RepoModel().revoke_users_group_permission(repo=repo,
962 group_name=users_group)
1001 group_name=users_group)
963
1002
964 Session().commit()
1003 Session().commit()
965 return dict(
1004 return dict(
966 msg='Revoked perm for user group: `%s` in repo: `%s`' % (
1005 msg='Revoked perm for user group: `%s` in repo: `%s`' % (
967 users_group.users_group_name, repo.repo_name
1006 users_group.users_group_name, repo.repo_name
968 ),
1007 ),
969 success=True
1008 success=True
970 )
1009 )
971 except Exception:
1010 except Exception:
972 log.error(traceback.format_exc())
1011 log.error(traceback.format_exc())
973 raise JSONRPCError(
1012 raise JSONRPCError(
974 'failed to edit permission for user group: `%s` in '
1013 'failed to edit permission for user group: `%s` in '
975 'repo: `%s`' % (
1014 'repo: `%s`' % (
976 users_group.users_group_name, repo.repo_name
1015 users_group.users_group_name, repo.repo_name
977 )
1016 )
978 )
1017 )
@@ -1,2053 +1,2057 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import hashlib
30 import hashlib
31 import time
31 import time
32 from collections import defaultdict
32 from collections import defaultdict
33
33
34 from sqlalchemy import *
34 from sqlalchemy import *
35 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.ext.hybrid import hybrid_property
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
37 from sqlalchemy.exc import DatabaseError
37 from sqlalchemy.exc import DatabaseError
38 from beaker.cache import cache_region, region_invalidate
38 from beaker.cache import cache_region, region_invalidate
39 from webob.exc import HTTPNotFound
39 from webob.exc import HTTPNotFound
40
40
41 from pylons.i18n.translation import lazy_ugettext as _
41 from pylons.i18n.translation import lazy_ugettext as _
42
42
43 from rhodecode.lib.vcs import get_backend
43 from rhodecode.lib.vcs import get_backend
44 from rhodecode.lib.vcs.utils.helpers import get_scm
44 from rhodecode.lib.vcs.utils.helpers import get_scm
45 from rhodecode.lib.vcs.exceptions import VCSError
45 from rhodecode.lib.vcs.exceptions import VCSError
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
47 from rhodecode.lib.vcs.backends.base import EmptyChangeset
47 from rhodecode.lib.vcs.backends.base import EmptyChangeset
48
48
49 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
49 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
50 safe_unicode, remove_suffix, remove_prefix
50 safe_unicode, remove_suffix, remove_prefix, time_to_datetime
51 from rhodecode.lib.compat import json
51 from rhodecode.lib.compat import json
52 from rhodecode.lib.caching_query import FromCache
52 from rhodecode.lib.caching_query import FromCache
53
53
54 from rhodecode.model.meta import Base, Session
54 from rhodecode.model.meta import Base, Session
55
55
56 URL_SEP = '/'
56 URL_SEP = '/'
57 log = logging.getLogger(__name__)
57 log = logging.getLogger(__name__)
58
58
59 #==============================================================================
59 #==============================================================================
60 # BASE CLASSES
60 # BASE CLASSES
61 #==============================================================================
61 #==============================================================================
62
62
63 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
63 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
64
64
65
65
66 class BaseModel(object):
66 class BaseModel(object):
67 """
67 """
68 Base Model for all classess
68 Base Model for all classess
69 """
69 """
70
70
71 @classmethod
71 @classmethod
72 def _get_keys(cls):
72 def _get_keys(cls):
73 """return column names for this model """
73 """return column names for this model """
74 return class_mapper(cls).c.keys()
74 return class_mapper(cls).c.keys()
75
75
76 def get_dict(self):
76 def get_dict(self):
77 """
77 """
78 return dict with keys and values corresponding
78 return dict with keys and values corresponding
79 to this model data """
79 to this model data """
80
80
81 d = {}
81 d = {}
82 for k in self._get_keys():
82 for k in self._get_keys():
83 d[k] = getattr(self, k)
83 d[k] = getattr(self, k)
84
84
85 # also use __json__() if present to get additional fields
85 # also use __json__() if present to get additional fields
86 _json_attr = getattr(self, '__json__', None)
86 _json_attr = getattr(self, '__json__', None)
87 if _json_attr:
87 if _json_attr:
88 # update with attributes from __json__
88 # update with attributes from __json__
89 if callable(_json_attr):
89 if callable(_json_attr):
90 _json_attr = _json_attr()
90 _json_attr = _json_attr()
91 for k, val in _json_attr.iteritems():
91 for k, val in _json_attr.iteritems():
92 d[k] = val
92 d[k] = val
93 return d
93 return d
94
94
95 def get_appstruct(self):
95 def get_appstruct(self):
96 """return list with keys and values tupples corresponding
96 """return list with keys and values tupples corresponding
97 to this model data """
97 to this model data """
98
98
99 l = []
99 l = []
100 for k in self._get_keys():
100 for k in self._get_keys():
101 l.append((k, getattr(self, k),))
101 l.append((k, getattr(self, k),))
102 return l
102 return l
103
103
104 def populate_obj(self, populate_dict):
104 def populate_obj(self, populate_dict):
105 """populate model with data from given populate_dict"""
105 """populate model with data from given populate_dict"""
106
106
107 for k in self._get_keys():
107 for k in self._get_keys():
108 if k in populate_dict:
108 if k in populate_dict:
109 setattr(self, k, populate_dict[k])
109 setattr(self, k, populate_dict[k])
110
110
111 @classmethod
111 @classmethod
112 def query(cls):
112 def query(cls):
113 return Session().query(cls)
113 return Session().query(cls)
114
114
115 @classmethod
115 @classmethod
116 def get(cls, id_):
116 def get(cls, id_):
117 if id_:
117 if id_:
118 return cls.query().get(id_)
118 return cls.query().get(id_)
119
119
120 @classmethod
120 @classmethod
121 def get_or_404(cls, id_):
121 def get_or_404(cls, id_):
122 try:
122 try:
123 id_ = int(id_)
123 id_ = int(id_)
124 except (TypeError, ValueError):
124 except (TypeError, ValueError):
125 raise HTTPNotFound
125 raise HTTPNotFound
126
126
127 res = cls.query().get(id_)
127 res = cls.query().get(id_)
128 if not res:
128 if not res:
129 raise HTTPNotFound
129 raise HTTPNotFound
130 return res
130 return res
131
131
132 @classmethod
132 @classmethod
133 def getAll(cls):
133 def getAll(cls):
134 return cls.query().all()
134 return cls.query().all()
135
135
136 @classmethod
136 @classmethod
137 def delete(cls, id_):
137 def delete(cls, id_):
138 obj = cls.query().get(id_)
138 obj = cls.query().get(id_)
139 Session().delete(obj)
139 Session().delete(obj)
140
140
141 def __repr__(self):
141 def __repr__(self):
142 if hasattr(self, '__unicode__'):
142 if hasattr(self, '__unicode__'):
143 # python repr needs to return str
143 # python repr needs to return str
144 return safe_str(self.__unicode__())
144 return safe_str(self.__unicode__())
145 return '<DB:%s>' % (self.__class__.__name__)
145 return '<DB:%s>' % (self.__class__.__name__)
146
146
147
147
148 class RhodeCodeSetting(Base, BaseModel):
148 class RhodeCodeSetting(Base, BaseModel):
149 __tablename__ = 'rhodecode_settings'
149 __tablename__ = 'rhodecode_settings'
150 __table_args__ = (
150 __table_args__ = (
151 UniqueConstraint('app_settings_name'),
151 UniqueConstraint('app_settings_name'),
152 {'extend_existing': True, 'mysql_engine': 'InnoDB',
152 {'extend_existing': True, 'mysql_engine': 'InnoDB',
153 'mysql_charset': 'utf8'}
153 'mysql_charset': 'utf8'}
154 )
154 )
155 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
155 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
156 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
156 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
157 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
157 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
158
158
159 def __init__(self, k='', v=''):
159 def __init__(self, k='', v=''):
160 self.app_settings_name = k
160 self.app_settings_name = k
161 self.app_settings_value = v
161 self.app_settings_value = v
162
162
163 @validates('_app_settings_value')
163 @validates('_app_settings_value')
164 def validate_settings_value(self, key, val):
164 def validate_settings_value(self, key, val):
165 assert type(val) == unicode
165 assert type(val) == unicode
166 return val
166 return val
167
167
168 @hybrid_property
168 @hybrid_property
169 def app_settings_value(self):
169 def app_settings_value(self):
170 v = self._app_settings_value
170 v = self._app_settings_value
171 if self.app_settings_name in ["ldap_active",
171 if self.app_settings_name in ["ldap_active",
172 "default_repo_enable_statistics",
172 "default_repo_enable_statistics",
173 "default_repo_enable_locking",
173 "default_repo_enable_locking",
174 "default_repo_private",
174 "default_repo_private",
175 "default_repo_enable_downloads"]:
175 "default_repo_enable_downloads"]:
176 v = str2bool(v)
176 v = str2bool(v)
177 return v
177 return v
178
178
179 @app_settings_value.setter
179 @app_settings_value.setter
180 def app_settings_value(self, val):
180 def app_settings_value(self, val):
181 """
181 """
182 Setter that will always make sure we use unicode in app_settings_value
182 Setter that will always make sure we use unicode in app_settings_value
183
183
184 :param val:
184 :param val:
185 """
185 """
186 self._app_settings_value = safe_unicode(val)
186 self._app_settings_value = safe_unicode(val)
187
187
188 def __unicode__(self):
188 def __unicode__(self):
189 return u"<%s('%s:%s')>" % (
189 return u"<%s('%s:%s')>" % (
190 self.__class__.__name__,
190 self.__class__.__name__,
191 self.app_settings_name, self.app_settings_value
191 self.app_settings_name, self.app_settings_value
192 )
192 )
193
193
194 @classmethod
194 @classmethod
195 def get_by_name(cls, key):
195 def get_by_name(cls, key):
196 return cls.query()\
196 return cls.query()\
197 .filter(cls.app_settings_name == key).scalar()
197 .filter(cls.app_settings_name == key).scalar()
198
198
199 @classmethod
199 @classmethod
200 def get_by_name_or_create(cls, key):
200 def get_by_name_or_create(cls, key):
201 res = cls.get_by_name(key)
201 res = cls.get_by_name(key)
202 if not res:
202 if not res:
203 res = cls(key)
203 res = cls(key)
204 return res
204 return res
205
205
206 @classmethod
206 @classmethod
207 def get_app_settings(cls, cache=False):
207 def get_app_settings(cls, cache=False):
208
208
209 ret = cls.query()
209 ret = cls.query()
210
210
211 if cache:
211 if cache:
212 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
212 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
213
213
214 if not ret:
214 if not ret:
215 raise Exception('Could not get application settings !')
215 raise Exception('Could not get application settings !')
216 settings = {}
216 settings = {}
217 for each in ret:
217 for each in ret:
218 settings['rhodecode_' + each.app_settings_name] = \
218 settings['rhodecode_' + each.app_settings_name] = \
219 each.app_settings_value
219 each.app_settings_value
220
220
221 return settings
221 return settings
222
222
223 @classmethod
223 @classmethod
224 def get_ldap_settings(cls, cache=False):
224 def get_ldap_settings(cls, cache=False):
225 ret = cls.query()\
225 ret = cls.query()\
226 .filter(cls.app_settings_name.startswith('ldap_')).all()
226 .filter(cls.app_settings_name.startswith('ldap_')).all()
227 fd = {}
227 fd = {}
228 for row in ret:
228 for row in ret:
229 fd.update({row.app_settings_name: row.app_settings_value})
229 fd.update({row.app_settings_name: row.app_settings_value})
230
230
231 return fd
231 return fd
232
232
233 @classmethod
233 @classmethod
234 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
234 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
235 ret = cls.query()\
235 ret = cls.query()\
236 .filter(cls.app_settings_name.startswith('default_')).all()
236 .filter(cls.app_settings_name.startswith('default_')).all()
237 fd = {}
237 fd = {}
238 for row in ret:
238 for row in ret:
239 key = row.app_settings_name
239 key = row.app_settings_name
240 if strip_prefix:
240 if strip_prefix:
241 key = remove_prefix(key, prefix='default_')
241 key = remove_prefix(key, prefix='default_')
242 fd.update({key: row.app_settings_value})
242 fd.update({key: row.app_settings_value})
243
243
244 return fd
244 return fd
245
245
246
246
247 class RhodeCodeUi(Base, BaseModel):
247 class RhodeCodeUi(Base, BaseModel):
248 __tablename__ = 'rhodecode_ui'
248 __tablename__ = 'rhodecode_ui'
249 __table_args__ = (
249 __table_args__ = (
250 UniqueConstraint('ui_key'),
250 UniqueConstraint('ui_key'),
251 {'extend_existing': True, 'mysql_engine': 'InnoDB',
251 {'extend_existing': True, 'mysql_engine': 'InnoDB',
252 'mysql_charset': 'utf8'}
252 'mysql_charset': 'utf8'}
253 )
253 )
254
254
255 HOOK_UPDATE = 'changegroup.update'
255 HOOK_UPDATE = 'changegroup.update'
256 HOOK_REPO_SIZE = 'changegroup.repo_size'
256 HOOK_REPO_SIZE = 'changegroup.repo_size'
257 HOOK_PUSH = 'changegroup.push_logger'
257 HOOK_PUSH = 'changegroup.push_logger'
258 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
258 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
259 HOOK_PULL = 'outgoing.pull_logger'
259 HOOK_PULL = 'outgoing.pull_logger'
260 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
260 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
261
261
262 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
262 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
263 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
263 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
264 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
264 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
265 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
265 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
266 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
266 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
267
267
268 @classmethod
268 @classmethod
269 def get_by_key(cls, key):
269 def get_by_key(cls, key):
270 return cls.query().filter(cls.ui_key == key).scalar()
270 return cls.query().filter(cls.ui_key == key).scalar()
271
271
272 @classmethod
272 @classmethod
273 def get_builtin_hooks(cls):
273 def get_builtin_hooks(cls):
274 q = cls.query()
274 q = cls.query()
275 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
275 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
276 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
276 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
277 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
277 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
278 return q.all()
278 return q.all()
279
279
280 @classmethod
280 @classmethod
281 def get_custom_hooks(cls):
281 def get_custom_hooks(cls):
282 q = cls.query()
282 q = cls.query()
283 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
283 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
284 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
284 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
285 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
285 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
286 q = q.filter(cls.ui_section == 'hooks')
286 q = q.filter(cls.ui_section == 'hooks')
287 return q.all()
287 return q.all()
288
288
289 @classmethod
289 @classmethod
290 def get_repos_location(cls):
290 def get_repos_location(cls):
291 return cls.get_by_key('/').ui_value
291 return cls.get_by_key('/').ui_value
292
292
293 @classmethod
293 @classmethod
294 def create_or_update_hook(cls, key, val):
294 def create_or_update_hook(cls, key, val):
295 new_ui = cls.get_by_key(key) or cls()
295 new_ui = cls.get_by_key(key) or cls()
296 new_ui.ui_section = 'hooks'
296 new_ui.ui_section = 'hooks'
297 new_ui.ui_active = True
297 new_ui.ui_active = True
298 new_ui.ui_key = key
298 new_ui.ui_key = key
299 new_ui.ui_value = val
299 new_ui.ui_value = val
300
300
301 Session().add(new_ui)
301 Session().add(new_ui)
302
302
303 def __repr__(self):
303 def __repr__(self):
304 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
304 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
305 self.ui_value)
305 self.ui_value)
306
306
307
307
308 class User(Base, BaseModel):
308 class User(Base, BaseModel):
309 __tablename__ = 'users'
309 __tablename__ = 'users'
310 __table_args__ = (
310 __table_args__ = (
311 UniqueConstraint('username'), UniqueConstraint('email'),
311 UniqueConstraint('username'), UniqueConstraint('email'),
312 Index('u_username_idx', 'username'),
312 Index('u_username_idx', 'username'),
313 Index('u_email_idx', 'email'),
313 Index('u_email_idx', 'email'),
314 {'extend_existing': True, 'mysql_engine': 'InnoDB',
314 {'extend_existing': True, 'mysql_engine': 'InnoDB',
315 'mysql_charset': 'utf8'}
315 'mysql_charset': 'utf8'}
316 )
316 )
317 DEFAULT_USER = 'default'
317 DEFAULT_USER = 'default'
318 DEFAULT_PERMISSIONS = [
318 DEFAULT_PERMISSIONS = [
319 'hg.register.manual_activate', 'hg.create.repository',
319 'hg.register.manual_activate', 'hg.create.repository',
320 'hg.fork.repository', 'repository.read', 'group.read'
320 'hg.fork.repository', 'repository.read', 'group.read'
321 ]
321 ]
322 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
322 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
323 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
323 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
324 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
324 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
325 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
325 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
326 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
326 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
327 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
327 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
328 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
328 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
329 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
329 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
330 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
331 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
332 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
332 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
333 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
333 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
334
334
335 user_log = relationship('UserLog')
335 user_log = relationship('UserLog')
336 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
336 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
337
337
338 repositories = relationship('Repository')
338 repositories = relationship('Repository')
339 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
339 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
340 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
340 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
341
341
342 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
342 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
343 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
343 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
344
344
345 group_member = relationship('UserGroupMember', cascade='all')
345 group_member = relationship('UserGroupMember', cascade='all')
346
346
347 notifications = relationship('UserNotification', cascade='all')
347 notifications = relationship('UserNotification', cascade='all')
348 # notifications assigned to this user
348 # notifications assigned to this user
349 user_created_notifications = relationship('Notification', cascade='all')
349 user_created_notifications = relationship('Notification', cascade='all')
350 # comments created by this user
350 # comments created by this user
351 user_comments = relationship('ChangesetComment', cascade='all')
351 user_comments = relationship('ChangesetComment', cascade='all')
352 #extra emails for this user
352 #extra emails for this user
353 user_emails = relationship('UserEmailMap', cascade='all')
353 user_emails = relationship('UserEmailMap', cascade='all')
354
354
355 @hybrid_property
355 @hybrid_property
356 def email(self):
356 def email(self):
357 return self._email
357 return self._email
358
358
359 @email.setter
359 @email.setter
360 def email(self, val):
360 def email(self, val):
361 self._email = val.lower() if val else None
361 self._email = val.lower() if val else None
362
362
363 @property
363 @property
364 def firstname(self):
364 def firstname(self):
365 # alias for future
365 # alias for future
366 return self.name
366 return self.name
367
367
368 @property
368 @property
369 def emails(self):
369 def emails(self):
370 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
370 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
371 return [self.email] + [x.email for x in other]
371 return [self.email] + [x.email for x in other]
372
372
373 @property
373 @property
374 def ip_addresses(self):
374 def ip_addresses(self):
375 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
375 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
376 return [x.ip_addr for x in ret]
376 return [x.ip_addr for x in ret]
377
377
378 @property
378 @property
379 def username_and_name(self):
379 def username_and_name(self):
380 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
380 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
381
381
382 @property
382 @property
383 def full_name(self):
383 def full_name(self):
384 return '%s %s' % (self.firstname, self.lastname)
384 return '%s %s' % (self.firstname, self.lastname)
385
385
386 @property
386 @property
387 def full_name_or_username(self):
387 def full_name_or_username(self):
388 return ('%s %s' % (self.firstname, self.lastname)
388 return ('%s %s' % (self.firstname, self.lastname)
389 if (self.firstname and self.lastname) else self.username)
389 if (self.firstname and self.lastname) else self.username)
390
390
391 @property
391 @property
392 def full_contact(self):
392 def full_contact(self):
393 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
393 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
394
394
395 @property
395 @property
396 def short_contact(self):
396 def short_contact(self):
397 return '%s %s' % (self.firstname, self.lastname)
397 return '%s %s' % (self.firstname, self.lastname)
398
398
399 @property
399 @property
400 def is_admin(self):
400 def is_admin(self):
401 return self.admin
401 return self.admin
402
402
403 @property
403 @property
404 def AuthUser(self):
404 def AuthUser(self):
405 """
405 """
406 Returns instance of AuthUser for this user
406 Returns instance of AuthUser for this user
407 """
407 """
408 from rhodecode.lib.auth import AuthUser
408 from rhodecode.lib.auth import AuthUser
409 return AuthUser(user_id=self.user_id, api_key=self.api_key,
409 return AuthUser(user_id=self.user_id, api_key=self.api_key,
410 username=self.username)
410 username=self.username)
411
411
412 def __unicode__(self):
412 def __unicode__(self):
413 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
413 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
414 self.user_id, self.username)
414 self.user_id, self.username)
415
415
416 @classmethod
416 @classmethod
417 def get_by_username(cls, username, case_insensitive=False, cache=False):
417 def get_by_username(cls, username, case_insensitive=False, cache=False):
418 if case_insensitive:
418 if case_insensitive:
419 q = cls.query().filter(cls.username.ilike(username))
419 q = cls.query().filter(cls.username.ilike(username))
420 else:
420 else:
421 q = cls.query().filter(cls.username == username)
421 q = cls.query().filter(cls.username == username)
422
422
423 if cache:
423 if cache:
424 q = q.options(FromCache(
424 q = q.options(FromCache(
425 "sql_cache_short",
425 "sql_cache_short",
426 "get_user_%s" % _hash_key(username)
426 "get_user_%s" % _hash_key(username)
427 )
427 )
428 )
428 )
429 return q.scalar()
429 return q.scalar()
430
430
431 @classmethod
431 @classmethod
432 def get_by_api_key(cls, api_key, cache=False):
432 def get_by_api_key(cls, api_key, cache=False):
433 q = cls.query().filter(cls.api_key == api_key)
433 q = cls.query().filter(cls.api_key == api_key)
434
434
435 if cache:
435 if cache:
436 q = q.options(FromCache("sql_cache_short",
436 q = q.options(FromCache("sql_cache_short",
437 "get_api_key_%s" % api_key))
437 "get_api_key_%s" % api_key))
438 return q.scalar()
438 return q.scalar()
439
439
440 @classmethod
440 @classmethod
441 def get_by_email(cls, email, case_insensitive=False, cache=False):
441 def get_by_email(cls, email, case_insensitive=False, cache=False):
442 if case_insensitive:
442 if case_insensitive:
443 q = cls.query().filter(cls.email.ilike(email))
443 q = cls.query().filter(cls.email.ilike(email))
444 else:
444 else:
445 q = cls.query().filter(cls.email == email)
445 q = cls.query().filter(cls.email == email)
446
446
447 if cache:
447 if cache:
448 q = q.options(FromCache("sql_cache_short",
448 q = q.options(FromCache("sql_cache_short",
449 "get_email_key_%s" % email))
449 "get_email_key_%s" % email))
450
450
451 ret = q.scalar()
451 ret = q.scalar()
452 if ret is None:
452 if ret is None:
453 q = UserEmailMap.query()
453 q = UserEmailMap.query()
454 # try fetching in alternate email map
454 # try fetching in alternate email map
455 if case_insensitive:
455 if case_insensitive:
456 q = q.filter(UserEmailMap.email.ilike(email))
456 q = q.filter(UserEmailMap.email.ilike(email))
457 else:
457 else:
458 q = q.filter(UserEmailMap.email == email)
458 q = q.filter(UserEmailMap.email == email)
459 q = q.options(joinedload(UserEmailMap.user))
459 q = q.options(joinedload(UserEmailMap.user))
460 if cache:
460 if cache:
461 q = q.options(FromCache("sql_cache_short",
461 q = q.options(FromCache("sql_cache_short",
462 "get_email_map_key_%s" % email))
462 "get_email_map_key_%s" % email))
463 ret = getattr(q.scalar(), 'user', None)
463 ret = getattr(q.scalar(), 'user', None)
464
464
465 return ret
465 return ret
466
466
467 @classmethod
467 @classmethod
468 def get_from_cs_author(cls, author):
468 def get_from_cs_author(cls, author):
469 """
469 """
470 Tries to get User objects out of commit author string
470 Tries to get User objects out of commit author string
471
471
472 :param author:
472 :param author:
473 """
473 """
474 from rhodecode.lib.helpers import email, author_name
474 from rhodecode.lib.helpers import email, author_name
475 # Valid email in the attribute passed, see if they're in the system
475 # Valid email in the attribute passed, see if they're in the system
476 _email = email(author)
476 _email = email(author)
477 if _email:
477 if _email:
478 user = cls.get_by_email(_email, case_insensitive=True)
478 user = cls.get_by_email(_email, case_insensitive=True)
479 if user:
479 if user:
480 return user
480 return user
481 # Maybe we can match by username?
481 # Maybe we can match by username?
482 _author = author_name(author)
482 _author = author_name(author)
483 user = cls.get_by_username(_author, case_insensitive=True)
483 user = cls.get_by_username(_author, case_insensitive=True)
484 if user:
484 if user:
485 return user
485 return user
486
486
487 def update_lastlogin(self):
487 def update_lastlogin(self):
488 """Update user lastlogin"""
488 """Update user lastlogin"""
489 self.last_login = datetime.datetime.now()
489 self.last_login = datetime.datetime.now()
490 Session().add(self)
490 Session().add(self)
491 log.debug('updated user %s lastlogin' % self.username)
491 log.debug('updated user %s lastlogin' % self.username)
492
492
493 def get_api_data(self):
493 def get_api_data(self):
494 """
494 """
495 Common function for generating user related data for API
495 Common function for generating user related data for API
496 """
496 """
497 user = self
497 user = self
498 data = dict(
498 data = dict(
499 user_id=user.user_id,
499 user_id=user.user_id,
500 username=user.username,
500 username=user.username,
501 firstname=user.name,
501 firstname=user.name,
502 lastname=user.lastname,
502 lastname=user.lastname,
503 email=user.email,
503 email=user.email,
504 emails=user.emails,
504 emails=user.emails,
505 api_key=user.api_key,
505 api_key=user.api_key,
506 active=user.active,
506 active=user.active,
507 admin=user.admin,
507 admin=user.admin,
508 ldap_dn=user.ldap_dn,
508 ldap_dn=user.ldap_dn,
509 last_login=user.last_login,
509 last_login=user.last_login,
510 ip_addresses=user.ip_addresses
510 ip_addresses=user.ip_addresses
511 )
511 )
512 return data
512 return data
513
513
514 def __json__(self):
514 def __json__(self):
515 data = dict(
515 data = dict(
516 full_name=self.full_name,
516 full_name=self.full_name,
517 full_name_or_username=self.full_name_or_username,
517 full_name_or_username=self.full_name_or_username,
518 short_contact=self.short_contact,
518 short_contact=self.short_contact,
519 full_contact=self.full_contact
519 full_contact=self.full_contact
520 )
520 )
521 data.update(self.get_api_data())
521 data.update(self.get_api_data())
522 return data
522 return data
523
523
524
524
525 class UserEmailMap(Base, BaseModel):
525 class UserEmailMap(Base, BaseModel):
526 __tablename__ = 'user_email_map'
526 __tablename__ = 'user_email_map'
527 __table_args__ = (
527 __table_args__ = (
528 Index('uem_email_idx', 'email'),
528 Index('uem_email_idx', 'email'),
529 UniqueConstraint('email'),
529 UniqueConstraint('email'),
530 {'extend_existing': True, 'mysql_engine': 'InnoDB',
530 {'extend_existing': True, 'mysql_engine': 'InnoDB',
531 'mysql_charset': 'utf8'}
531 'mysql_charset': 'utf8'}
532 )
532 )
533 __mapper_args__ = {}
533 __mapper_args__ = {}
534
534
535 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
535 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
536 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
536 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
537 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
537 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
538 user = relationship('User', lazy='joined')
538 user = relationship('User', lazy='joined')
539
539
540 @validates('_email')
540 @validates('_email')
541 def validate_email(self, key, email):
541 def validate_email(self, key, email):
542 # check if this email is not main one
542 # check if this email is not main one
543 main_email = Session().query(User).filter(User.email == email).scalar()
543 main_email = Session().query(User).filter(User.email == email).scalar()
544 if main_email is not None:
544 if main_email is not None:
545 raise AttributeError('email %s is present is user table' % email)
545 raise AttributeError('email %s is present is user table' % email)
546 return email
546 return email
547
547
548 @hybrid_property
548 @hybrid_property
549 def email(self):
549 def email(self):
550 return self._email
550 return self._email
551
551
552 @email.setter
552 @email.setter
553 def email(self, val):
553 def email(self, val):
554 self._email = val.lower() if val else None
554 self._email = val.lower() if val else None
555
555
556
556
557 class UserIpMap(Base, BaseModel):
557 class UserIpMap(Base, BaseModel):
558 __tablename__ = 'user_ip_map'
558 __tablename__ = 'user_ip_map'
559 __table_args__ = (
559 __table_args__ = (
560 UniqueConstraint('user_id', 'ip_addr'),
560 UniqueConstraint('user_id', 'ip_addr'),
561 {'extend_existing': True, 'mysql_engine': 'InnoDB',
561 {'extend_existing': True, 'mysql_engine': 'InnoDB',
562 'mysql_charset': 'utf8'}
562 'mysql_charset': 'utf8'}
563 )
563 )
564 __mapper_args__ = {}
564 __mapper_args__ = {}
565
565
566 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
566 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
567 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
567 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
568 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
568 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
569 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
569 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
570 user = relationship('User', lazy='joined')
570 user = relationship('User', lazy='joined')
571
571
572 @classmethod
572 @classmethod
573 def _get_ip_range(cls, ip_addr):
573 def _get_ip_range(cls, ip_addr):
574 from rhodecode.lib import ipaddr
574 from rhodecode.lib import ipaddr
575 net = ipaddr.IPNetwork(address=ip_addr)
575 net = ipaddr.IPNetwork(address=ip_addr)
576 return [str(net.network), str(net.broadcast)]
576 return [str(net.network), str(net.broadcast)]
577
577
578 def __json__(self):
578 def __json__(self):
579 return dict(
579 return dict(
580 ip_addr=self.ip_addr,
580 ip_addr=self.ip_addr,
581 ip_range=self._get_ip_range(self.ip_addr)
581 ip_range=self._get_ip_range(self.ip_addr)
582 )
582 )
583
583
584
584
585 class UserLog(Base, BaseModel):
585 class UserLog(Base, BaseModel):
586 __tablename__ = 'user_logs'
586 __tablename__ = 'user_logs'
587 __table_args__ = (
587 __table_args__ = (
588 {'extend_existing': True, 'mysql_engine': 'InnoDB',
588 {'extend_existing': True, 'mysql_engine': 'InnoDB',
589 'mysql_charset': 'utf8'},
589 'mysql_charset': 'utf8'},
590 )
590 )
591 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
591 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
592 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
592 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
593 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
593 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
594 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
594 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
595 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
595 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
596 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
596 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
597 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
597 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
598 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
598 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
599
599
600 @property
600 @property
601 def action_as_day(self):
601 def action_as_day(self):
602 return datetime.date(*self.action_date.timetuple()[:3])
602 return datetime.date(*self.action_date.timetuple()[:3])
603
603
604 user = relationship('User')
604 user = relationship('User')
605 repository = relationship('Repository', cascade='')
605 repository = relationship('Repository', cascade='')
606
606
607
607
608 class UserGroup(Base, BaseModel):
608 class UserGroup(Base, BaseModel):
609 __tablename__ = 'users_groups'
609 __tablename__ = 'users_groups'
610 __table_args__ = (
610 __table_args__ = (
611 {'extend_existing': True, 'mysql_engine': 'InnoDB',
611 {'extend_existing': True, 'mysql_engine': 'InnoDB',
612 'mysql_charset': 'utf8'},
612 'mysql_charset': 'utf8'},
613 )
613 )
614
614
615 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
615 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
616 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
616 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
617 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
617 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
618 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
618 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
619
619
620 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
620 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
621 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
621 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
622 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
622 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
623
623
624 def __unicode__(self):
624 def __unicode__(self):
625 return u'<userGroup(%s)>' % (self.users_group_name)
625 return u'<userGroup(%s)>' % (self.users_group_name)
626
626
627 @classmethod
627 @classmethod
628 def get_by_group_name(cls, group_name, cache=False,
628 def get_by_group_name(cls, group_name, cache=False,
629 case_insensitive=False):
629 case_insensitive=False):
630 if case_insensitive:
630 if case_insensitive:
631 q = cls.query().filter(cls.users_group_name.ilike(group_name))
631 q = cls.query().filter(cls.users_group_name.ilike(group_name))
632 else:
632 else:
633 q = cls.query().filter(cls.users_group_name == group_name)
633 q = cls.query().filter(cls.users_group_name == group_name)
634 if cache:
634 if cache:
635 q = q.options(FromCache(
635 q = q.options(FromCache(
636 "sql_cache_short",
636 "sql_cache_short",
637 "get_user_%s" % _hash_key(group_name)
637 "get_user_%s" % _hash_key(group_name)
638 )
638 )
639 )
639 )
640 return q.scalar()
640 return q.scalar()
641
641
642 @classmethod
642 @classmethod
643 def get(cls, users_group_id, cache=False):
643 def get(cls, users_group_id, cache=False):
644 users_group = cls.query()
644 users_group = cls.query()
645 if cache:
645 if cache:
646 users_group = users_group.options(FromCache("sql_cache_short",
646 users_group = users_group.options(FromCache("sql_cache_short",
647 "get_users_group_%s" % users_group_id))
647 "get_users_group_%s" % users_group_id))
648 return users_group.get(users_group_id)
648 return users_group.get(users_group_id)
649
649
650 def get_api_data(self):
650 def get_api_data(self):
651 users_group = self
651 users_group = self
652
652
653 data = dict(
653 data = dict(
654 users_group_id=users_group.users_group_id,
654 users_group_id=users_group.users_group_id,
655 group_name=users_group.users_group_name,
655 group_name=users_group.users_group_name,
656 active=users_group.users_group_active,
656 active=users_group.users_group_active,
657 )
657 )
658
658
659 return data
659 return data
660
660
661
661
662 class UserGroupMember(Base, BaseModel):
662 class UserGroupMember(Base, BaseModel):
663 __tablename__ = 'users_groups_members'
663 __tablename__ = 'users_groups_members'
664 __table_args__ = (
664 __table_args__ = (
665 {'extend_existing': True, 'mysql_engine': 'InnoDB',
665 {'extend_existing': True, 'mysql_engine': 'InnoDB',
666 'mysql_charset': 'utf8'},
666 'mysql_charset': 'utf8'},
667 )
667 )
668
668
669 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
669 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
670 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
670 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
671 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
671 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
672
672
673 user = relationship('User', lazy='joined')
673 user = relationship('User', lazy='joined')
674 users_group = relationship('UserGroup')
674 users_group = relationship('UserGroup')
675
675
676 def __init__(self, gr_id='', u_id=''):
676 def __init__(self, gr_id='', u_id=''):
677 self.users_group_id = gr_id
677 self.users_group_id = gr_id
678 self.user_id = u_id
678 self.user_id = u_id
679
679
680
680
681 class RepositoryField(Base, BaseModel):
681 class RepositoryField(Base, BaseModel):
682 __tablename__ = 'repositories_fields'
682 __tablename__ = 'repositories_fields'
683 __table_args__ = (
683 __table_args__ = (
684 UniqueConstraint('repository_id', 'field_key'), # no-multi field
684 UniqueConstraint('repository_id', 'field_key'), # no-multi field
685 {'extend_existing': True, 'mysql_engine': 'InnoDB',
685 {'extend_existing': True, 'mysql_engine': 'InnoDB',
686 'mysql_charset': 'utf8'},
686 'mysql_charset': 'utf8'},
687 )
687 )
688 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
688 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
689
689
690 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
690 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
691 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
691 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
692 field_key = Column("field_key", String(250, convert_unicode=False, assert_unicode=None))
692 field_key = Column("field_key", String(250, convert_unicode=False, assert_unicode=None))
693 field_label = Column("field_label", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
693 field_label = Column("field_label", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
694 field_value = Column("field_value", String(10000, convert_unicode=False, assert_unicode=None), nullable=False)
694 field_value = Column("field_value", String(10000, convert_unicode=False, assert_unicode=None), nullable=False)
695 field_desc = Column("field_desc", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
695 field_desc = Column("field_desc", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
696 field_type = Column("field_type", String(256), nullable=False, unique=None)
696 field_type = Column("field_type", String(256), nullable=False, unique=None)
697 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
697 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
698
698
699 repository = relationship('Repository')
699 repository = relationship('Repository')
700
700
701 @property
701 @property
702 def field_key_prefixed(self):
702 def field_key_prefixed(self):
703 return 'ex_%s' % self.field_key
703 return 'ex_%s' % self.field_key
704
704
705 @classmethod
705 @classmethod
706 def un_prefix_key(cls, key):
706 def un_prefix_key(cls, key):
707 if key.startswith(cls.PREFIX):
707 if key.startswith(cls.PREFIX):
708 return key[len(cls.PREFIX):]
708 return key[len(cls.PREFIX):]
709 return key
709 return key
710
710
711 @classmethod
711 @classmethod
712 def get_by_key_name(cls, key, repo):
712 def get_by_key_name(cls, key, repo):
713 row = cls.query()\
713 row = cls.query()\
714 .filter(cls.repository == repo)\
714 .filter(cls.repository == repo)\
715 .filter(cls.field_key == key).scalar()
715 .filter(cls.field_key == key).scalar()
716 return row
716 return row
717
717
718
718
719 class Repository(Base, BaseModel):
719 class Repository(Base, BaseModel):
720 __tablename__ = 'repositories'
720 __tablename__ = 'repositories'
721 __table_args__ = (
721 __table_args__ = (
722 UniqueConstraint('repo_name'),
722 UniqueConstraint('repo_name'),
723 Index('r_repo_name_idx', 'repo_name'),
723 Index('r_repo_name_idx', 'repo_name'),
724 {'extend_existing': True, 'mysql_engine': 'InnoDB',
724 {'extend_existing': True, 'mysql_engine': 'InnoDB',
725 'mysql_charset': 'utf8'},
725 'mysql_charset': 'utf8'},
726 )
726 )
727
727
728 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
728 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
729 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
729 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
730 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
730 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
731 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
731 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
732 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
732 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
733 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
733 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
734 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
734 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
735 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
735 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
736 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
736 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
737 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
737 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
738 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
738 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
739 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
739 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
740 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
740 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
741 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
741 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
742 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
742 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
743
743
744 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
744 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
745 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
745 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
746
746
747 user = relationship('User')
747 user = relationship('User')
748 fork = relationship('Repository', remote_side=repo_id)
748 fork = relationship('Repository', remote_side=repo_id)
749 group = relationship('RepoGroup')
749 group = relationship('RepoGroup')
750 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
750 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
751 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
751 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
752 stats = relationship('Statistics', cascade='all', uselist=False)
752 stats = relationship('Statistics', cascade='all', uselist=False)
753
753
754 followers = relationship('UserFollowing',
754 followers = relationship('UserFollowing',
755 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
755 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
756 cascade='all')
756 cascade='all')
757 extra_fields = relationship('RepositoryField',
757 extra_fields = relationship('RepositoryField',
758 cascade="all, delete, delete-orphan")
758 cascade="all, delete, delete-orphan")
759
759
760 logs = relationship('UserLog')
760 logs = relationship('UserLog')
761 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
761 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
762
762
763 pull_requests_org = relationship('PullRequest',
763 pull_requests_org = relationship('PullRequest',
764 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
764 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
765 cascade="all, delete, delete-orphan")
765 cascade="all, delete, delete-orphan")
766
766
767 pull_requests_other = relationship('PullRequest',
767 pull_requests_other = relationship('PullRequest',
768 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
768 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
769 cascade="all, delete, delete-orphan")
769 cascade="all, delete, delete-orphan")
770
770
771 def __unicode__(self):
771 def __unicode__(self):
772 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
772 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
773 self.repo_name)
773 self.repo_name)
774
774
775 @hybrid_property
775 @hybrid_property
776 def locked(self):
776 def locked(self):
777 # always should return [user_id, timelocked]
777 # always should return [user_id, timelocked]
778 if self._locked:
778 if self._locked:
779 _lock_info = self._locked.split(':')
779 _lock_info = self._locked.split(':')
780 return int(_lock_info[0]), _lock_info[1]
780 return int(_lock_info[0]), _lock_info[1]
781 return [None, None]
781 return [None, None]
782
782
783 @locked.setter
783 @locked.setter
784 def locked(self, val):
784 def locked(self, val):
785 if val and isinstance(val, (list, tuple)):
785 if val and isinstance(val, (list, tuple)):
786 self._locked = ':'.join(map(str, val))
786 self._locked = ':'.join(map(str, val))
787 else:
787 else:
788 self._locked = None
788 self._locked = None
789
789
790 @hybrid_property
790 @hybrid_property
791 def changeset_cache(self):
791 def changeset_cache(self):
792 from rhodecode.lib.vcs.backends.base import EmptyChangeset
792 from rhodecode.lib.vcs.backends.base import EmptyChangeset
793 dummy = EmptyChangeset().__json__()
793 dummy = EmptyChangeset().__json__()
794 if not self._changeset_cache:
794 if not self._changeset_cache:
795 return dummy
795 return dummy
796 try:
796 try:
797 return json.loads(self._changeset_cache)
797 return json.loads(self._changeset_cache)
798 except TypeError:
798 except TypeError:
799 return dummy
799 return dummy
800
800
801 @changeset_cache.setter
801 @changeset_cache.setter
802 def changeset_cache(self, val):
802 def changeset_cache(self, val):
803 try:
803 try:
804 self._changeset_cache = json.dumps(val)
804 self._changeset_cache = json.dumps(val)
805 except:
805 except:
806 log.error(traceback.format_exc())
806 log.error(traceback.format_exc())
807
807
808 @classmethod
808 @classmethod
809 def url_sep(cls):
809 def url_sep(cls):
810 return URL_SEP
810 return URL_SEP
811
811
812 @classmethod
812 @classmethod
813 def normalize_repo_name(cls, repo_name):
813 def normalize_repo_name(cls, repo_name):
814 """
814 """
815 Normalizes os specific repo_name to the format internally stored inside
815 Normalizes os specific repo_name to the format internally stored inside
816 dabatabase using URL_SEP
816 dabatabase using URL_SEP
817
817
818 :param cls:
818 :param cls:
819 :param repo_name:
819 :param repo_name:
820 """
820 """
821 return cls.url_sep().join(repo_name.split(os.sep))
821 return cls.url_sep().join(repo_name.split(os.sep))
822
822
823 @classmethod
823 @classmethod
824 def get_by_repo_name(cls, repo_name):
824 def get_by_repo_name(cls, repo_name):
825 q = Session().query(cls).filter(cls.repo_name == repo_name)
825 q = Session().query(cls).filter(cls.repo_name == repo_name)
826 q = q.options(joinedload(Repository.fork))\
826 q = q.options(joinedload(Repository.fork))\
827 .options(joinedload(Repository.user))\
827 .options(joinedload(Repository.user))\
828 .options(joinedload(Repository.group))
828 .options(joinedload(Repository.group))
829 return q.scalar()
829 return q.scalar()
830
830
831 @classmethod
831 @classmethod
832 def get_by_full_path(cls, repo_full_path):
832 def get_by_full_path(cls, repo_full_path):
833 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
833 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
834 repo_name = cls.normalize_repo_name(repo_name)
834 repo_name = cls.normalize_repo_name(repo_name)
835 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
835 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
836
836
837 @classmethod
837 @classmethod
838 def get_repo_forks(cls, repo_id):
838 def get_repo_forks(cls, repo_id):
839 return cls.query().filter(Repository.fork_id == repo_id)
839 return cls.query().filter(Repository.fork_id == repo_id)
840
840
841 @classmethod
841 @classmethod
842 def base_path(cls):
842 def base_path(cls):
843 """
843 """
844 Returns base path when all repos are stored
844 Returns base path when all repos are stored
845
845
846 :param cls:
846 :param cls:
847 """
847 """
848 q = Session().query(RhodeCodeUi)\
848 q = Session().query(RhodeCodeUi)\
849 .filter(RhodeCodeUi.ui_key == cls.url_sep())
849 .filter(RhodeCodeUi.ui_key == cls.url_sep())
850 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
850 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
851 return q.one().ui_value
851 return q.one().ui_value
852
852
853 @property
853 @property
854 def forks(self):
854 def forks(self):
855 """
855 """
856 Return forks of this repo
856 Return forks of this repo
857 """
857 """
858 return Repository.get_repo_forks(self.repo_id)
858 return Repository.get_repo_forks(self.repo_id)
859
859
860 @property
860 @property
861 def parent(self):
861 def parent(self):
862 """
862 """
863 Returns fork parent
863 Returns fork parent
864 """
864 """
865 return self.fork
865 return self.fork
866
866
867 @property
867 @property
868 def just_name(self):
868 def just_name(self):
869 return self.repo_name.split(Repository.url_sep())[-1]
869 return self.repo_name.split(Repository.url_sep())[-1]
870
870
871 @property
871 @property
872 def groups_with_parents(self):
872 def groups_with_parents(self):
873 groups = []
873 groups = []
874 if self.group is None:
874 if self.group is None:
875 return groups
875 return groups
876
876
877 cur_gr = self.group
877 cur_gr = self.group
878 groups.insert(0, cur_gr)
878 groups.insert(0, cur_gr)
879 while 1:
879 while 1:
880 gr = getattr(cur_gr, 'parent_group', None)
880 gr = getattr(cur_gr, 'parent_group', None)
881 cur_gr = cur_gr.parent_group
881 cur_gr = cur_gr.parent_group
882 if gr is None:
882 if gr is None:
883 break
883 break
884 groups.insert(0, gr)
884 groups.insert(0, gr)
885
885
886 return groups
886 return groups
887
887
888 @property
888 @property
889 def groups_and_repo(self):
889 def groups_and_repo(self):
890 return self.groups_with_parents, self.just_name
890 return self.groups_with_parents, self.just_name
891
891
892 @LazyProperty
892 @LazyProperty
893 def repo_path(self):
893 def repo_path(self):
894 """
894 """
895 Returns base full path for that repository means where it actually
895 Returns base full path for that repository means where it actually
896 exists on a filesystem
896 exists on a filesystem
897 """
897 """
898 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
898 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
899 Repository.url_sep())
899 Repository.url_sep())
900 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
900 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
901 return q.one().ui_value
901 return q.one().ui_value
902
902
903 @property
903 @property
904 def repo_full_path(self):
904 def repo_full_path(self):
905 p = [self.repo_path]
905 p = [self.repo_path]
906 # we need to split the name by / since this is how we store the
906 # we need to split the name by / since this is how we store the
907 # names in the database, but that eventually needs to be converted
907 # names in the database, but that eventually needs to be converted
908 # into a valid system path
908 # into a valid system path
909 p += self.repo_name.split(Repository.url_sep())
909 p += self.repo_name.split(Repository.url_sep())
910 return os.path.join(*p)
910 return os.path.join(*p)
911
911
912 @property
912 @property
913 def cache_keys(self):
913 def cache_keys(self):
914 """
914 """
915 Returns associated cache keys for that repo
915 Returns associated cache keys for that repo
916 """
916 """
917 return CacheInvalidation.query()\
917 return CacheInvalidation.query()\
918 .filter(CacheInvalidation.cache_args == self.repo_name)\
918 .filter(CacheInvalidation.cache_args == self.repo_name)\
919 .order_by(CacheInvalidation.cache_key)\
919 .order_by(CacheInvalidation.cache_key)\
920 .all()
920 .all()
921
921
922 def get_new_name(self, repo_name):
922 def get_new_name(self, repo_name):
923 """
923 """
924 returns new full repository name based on assigned group and new new
924 returns new full repository name based on assigned group and new new
925
925
926 :param group_name:
926 :param group_name:
927 """
927 """
928 path_prefix = self.group.full_path_splitted if self.group else []
928 path_prefix = self.group.full_path_splitted if self.group else []
929 return Repository.url_sep().join(path_prefix + [repo_name])
929 return Repository.url_sep().join(path_prefix + [repo_name])
930
930
931 @property
931 @property
932 def _ui(self):
932 def _ui(self):
933 """
933 """
934 Creates an db based ui object for this repository
934 Creates an db based ui object for this repository
935 """
935 """
936 from rhodecode.lib.utils import make_ui
936 from rhodecode.lib.utils import make_ui
937 return make_ui('db', clear_session=False)
937 return make_ui('db', clear_session=False)
938
938
939 @classmethod
939 @classmethod
940 def inject_ui(cls, repo, extras={}):
940 def inject_ui(cls, repo, extras={}):
941 repo.inject_ui(extras)
941 repo.inject_ui(extras)
942
942
943 @classmethod
943 @classmethod
944 def is_valid(cls, repo_name):
944 def is_valid(cls, repo_name):
945 """
945 """
946 returns True if given repo name is a valid filesystem repository
946 returns True if given repo name is a valid filesystem repository
947
947
948 :param cls:
948 :param cls:
949 :param repo_name:
949 :param repo_name:
950 """
950 """
951 from rhodecode.lib.utils import is_valid_repo
951 from rhodecode.lib.utils import is_valid_repo
952
952
953 return is_valid_repo(repo_name, cls.base_path())
953 return is_valid_repo(repo_name, cls.base_path())
954
954
955 def get_api_data(self):
955 def get_api_data(self):
956 """
956 """
957 Common function for generating repo api data
957 Common function for generating repo api data
958
958
959 """
959 """
960 repo = self
960 repo = self
961 data = dict(
961 data = dict(
962 repo_id=repo.repo_id,
962 repo_id=repo.repo_id,
963 repo_name=repo.repo_name,
963 repo_name=repo.repo_name,
964 repo_type=repo.repo_type,
964 repo_type=repo.repo_type,
965 clone_uri=repo.clone_uri,
965 clone_uri=repo.clone_uri,
966 private=repo.private,
966 private=repo.private,
967 created_on=repo.created_on,
967 created_on=repo.created_on,
968 description=repo.description,
968 description=repo.description,
969 landing_rev=repo.landing_rev,
969 landing_rev=repo.landing_rev,
970 owner=repo.user.username,
970 owner=repo.user.username,
971 fork_of=repo.fork.repo_name if repo.fork else None,
971 fork_of=repo.fork.repo_name if repo.fork else None,
972 enable_statistics=repo.enable_statistics,
972 enable_statistics=repo.enable_statistics,
973 enable_locking=repo.enable_locking,
973 enable_locking=repo.enable_locking,
974 enable_downloads=repo.enable_downloads,
974 enable_downloads=repo.enable_downloads,
975 last_changeset=repo.changeset_cache
975 last_changeset=repo.changeset_cache,
976 locked_by=User.get(self.locked[0]).get_api_data() \
977 if self.locked[0] else None,
978 locked_date=time_to_datetime(self.locked[1]) \
979 if self.locked[1] else None
976 )
980 )
977 rc_config = RhodeCodeSetting.get_app_settings()
981 rc_config = RhodeCodeSetting.get_app_settings()
978 repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
982 repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
979 if repository_fields:
983 if repository_fields:
980 for f in self.extra_fields:
984 for f in self.extra_fields:
981 data[f.field_key_prefixed] = f.field_value
985 data[f.field_key_prefixed] = f.field_value
982
986
983 return data
987 return data
984
988
985 @classmethod
989 @classmethod
986 def lock(cls, repo, user_id):
990 def lock(cls, repo, user_id):
987 repo.locked = [user_id, time.time()]
991 repo.locked = [user_id, time.time()]
988 Session().add(repo)
992 Session().add(repo)
989 Session().commit()
993 Session().commit()
990
994
991 @classmethod
995 @classmethod
992 def unlock(cls, repo):
996 def unlock(cls, repo):
993 repo.locked = None
997 repo.locked = None
994 Session().add(repo)
998 Session().add(repo)
995 Session().commit()
999 Session().commit()
996
1000
997 @classmethod
1001 @classmethod
998 def getlock(cls, repo):
1002 def getlock(cls, repo):
999 return repo.locked
1003 return repo.locked
1000
1004
1001 @property
1005 @property
1002 def last_db_change(self):
1006 def last_db_change(self):
1003 return self.updated_on
1007 return self.updated_on
1004
1008
1005 def clone_url(self, **override):
1009 def clone_url(self, **override):
1006 from pylons import url
1010 from pylons import url
1007 from urlparse import urlparse
1011 from urlparse import urlparse
1008 import urllib
1012 import urllib
1009 parsed_url = urlparse(url('home', qualified=True))
1013 parsed_url = urlparse(url('home', qualified=True))
1010 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
1014 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
1011 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
1015 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
1012 args = {
1016 args = {
1013 'user': '',
1017 'user': '',
1014 'pass': '',
1018 'pass': '',
1015 'scheme': parsed_url.scheme,
1019 'scheme': parsed_url.scheme,
1016 'netloc': parsed_url.netloc,
1020 'netloc': parsed_url.netloc,
1017 'prefix': decoded_path,
1021 'prefix': decoded_path,
1018 'path': self.repo_name
1022 'path': self.repo_name
1019 }
1023 }
1020
1024
1021 args.update(override)
1025 args.update(override)
1022 return default_clone_uri % args
1026 return default_clone_uri % args
1023
1027
1024 #==========================================================================
1028 #==========================================================================
1025 # SCM PROPERTIES
1029 # SCM PROPERTIES
1026 #==========================================================================
1030 #==========================================================================
1027
1031
1028 def get_changeset(self, rev=None):
1032 def get_changeset(self, rev=None):
1029 return get_changeset_safe(self.scm_instance, rev)
1033 return get_changeset_safe(self.scm_instance, rev)
1030
1034
1031 def get_landing_changeset(self):
1035 def get_landing_changeset(self):
1032 """
1036 """
1033 Returns landing changeset, or if that doesn't exist returns the tip
1037 Returns landing changeset, or if that doesn't exist returns the tip
1034 """
1038 """
1035 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
1039 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
1036 return cs
1040 return cs
1037
1041
1038 def update_changeset_cache(self, cs_cache=None):
1042 def update_changeset_cache(self, cs_cache=None):
1039 """
1043 """
1040 Update cache of last changeset for repository, keys should be::
1044 Update cache of last changeset for repository, keys should be::
1041
1045
1042 short_id
1046 short_id
1043 raw_id
1047 raw_id
1044 revision
1048 revision
1045 message
1049 message
1046 date
1050 date
1047 author
1051 author
1048
1052
1049 :param cs_cache:
1053 :param cs_cache:
1050 """
1054 """
1051 from rhodecode.lib.vcs.backends.base import BaseChangeset
1055 from rhodecode.lib.vcs.backends.base import BaseChangeset
1052 if cs_cache is None:
1056 if cs_cache is None:
1053 cs_cache = EmptyChangeset()
1057 cs_cache = EmptyChangeset()
1054 # use no-cache version here
1058 # use no-cache version here
1055 scm_repo = self.scm_instance_no_cache
1059 scm_repo = self.scm_instance_no_cache
1056 if scm_repo:
1060 if scm_repo:
1057 cs_cache = scm_repo.get_changeset()
1061 cs_cache = scm_repo.get_changeset()
1058
1062
1059 if isinstance(cs_cache, BaseChangeset):
1063 if isinstance(cs_cache, BaseChangeset):
1060 cs_cache = cs_cache.__json__()
1064 cs_cache = cs_cache.__json__()
1061
1065
1062 if (cs_cache != self.changeset_cache or not self.changeset_cache):
1066 if (cs_cache != self.changeset_cache or not self.changeset_cache):
1063 _default = datetime.datetime.fromtimestamp(0)
1067 _default = datetime.datetime.fromtimestamp(0)
1064 last_change = cs_cache.get('date') or _default
1068 last_change = cs_cache.get('date') or _default
1065 log.debug('updated repo %s with new cs cache %s' % (self, cs_cache))
1069 log.debug('updated repo %s with new cs cache %s' % (self, cs_cache))
1066 self.updated_on = last_change
1070 self.updated_on = last_change
1067 self.changeset_cache = cs_cache
1071 self.changeset_cache = cs_cache
1068 Session().add(self)
1072 Session().add(self)
1069 Session().commit()
1073 Session().commit()
1070 else:
1074 else:
1071 log.debug('Skipping repo:%s already with latest changes' % self)
1075 log.debug('Skipping repo:%s already with latest changes' % self)
1072
1076
1073 @property
1077 @property
1074 def tip(self):
1078 def tip(self):
1075 return self.get_changeset('tip')
1079 return self.get_changeset('tip')
1076
1080
1077 @property
1081 @property
1078 def author(self):
1082 def author(self):
1079 return self.tip.author
1083 return self.tip.author
1080
1084
1081 @property
1085 @property
1082 def last_change(self):
1086 def last_change(self):
1083 return self.scm_instance.last_change
1087 return self.scm_instance.last_change
1084
1088
1085 def get_comments(self, revisions=None):
1089 def get_comments(self, revisions=None):
1086 """
1090 """
1087 Returns comments for this repository grouped by revisions
1091 Returns comments for this repository grouped by revisions
1088
1092
1089 :param revisions: filter query by revisions only
1093 :param revisions: filter query by revisions only
1090 """
1094 """
1091 cmts = ChangesetComment.query()\
1095 cmts = ChangesetComment.query()\
1092 .filter(ChangesetComment.repo == self)
1096 .filter(ChangesetComment.repo == self)
1093 if revisions:
1097 if revisions:
1094 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1098 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1095 grouped = defaultdict(list)
1099 grouped = defaultdict(list)
1096 for cmt in cmts.all():
1100 for cmt in cmts.all():
1097 grouped[cmt.revision].append(cmt)
1101 grouped[cmt.revision].append(cmt)
1098 return grouped
1102 return grouped
1099
1103
1100 def statuses(self, revisions=None):
1104 def statuses(self, revisions=None):
1101 """
1105 """
1102 Returns statuses for this repository
1106 Returns statuses for this repository
1103
1107
1104 :param revisions: list of revisions to get statuses for
1108 :param revisions: list of revisions to get statuses for
1105 :type revisions: list
1109 :type revisions: list
1106 """
1110 """
1107
1111
1108 statuses = ChangesetStatus.query()\
1112 statuses = ChangesetStatus.query()\
1109 .filter(ChangesetStatus.repo == self)\
1113 .filter(ChangesetStatus.repo == self)\
1110 .filter(ChangesetStatus.version == 0)
1114 .filter(ChangesetStatus.version == 0)
1111 if revisions:
1115 if revisions:
1112 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1116 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1113 grouped = {}
1117 grouped = {}
1114
1118
1115 #maybe we have open new pullrequest without a status ?
1119 #maybe we have open new pullrequest without a status ?
1116 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1120 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1117 status_lbl = ChangesetStatus.get_status_lbl(stat)
1121 status_lbl = ChangesetStatus.get_status_lbl(stat)
1118 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1122 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1119 for rev in pr.revisions:
1123 for rev in pr.revisions:
1120 pr_id = pr.pull_request_id
1124 pr_id = pr.pull_request_id
1121 pr_repo = pr.other_repo.repo_name
1125 pr_repo = pr.other_repo.repo_name
1122 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1126 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1123
1127
1124 for stat in statuses.all():
1128 for stat in statuses.all():
1125 pr_id = pr_repo = None
1129 pr_id = pr_repo = None
1126 if stat.pull_request:
1130 if stat.pull_request:
1127 pr_id = stat.pull_request.pull_request_id
1131 pr_id = stat.pull_request.pull_request_id
1128 pr_repo = stat.pull_request.other_repo.repo_name
1132 pr_repo = stat.pull_request.other_repo.repo_name
1129 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1133 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1130 pr_id, pr_repo]
1134 pr_id, pr_repo]
1131 return grouped
1135 return grouped
1132
1136
1133 def _repo_size(self):
1137 def _repo_size(self):
1134 from rhodecode.lib import helpers as h
1138 from rhodecode.lib import helpers as h
1135 log.debug('calculating repository size...')
1139 log.debug('calculating repository size...')
1136 return h.format_byte_size(self.scm_instance.size)
1140 return h.format_byte_size(self.scm_instance.size)
1137
1141
1138 #==========================================================================
1142 #==========================================================================
1139 # SCM CACHE INSTANCE
1143 # SCM CACHE INSTANCE
1140 #==========================================================================
1144 #==========================================================================
1141
1145
1142 @property
1146 @property
1143 def invalidate(self):
1147 def invalidate(self):
1144 return CacheInvalidation.invalidate(self.repo_name)
1148 return CacheInvalidation.invalidate(self.repo_name)
1145
1149
1146 def set_invalidate(self):
1150 def set_invalidate(self):
1147 """
1151 """
1148 set a cache for invalidation for this instance
1152 set a cache for invalidation for this instance
1149 """
1153 """
1150 CacheInvalidation.set_invalidate(repo_name=self.repo_name)
1154 CacheInvalidation.set_invalidate(repo_name=self.repo_name)
1151
1155
1152 @LazyProperty
1156 @LazyProperty
1153 def scm_instance_no_cache(self):
1157 def scm_instance_no_cache(self):
1154 return self.__get_instance()
1158 return self.__get_instance()
1155
1159
1156 @LazyProperty
1160 @LazyProperty
1157 def scm_instance(self):
1161 def scm_instance(self):
1158 import rhodecode
1162 import rhodecode
1159 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1163 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1160 if full_cache:
1164 if full_cache:
1161 return self.scm_instance_cached()
1165 return self.scm_instance_cached()
1162 return self.__get_instance()
1166 return self.__get_instance()
1163
1167
1164 def scm_instance_cached(self, cache_map=None):
1168 def scm_instance_cached(self, cache_map=None):
1165 @cache_region('long_term')
1169 @cache_region('long_term')
1166 def _c(repo_name):
1170 def _c(repo_name):
1167 return self.__get_instance()
1171 return self.__get_instance()
1168 rn = self.repo_name
1172 rn = self.repo_name
1169 log.debug('Getting cached instance of repo')
1173 log.debug('Getting cached instance of repo')
1170
1174
1171 if cache_map:
1175 if cache_map:
1172 # get using prefilled cache_map
1176 # get using prefilled cache_map
1173 invalidate_repo = cache_map[self.repo_name]
1177 invalidate_repo = cache_map[self.repo_name]
1174 if invalidate_repo:
1178 if invalidate_repo:
1175 invalidate_repo = (None if invalidate_repo.cache_active
1179 invalidate_repo = (None if invalidate_repo.cache_active
1176 else invalidate_repo)
1180 else invalidate_repo)
1177 else:
1181 else:
1178 # get from invalidate
1182 # get from invalidate
1179 invalidate_repo = self.invalidate
1183 invalidate_repo = self.invalidate
1180
1184
1181 if invalidate_repo is not None:
1185 if invalidate_repo is not None:
1182 region_invalidate(_c, None, rn)
1186 region_invalidate(_c, None, rn)
1183 # update our cache
1187 # update our cache
1184 CacheInvalidation.set_valid(invalidate_repo.cache_key)
1188 CacheInvalidation.set_valid(invalidate_repo.cache_key)
1185 return _c(rn)
1189 return _c(rn)
1186
1190
1187 def __get_instance(self):
1191 def __get_instance(self):
1188 repo_full_path = self.repo_full_path
1192 repo_full_path = self.repo_full_path
1189 try:
1193 try:
1190 alias = get_scm(repo_full_path)[0]
1194 alias = get_scm(repo_full_path)[0]
1191 log.debug('Creating instance of %s repository from %s'
1195 log.debug('Creating instance of %s repository from %s'
1192 % (alias, repo_full_path))
1196 % (alias, repo_full_path))
1193 backend = get_backend(alias)
1197 backend = get_backend(alias)
1194 except VCSError:
1198 except VCSError:
1195 log.error(traceback.format_exc())
1199 log.error(traceback.format_exc())
1196 log.error('Perhaps this repository is in db and not in '
1200 log.error('Perhaps this repository is in db and not in '
1197 'filesystem run rescan repositories with '
1201 'filesystem run rescan repositories with '
1198 '"destroy old data " option from admin panel')
1202 '"destroy old data " option from admin panel')
1199 return
1203 return
1200
1204
1201 if alias == 'hg':
1205 if alias == 'hg':
1202
1206
1203 repo = backend(safe_str(repo_full_path), create=False,
1207 repo = backend(safe_str(repo_full_path), create=False,
1204 baseui=self._ui)
1208 baseui=self._ui)
1205 # skip hidden web repository
1209 # skip hidden web repository
1206 if repo._get_hidden():
1210 if repo._get_hidden():
1207 return
1211 return
1208 else:
1212 else:
1209 repo = backend(repo_full_path, create=False)
1213 repo = backend(repo_full_path, create=False)
1210
1214
1211 return repo
1215 return repo
1212
1216
1213
1217
1214 class RepoGroup(Base, BaseModel):
1218 class RepoGroup(Base, BaseModel):
1215 __tablename__ = 'groups'
1219 __tablename__ = 'groups'
1216 __table_args__ = (
1220 __table_args__ = (
1217 UniqueConstraint('group_name', 'group_parent_id'),
1221 UniqueConstraint('group_name', 'group_parent_id'),
1218 CheckConstraint('group_id != group_parent_id'),
1222 CheckConstraint('group_id != group_parent_id'),
1219 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1223 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1220 'mysql_charset': 'utf8'},
1224 'mysql_charset': 'utf8'},
1221 )
1225 )
1222 __mapper_args__ = {'order_by': 'group_name'}
1226 __mapper_args__ = {'order_by': 'group_name'}
1223
1227
1224 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1228 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1225 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1229 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1226 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1230 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1227 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1231 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1228 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1232 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1229
1233
1230 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1234 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1231 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1235 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1232
1236
1233 parent_group = relationship('RepoGroup', remote_side=group_id)
1237 parent_group = relationship('RepoGroup', remote_side=group_id)
1234
1238
1235 def __init__(self, group_name='', parent_group=None):
1239 def __init__(self, group_name='', parent_group=None):
1236 self.group_name = group_name
1240 self.group_name = group_name
1237 self.parent_group = parent_group
1241 self.parent_group = parent_group
1238
1242
1239 def __unicode__(self):
1243 def __unicode__(self):
1240 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
1244 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
1241 self.group_name)
1245 self.group_name)
1242
1246
1243 @classmethod
1247 @classmethod
1244 def groups_choices(cls, groups=None, show_empty_group=True):
1248 def groups_choices(cls, groups=None, show_empty_group=True):
1245 from webhelpers.html import literal as _literal
1249 from webhelpers.html import literal as _literal
1246 if not groups:
1250 if not groups:
1247 groups = cls.query().all()
1251 groups = cls.query().all()
1248
1252
1249 repo_groups = []
1253 repo_groups = []
1250 if show_empty_group:
1254 if show_empty_group:
1251 repo_groups = [('-1', '-- no parent --')]
1255 repo_groups = [('-1', '-- no parent --')]
1252 sep = ' &raquo; '
1256 sep = ' &raquo; '
1253 _name = lambda k: _literal(sep.join(k))
1257 _name = lambda k: _literal(sep.join(k))
1254
1258
1255 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1259 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1256 for x in groups])
1260 for x in groups])
1257
1261
1258 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1262 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1259 return repo_groups
1263 return repo_groups
1260
1264
1261 @classmethod
1265 @classmethod
1262 def url_sep(cls):
1266 def url_sep(cls):
1263 return URL_SEP
1267 return URL_SEP
1264
1268
1265 @classmethod
1269 @classmethod
1266 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1270 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1267 if case_insensitive:
1271 if case_insensitive:
1268 gr = cls.query()\
1272 gr = cls.query()\
1269 .filter(cls.group_name.ilike(group_name))
1273 .filter(cls.group_name.ilike(group_name))
1270 else:
1274 else:
1271 gr = cls.query()\
1275 gr = cls.query()\
1272 .filter(cls.group_name == group_name)
1276 .filter(cls.group_name == group_name)
1273 if cache:
1277 if cache:
1274 gr = gr.options(FromCache(
1278 gr = gr.options(FromCache(
1275 "sql_cache_short",
1279 "sql_cache_short",
1276 "get_group_%s" % _hash_key(group_name)
1280 "get_group_%s" % _hash_key(group_name)
1277 )
1281 )
1278 )
1282 )
1279 return gr.scalar()
1283 return gr.scalar()
1280
1284
1281 @property
1285 @property
1282 def parents(self):
1286 def parents(self):
1283 parents_recursion_limit = 5
1287 parents_recursion_limit = 5
1284 groups = []
1288 groups = []
1285 if self.parent_group is None:
1289 if self.parent_group is None:
1286 return groups
1290 return groups
1287 cur_gr = self.parent_group
1291 cur_gr = self.parent_group
1288 groups.insert(0, cur_gr)
1292 groups.insert(0, cur_gr)
1289 cnt = 0
1293 cnt = 0
1290 while 1:
1294 while 1:
1291 cnt += 1
1295 cnt += 1
1292 gr = getattr(cur_gr, 'parent_group', None)
1296 gr = getattr(cur_gr, 'parent_group', None)
1293 cur_gr = cur_gr.parent_group
1297 cur_gr = cur_gr.parent_group
1294 if gr is None:
1298 if gr is None:
1295 break
1299 break
1296 if cnt == parents_recursion_limit:
1300 if cnt == parents_recursion_limit:
1297 # this will prevent accidental infinit loops
1301 # this will prevent accidental infinit loops
1298 log.error('group nested more than %s' %
1302 log.error('group nested more than %s' %
1299 parents_recursion_limit)
1303 parents_recursion_limit)
1300 break
1304 break
1301
1305
1302 groups.insert(0, gr)
1306 groups.insert(0, gr)
1303 return groups
1307 return groups
1304
1308
1305 @property
1309 @property
1306 def children(self):
1310 def children(self):
1307 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1311 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1308
1312
1309 @property
1313 @property
1310 def name(self):
1314 def name(self):
1311 return self.group_name.split(RepoGroup.url_sep())[-1]
1315 return self.group_name.split(RepoGroup.url_sep())[-1]
1312
1316
1313 @property
1317 @property
1314 def full_path(self):
1318 def full_path(self):
1315 return self.group_name
1319 return self.group_name
1316
1320
1317 @property
1321 @property
1318 def full_path_splitted(self):
1322 def full_path_splitted(self):
1319 return self.group_name.split(RepoGroup.url_sep())
1323 return self.group_name.split(RepoGroup.url_sep())
1320
1324
1321 @property
1325 @property
1322 def repositories(self):
1326 def repositories(self):
1323 return Repository.query()\
1327 return Repository.query()\
1324 .filter(Repository.group == self)\
1328 .filter(Repository.group == self)\
1325 .order_by(Repository.repo_name)
1329 .order_by(Repository.repo_name)
1326
1330
1327 @property
1331 @property
1328 def repositories_recursive_count(self):
1332 def repositories_recursive_count(self):
1329 cnt = self.repositories.count()
1333 cnt = self.repositories.count()
1330
1334
1331 def children_count(group):
1335 def children_count(group):
1332 cnt = 0
1336 cnt = 0
1333 for child in group.children:
1337 for child in group.children:
1334 cnt += child.repositories.count()
1338 cnt += child.repositories.count()
1335 cnt += children_count(child)
1339 cnt += children_count(child)
1336 return cnt
1340 return cnt
1337
1341
1338 return cnt + children_count(self)
1342 return cnt + children_count(self)
1339
1343
1340 def _recursive_objects(self, include_repos=True):
1344 def _recursive_objects(self, include_repos=True):
1341 all_ = []
1345 all_ = []
1342
1346
1343 def _get_members(root_gr):
1347 def _get_members(root_gr):
1344 if include_repos:
1348 if include_repos:
1345 for r in root_gr.repositories:
1349 for r in root_gr.repositories:
1346 all_.append(r)
1350 all_.append(r)
1347 childs = root_gr.children.all()
1351 childs = root_gr.children.all()
1348 if childs:
1352 if childs:
1349 for gr in childs:
1353 for gr in childs:
1350 all_.append(gr)
1354 all_.append(gr)
1351 _get_members(gr)
1355 _get_members(gr)
1352
1356
1353 _get_members(self)
1357 _get_members(self)
1354 return [self] + all_
1358 return [self] + all_
1355
1359
1356 def recursive_groups_and_repos(self):
1360 def recursive_groups_and_repos(self):
1357 """
1361 """
1358 Recursive return all groups, with repositories in those groups
1362 Recursive return all groups, with repositories in those groups
1359 """
1363 """
1360 return self._recursive_objects()
1364 return self._recursive_objects()
1361
1365
1362 def recursive_groups(self):
1366 def recursive_groups(self):
1363 """
1367 """
1364 Returns all children groups for this group including children of children
1368 Returns all children groups for this group including children of children
1365 """
1369 """
1366 return self._recursive_objects(include_repos=False)
1370 return self._recursive_objects(include_repos=False)
1367
1371
1368 def get_new_name(self, group_name):
1372 def get_new_name(self, group_name):
1369 """
1373 """
1370 returns new full group name based on parent and new name
1374 returns new full group name based on parent and new name
1371
1375
1372 :param group_name:
1376 :param group_name:
1373 """
1377 """
1374 path_prefix = (self.parent_group.full_path_splitted if
1378 path_prefix = (self.parent_group.full_path_splitted if
1375 self.parent_group else [])
1379 self.parent_group else [])
1376 return RepoGroup.url_sep().join(path_prefix + [group_name])
1380 return RepoGroup.url_sep().join(path_prefix + [group_name])
1377
1381
1378
1382
1379 class Permission(Base, BaseModel):
1383 class Permission(Base, BaseModel):
1380 __tablename__ = 'permissions'
1384 __tablename__ = 'permissions'
1381 __table_args__ = (
1385 __table_args__ = (
1382 Index('p_perm_name_idx', 'permission_name'),
1386 Index('p_perm_name_idx', 'permission_name'),
1383 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1387 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1384 'mysql_charset': 'utf8'},
1388 'mysql_charset': 'utf8'},
1385 )
1389 )
1386 PERMS = [
1390 PERMS = [
1387 ('repository.none', _('Repository no access')),
1391 ('repository.none', _('Repository no access')),
1388 ('repository.read', _('Repository read access')),
1392 ('repository.read', _('Repository read access')),
1389 ('repository.write', _('Repository write access')),
1393 ('repository.write', _('Repository write access')),
1390 ('repository.admin', _('Repository admin access')),
1394 ('repository.admin', _('Repository admin access')),
1391
1395
1392 ('group.none', _('Repository group no access')),
1396 ('group.none', _('Repository group no access')),
1393 ('group.read', _('Repository group read access')),
1397 ('group.read', _('Repository group read access')),
1394 ('group.write', _('Repository group write access')),
1398 ('group.write', _('Repository group write access')),
1395 ('group.admin', _('Repository group admin access')),
1399 ('group.admin', _('Repository group admin access')),
1396
1400
1397 ('hg.admin', _('RhodeCode Administrator')),
1401 ('hg.admin', _('RhodeCode Administrator')),
1398 ('hg.create.none', _('Repository creation disabled')),
1402 ('hg.create.none', _('Repository creation disabled')),
1399 ('hg.create.repository', _('Repository creation enabled')),
1403 ('hg.create.repository', _('Repository creation enabled')),
1400 ('hg.fork.none', _('Repository forking disabled')),
1404 ('hg.fork.none', _('Repository forking disabled')),
1401 ('hg.fork.repository', _('Repository forking enabled')),
1405 ('hg.fork.repository', _('Repository forking enabled')),
1402 ('hg.register.none', _('Register disabled')),
1406 ('hg.register.none', _('Register disabled')),
1403 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1407 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1404 'with manual activation')),
1408 'with manual activation')),
1405
1409
1406 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1410 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1407 'with auto activation')),
1411 'with auto activation')),
1408 ]
1412 ]
1409
1413
1410 # defines which permissions are more important higher the more important
1414 # defines which permissions are more important higher the more important
1411 PERM_WEIGHTS = {
1415 PERM_WEIGHTS = {
1412 'repository.none': 0,
1416 'repository.none': 0,
1413 'repository.read': 1,
1417 'repository.read': 1,
1414 'repository.write': 3,
1418 'repository.write': 3,
1415 'repository.admin': 4,
1419 'repository.admin': 4,
1416
1420
1417 'group.none': 0,
1421 'group.none': 0,
1418 'group.read': 1,
1422 'group.read': 1,
1419 'group.write': 3,
1423 'group.write': 3,
1420 'group.admin': 4,
1424 'group.admin': 4,
1421
1425
1422 'hg.fork.none': 0,
1426 'hg.fork.none': 0,
1423 'hg.fork.repository': 1,
1427 'hg.fork.repository': 1,
1424 'hg.create.none': 0,
1428 'hg.create.none': 0,
1425 'hg.create.repository':1
1429 'hg.create.repository':1
1426 }
1430 }
1427
1431
1428 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1432 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1429 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1433 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1430 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1434 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1431
1435
1432 def __unicode__(self):
1436 def __unicode__(self):
1433 return u"<%s('%s:%s')>" % (
1437 return u"<%s('%s:%s')>" % (
1434 self.__class__.__name__, self.permission_id, self.permission_name
1438 self.__class__.__name__, self.permission_id, self.permission_name
1435 )
1439 )
1436
1440
1437 @classmethod
1441 @classmethod
1438 def get_by_key(cls, key):
1442 def get_by_key(cls, key):
1439 return cls.query().filter(cls.permission_name == key).scalar()
1443 return cls.query().filter(cls.permission_name == key).scalar()
1440
1444
1441 @classmethod
1445 @classmethod
1442 def get_default_perms(cls, default_user_id):
1446 def get_default_perms(cls, default_user_id):
1443 q = Session().query(UserRepoToPerm, Repository, cls)\
1447 q = Session().query(UserRepoToPerm, Repository, cls)\
1444 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1448 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1445 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1449 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1446 .filter(UserRepoToPerm.user_id == default_user_id)
1450 .filter(UserRepoToPerm.user_id == default_user_id)
1447
1451
1448 return q.all()
1452 return q.all()
1449
1453
1450 @classmethod
1454 @classmethod
1451 def get_default_group_perms(cls, default_user_id):
1455 def get_default_group_perms(cls, default_user_id):
1452 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1456 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1453 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1457 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1454 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1458 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1455 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1459 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1456
1460
1457 return q.all()
1461 return q.all()
1458
1462
1459
1463
1460 class UserRepoToPerm(Base, BaseModel):
1464 class UserRepoToPerm(Base, BaseModel):
1461 __tablename__ = 'repo_to_perm'
1465 __tablename__ = 'repo_to_perm'
1462 __table_args__ = (
1466 __table_args__ = (
1463 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1467 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1464 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1468 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1465 'mysql_charset': 'utf8'}
1469 'mysql_charset': 'utf8'}
1466 )
1470 )
1467 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1471 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1468 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1472 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1469 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1473 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1470 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1474 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1471
1475
1472 user = relationship('User')
1476 user = relationship('User')
1473 repository = relationship('Repository')
1477 repository = relationship('Repository')
1474 permission = relationship('Permission')
1478 permission = relationship('Permission')
1475
1479
1476 @classmethod
1480 @classmethod
1477 def create(cls, user, repository, permission):
1481 def create(cls, user, repository, permission):
1478 n = cls()
1482 n = cls()
1479 n.user = user
1483 n.user = user
1480 n.repository = repository
1484 n.repository = repository
1481 n.permission = permission
1485 n.permission = permission
1482 Session().add(n)
1486 Session().add(n)
1483 return n
1487 return n
1484
1488
1485 def __unicode__(self):
1489 def __unicode__(self):
1486 return u'<user:%s => %s >' % (self.user, self.repository)
1490 return u'<user:%s => %s >' % (self.user, self.repository)
1487
1491
1488
1492
1489 class UserToPerm(Base, BaseModel):
1493 class UserToPerm(Base, BaseModel):
1490 __tablename__ = 'user_to_perm'
1494 __tablename__ = 'user_to_perm'
1491 __table_args__ = (
1495 __table_args__ = (
1492 UniqueConstraint('user_id', 'permission_id'),
1496 UniqueConstraint('user_id', 'permission_id'),
1493 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1497 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1494 'mysql_charset': 'utf8'}
1498 'mysql_charset': 'utf8'}
1495 )
1499 )
1496 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1500 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1497 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1501 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1498 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1502 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1499
1503
1500 user = relationship('User')
1504 user = relationship('User')
1501 permission = relationship('Permission', lazy='joined')
1505 permission = relationship('Permission', lazy='joined')
1502
1506
1503
1507
1504 class UserGroupRepoToPerm(Base, BaseModel):
1508 class UserGroupRepoToPerm(Base, BaseModel):
1505 __tablename__ = 'users_group_repo_to_perm'
1509 __tablename__ = 'users_group_repo_to_perm'
1506 __table_args__ = (
1510 __table_args__ = (
1507 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1511 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1508 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1512 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1509 'mysql_charset': 'utf8'}
1513 'mysql_charset': 'utf8'}
1510 )
1514 )
1511 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1515 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1512 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1516 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1513 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1517 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1514 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1518 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1515
1519
1516 users_group = relationship('UserGroup')
1520 users_group = relationship('UserGroup')
1517 permission = relationship('Permission')
1521 permission = relationship('Permission')
1518 repository = relationship('Repository')
1522 repository = relationship('Repository')
1519
1523
1520 @classmethod
1524 @classmethod
1521 def create(cls, users_group, repository, permission):
1525 def create(cls, users_group, repository, permission):
1522 n = cls()
1526 n = cls()
1523 n.users_group = users_group
1527 n.users_group = users_group
1524 n.repository = repository
1528 n.repository = repository
1525 n.permission = permission
1529 n.permission = permission
1526 Session().add(n)
1530 Session().add(n)
1527 return n
1531 return n
1528
1532
1529 def __unicode__(self):
1533 def __unicode__(self):
1530 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1534 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1531
1535
1532
1536
1533 class UserGroupToPerm(Base, BaseModel):
1537 class UserGroupToPerm(Base, BaseModel):
1534 __tablename__ = 'users_group_to_perm'
1538 __tablename__ = 'users_group_to_perm'
1535 __table_args__ = (
1539 __table_args__ = (
1536 UniqueConstraint('users_group_id', 'permission_id',),
1540 UniqueConstraint('users_group_id', 'permission_id',),
1537 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1541 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1538 'mysql_charset': 'utf8'}
1542 'mysql_charset': 'utf8'}
1539 )
1543 )
1540 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1544 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1541 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1545 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1542 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1546 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1543
1547
1544 users_group = relationship('UserGroup')
1548 users_group = relationship('UserGroup')
1545 permission = relationship('Permission')
1549 permission = relationship('Permission')
1546
1550
1547
1551
1548 class UserRepoGroupToPerm(Base, BaseModel):
1552 class UserRepoGroupToPerm(Base, BaseModel):
1549 __tablename__ = 'user_repo_group_to_perm'
1553 __tablename__ = 'user_repo_group_to_perm'
1550 __table_args__ = (
1554 __table_args__ = (
1551 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1555 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1552 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1556 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1553 'mysql_charset': 'utf8'}
1557 'mysql_charset': 'utf8'}
1554 )
1558 )
1555
1559
1556 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1560 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1557 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1561 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1558 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1562 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1559 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1563 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1560
1564
1561 user = relationship('User')
1565 user = relationship('User')
1562 group = relationship('RepoGroup')
1566 group = relationship('RepoGroup')
1563 permission = relationship('Permission')
1567 permission = relationship('Permission')
1564
1568
1565
1569
1566 class UserGroupRepoGroupToPerm(Base, BaseModel):
1570 class UserGroupRepoGroupToPerm(Base, BaseModel):
1567 __tablename__ = 'users_group_repo_group_to_perm'
1571 __tablename__ = 'users_group_repo_group_to_perm'
1568 __table_args__ = (
1572 __table_args__ = (
1569 UniqueConstraint('users_group_id', 'group_id'),
1573 UniqueConstraint('users_group_id', 'group_id'),
1570 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1574 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1571 'mysql_charset': 'utf8'}
1575 'mysql_charset': 'utf8'}
1572 )
1576 )
1573
1577
1574 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1578 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1575 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1579 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1576 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1580 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1577 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1581 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1578
1582
1579 users_group = relationship('UserGroup')
1583 users_group = relationship('UserGroup')
1580 permission = relationship('Permission')
1584 permission = relationship('Permission')
1581 group = relationship('RepoGroup')
1585 group = relationship('RepoGroup')
1582
1586
1583
1587
1584 class Statistics(Base, BaseModel):
1588 class Statistics(Base, BaseModel):
1585 __tablename__ = 'statistics'
1589 __tablename__ = 'statistics'
1586 __table_args__ = (
1590 __table_args__ = (
1587 UniqueConstraint('repository_id'),
1591 UniqueConstraint('repository_id'),
1588 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1592 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1589 'mysql_charset': 'utf8'}
1593 'mysql_charset': 'utf8'}
1590 )
1594 )
1591 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1595 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1592 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1596 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1593 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1597 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1594 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1598 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1595 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1599 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1596 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1600 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1597
1601
1598 repository = relationship('Repository', single_parent=True)
1602 repository = relationship('Repository', single_parent=True)
1599
1603
1600
1604
1601 class UserFollowing(Base, BaseModel):
1605 class UserFollowing(Base, BaseModel):
1602 __tablename__ = 'user_followings'
1606 __tablename__ = 'user_followings'
1603 __table_args__ = (
1607 __table_args__ = (
1604 UniqueConstraint('user_id', 'follows_repository_id'),
1608 UniqueConstraint('user_id', 'follows_repository_id'),
1605 UniqueConstraint('user_id', 'follows_user_id'),
1609 UniqueConstraint('user_id', 'follows_user_id'),
1606 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1610 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1607 'mysql_charset': 'utf8'}
1611 'mysql_charset': 'utf8'}
1608 )
1612 )
1609
1613
1610 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1614 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1611 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1615 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1612 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1616 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1613 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1617 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1614 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1618 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1615
1619
1616 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1620 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1617
1621
1618 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1622 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1619 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1623 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1620
1624
1621 @classmethod
1625 @classmethod
1622 def get_repo_followers(cls, repo_id):
1626 def get_repo_followers(cls, repo_id):
1623 return cls.query().filter(cls.follows_repo_id == repo_id)
1627 return cls.query().filter(cls.follows_repo_id == repo_id)
1624
1628
1625
1629
1626 class CacheInvalidation(Base, BaseModel):
1630 class CacheInvalidation(Base, BaseModel):
1627 __tablename__ = 'cache_invalidation'
1631 __tablename__ = 'cache_invalidation'
1628 __table_args__ = (
1632 __table_args__ = (
1629 UniqueConstraint('cache_key'),
1633 UniqueConstraint('cache_key'),
1630 Index('key_idx', 'cache_key'),
1634 Index('key_idx', 'cache_key'),
1631 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1635 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1632 'mysql_charset': 'utf8'},
1636 'mysql_charset': 'utf8'},
1633 )
1637 )
1634 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1638 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1635 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1639 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1636 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1640 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1637 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1641 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1638
1642
1639 def __init__(self, cache_key, cache_args=''):
1643 def __init__(self, cache_key, cache_args=''):
1640 self.cache_key = cache_key
1644 self.cache_key = cache_key
1641 self.cache_args = cache_args
1645 self.cache_args = cache_args
1642 self.cache_active = False
1646 self.cache_active = False
1643
1647
1644 def __unicode__(self):
1648 def __unicode__(self):
1645 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1649 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1646 self.cache_id, self.cache_key)
1650 self.cache_id, self.cache_key)
1647
1651
1648 @property
1652 @property
1649 def prefix(self):
1653 def prefix(self):
1650 _split = self.cache_key.split(self.cache_args, 1)
1654 _split = self.cache_key.split(self.cache_args, 1)
1651 if _split and len(_split) == 2:
1655 if _split and len(_split) == 2:
1652 return _split[0]
1656 return _split[0]
1653 return ''
1657 return ''
1654
1658
1655 @classmethod
1659 @classmethod
1656 def clear_cache(cls):
1660 def clear_cache(cls):
1657 cls.query().delete()
1661 cls.query().delete()
1658
1662
1659 @classmethod
1663 @classmethod
1660 def _get_key(cls, key):
1664 def _get_key(cls, key):
1661 """
1665 """
1662 Wrapper for generating a key, together with a prefix
1666 Wrapper for generating a key, together with a prefix
1663
1667
1664 :param key:
1668 :param key:
1665 """
1669 """
1666 import rhodecode
1670 import rhodecode
1667 prefix = ''
1671 prefix = ''
1668 org_key = key
1672 org_key = key
1669 iid = rhodecode.CONFIG.get('instance_id')
1673 iid = rhodecode.CONFIG.get('instance_id')
1670 if iid:
1674 if iid:
1671 prefix = iid
1675 prefix = iid
1672
1676
1673 return "%s%s" % (prefix, key), prefix, org_key
1677 return "%s%s" % (prefix, key), prefix, org_key
1674
1678
1675 @classmethod
1679 @classmethod
1676 def get_by_key(cls, key):
1680 def get_by_key(cls, key):
1677 return cls.query().filter(cls.cache_key == key).scalar()
1681 return cls.query().filter(cls.cache_key == key).scalar()
1678
1682
1679 @classmethod
1683 @classmethod
1680 def get_by_repo_name(cls, repo_name):
1684 def get_by_repo_name(cls, repo_name):
1681 return cls.query().filter(cls.cache_args == repo_name).all()
1685 return cls.query().filter(cls.cache_args == repo_name).all()
1682
1686
1683 @classmethod
1687 @classmethod
1684 def _get_or_create_key(cls, key, repo_name, commit=True):
1688 def _get_or_create_key(cls, key, repo_name, commit=True):
1685 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1689 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1686 if not inv_obj:
1690 if not inv_obj:
1687 try:
1691 try:
1688 inv_obj = CacheInvalidation(key, repo_name)
1692 inv_obj = CacheInvalidation(key, repo_name)
1689 Session().add(inv_obj)
1693 Session().add(inv_obj)
1690 if commit:
1694 if commit:
1691 Session().commit()
1695 Session().commit()
1692 except Exception:
1696 except Exception:
1693 log.error(traceback.format_exc())
1697 log.error(traceback.format_exc())
1694 Session().rollback()
1698 Session().rollback()
1695 return inv_obj
1699 return inv_obj
1696
1700
1697 @classmethod
1701 @classmethod
1698 def invalidate(cls, key):
1702 def invalidate(cls, key):
1699 """
1703 """
1700 Returns Invalidation object if this given key should be invalidated
1704 Returns Invalidation object if this given key should be invalidated
1701 None otherwise. `cache_active = False` means that this cache
1705 None otherwise. `cache_active = False` means that this cache
1702 state is not valid and needs to be invalidated
1706 state is not valid and needs to be invalidated
1703
1707
1704 :param key:
1708 :param key:
1705 """
1709 """
1706 repo_name = key
1710 repo_name = key
1707 repo_name = remove_suffix(repo_name, '_README')
1711 repo_name = remove_suffix(repo_name, '_README')
1708 repo_name = remove_suffix(repo_name, '_RSS')
1712 repo_name = remove_suffix(repo_name, '_RSS')
1709 repo_name = remove_suffix(repo_name, '_ATOM')
1713 repo_name = remove_suffix(repo_name, '_ATOM')
1710
1714
1711 # adds instance prefix
1715 # adds instance prefix
1712 key, _prefix, _org_key = cls._get_key(key)
1716 key, _prefix, _org_key = cls._get_key(key)
1713 inv = cls._get_or_create_key(key, repo_name)
1717 inv = cls._get_or_create_key(key, repo_name)
1714
1718
1715 if inv and inv.cache_active is False:
1719 if inv and inv.cache_active is False:
1716 return inv
1720 return inv
1717
1721
1718 @classmethod
1722 @classmethod
1719 def set_invalidate(cls, key=None, repo_name=None):
1723 def set_invalidate(cls, key=None, repo_name=None):
1720 """
1724 """
1721 Mark this Cache key for invalidation, either by key or whole
1725 Mark this Cache key for invalidation, either by key or whole
1722 cache sets based on repo_name
1726 cache sets based on repo_name
1723
1727
1724 :param key:
1728 :param key:
1725 """
1729 """
1726 invalidated_keys = []
1730 invalidated_keys = []
1727 if key:
1731 if key:
1728 key, _prefix, _org_key = cls._get_key(key)
1732 key, _prefix, _org_key = cls._get_key(key)
1729 inv_objs = Session().query(cls).filter(cls.cache_key == key).all()
1733 inv_objs = Session().query(cls).filter(cls.cache_key == key).all()
1730 elif repo_name:
1734 elif repo_name:
1731 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1735 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1732
1736
1733 try:
1737 try:
1734 for inv_obj in inv_objs:
1738 for inv_obj in inv_objs:
1735 inv_obj.cache_active = False
1739 inv_obj.cache_active = False
1736 log.debug('marking %s key for invalidation based on key=%s,repo_name=%s'
1740 log.debug('marking %s key for invalidation based on key=%s,repo_name=%s'
1737 % (inv_obj, key, safe_str(repo_name)))
1741 % (inv_obj, key, safe_str(repo_name)))
1738 invalidated_keys.append(inv_obj.cache_key)
1742 invalidated_keys.append(inv_obj.cache_key)
1739 Session().add(inv_obj)
1743 Session().add(inv_obj)
1740 Session().commit()
1744 Session().commit()
1741 except Exception:
1745 except Exception:
1742 log.error(traceback.format_exc())
1746 log.error(traceback.format_exc())
1743 Session().rollback()
1747 Session().rollback()
1744 return invalidated_keys
1748 return invalidated_keys
1745
1749
1746 @classmethod
1750 @classmethod
1747 def set_valid(cls, key):
1751 def set_valid(cls, key):
1748 """
1752 """
1749 Mark this cache key as active and currently cached
1753 Mark this cache key as active and currently cached
1750
1754
1751 :param key:
1755 :param key:
1752 """
1756 """
1753 inv_obj = cls.get_by_key(key)
1757 inv_obj = cls.get_by_key(key)
1754 inv_obj.cache_active = True
1758 inv_obj.cache_active = True
1755 Session().add(inv_obj)
1759 Session().add(inv_obj)
1756 Session().commit()
1760 Session().commit()
1757
1761
1758 @classmethod
1762 @classmethod
1759 def get_cache_map(cls):
1763 def get_cache_map(cls):
1760
1764
1761 class cachemapdict(dict):
1765 class cachemapdict(dict):
1762
1766
1763 def __init__(self, *args, **kwargs):
1767 def __init__(self, *args, **kwargs):
1764 fixkey = kwargs.get('fixkey')
1768 fixkey = kwargs.get('fixkey')
1765 if fixkey:
1769 if fixkey:
1766 del kwargs['fixkey']
1770 del kwargs['fixkey']
1767 self.fixkey = fixkey
1771 self.fixkey = fixkey
1768 super(cachemapdict, self).__init__(*args, **kwargs)
1772 super(cachemapdict, self).__init__(*args, **kwargs)
1769
1773
1770 def __getattr__(self, name):
1774 def __getattr__(self, name):
1771 key = name
1775 key = name
1772 if self.fixkey:
1776 if self.fixkey:
1773 key, _prefix, _org_key = cls._get_key(key)
1777 key, _prefix, _org_key = cls._get_key(key)
1774 if key in self.__dict__:
1778 if key in self.__dict__:
1775 return self.__dict__[key]
1779 return self.__dict__[key]
1776 else:
1780 else:
1777 return self[key]
1781 return self[key]
1778
1782
1779 def __getitem__(self, key):
1783 def __getitem__(self, key):
1780 if self.fixkey:
1784 if self.fixkey:
1781 key, _prefix, _org_key = cls._get_key(key)
1785 key, _prefix, _org_key = cls._get_key(key)
1782 try:
1786 try:
1783 return super(cachemapdict, self).__getitem__(key)
1787 return super(cachemapdict, self).__getitem__(key)
1784 except KeyError:
1788 except KeyError:
1785 return
1789 return
1786
1790
1787 cache_map = cachemapdict(fixkey=True)
1791 cache_map = cachemapdict(fixkey=True)
1788 for obj in cls.query().all():
1792 for obj in cls.query().all():
1789 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1793 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1790 return cache_map
1794 return cache_map
1791
1795
1792
1796
1793 class ChangesetComment(Base, BaseModel):
1797 class ChangesetComment(Base, BaseModel):
1794 __tablename__ = 'changeset_comments'
1798 __tablename__ = 'changeset_comments'
1795 __table_args__ = (
1799 __table_args__ = (
1796 Index('cc_revision_idx', 'revision'),
1800 Index('cc_revision_idx', 'revision'),
1797 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1801 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1798 'mysql_charset': 'utf8'},
1802 'mysql_charset': 'utf8'},
1799 )
1803 )
1800 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1804 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1801 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1805 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1802 revision = Column('revision', String(40), nullable=True)
1806 revision = Column('revision', String(40), nullable=True)
1803 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1807 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1804 line_no = Column('line_no', Unicode(10), nullable=True)
1808 line_no = Column('line_no', Unicode(10), nullable=True)
1805 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1809 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1806 f_path = Column('f_path', Unicode(1000), nullable=True)
1810 f_path = Column('f_path', Unicode(1000), nullable=True)
1807 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1811 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1808 text = Column('text', UnicodeText(25000), nullable=False)
1812 text = Column('text', UnicodeText(25000), nullable=False)
1809 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1813 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1810 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1814 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1811
1815
1812 author = relationship('User', lazy='joined')
1816 author = relationship('User', lazy='joined')
1813 repo = relationship('Repository')
1817 repo = relationship('Repository')
1814 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1818 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1815 pull_request = relationship('PullRequest', lazy='joined')
1819 pull_request = relationship('PullRequest', lazy='joined')
1816
1820
1817 @classmethod
1821 @classmethod
1818 def get_users(cls, revision=None, pull_request_id=None):
1822 def get_users(cls, revision=None, pull_request_id=None):
1819 """
1823 """
1820 Returns user associated with this ChangesetComment. ie those
1824 Returns user associated with this ChangesetComment. ie those
1821 who actually commented
1825 who actually commented
1822
1826
1823 :param cls:
1827 :param cls:
1824 :param revision:
1828 :param revision:
1825 """
1829 """
1826 q = Session().query(User)\
1830 q = Session().query(User)\
1827 .join(ChangesetComment.author)
1831 .join(ChangesetComment.author)
1828 if revision:
1832 if revision:
1829 q = q.filter(cls.revision == revision)
1833 q = q.filter(cls.revision == revision)
1830 elif pull_request_id:
1834 elif pull_request_id:
1831 q = q.filter(cls.pull_request_id == pull_request_id)
1835 q = q.filter(cls.pull_request_id == pull_request_id)
1832 return q.all()
1836 return q.all()
1833
1837
1834
1838
1835 class ChangesetStatus(Base, BaseModel):
1839 class ChangesetStatus(Base, BaseModel):
1836 __tablename__ = 'changeset_statuses'
1840 __tablename__ = 'changeset_statuses'
1837 __table_args__ = (
1841 __table_args__ = (
1838 Index('cs_revision_idx', 'revision'),
1842 Index('cs_revision_idx', 'revision'),
1839 Index('cs_version_idx', 'version'),
1843 Index('cs_version_idx', 'version'),
1840 UniqueConstraint('repo_id', 'revision', 'version'),
1844 UniqueConstraint('repo_id', 'revision', 'version'),
1841 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1845 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1842 'mysql_charset': 'utf8'}
1846 'mysql_charset': 'utf8'}
1843 )
1847 )
1844 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1848 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1845 STATUS_APPROVED = 'approved'
1849 STATUS_APPROVED = 'approved'
1846 STATUS_REJECTED = 'rejected'
1850 STATUS_REJECTED = 'rejected'
1847 STATUS_UNDER_REVIEW = 'under_review'
1851 STATUS_UNDER_REVIEW = 'under_review'
1848
1852
1849 STATUSES = [
1853 STATUSES = [
1850 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1854 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1851 (STATUS_APPROVED, _("Approved")),
1855 (STATUS_APPROVED, _("Approved")),
1852 (STATUS_REJECTED, _("Rejected")),
1856 (STATUS_REJECTED, _("Rejected")),
1853 (STATUS_UNDER_REVIEW, _("Under Review")),
1857 (STATUS_UNDER_REVIEW, _("Under Review")),
1854 ]
1858 ]
1855
1859
1856 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1860 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1857 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1861 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1858 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1862 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1859 revision = Column('revision', String(40), nullable=False)
1863 revision = Column('revision', String(40), nullable=False)
1860 status = Column('status', String(128), nullable=False, default=DEFAULT)
1864 status = Column('status', String(128), nullable=False, default=DEFAULT)
1861 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1865 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1862 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1866 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1863 version = Column('version', Integer(), nullable=False, default=0)
1867 version = Column('version', Integer(), nullable=False, default=0)
1864 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1868 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1865
1869
1866 author = relationship('User', lazy='joined')
1870 author = relationship('User', lazy='joined')
1867 repo = relationship('Repository')
1871 repo = relationship('Repository')
1868 comment = relationship('ChangesetComment', lazy='joined')
1872 comment = relationship('ChangesetComment', lazy='joined')
1869 pull_request = relationship('PullRequest', lazy='joined')
1873 pull_request = relationship('PullRequest', lazy='joined')
1870
1874
1871 def __unicode__(self):
1875 def __unicode__(self):
1872 return u"<%s('%s:%s')>" % (
1876 return u"<%s('%s:%s')>" % (
1873 self.__class__.__name__,
1877 self.__class__.__name__,
1874 self.status, self.author
1878 self.status, self.author
1875 )
1879 )
1876
1880
1877 @classmethod
1881 @classmethod
1878 def get_status_lbl(cls, value):
1882 def get_status_lbl(cls, value):
1879 return dict(cls.STATUSES).get(value)
1883 return dict(cls.STATUSES).get(value)
1880
1884
1881 @property
1885 @property
1882 def status_lbl(self):
1886 def status_lbl(self):
1883 return ChangesetStatus.get_status_lbl(self.status)
1887 return ChangesetStatus.get_status_lbl(self.status)
1884
1888
1885
1889
1886 class PullRequest(Base, BaseModel):
1890 class PullRequest(Base, BaseModel):
1887 __tablename__ = 'pull_requests'
1891 __tablename__ = 'pull_requests'
1888 __table_args__ = (
1892 __table_args__ = (
1889 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1893 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1890 'mysql_charset': 'utf8'},
1894 'mysql_charset': 'utf8'},
1891 )
1895 )
1892
1896
1893 STATUS_NEW = u'new'
1897 STATUS_NEW = u'new'
1894 STATUS_OPEN = u'open'
1898 STATUS_OPEN = u'open'
1895 STATUS_CLOSED = u'closed'
1899 STATUS_CLOSED = u'closed'
1896
1900
1897 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1901 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1898 title = Column('title', Unicode(256), nullable=True)
1902 title = Column('title', Unicode(256), nullable=True)
1899 description = Column('description', UnicodeText(10240), nullable=True)
1903 description = Column('description', UnicodeText(10240), nullable=True)
1900 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1904 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1901 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1905 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1902 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1906 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1903 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1907 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1904 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1908 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1905 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1909 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1906 org_ref = Column('org_ref', Unicode(256), nullable=False)
1910 org_ref = Column('org_ref', Unicode(256), nullable=False)
1907 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1911 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1908 other_ref = Column('other_ref', Unicode(256), nullable=False)
1912 other_ref = Column('other_ref', Unicode(256), nullable=False)
1909
1913
1910 @hybrid_property
1914 @hybrid_property
1911 def revisions(self):
1915 def revisions(self):
1912 return self._revisions.split(':')
1916 return self._revisions.split(':')
1913
1917
1914 @revisions.setter
1918 @revisions.setter
1915 def revisions(self, val):
1919 def revisions(self, val):
1916 self._revisions = ':'.join(val)
1920 self._revisions = ':'.join(val)
1917
1921
1918 @property
1922 @property
1919 def org_ref_parts(self):
1923 def org_ref_parts(self):
1920 return self.org_ref.split(':')
1924 return self.org_ref.split(':')
1921
1925
1922 @property
1926 @property
1923 def other_ref_parts(self):
1927 def other_ref_parts(self):
1924 return self.other_ref.split(':')
1928 return self.other_ref.split(':')
1925
1929
1926 author = relationship('User', lazy='joined')
1930 author = relationship('User', lazy='joined')
1927 reviewers = relationship('PullRequestReviewers',
1931 reviewers = relationship('PullRequestReviewers',
1928 cascade="all, delete, delete-orphan")
1932 cascade="all, delete, delete-orphan")
1929 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1933 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1930 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1934 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1931 statuses = relationship('ChangesetStatus')
1935 statuses = relationship('ChangesetStatus')
1932 comments = relationship('ChangesetComment',
1936 comments = relationship('ChangesetComment',
1933 cascade="all, delete, delete-orphan")
1937 cascade="all, delete, delete-orphan")
1934
1938
1935 def is_closed(self):
1939 def is_closed(self):
1936 return self.status == self.STATUS_CLOSED
1940 return self.status == self.STATUS_CLOSED
1937
1941
1938 @property
1942 @property
1939 def last_review_status(self):
1943 def last_review_status(self):
1940 return self.statuses[-1].status if self.statuses else ''
1944 return self.statuses[-1].status if self.statuses else ''
1941
1945
1942 def __json__(self):
1946 def __json__(self):
1943 return dict(
1947 return dict(
1944 revisions=self.revisions
1948 revisions=self.revisions
1945 )
1949 )
1946
1950
1947
1951
1948 class PullRequestReviewers(Base, BaseModel):
1952 class PullRequestReviewers(Base, BaseModel):
1949 __tablename__ = 'pull_request_reviewers'
1953 __tablename__ = 'pull_request_reviewers'
1950 __table_args__ = (
1954 __table_args__ = (
1951 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1955 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1952 'mysql_charset': 'utf8'},
1956 'mysql_charset': 'utf8'},
1953 )
1957 )
1954
1958
1955 def __init__(self, user=None, pull_request=None):
1959 def __init__(self, user=None, pull_request=None):
1956 self.user = user
1960 self.user = user
1957 self.pull_request = pull_request
1961 self.pull_request = pull_request
1958
1962
1959 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1963 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1960 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1964 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1961 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1965 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1962
1966
1963 user = relationship('User')
1967 user = relationship('User')
1964 pull_request = relationship('PullRequest')
1968 pull_request = relationship('PullRequest')
1965
1969
1966
1970
1967 class Notification(Base, BaseModel):
1971 class Notification(Base, BaseModel):
1968 __tablename__ = 'notifications'
1972 __tablename__ = 'notifications'
1969 __table_args__ = (
1973 __table_args__ = (
1970 Index('notification_type_idx', 'type'),
1974 Index('notification_type_idx', 'type'),
1971 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1975 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1972 'mysql_charset': 'utf8'},
1976 'mysql_charset': 'utf8'},
1973 )
1977 )
1974
1978
1975 TYPE_CHANGESET_COMMENT = u'cs_comment'
1979 TYPE_CHANGESET_COMMENT = u'cs_comment'
1976 TYPE_MESSAGE = u'message'
1980 TYPE_MESSAGE = u'message'
1977 TYPE_MENTION = u'mention'
1981 TYPE_MENTION = u'mention'
1978 TYPE_REGISTRATION = u'registration'
1982 TYPE_REGISTRATION = u'registration'
1979 TYPE_PULL_REQUEST = u'pull_request'
1983 TYPE_PULL_REQUEST = u'pull_request'
1980 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1984 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1981
1985
1982 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1986 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1983 subject = Column('subject', Unicode(512), nullable=True)
1987 subject = Column('subject', Unicode(512), nullable=True)
1984 body = Column('body', UnicodeText(50000), nullable=True)
1988 body = Column('body', UnicodeText(50000), nullable=True)
1985 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1989 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1986 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1990 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1987 type_ = Column('type', Unicode(256))
1991 type_ = Column('type', Unicode(256))
1988
1992
1989 created_by_user = relationship('User')
1993 created_by_user = relationship('User')
1990 notifications_to_users = relationship('UserNotification', lazy='joined',
1994 notifications_to_users = relationship('UserNotification', lazy='joined',
1991 cascade="all, delete, delete-orphan")
1995 cascade="all, delete, delete-orphan")
1992
1996
1993 @property
1997 @property
1994 def recipients(self):
1998 def recipients(self):
1995 return [x.user for x in UserNotification.query()\
1999 return [x.user for x in UserNotification.query()\
1996 .filter(UserNotification.notification == self)\
2000 .filter(UserNotification.notification == self)\
1997 .order_by(UserNotification.user_id.asc()).all()]
2001 .order_by(UserNotification.user_id.asc()).all()]
1998
2002
1999 @classmethod
2003 @classmethod
2000 def create(cls, created_by, subject, body, recipients, type_=None):
2004 def create(cls, created_by, subject, body, recipients, type_=None):
2001 if type_ is None:
2005 if type_ is None:
2002 type_ = Notification.TYPE_MESSAGE
2006 type_ = Notification.TYPE_MESSAGE
2003
2007
2004 notification = cls()
2008 notification = cls()
2005 notification.created_by_user = created_by
2009 notification.created_by_user = created_by
2006 notification.subject = subject
2010 notification.subject = subject
2007 notification.body = body
2011 notification.body = body
2008 notification.type_ = type_
2012 notification.type_ = type_
2009 notification.created_on = datetime.datetime.now()
2013 notification.created_on = datetime.datetime.now()
2010
2014
2011 for u in recipients:
2015 for u in recipients:
2012 assoc = UserNotification()
2016 assoc = UserNotification()
2013 assoc.notification = notification
2017 assoc.notification = notification
2014 u.notifications.append(assoc)
2018 u.notifications.append(assoc)
2015 Session().add(notification)
2019 Session().add(notification)
2016 return notification
2020 return notification
2017
2021
2018 @property
2022 @property
2019 def description(self):
2023 def description(self):
2020 from rhodecode.model.notification import NotificationModel
2024 from rhodecode.model.notification import NotificationModel
2021 return NotificationModel().make_description(self)
2025 return NotificationModel().make_description(self)
2022
2026
2023
2027
2024 class UserNotification(Base, BaseModel):
2028 class UserNotification(Base, BaseModel):
2025 __tablename__ = 'user_to_notification'
2029 __tablename__ = 'user_to_notification'
2026 __table_args__ = (
2030 __table_args__ = (
2027 UniqueConstraint('user_id', 'notification_id'),
2031 UniqueConstraint('user_id', 'notification_id'),
2028 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2032 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2029 'mysql_charset': 'utf8'}
2033 'mysql_charset': 'utf8'}
2030 )
2034 )
2031 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
2035 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
2032 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
2036 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
2033 read = Column('read', Boolean, default=False)
2037 read = Column('read', Boolean, default=False)
2034 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
2038 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
2035
2039
2036 user = relationship('User', lazy="joined")
2040 user = relationship('User', lazy="joined")
2037 notification = relationship('Notification', lazy="joined",
2041 notification = relationship('Notification', lazy="joined",
2038 order_by=lambda: Notification.created_on.desc(),)
2042 order_by=lambda: Notification.created_on.desc(),)
2039
2043
2040 def mark_as_read(self):
2044 def mark_as_read(self):
2041 self.read = True
2045 self.read = True
2042 Session().add(self)
2046 Session().add(self)
2043
2047
2044
2048
2045 class DbMigrateVersion(Base, BaseModel):
2049 class DbMigrateVersion(Base, BaseModel):
2046 __tablename__ = 'db_migrate_version'
2050 __tablename__ = 'db_migrate_version'
2047 __table_args__ = (
2051 __table_args__ = (
2048 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2052 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2049 'mysql_charset': 'utf8'},
2053 'mysql_charset': 'utf8'},
2050 )
2054 )
2051 repository_id = Column('repository_id', String(250), primary_key=True)
2055 repository_id = Column('repository_id', String(250), primary_key=True)
2052 repository_path = Column('repository_path', Text)
2056 repository_path = Column('repository_path', Text)
2053 version = Column('version', Integer)
2057 version = Column('version', Integer)
@@ -1,1306 +1,1331 b''
1 from __future__ import with_statement
1 from __future__ import with_statement
2 import random
2 import random
3 import mock
3 import mock
4
4
5 from rhodecode.tests import *
5 from rhodecode.tests import *
6 from rhodecode.lib.compat import json
6 from rhodecode.lib.compat import json
7 from rhodecode.lib.auth import AuthUser
7 from rhodecode.lib.auth import AuthUser
8 from rhodecode.model.user import UserModel
8 from rhodecode.model.user import UserModel
9 from rhodecode.model.users_group import UserGroupModel
9 from rhodecode.model.users_group import UserGroupModel
10 from rhodecode.model.repo import RepoModel
10 from rhodecode.model.repo import RepoModel
11 from rhodecode.model.meta import Session
11 from rhodecode.model.meta import Session
12 from rhodecode.model.scm import ScmModel
12 from rhodecode.model.scm import ScmModel
13 from rhodecode.model.db import Repository
13 from rhodecode.model.db import Repository
14
14
15 API_URL = '/_admin/api'
15 API_URL = '/_admin/api'
16
16
17
17
18 def _build_data(apikey, method, **kw):
18 def _build_data(apikey, method, **kw):
19 """
19 """
20 Builds API data with given random ID
20 Builds API data with given random ID
21
21
22 :param random_id:
22 :param random_id:
23 :type random_id:
23 :type random_id:
24 """
24 """
25 random_id = random.randrange(1, 9999)
25 random_id = random.randrange(1, 9999)
26 return random_id, json.dumps({
26 return random_id, json.dumps({
27 "id": random_id,
27 "id": random_id,
28 "api_key": apikey,
28 "api_key": apikey,
29 "method": method,
29 "method": method,
30 "args": kw
30 "args": kw
31 })
31 })
32
32
33 jsonify = lambda obj: json.loads(json.dumps(obj))
33 jsonify = lambda obj: json.loads(json.dumps(obj))
34
34
35
35
36 def crash(*args, **kwargs):
36 def crash(*args, **kwargs):
37 raise Exception('Total Crash !')
37 raise Exception('Total Crash !')
38
38
39
39
40 def api_call(test_obj, params):
40 def api_call(test_obj, params):
41 response = test_obj.app.post(API_URL, content_type='application/json',
41 response = test_obj.app.post(API_URL, content_type='application/json',
42 params=params)
42 params=params)
43 return response
43 return response
44
44
45
45
46 TEST_USER_GROUP = 'test_users_group'
46 TEST_USER_GROUP = 'test_users_group'
47
47
48
48
49 def make_users_group(name=TEST_USER_GROUP):
49 def make_users_group(name=TEST_USER_GROUP):
50 gr = UserGroupModel().create(name=name)
50 gr = UserGroupModel().create(name=name)
51 UserGroupModel().add_user_to_group(users_group=gr,
51 UserGroupModel().add_user_to_group(users_group=gr,
52 user=TEST_USER_ADMIN_LOGIN)
52 user=TEST_USER_ADMIN_LOGIN)
53 Session().commit()
53 Session().commit()
54 return gr
54 return gr
55
55
56
56
57 def destroy_users_group(name=TEST_USER_GROUP):
57 def destroy_users_group(name=TEST_USER_GROUP):
58 UserGroupModel().delete(users_group=name, force=True)
58 UserGroupModel().delete(users_group=name, force=True)
59 Session().commit()
59 Session().commit()
60
60
61
61
62 def create_repo(repo_name, repo_type, owner=None):
62 def create_repo(repo_name, repo_type, owner=None):
63 # create new repo
63 # create new repo
64 form_data = _get_repo_create_params(
64 form_data = _get_repo_create_params(
65 repo_name_full=repo_name,
65 repo_name_full=repo_name,
66 repo_description='description %s' % repo_name,
66 repo_description='description %s' % repo_name,
67 )
67 )
68 cur_user = UserModel().get_by_username(owner or TEST_USER_ADMIN_LOGIN)
68 cur_user = UserModel().get_by_username(owner or TEST_USER_ADMIN_LOGIN)
69 r = RepoModel().create(form_data, cur_user)
69 r = RepoModel().create(form_data, cur_user)
70 Session().commit()
70 Session().commit()
71 return r
71 return r
72
72
73
73
74 def create_fork(fork_name, fork_type, fork_of):
74 def create_fork(fork_name, fork_type, fork_of):
75 fork = RepoModel(Session())._get_repo(fork_of)
75 fork = RepoModel(Session())._get_repo(fork_of)
76 r = create_repo(fork_name, fork_type)
76 r = create_repo(fork_name, fork_type)
77 r.fork = fork
77 r.fork = fork
78 Session().add(r)
78 Session().add(r)
79 Session().commit()
79 Session().commit()
80 return r
80 return r
81
81
82
82
83 def destroy_repo(repo_name):
83 def destroy_repo(repo_name):
84 RepoModel().delete(repo_name)
84 RepoModel().delete(repo_name)
85 Session().commit()
85 Session().commit()
86
86
87
87
88 class BaseTestApi(object):
88 class BaseTestApi(object):
89 REPO = None
89 REPO = None
90 REPO_TYPE = None
90 REPO_TYPE = None
91
91
92 @classmethod
92 @classmethod
93 def setUpClass(self):
93 def setUpClass(self):
94 self.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
94 self.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
95 self.apikey = self.usr.api_key
95 self.apikey = self.usr.api_key
96 self.test_user = UserModel().create_or_update(
96 self.test_user = UserModel().create_or_update(
97 username='test-api',
97 username='test-api',
98 password='test',
98 password='test',
99 email='test@api.rhodecode.org',
99 email='test@api.rhodecode.org',
100 firstname='first',
100 firstname='first',
101 lastname='last'
101 lastname='last'
102 )
102 )
103 Session().commit()
103 Session().commit()
104 self.TEST_USER_LOGIN = self.test_user.username
104 self.TEST_USER_LOGIN = self.test_user.username
105 self.apikey_regular = self.test_user.api_key
105 self.apikey_regular = self.test_user.api_key
106
106
107 @classmethod
107 @classmethod
108 def teardownClass(self):
108 def teardownClass(self):
109 pass
109 pass
110
110
111 def setUp(self):
111 def setUp(self):
112 self.maxDiff = None
112 self.maxDiff = None
113 make_users_group()
113 make_users_group()
114
114
115 def tearDown(self):
115 def tearDown(self):
116 destroy_users_group()
116 destroy_users_group()
117
117
118 def _compare_ok(self, id_, expected, given):
118 def _compare_ok(self, id_, expected, given):
119 expected = jsonify({
119 expected = jsonify({
120 'id': id_,
120 'id': id_,
121 'error': None,
121 'error': None,
122 'result': expected
122 'result': expected
123 })
123 })
124 given = json.loads(given)
124 given = json.loads(given)
125 self.assertEqual(expected, given)
125 self.assertEqual(expected, given)
126
126
127 def _compare_error(self, id_, expected, given):
127 def _compare_error(self, id_, expected, given):
128 expected = jsonify({
128 expected = jsonify({
129 'id': id_,
129 'id': id_,
130 'error': expected,
130 'error': expected,
131 'result': None
131 'result': None
132 })
132 })
133 given = json.loads(given)
133 given = json.loads(given)
134 self.assertEqual(expected, given)
134 self.assertEqual(expected, given)
135
135
136 # def test_Optional(self):
136 # def test_Optional(self):
137 # from rhodecode.controllers.api.api import Optional
137 # from rhodecode.controllers.api.api import Optional
138 # option1 = Optional(None)
138 # option1 = Optional(None)
139 # self.assertEqual('<Optional:%s>' % None, repr(option1))
139 # self.assertEqual('<Optional:%s>' % None, repr(option1))
140 #
140 #
141 # self.assertEqual(1, Optional.extract(Optional(1)))
141 # self.assertEqual(1, Optional.extract(Optional(1)))
142 # self.assertEqual('trololo', Optional.extract('trololo'))
142 # self.assertEqual('trololo', Optional.extract('trololo'))
143
143
144 def test_api_wrong_key(self):
144 def test_api_wrong_key(self):
145 id_, params = _build_data('trololo', 'get_user')
145 id_, params = _build_data('trololo', 'get_user')
146 response = api_call(self, params)
146 response = api_call(self, params)
147
147
148 expected = 'Invalid API KEY'
148 expected = 'Invalid API KEY'
149 self._compare_error(id_, expected, given=response.body)
149 self._compare_error(id_, expected, given=response.body)
150
150
151 def test_api_missing_non_optional_param(self):
151 def test_api_missing_non_optional_param(self):
152 id_, params = _build_data(self.apikey, 'get_repo')
152 id_, params = _build_data(self.apikey, 'get_repo')
153 response = api_call(self, params)
153 response = api_call(self, params)
154
154
155 expected = 'Missing non optional `repoid` arg in JSON DATA'
155 expected = 'Missing non optional `repoid` arg in JSON DATA'
156 self._compare_error(id_, expected, given=response.body)
156 self._compare_error(id_, expected, given=response.body)
157
157
158 def test_api_missing_non_optional_param_args_null(self):
158 def test_api_missing_non_optional_param_args_null(self):
159 id_, params = _build_data(self.apikey, 'get_repo')
159 id_, params = _build_data(self.apikey, 'get_repo')
160 params = params.replace('"args": {}', '"args": null')
160 params = params.replace('"args": {}', '"args": null')
161 response = api_call(self, params)
161 response = api_call(self, params)
162
162
163 expected = 'Missing non optional `repoid` arg in JSON DATA'
163 expected = 'Missing non optional `repoid` arg in JSON DATA'
164 self._compare_error(id_, expected, given=response.body)
164 self._compare_error(id_, expected, given=response.body)
165
165
166 def test_api_missing_non_optional_param_args_bad(self):
166 def test_api_missing_non_optional_param_args_bad(self):
167 id_, params = _build_data(self.apikey, 'get_repo')
167 id_, params = _build_data(self.apikey, 'get_repo')
168 params = params.replace('"args": {}', '"args": 1')
168 params = params.replace('"args": {}', '"args": 1')
169 response = api_call(self, params)
169 response = api_call(self, params)
170
170
171 expected = 'Missing non optional `repoid` arg in JSON DATA'
171 expected = 'Missing non optional `repoid` arg in JSON DATA'
172 self._compare_error(id_, expected, given=response.body)
172 self._compare_error(id_, expected, given=response.body)
173
173
174 def test_api_args_is_null(self):
174 def test_api_args_is_null(self):
175 id_, params = _build_data(self.apikey, 'get_users',)
175 id_, params = _build_data(self.apikey, 'get_users',)
176 params = params.replace('"args": {}', '"args": null')
176 params = params.replace('"args": {}', '"args": null')
177 response = api_call(self, params)
177 response = api_call(self, params)
178 self.assertEqual(response.status, '200 OK')
178 self.assertEqual(response.status, '200 OK')
179
179
180 def test_api_args_is_bad(self):
180 def test_api_args_is_bad(self):
181 id_, params = _build_data(self.apikey, 'get_users',)
181 id_, params = _build_data(self.apikey, 'get_users',)
182 params = params.replace('"args": {}', '"args": 1')
182 params = params.replace('"args": {}', '"args": 1')
183 response = api_call(self, params)
183 response = api_call(self, params)
184 self.assertEqual(response.status, '200 OK')
184 self.assertEqual(response.status, '200 OK')
185
185
186 def test_api_get_users(self):
186 def test_api_get_users(self):
187 id_, params = _build_data(self.apikey, 'get_users',)
187 id_, params = _build_data(self.apikey, 'get_users',)
188 response = api_call(self, params)
188 response = api_call(self, params)
189 ret_all = []
189 ret_all = []
190 for usr in UserModel().get_all():
190 for usr in UserModel().get_all():
191 ret = usr.get_api_data()
191 ret = usr.get_api_data()
192 ret_all.append(jsonify(ret))
192 ret_all.append(jsonify(ret))
193 expected = ret_all
193 expected = ret_all
194 self._compare_ok(id_, expected, given=response.body)
194 self._compare_ok(id_, expected, given=response.body)
195
195
196 def test_api_get_user(self):
196 def test_api_get_user(self):
197 id_, params = _build_data(self.apikey, 'get_user',
197 id_, params = _build_data(self.apikey, 'get_user',
198 userid=TEST_USER_ADMIN_LOGIN)
198 userid=TEST_USER_ADMIN_LOGIN)
199 response = api_call(self, params)
199 response = api_call(self, params)
200
200
201 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
201 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
202 ret = usr.get_api_data()
202 ret = usr.get_api_data()
203 ret['permissions'] = AuthUser(usr.user_id).permissions
203 ret['permissions'] = AuthUser(usr.user_id).permissions
204
204
205 expected = ret
205 expected = ret
206 self._compare_ok(id_, expected, given=response.body)
206 self._compare_ok(id_, expected, given=response.body)
207
207
208 def test_api_get_user_that_does_not_exist(self):
208 def test_api_get_user_that_does_not_exist(self):
209 id_, params = _build_data(self.apikey, 'get_user',
209 id_, params = _build_data(self.apikey, 'get_user',
210 userid='trololo')
210 userid='trololo')
211 response = api_call(self, params)
211 response = api_call(self, params)
212
212
213 expected = "user `%s` does not exist" % 'trololo'
213 expected = "user `%s` does not exist" % 'trololo'
214 self._compare_error(id_, expected, given=response.body)
214 self._compare_error(id_, expected, given=response.body)
215
215
216 def test_api_get_user_without_giving_userid(self):
216 def test_api_get_user_without_giving_userid(self):
217 id_, params = _build_data(self.apikey, 'get_user')
217 id_, params = _build_data(self.apikey, 'get_user')
218 response = api_call(self, params)
218 response = api_call(self, params)
219
219
220 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
220 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
221 ret = usr.get_api_data()
221 ret = usr.get_api_data()
222 ret['permissions'] = AuthUser(usr.user_id).permissions
222 ret['permissions'] = AuthUser(usr.user_id).permissions
223
223
224 expected = ret
224 expected = ret
225 self._compare_ok(id_, expected, given=response.body)
225 self._compare_ok(id_, expected, given=response.body)
226
226
227 def test_api_get_user_without_giving_userid_non_admin(self):
227 def test_api_get_user_without_giving_userid_non_admin(self):
228 id_, params = _build_data(self.apikey_regular, 'get_user')
228 id_, params = _build_data(self.apikey_regular, 'get_user')
229 response = api_call(self, params)
229 response = api_call(self, params)
230
230
231 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
231 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
232 ret = usr.get_api_data()
232 ret = usr.get_api_data()
233 ret['permissions'] = AuthUser(usr.user_id).permissions
233 ret['permissions'] = AuthUser(usr.user_id).permissions
234
234
235 expected = ret
235 expected = ret
236 self._compare_ok(id_, expected, given=response.body)
236 self._compare_ok(id_, expected, given=response.body)
237
237
238 def test_api_get_user_with_giving_userid_non_admin(self):
238 def test_api_get_user_with_giving_userid_non_admin(self):
239 id_, params = _build_data(self.apikey_regular, 'get_user',
239 id_, params = _build_data(self.apikey_regular, 'get_user',
240 userid=self.TEST_USER_LOGIN)
240 userid=self.TEST_USER_LOGIN)
241 response = api_call(self, params)
241 response = api_call(self, params)
242
242
243 expected = 'userid is not the same as your user'
243 expected = 'userid is not the same as your user'
244 self._compare_error(id_, expected, given=response.body)
244 self._compare_error(id_, expected, given=response.body)
245
245
246 def test_api_pull(self):
246 def test_api_pull(self):
247 #TODO: issues with rhodecode_extras here.. not sure why !
247 #TODO: issues with rhodecode_extras here.. not sure why !
248 pass
248 pass
249
249
250 # repo_name = 'test_pull'
250 # repo_name = 'test_pull'
251 # r = create_repo(repo_name, self.REPO_TYPE)
251 # r = create_repo(repo_name, self.REPO_TYPE)
252 # r.clone_uri = TEST_self.REPO
252 # r.clone_uri = TEST_self.REPO
253 # Session.add(r)
253 # Session.add(r)
254 # Session.commit()
254 # Session.commit()
255 #
255 #
256 # id_, params = _build_data(self.apikey, 'pull',
256 # id_, params = _build_data(self.apikey, 'pull',
257 # repoid=repo_name,)
257 # repoid=repo_name,)
258 # response = self.app.post(API_URL, content_type='application/json',
258 # response = self.app.post(API_URL, content_type='application/json',
259 # params=params)
259 # params=params)
260 #
260 #
261 # expected = 'Pulled from `%s`' % repo_name
261 # expected = 'Pulled from `%s`' % repo_name
262 # self._compare_ok(id_, expected, given=response.body)
262 # self._compare_ok(id_, expected, given=response.body)
263 #
263 #
264 # destroy_repo(repo_name)
264 # destroy_repo(repo_name)
265
265
266 def test_api_pull_error(self):
266 def test_api_pull_error(self):
267 id_, params = _build_data(self.apikey, 'pull',
267 id_, params = _build_data(self.apikey, 'pull',
268 repoid=self.REPO,)
268 repoid=self.REPO,)
269 response = api_call(self, params)
269 response = api_call(self, params)
270
270
271 expected = 'Unable to pull changes from `%s`' % self.REPO
271 expected = 'Unable to pull changes from `%s`' % self.REPO
272 self._compare_error(id_, expected, given=response.body)
272 self._compare_error(id_, expected, given=response.body)
273
273
274 def test_api_rescan_repos(self):
274 def test_api_rescan_repos(self):
275 id_, params = _build_data(self.apikey, 'rescan_repos')
275 id_, params = _build_data(self.apikey, 'rescan_repos')
276 response = api_call(self, params)
276 response = api_call(self, params)
277
277
278 expected = {'added': [], 'removed': []}
278 expected = {'added': [], 'removed': []}
279 self._compare_ok(id_, expected, given=response.body)
279 self._compare_ok(id_, expected, given=response.body)
280
280
281 @mock.patch.object(ScmModel, 'repo_scan', crash)
281 @mock.patch.object(ScmModel, 'repo_scan', crash)
282 def test_api_rescann_error(self):
282 def test_api_rescann_error(self):
283 id_, params = _build_data(self.apikey, 'rescan_repos',)
283 id_, params = _build_data(self.apikey, 'rescan_repos',)
284 response = api_call(self, params)
284 response = api_call(self, params)
285
285
286 expected = 'Error occurred during rescan repositories action'
286 expected = 'Error occurred during rescan repositories action'
287 self._compare_error(id_, expected, given=response.body)
287 self._compare_error(id_, expected, given=response.body)
288
288
289 def test_api_invalidate_cache(self):
289 def test_api_invalidate_cache(self):
290 id_, params = _build_data(self.apikey, 'invalidate_cache',
290 id_, params = _build_data(self.apikey, 'invalidate_cache',
291 repoid=self.REPO)
291 repoid=self.REPO)
292 response = api_call(self, params)
292 response = api_call(self, params)
293
293
294 expected = ("Cache for repository `%s` was invalidated: "
294 expected = ("Cache for repository `%s` was invalidated: "
295 "invalidated cache keys: %s" % (self.REPO,
295 "invalidated cache keys: %s" % (self.REPO,
296 [unicode(self.REPO)]))
296 [unicode(self.REPO)]))
297 self._compare_ok(id_, expected, given=response.body)
297 self._compare_ok(id_, expected, given=response.body)
298
298
299 @mock.patch.object(ScmModel, 'mark_for_invalidation', crash)
299 @mock.patch.object(ScmModel, 'mark_for_invalidation', crash)
300 def test_api_invalidate_cache_error(self):
300 def test_api_invalidate_cache_error(self):
301 id_, params = _build_data(self.apikey, 'invalidate_cache',
301 id_, params = _build_data(self.apikey, 'invalidate_cache',
302 repoid=self.REPO)
302 repoid=self.REPO)
303 response = api_call(self, params)
303 response = api_call(self, params)
304
304
305 expected = 'Error occurred during cache invalidation action'
305 expected = 'Error occurred during cache invalidation action'
306 self._compare_error(id_, expected, given=response.body)
306 self._compare_error(id_, expected, given=response.body)
307
307
308 def test_api_lock_repo_lock_aquire(self):
308 def test_api_lock_repo_lock_aquire(self):
309 id_, params = _build_data(self.apikey, 'lock',
309 id_, params = _build_data(self.apikey, 'lock',
310 userid=TEST_USER_ADMIN_LOGIN,
310 userid=TEST_USER_ADMIN_LOGIN,
311 repoid=self.REPO,
311 repoid=self.REPO,
312 locked=True)
312 locked=True)
313 response = api_call(self, params)
313 response = api_call(self, params)
314 expected = ('User `%s` set lock state for repo `%s` to `%s`'
314 expected = ('User `%s` set lock state for repo `%s` to `%s`'
315 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
315 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
316 self._compare_ok(id_, expected, given=response.body)
316 self._compare_ok(id_, expected, given=response.body)
317
317
318 def test_api_lock_repo_lock_aquire_by_non_admin(self):
318 def test_api_lock_repo_lock_aquire_by_non_admin(self):
319 repo_name = 'api_delete_me'
319 repo_name = 'api_delete_me'
320 create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
320 create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
321 try:
321 try:
322 id_, params = _build_data(self.apikey_regular, 'lock',
322 id_, params = _build_data(self.apikey_regular, 'lock',
323 repoid=repo_name,
323 repoid=repo_name,
324 locked=True)
324 locked=True)
325 response = api_call(self, params)
325 response = api_call(self, params)
326 expected = ('User `%s` set lock state for repo `%s` to `%s`'
326 expected = ('User `%s` set lock state for repo `%s` to `%s`'
327 % (self.TEST_USER_LOGIN, repo_name, True))
327 % (self.TEST_USER_LOGIN, repo_name, True))
328 self._compare_ok(id_, expected, given=response.body)
328 self._compare_ok(id_, expected, given=response.body)
329 finally:
329 finally:
330 destroy_repo(repo_name)
330 destroy_repo(repo_name)
331
331
332 def test_api_lock_repo_lock_aquire_non_admin_with_userid(self):
332 def test_api_lock_repo_lock_aquire_non_admin_with_userid(self):
333 repo_name = 'api_delete_me'
333 repo_name = 'api_delete_me'
334 create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
334 create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
335 try:
335 try:
336 id_, params = _build_data(self.apikey_regular, 'lock',
336 id_, params = _build_data(self.apikey_regular, 'lock',
337 userid=TEST_USER_ADMIN_LOGIN,
337 userid=TEST_USER_ADMIN_LOGIN,
338 repoid=repo_name,
338 repoid=repo_name,
339 locked=True)
339 locked=True)
340 response = api_call(self, params)
340 response = api_call(self, params)
341 expected = 'userid is not the same as your user'
341 expected = 'userid is not the same as your user'
342 self._compare_error(id_, expected, given=response.body)
342 self._compare_error(id_, expected, given=response.body)
343 finally:
343 finally:
344 destroy_repo(repo_name)
344 destroy_repo(repo_name)
345
345
346 def test_api_lock_repo_lock_aquire_non_admin_not_his_repo(self):
346 def test_api_lock_repo_lock_aquire_non_admin_not_his_repo(self):
347 id_, params = _build_data(self.apikey_regular, 'lock',
347 id_, params = _build_data(self.apikey_regular, 'lock',
348 repoid=self.REPO,
348 repoid=self.REPO,
349 locked=True)
349 locked=True)
350 response = api_call(self, params)
350 response = api_call(self, params)
351 expected = 'repository `%s` does not exist' % (self.REPO)
351 expected = 'repository `%s` does not exist' % (self.REPO)
352 self._compare_error(id_, expected, given=response.body)
352 self._compare_error(id_, expected, given=response.body)
353
353
354 def test_api_lock_repo_lock_release(self):
354 def test_api_lock_repo_lock_release(self):
355 id_, params = _build_data(self.apikey, 'lock',
355 id_, params = _build_data(self.apikey, 'lock',
356 userid=TEST_USER_ADMIN_LOGIN,
356 userid=TEST_USER_ADMIN_LOGIN,
357 repoid=self.REPO,
357 repoid=self.REPO,
358 locked=False)
358 locked=False)
359 response = api_call(self, params)
359 response = api_call(self, params)
360 expected = ('User `%s` set lock state for repo `%s` to `%s`'
360 expected = ('User `%s` set lock state for repo `%s` to `%s`'
361 % (TEST_USER_ADMIN_LOGIN, self.REPO, False))
361 % (TEST_USER_ADMIN_LOGIN, self.REPO, False))
362 self._compare_ok(id_, expected, given=response.body)
362 self._compare_ok(id_, expected, given=response.body)
363
363
364 def test_api_lock_repo_lock_aquire_optional_userid(self):
364 def test_api_lock_repo_lock_aquire_optional_userid(self):
365 id_, params = _build_data(self.apikey, 'lock',
365 id_, params = _build_data(self.apikey, 'lock',
366 repoid=self.REPO,
366 repoid=self.REPO,
367 locked=True)
367 locked=True)
368 response = api_call(self, params)
368 response = api_call(self, params)
369 expected = ('User `%s` set lock state for repo `%s` to `%s`'
369 expected = ('User `%s` set lock state for repo `%s` to `%s`'
370 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
370 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
371 self._compare_ok(id_, expected, given=response.body)
371 self._compare_ok(id_, expected, given=response.body)
372
372
373 def test_api_lock_repo_lock_optional_locked(self):
373 def test_api_lock_repo_lock_optional_locked(self):
374 from rhodecode.lib import helpers
375 from rhodecode.lib.utils2 import time_to_datetime
374 from rhodecode.lib.utils2 import time_to_datetime
376 _locked_since = helpers.fmt_date(time_to_datetime(Repository\
375 _locked_since = json.dumps(time_to_datetime(Repository\
377 .get_by_repo_name(self.REPO).locked[1]))
376 .get_by_repo_name(self.REPO).locked[1]))
378 id_, params = _build_data(self.apikey, 'lock',
377 id_, params = _build_data(self.apikey, 'lock',
379 repoid=self.REPO)
378 repoid=self.REPO)
380 response = api_call(self, params)
379 response = api_call(self, params)
381 expected = ('Repo `%s` locked by `%s`. Locked=`True`. Locked since: `%s`'
380 expected = ('Repo `%s` locked by `%s`. Locked=`True`. Locked since: `%s`'
382 % (self.REPO, TEST_USER_ADMIN_LOGIN, _locked_since))
381 % (self.REPO, TEST_USER_ADMIN_LOGIN, _locked_since))
383 self._compare_ok(id_, expected, given=response.body)
382 self._compare_ok(id_, expected, given=response.body)
384
383
385 @mock.patch.object(Repository, 'lock', crash)
384 @mock.patch.object(Repository, 'lock', crash)
386 def test_api_lock_error(self):
385 def test_api_lock_error(self):
387 id_, params = _build_data(self.apikey, 'lock',
386 id_, params = _build_data(self.apikey, 'lock',
388 userid=TEST_USER_ADMIN_LOGIN,
387 userid=TEST_USER_ADMIN_LOGIN,
389 repoid=self.REPO,
388 repoid=self.REPO,
390 locked=True)
389 locked=True)
391 response = api_call(self, params)
390 response = api_call(self, params)
392
391
393 expected = 'Error occurred locking repository `%s`' % self.REPO
392 expected = 'Error occurred locking repository `%s`' % self.REPO
394 self._compare_error(id_, expected, given=response.body)
393 self._compare_error(id_, expected, given=response.body)
395
394
395 def test_api_get_locks_regular_user(self):
396 id_, params = _build_data(self.apikey_regular, 'get_locks')
397 response = api_call(self, params)
398 expected = []
399 self._compare_ok(id_, expected, given=response.body)
400
401 def test_api_get_locks_with_userid_regular_user(self):
402 id_, params = _build_data(self.apikey_regular, 'get_locks',
403 userid=TEST_USER_ADMIN_LOGIN)
404 response = api_call(self, params)
405 expected = 'userid is not the same as your user'
406 self._compare_error(id_, expected, given=response.body)
407
408 def test_api_get_locks(self):
409 id_, params = _build_data(self.apikey, 'get_locks')
410 response = api_call(self, params)
411 expected = []
412 self._compare_ok(id_, expected, given=response.body)
413
414 def test_api_get_locks_with_userid(self):
415 id_, params = _build_data(self.apikey, 'get_locks',
416 userid=TEST_USER_REGULAR_LOGIN)
417 response = api_call(self, params)
418 expected = []
419 self._compare_ok(id_, expected, given=response.body)
420
396 def test_api_create_existing_user(self):
421 def test_api_create_existing_user(self):
397 id_, params = _build_data(self.apikey, 'create_user',
422 id_, params = _build_data(self.apikey, 'create_user',
398 username=TEST_USER_ADMIN_LOGIN,
423 username=TEST_USER_ADMIN_LOGIN,
399 email='test@foo.com',
424 email='test@foo.com',
400 password='trololo')
425 password='trololo')
401 response = api_call(self, params)
426 response = api_call(self, params)
402
427
403 expected = "user `%s` already exist" % TEST_USER_ADMIN_LOGIN
428 expected = "user `%s` already exist" % TEST_USER_ADMIN_LOGIN
404 self._compare_error(id_, expected, given=response.body)
429 self._compare_error(id_, expected, given=response.body)
405
430
406 def test_api_create_user_with_existing_email(self):
431 def test_api_create_user_with_existing_email(self):
407 id_, params = _build_data(self.apikey, 'create_user',
432 id_, params = _build_data(self.apikey, 'create_user',
408 username=TEST_USER_ADMIN_LOGIN + 'new',
433 username=TEST_USER_ADMIN_LOGIN + 'new',
409 email=TEST_USER_REGULAR_EMAIL,
434 email=TEST_USER_REGULAR_EMAIL,
410 password='trololo')
435 password='trololo')
411 response = api_call(self, params)
436 response = api_call(self, params)
412
437
413 expected = "email `%s` already exist" % TEST_USER_REGULAR_EMAIL
438 expected = "email `%s` already exist" % TEST_USER_REGULAR_EMAIL
414 self._compare_error(id_, expected, given=response.body)
439 self._compare_error(id_, expected, given=response.body)
415
440
416 def test_api_create_user(self):
441 def test_api_create_user(self):
417 username = 'test_new_api_user'
442 username = 'test_new_api_user'
418 email = username + "@foo.com"
443 email = username + "@foo.com"
419
444
420 id_, params = _build_data(self.apikey, 'create_user',
445 id_, params = _build_data(self.apikey, 'create_user',
421 username=username,
446 username=username,
422 email=email,
447 email=email,
423 password='trololo')
448 password='trololo')
424 response = api_call(self, params)
449 response = api_call(self, params)
425
450
426 usr = UserModel().get_by_username(username)
451 usr = UserModel().get_by_username(username)
427 ret = dict(
452 ret = dict(
428 msg='created new user `%s`' % username,
453 msg='created new user `%s`' % username,
429 user=jsonify(usr.get_api_data())
454 user=jsonify(usr.get_api_data())
430 )
455 )
431
456
432 expected = ret
457 expected = ret
433 self._compare_ok(id_, expected, given=response.body)
458 self._compare_ok(id_, expected, given=response.body)
434
459
435 UserModel().delete(usr.user_id)
460 UserModel().delete(usr.user_id)
436 Session().commit()
461 Session().commit()
437
462
438 @mock.patch.object(UserModel, 'create_or_update', crash)
463 @mock.patch.object(UserModel, 'create_or_update', crash)
439 def test_api_create_user_when_exception_happened(self):
464 def test_api_create_user_when_exception_happened(self):
440
465
441 username = 'test_new_api_user'
466 username = 'test_new_api_user'
442 email = username + "@foo.com"
467 email = username + "@foo.com"
443
468
444 id_, params = _build_data(self.apikey, 'create_user',
469 id_, params = _build_data(self.apikey, 'create_user',
445 username=username,
470 username=username,
446 email=email,
471 email=email,
447 password='trololo')
472 password='trololo')
448 response = api_call(self, params)
473 response = api_call(self, params)
449 expected = 'failed to create user `%s`' % username
474 expected = 'failed to create user `%s`' % username
450 self._compare_error(id_, expected, given=response.body)
475 self._compare_error(id_, expected, given=response.body)
451
476
452 def test_api_delete_user(self):
477 def test_api_delete_user(self):
453 usr = UserModel().create_or_update(username=u'test_user',
478 usr = UserModel().create_or_update(username=u'test_user',
454 password=u'qweqwe',
479 password=u'qweqwe',
455 email=u'u232@rhodecode.org',
480 email=u'u232@rhodecode.org',
456 firstname=u'u1', lastname=u'u1')
481 firstname=u'u1', lastname=u'u1')
457 Session().commit()
482 Session().commit()
458 username = usr.username
483 username = usr.username
459 email = usr.email
484 email = usr.email
460 usr_id = usr.user_id
485 usr_id = usr.user_id
461 ## DELETE THIS USER NOW
486 ## DELETE THIS USER NOW
462
487
463 id_, params = _build_data(self.apikey, 'delete_user',
488 id_, params = _build_data(self.apikey, 'delete_user',
464 userid=username,)
489 userid=username,)
465 response = api_call(self, params)
490 response = api_call(self, params)
466
491
467 ret = {'msg': 'deleted user ID:%s %s' % (usr_id, username),
492 ret = {'msg': 'deleted user ID:%s %s' % (usr_id, username),
468 'user': None}
493 'user': None}
469 expected = ret
494 expected = ret
470 self._compare_ok(id_, expected, given=response.body)
495 self._compare_ok(id_, expected, given=response.body)
471
496
472 @mock.patch.object(UserModel, 'delete', crash)
497 @mock.patch.object(UserModel, 'delete', crash)
473 def test_api_delete_user_when_exception_happened(self):
498 def test_api_delete_user_when_exception_happened(self):
474 usr = UserModel().create_or_update(username=u'test_user',
499 usr = UserModel().create_or_update(username=u'test_user',
475 password=u'qweqwe',
500 password=u'qweqwe',
476 email=u'u232@rhodecode.org',
501 email=u'u232@rhodecode.org',
477 firstname=u'u1', lastname=u'u1')
502 firstname=u'u1', lastname=u'u1')
478 Session().commit()
503 Session().commit()
479 username = usr.username
504 username = usr.username
480
505
481 id_, params = _build_data(self.apikey, 'delete_user',
506 id_, params = _build_data(self.apikey, 'delete_user',
482 userid=username,)
507 userid=username,)
483 response = api_call(self, params)
508 response = api_call(self, params)
484 ret = 'failed to delete ID:%s %s' % (usr.user_id,
509 ret = 'failed to delete ID:%s %s' % (usr.user_id,
485 usr.username)
510 usr.username)
486 expected = ret
511 expected = ret
487 self._compare_error(id_, expected, given=response.body)
512 self._compare_error(id_, expected, given=response.body)
488
513
489 @parameterized.expand([('firstname', 'new_username'),
514 @parameterized.expand([('firstname', 'new_username'),
490 ('lastname', 'new_username'),
515 ('lastname', 'new_username'),
491 ('email', 'new_username'),
516 ('email', 'new_username'),
492 ('admin', True),
517 ('admin', True),
493 ('admin', False),
518 ('admin', False),
494 ('ldap_dn', 'test'),
519 ('ldap_dn', 'test'),
495 ('ldap_dn', None),
520 ('ldap_dn', None),
496 ('active', False),
521 ('active', False),
497 ('active', True),
522 ('active', True),
498 ('password', 'newpass')
523 ('password', 'newpass')
499 ])
524 ])
500 def test_api_update_user(self, name, expected):
525 def test_api_update_user(self, name, expected):
501 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
526 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
502 kw = {name: expected,
527 kw = {name: expected,
503 'userid': usr.user_id}
528 'userid': usr.user_id}
504 id_, params = _build_data(self.apikey, 'update_user', **kw)
529 id_, params = _build_data(self.apikey, 'update_user', **kw)
505 response = api_call(self, params)
530 response = api_call(self, params)
506
531
507 ret = {
532 ret = {
508 'msg': 'updated user ID:%s %s' % (usr.user_id, self.TEST_USER_LOGIN),
533 'msg': 'updated user ID:%s %s' % (usr.user_id, self.TEST_USER_LOGIN),
509 'user': jsonify(UserModel()\
534 'user': jsonify(UserModel()\
510 .get_by_username(self.TEST_USER_LOGIN)\
535 .get_by_username(self.TEST_USER_LOGIN)\
511 .get_api_data())
536 .get_api_data())
512 }
537 }
513
538
514 expected = ret
539 expected = ret
515 self._compare_ok(id_, expected, given=response.body)
540 self._compare_ok(id_, expected, given=response.body)
516
541
517 def test_api_update_user_no_changed_params(self):
542 def test_api_update_user_no_changed_params(self):
518 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
543 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
519 ret = jsonify(usr.get_api_data())
544 ret = jsonify(usr.get_api_data())
520 id_, params = _build_data(self.apikey, 'update_user',
545 id_, params = _build_data(self.apikey, 'update_user',
521 userid=TEST_USER_ADMIN_LOGIN)
546 userid=TEST_USER_ADMIN_LOGIN)
522
547
523 response = api_call(self, params)
548 response = api_call(self, params)
524 ret = {
549 ret = {
525 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
550 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
526 'user': ret
551 'user': ret
527 }
552 }
528 expected = ret
553 expected = ret
529 self._compare_ok(id_, expected, given=response.body)
554 self._compare_ok(id_, expected, given=response.body)
530
555
531 def test_api_update_user_by_user_id(self):
556 def test_api_update_user_by_user_id(self):
532 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
557 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
533 ret = jsonify(usr.get_api_data())
558 ret = jsonify(usr.get_api_data())
534 id_, params = _build_data(self.apikey, 'update_user',
559 id_, params = _build_data(self.apikey, 'update_user',
535 userid=usr.user_id)
560 userid=usr.user_id)
536
561
537 response = api_call(self, params)
562 response = api_call(self, params)
538 ret = {
563 ret = {
539 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
564 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
540 'user': ret
565 'user': ret
541 }
566 }
542 expected = ret
567 expected = ret
543 self._compare_ok(id_, expected, given=response.body)
568 self._compare_ok(id_, expected, given=response.body)
544
569
545 @mock.patch.object(UserModel, 'update_user', crash)
570 @mock.patch.object(UserModel, 'update_user', crash)
546 def test_api_update_user_when_exception_happens(self):
571 def test_api_update_user_when_exception_happens(self):
547 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
572 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
548 ret = jsonify(usr.get_api_data())
573 ret = jsonify(usr.get_api_data())
549 id_, params = _build_data(self.apikey, 'update_user',
574 id_, params = _build_data(self.apikey, 'update_user',
550 userid=usr.user_id)
575 userid=usr.user_id)
551
576
552 response = api_call(self, params)
577 response = api_call(self, params)
553 ret = 'failed to update user `%s`' % usr.user_id
578 ret = 'failed to update user `%s`' % usr.user_id
554
579
555 expected = ret
580 expected = ret
556 self._compare_error(id_, expected, given=response.body)
581 self._compare_error(id_, expected, given=response.body)
557
582
558 def test_api_get_repo(self):
583 def test_api_get_repo(self):
559 new_group = 'some_new_group'
584 new_group = 'some_new_group'
560 make_users_group(new_group)
585 make_users_group(new_group)
561 RepoModel().grant_users_group_permission(repo=self.REPO,
586 RepoModel().grant_users_group_permission(repo=self.REPO,
562 group_name=new_group,
587 group_name=new_group,
563 perm='repository.read')
588 perm='repository.read')
564 Session().commit()
589 Session().commit()
565 id_, params = _build_data(self.apikey, 'get_repo',
590 id_, params = _build_data(self.apikey, 'get_repo',
566 repoid=self.REPO)
591 repoid=self.REPO)
567 response = api_call(self, params)
592 response = api_call(self, params)
568
593
569 repo = RepoModel().get_by_repo_name(self.REPO)
594 repo = RepoModel().get_by_repo_name(self.REPO)
570 ret = repo.get_api_data()
595 ret = repo.get_api_data()
571
596
572 members = []
597 members = []
573 followers = []
598 followers = []
574 for user in repo.repo_to_perm:
599 for user in repo.repo_to_perm:
575 perm = user.permission.permission_name
600 perm = user.permission.permission_name
576 user = user.user
601 user = user.user
577 user_data = user.get_api_data()
602 user_data = user.get_api_data()
578 user_data['type'] = "user"
603 user_data['type'] = "user"
579 user_data['permission'] = perm
604 user_data['permission'] = perm
580 members.append(user_data)
605 members.append(user_data)
581
606
582 for users_group in repo.users_group_to_perm:
607 for users_group in repo.users_group_to_perm:
583 perm = users_group.permission.permission_name
608 perm = users_group.permission.permission_name
584 users_group = users_group.users_group
609 users_group = users_group.users_group
585 users_group_data = users_group.get_api_data()
610 users_group_data = users_group.get_api_data()
586 users_group_data['type'] = "users_group"
611 users_group_data['type'] = "users_group"
587 users_group_data['permission'] = perm
612 users_group_data['permission'] = perm
588 members.append(users_group_data)
613 members.append(users_group_data)
589
614
590 for user in repo.followers:
615 for user in repo.followers:
591 followers.append(user.user.get_api_data())
616 followers.append(user.user.get_api_data())
592
617
593 ret['members'] = members
618 ret['members'] = members
594 ret['followers'] = followers
619 ret['followers'] = followers
595
620
596 expected = ret
621 expected = ret
597 self._compare_ok(id_, expected, given=response.body)
622 self._compare_ok(id_, expected, given=response.body)
598 destroy_users_group(new_group)
623 destroy_users_group(new_group)
599
624
600 def test_api_get_repo_by_non_admin(self):
625 def test_api_get_repo_by_non_admin(self):
601 id_, params = _build_data(self.apikey, 'get_repo',
626 id_, params = _build_data(self.apikey, 'get_repo',
602 repoid=self.REPO)
627 repoid=self.REPO)
603 response = api_call(self, params)
628 response = api_call(self, params)
604
629
605 repo = RepoModel().get_by_repo_name(self.REPO)
630 repo = RepoModel().get_by_repo_name(self.REPO)
606 ret = repo.get_api_data()
631 ret = repo.get_api_data()
607
632
608 members = []
633 members = []
609 followers = []
634 followers = []
610 for user in repo.repo_to_perm:
635 for user in repo.repo_to_perm:
611 perm = user.permission.permission_name
636 perm = user.permission.permission_name
612 user = user.user
637 user = user.user
613 user_data = user.get_api_data()
638 user_data = user.get_api_data()
614 user_data['type'] = "user"
639 user_data['type'] = "user"
615 user_data['permission'] = perm
640 user_data['permission'] = perm
616 members.append(user_data)
641 members.append(user_data)
617
642
618 for users_group in repo.users_group_to_perm:
643 for users_group in repo.users_group_to_perm:
619 perm = users_group.permission.permission_name
644 perm = users_group.permission.permission_name
620 users_group = users_group.users_group
645 users_group = users_group.users_group
621 users_group_data = users_group.get_api_data()
646 users_group_data = users_group.get_api_data()
622 users_group_data['type'] = "users_group"
647 users_group_data['type'] = "users_group"
623 users_group_data['permission'] = perm
648 users_group_data['permission'] = perm
624 members.append(users_group_data)
649 members.append(users_group_data)
625
650
626 for user in repo.followers:
651 for user in repo.followers:
627 followers.append(user.user.get_api_data())
652 followers.append(user.user.get_api_data())
628
653
629 ret['members'] = members
654 ret['members'] = members
630 ret['followers'] = followers
655 ret['followers'] = followers
631
656
632 expected = ret
657 expected = ret
633 self._compare_ok(id_, expected, given=response.body)
658 self._compare_ok(id_, expected, given=response.body)
634
659
635 def test_api_get_repo_by_non_admin_no_permission_to_repo(self):
660 def test_api_get_repo_by_non_admin_no_permission_to_repo(self):
636 RepoModel().grant_user_permission(repo=self.REPO,
661 RepoModel().grant_user_permission(repo=self.REPO,
637 user=self.TEST_USER_LOGIN,
662 user=self.TEST_USER_LOGIN,
638 perm='repository.none')
663 perm='repository.none')
639
664
640 id_, params = _build_data(self.apikey_regular, 'get_repo',
665 id_, params = _build_data(self.apikey_regular, 'get_repo',
641 repoid=self.REPO)
666 repoid=self.REPO)
642 response = api_call(self, params)
667 response = api_call(self, params)
643
668
644 expected = 'repository `%s` does not exist' % (self.REPO)
669 expected = 'repository `%s` does not exist' % (self.REPO)
645 self._compare_error(id_, expected, given=response.body)
670 self._compare_error(id_, expected, given=response.body)
646
671
647 def test_api_get_repo_that_doesn_not_exist(self):
672 def test_api_get_repo_that_doesn_not_exist(self):
648 id_, params = _build_data(self.apikey, 'get_repo',
673 id_, params = _build_data(self.apikey, 'get_repo',
649 repoid='no-such-repo')
674 repoid='no-such-repo')
650 response = api_call(self, params)
675 response = api_call(self, params)
651
676
652 ret = 'repository `%s` does not exist' % 'no-such-repo'
677 ret = 'repository `%s` does not exist' % 'no-such-repo'
653 expected = ret
678 expected = ret
654 self._compare_error(id_, expected, given=response.body)
679 self._compare_error(id_, expected, given=response.body)
655
680
656 def test_api_get_repos(self):
681 def test_api_get_repos(self):
657 id_, params = _build_data(self.apikey, 'get_repos')
682 id_, params = _build_data(self.apikey, 'get_repos')
658 response = api_call(self, params)
683 response = api_call(self, params)
659
684
660 result = []
685 result = []
661 for repo in RepoModel().get_all():
686 for repo in RepoModel().get_all():
662 result.append(repo.get_api_data())
687 result.append(repo.get_api_data())
663 ret = jsonify(result)
688 ret = jsonify(result)
664
689
665 expected = ret
690 expected = ret
666 self._compare_ok(id_, expected, given=response.body)
691 self._compare_ok(id_, expected, given=response.body)
667
692
668 def test_api_get_repos_non_admin(self):
693 def test_api_get_repos_non_admin(self):
669 id_, params = _build_data(self.apikey_regular, 'get_repos')
694 id_, params = _build_data(self.apikey_regular, 'get_repos')
670 response = api_call(self, params)
695 response = api_call(self, params)
671
696
672 result = []
697 result = []
673 for repo in RepoModel().get_all_user_repos(self.TEST_USER_LOGIN):
698 for repo in RepoModel().get_all_user_repos(self.TEST_USER_LOGIN):
674 result.append(repo.get_api_data())
699 result.append(repo.get_api_data())
675 ret = jsonify(result)
700 ret = jsonify(result)
676
701
677 expected = ret
702 expected = ret
678 self._compare_ok(id_, expected, given=response.body)
703 self._compare_ok(id_, expected, given=response.body)
679
704
680 @parameterized.expand([('all', 'all'),
705 @parameterized.expand([('all', 'all'),
681 ('dirs', 'dirs'),
706 ('dirs', 'dirs'),
682 ('files', 'files'), ])
707 ('files', 'files'), ])
683 def test_api_get_repo_nodes(self, name, ret_type):
708 def test_api_get_repo_nodes(self, name, ret_type):
684 rev = 'tip'
709 rev = 'tip'
685 path = '/'
710 path = '/'
686 id_, params = _build_data(self.apikey, 'get_repo_nodes',
711 id_, params = _build_data(self.apikey, 'get_repo_nodes',
687 repoid=self.REPO, revision=rev,
712 repoid=self.REPO, revision=rev,
688 root_path=path,
713 root_path=path,
689 ret_type=ret_type)
714 ret_type=ret_type)
690 response = api_call(self, params)
715 response = api_call(self, params)
691
716
692 # we don't the actual return types here since it's tested somewhere
717 # we don't the actual return types here since it's tested somewhere
693 # else
718 # else
694 expected = json.loads(response.body)['result']
719 expected = json.loads(response.body)['result']
695 self._compare_ok(id_, expected, given=response.body)
720 self._compare_ok(id_, expected, given=response.body)
696
721
697 def test_api_get_repo_nodes_bad_revisions(self):
722 def test_api_get_repo_nodes_bad_revisions(self):
698 rev = 'i-dont-exist'
723 rev = 'i-dont-exist'
699 path = '/'
724 path = '/'
700 id_, params = _build_data(self.apikey, 'get_repo_nodes',
725 id_, params = _build_data(self.apikey, 'get_repo_nodes',
701 repoid=self.REPO, revision=rev,
726 repoid=self.REPO, revision=rev,
702 root_path=path,)
727 root_path=path,)
703 response = api_call(self, params)
728 response = api_call(self, params)
704
729
705 expected = 'failed to get repo: `%s` nodes' % self.REPO
730 expected = 'failed to get repo: `%s` nodes' % self.REPO
706 self._compare_error(id_, expected, given=response.body)
731 self._compare_error(id_, expected, given=response.body)
707
732
708 def test_api_get_repo_nodes_bad_path(self):
733 def test_api_get_repo_nodes_bad_path(self):
709 rev = 'tip'
734 rev = 'tip'
710 path = '/idontexits'
735 path = '/idontexits'
711 id_, params = _build_data(self.apikey, 'get_repo_nodes',
736 id_, params = _build_data(self.apikey, 'get_repo_nodes',
712 repoid=self.REPO, revision=rev,
737 repoid=self.REPO, revision=rev,
713 root_path=path,)
738 root_path=path,)
714 response = api_call(self, params)
739 response = api_call(self, params)
715
740
716 expected = 'failed to get repo: `%s` nodes' % self.REPO
741 expected = 'failed to get repo: `%s` nodes' % self.REPO
717 self._compare_error(id_, expected, given=response.body)
742 self._compare_error(id_, expected, given=response.body)
718
743
719 def test_api_get_repo_nodes_bad_ret_type(self):
744 def test_api_get_repo_nodes_bad_ret_type(self):
720 rev = 'tip'
745 rev = 'tip'
721 path = '/'
746 path = '/'
722 ret_type = 'error'
747 ret_type = 'error'
723 id_, params = _build_data(self.apikey, 'get_repo_nodes',
748 id_, params = _build_data(self.apikey, 'get_repo_nodes',
724 repoid=self.REPO, revision=rev,
749 repoid=self.REPO, revision=rev,
725 root_path=path,
750 root_path=path,
726 ret_type=ret_type)
751 ret_type=ret_type)
727 response = api_call(self, params)
752 response = api_call(self, params)
728
753
729 expected = 'ret_type must be one of %s' % (['files', 'dirs', 'all'])
754 expected = 'ret_type must be one of %s' % (['files', 'dirs', 'all'])
730 self._compare_error(id_, expected, given=response.body)
755 self._compare_error(id_, expected, given=response.body)
731
756
732 def test_api_create_repo(self):
757 def test_api_create_repo(self):
733 repo_name = 'api-repo'
758 repo_name = 'api-repo'
734 id_, params = _build_data(self.apikey, 'create_repo',
759 id_, params = _build_data(self.apikey, 'create_repo',
735 repo_name=repo_name,
760 repo_name=repo_name,
736 owner=TEST_USER_ADMIN_LOGIN,
761 owner=TEST_USER_ADMIN_LOGIN,
737 repo_type='hg',
762 repo_type='hg',
738 )
763 )
739 response = api_call(self, params)
764 response = api_call(self, params)
740
765
741 repo = RepoModel().get_by_repo_name(repo_name)
766 repo = RepoModel().get_by_repo_name(repo_name)
742 ret = {
767 ret = {
743 'msg': 'Created new repository `%s`' % repo_name,
768 'msg': 'Created new repository `%s`' % repo_name,
744 'repo': jsonify(repo.get_api_data())
769 'repo': jsonify(repo.get_api_data())
745 }
770 }
746 expected = ret
771 expected = ret
747 self._compare_ok(id_, expected, given=response.body)
772 self._compare_ok(id_, expected, given=response.body)
748 destroy_repo(repo_name)
773 destroy_repo(repo_name)
749
774
750 def test_api_create_repo_unknown_owner(self):
775 def test_api_create_repo_unknown_owner(self):
751 repo_name = 'api-repo'
776 repo_name = 'api-repo'
752 owner = 'i-dont-exist'
777 owner = 'i-dont-exist'
753 id_, params = _build_data(self.apikey, 'create_repo',
778 id_, params = _build_data(self.apikey, 'create_repo',
754 repo_name=repo_name,
779 repo_name=repo_name,
755 owner=owner,
780 owner=owner,
756 repo_type='hg',
781 repo_type='hg',
757 )
782 )
758 response = api_call(self, params)
783 response = api_call(self, params)
759 expected = 'user `%s` does not exist' % owner
784 expected = 'user `%s` does not exist' % owner
760 self._compare_error(id_, expected, given=response.body)
785 self._compare_error(id_, expected, given=response.body)
761
786
762 def test_api_create_repo_dont_specify_owner(self):
787 def test_api_create_repo_dont_specify_owner(self):
763 repo_name = 'api-repo'
788 repo_name = 'api-repo'
764 owner = 'i-dont-exist'
789 owner = 'i-dont-exist'
765 id_, params = _build_data(self.apikey, 'create_repo',
790 id_, params = _build_data(self.apikey, 'create_repo',
766 repo_name=repo_name,
791 repo_name=repo_name,
767 repo_type='hg',
792 repo_type='hg',
768 )
793 )
769 response = api_call(self, params)
794 response = api_call(self, params)
770
795
771 repo = RepoModel().get_by_repo_name(repo_name)
796 repo = RepoModel().get_by_repo_name(repo_name)
772 ret = {
797 ret = {
773 'msg': 'Created new repository `%s`' % repo_name,
798 'msg': 'Created new repository `%s`' % repo_name,
774 'repo': jsonify(repo.get_api_data())
799 'repo': jsonify(repo.get_api_data())
775 }
800 }
776 expected = ret
801 expected = ret
777 self._compare_ok(id_, expected, given=response.body)
802 self._compare_ok(id_, expected, given=response.body)
778 destroy_repo(repo_name)
803 destroy_repo(repo_name)
779
804
780 def test_api_create_repo_by_non_admin(self):
805 def test_api_create_repo_by_non_admin(self):
781 repo_name = 'api-repo'
806 repo_name = 'api-repo'
782 owner = 'i-dont-exist'
807 owner = 'i-dont-exist'
783 id_, params = _build_data(self.apikey_regular, 'create_repo',
808 id_, params = _build_data(self.apikey_regular, 'create_repo',
784 repo_name=repo_name,
809 repo_name=repo_name,
785 repo_type='hg',
810 repo_type='hg',
786 )
811 )
787 response = api_call(self, params)
812 response = api_call(self, params)
788
813
789 repo = RepoModel().get_by_repo_name(repo_name)
814 repo = RepoModel().get_by_repo_name(repo_name)
790 ret = {
815 ret = {
791 'msg': 'Created new repository `%s`' % repo_name,
816 'msg': 'Created new repository `%s`' % repo_name,
792 'repo': jsonify(repo.get_api_data())
817 'repo': jsonify(repo.get_api_data())
793 }
818 }
794 expected = ret
819 expected = ret
795 self._compare_ok(id_, expected, given=response.body)
820 self._compare_ok(id_, expected, given=response.body)
796 destroy_repo(repo_name)
821 destroy_repo(repo_name)
797
822
798 def test_api_create_repo_by_non_admin_specify_owner(self):
823 def test_api_create_repo_by_non_admin_specify_owner(self):
799 repo_name = 'api-repo'
824 repo_name = 'api-repo'
800 owner = 'i-dont-exist'
825 owner = 'i-dont-exist'
801 id_, params = _build_data(self.apikey_regular, 'create_repo',
826 id_, params = _build_data(self.apikey_regular, 'create_repo',
802 repo_name=repo_name,
827 repo_name=repo_name,
803 repo_type='hg',
828 repo_type='hg',
804 owner=owner
829 owner=owner
805 )
830 )
806 response = api_call(self, params)
831 response = api_call(self, params)
807
832
808 expected = 'Only RhodeCode admin can specify `owner` param'
833 expected = 'Only RhodeCode admin can specify `owner` param'
809 self._compare_error(id_, expected, given=response.body)
834 self._compare_error(id_, expected, given=response.body)
810 destroy_repo(repo_name)
835 destroy_repo(repo_name)
811
836
812 def test_api_create_repo_exists(self):
837 def test_api_create_repo_exists(self):
813 repo_name = self.REPO
838 repo_name = self.REPO
814 id_, params = _build_data(self.apikey, 'create_repo',
839 id_, params = _build_data(self.apikey, 'create_repo',
815 repo_name=repo_name,
840 repo_name=repo_name,
816 owner=TEST_USER_ADMIN_LOGIN,
841 owner=TEST_USER_ADMIN_LOGIN,
817 repo_type='hg',
842 repo_type='hg',
818 )
843 )
819 response = api_call(self, params)
844 response = api_call(self, params)
820 expected = "repo `%s` already exist" % repo_name
845 expected = "repo `%s` already exist" % repo_name
821 self._compare_error(id_, expected, given=response.body)
846 self._compare_error(id_, expected, given=response.body)
822
847
823 @mock.patch.object(RepoModel, 'create_repo', crash)
848 @mock.patch.object(RepoModel, 'create_repo', crash)
824 def test_api_create_repo_exception_occurred(self):
849 def test_api_create_repo_exception_occurred(self):
825 repo_name = 'api-repo'
850 repo_name = 'api-repo'
826 id_, params = _build_data(self.apikey, 'create_repo',
851 id_, params = _build_data(self.apikey, 'create_repo',
827 repo_name=repo_name,
852 repo_name=repo_name,
828 owner=TEST_USER_ADMIN_LOGIN,
853 owner=TEST_USER_ADMIN_LOGIN,
829 repo_type='hg',
854 repo_type='hg',
830 )
855 )
831 response = api_call(self, params)
856 response = api_call(self, params)
832 expected = 'failed to create repository `%s`' % repo_name
857 expected = 'failed to create repository `%s`' % repo_name
833 self._compare_error(id_, expected, given=response.body)
858 self._compare_error(id_, expected, given=response.body)
834
859
835 def test_api_delete_repo(self):
860 def test_api_delete_repo(self):
836 repo_name = 'api_delete_me'
861 repo_name = 'api_delete_me'
837 create_repo(repo_name, self.REPO_TYPE)
862 create_repo(repo_name, self.REPO_TYPE)
838
863
839 id_, params = _build_data(self.apikey, 'delete_repo',
864 id_, params = _build_data(self.apikey, 'delete_repo',
840 repoid=repo_name,)
865 repoid=repo_name,)
841 response = api_call(self, params)
866 response = api_call(self, params)
842
867
843 ret = {
868 ret = {
844 'msg': 'Deleted repository `%s`' % repo_name,
869 'msg': 'Deleted repository `%s`' % repo_name,
845 'success': True
870 'success': True
846 }
871 }
847 expected = ret
872 expected = ret
848 self._compare_ok(id_, expected, given=response.body)
873 self._compare_ok(id_, expected, given=response.body)
849
874
850 def test_api_delete_repo_by_non_admin(self):
875 def test_api_delete_repo_by_non_admin(self):
851 repo_name = 'api_delete_me'
876 repo_name = 'api_delete_me'
852 create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
877 create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
853 try:
878 try:
854 id_, params = _build_data(self.apikey_regular, 'delete_repo',
879 id_, params = _build_data(self.apikey_regular, 'delete_repo',
855 repoid=repo_name,)
880 repoid=repo_name,)
856 response = api_call(self, params)
881 response = api_call(self, params)
857
882
858 ret = {
883 ret = {
859 'msg': 'Deleted repository `%s`' % repo_name,
884 'msg': 'Deleted repository `%s`' % repo_name,
860 'success': True
885 'success': True
861 }
886 }
862 expected = ret
887 expected = ret
863 self._compare_ok(id_, expected, given=response.body)
888 self._compare_ok(id_, expected, given=response.body)
864 finally:
889 finally:
865 destroy_repo(repo_name)
890 destroy_repo(repo_name)
866
891
867 def test_api_delete_repo_by_non_admin_no_permission(self):
892 def test_api_delete_repo_by_non_admin_no_permission(self):
868 repo_name = 'api_delete_me'
893 repo_name = 'api_delete_me'
869 create_repo(repo_name, self.REPO_TYPE)
894 create_repo(repo_name, self.REPO_TYPE)
870 try:
895 try:
871 id_, params = _build_data(self.apikey_regular, 'delete_repo',
896 id_, params = _build_data(self.apikey_regular, 'delete_repo',
872 repoid=repo_name,)
897 repoid=repo_name,)
873 response = api_call(self, params)
898 response = api_call(self, params)
874 expected = 'repository `%s` does not exist' % (repo_name)
899 expected = 'repository `%s` does not exist' % (repo_name)
875 self._compare_error(id_, expected, given=response.body)
900 self._compare_error(id_, expected, given=response.body)
876 finally:
901 finally:
877 destroy_repo(repo_name)
902 destroy_repo(repo_name)
878
903
879 def test_api_delete_repo_exception_occurred(self):
904 def test_api_delete_repo_exception_occurred(self):
880 repo_name = 'api_delete_me'
905 repo_name = 'api_delete_me'
881 create_repo(repo_name, self.REPO_TYPE)
906 create_repo(repo_name, self.REPO_TYPE)
882 try:
907 try:
883 with mock.patch.object(RepoModel, 'delete', crash):
908 with mock.patch.object(RepoModel, 'delete', crash):
884 id_, params = _build_data(self.apikey, 'delete_repo',
909 id_, params = _build_data(self.apikey, 'delete_repo',
885 repoid=repo_name,)
910 repoid=repo_name,)
886 response = api_call(self, params)
911 response = api_call(self, params)
887
912
888 expected = 'failed to delete repository `%s`' % repo_name
913 expected = 'failed to delete repository `%s`' % repo_name
889 self._compare_error(id_, expected, given=response.body)
914 self._compare_error(id_, expected, given=response.body)
890 finally:
915 finally:
891 destroy_repo(repo_name)
916 destroy_repo(repo_name)
892
917
893 def test_api_fork_repo(self):
918 def test_api_fork_repo(self):
894 fork_name = 'api-repo-fork'
919 fork_name = 'api-repo-fork'
895 id_, params = _build_data(self.apikey, 'fork_repo',
920 id_, params = _build_data(self.apikey, 'fork_repo',
896 repoid=self.REPO,
921 repoid=self.REPO,
897 fork_name=fork_name,
922 fork_name=fork_name,
898 owner=TEST_USER_ADMIN_LOGIN,
923 owner=TEST_USER_ADMIN_LOGIN,
899 )
924 )
900 response = api_call(self, params)
925 response = api_call(self, params)
901
926
902 ret = {
927 ret = {
903 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
928 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
904 fork_name),
929 fork_name),
905 'success': True
930 'success': True
906 }
931 }
907 expected = ret
932 expected = ret
908 self._compare_ok(id_, expected, given=response.body)
933 self._compare_ok(id_, expected, given=response.body)
909 destroy_repo(fork_name)
934 destroy_repo(fork_name)
910
935
911 def test_api_fork_repo_non_admin(self):
936 def test_api_fork_repo_non_admin(self):
912 fork_name = 'api-repo-fork'
937 fork_name = 'api-repo-fork'
913 id_, params = _build_data(self.apikey_regular, 'fork_repo',
938 id_, params = _build_data(self.apikey_regular, 'fork_repo',
914 repoid=self.REPO,
939 repoid=self.REPO,
915 fork_name=fork_name,
940 fork_name=fork_name,
916 )
941 )
917 response = api_call(self, params)
942 response = api_call(self, params)
918
943
919 ret = {
944 ret = {
920 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
945 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
921 fork_name),
946 fork_name),
922 'success': True
947 'success': True
923 }
948 }
924 expected = ret
949 expected = ret
925 self._compare_ok(id_, expected, given=response.body)
950 self._compare_ok(id_, expected, given=response.body)
926 destroy_repo(fork_name)
951 destroy_repo(fork_name)
927
952
928 def test_api_fork_repo_non_admin_specify_owner(self):
953 def test_api_fork_repo_non_admin_specify_owner(self):
929 fork_name = 'api-repo-fork'
954 fork_name = 'api-repo-fork'
930 id_, params = _build_data(self.apikey_regular, 'fork_repo',
955 id_, params = _build_data(self.apikey_regular, 'fork_repo',
931 repoid=self.REPO,
956 repoid=self.REPO,
932 fork_name=fork_name,
957 fork_name=fork_name,
933 owner=TEST_USER_ADMIN_LOGIN,
958 owner=TEST_USER_ADMIN_LOGIN,
934 )
959 )
935 response = api_call(self, params)
960 response = api_call(self, params)
936 expected = 'Only RhodeCode admin can specify `owner` param'
961 expected = 'Only RhodeCode admin can specify `owner` param'
937 self._compare_error(id_, expected, given=response.body)
962 self._compare_error(id_, expected, given=response.body)
938 destroy_repo(fork_name)
963 destroy_repo(fork_name)
939
964
940 def test_api_fork_repo_non_admin_no_permission_to_fork(self):
965 def test_api_fork_repo_non_admin_no_permission_to_fork(self):
941 RepoModel().grant_user_permission(repo=self.REPO,
966 RepoModel().grant_user_permission(repo=self.REPO,
942 user=self.TEST_USER_LOGIN,
967 user=self.TEST_USER_LOGIN,
943 perm='repository.none')
968 perm='repository.none')
944 fork_name = 'api-repo-fork'
969 fork_name = 'api-repo-fork'
945 id_, params = _build_data(self.apikey_regular, 'fork_repo',
970 id_, params = _build_data(self.apikey_regular, 'fork_repo',
946 repoid=self.REPO,
971 repoid=self.REPO,
947 fork_name=fork_name,
972 fork_name=fork_name,
948 )
973 )
949 response = api_call(self, params)
974 response = api_call(self, params)
950 expected = 'repository `%s` does not exist' % (self.REPO)
975 expected = 'repository `%s` does not exist' % (self.REPO)
951 self._compare_error(id_, expected, given=response.body)
976 self._compare_error(id_, expected, given=response.body)
952 destroy_repo(fork_name)
977 destroy_repo(fork_name)
953
978
954 def test_api_fork_repo_unknown_owner(self):
979 def test_api_fork_repo_unknown_owner(self):
955 fork_name = 'api-repo-fork'
980 fork_name = 'api-repo-fork'
956 owner = 'i-dont-exist'
981 owner = 'i-dont-exist'
957 id_, params = _build_data(self.apikey, 'fork_repo',
982 id_, params = _build_data(self.apikey, 'fork_repo',
958 repoid=self.REPO,
983 repoid=self.REPO,
959 fork_name=fork_name,
984 fork_name=fork_name,
960 owner=owner,
985 owner=owner,
961 )
986 )
962 response = api_call(self, params)
987 response = api_call(self, params)
963 expected = 'user `%s` does not exist' % owner
988 expected = 'user `%s` does not exist' % owner
964 self._compare_error(id_, expected, given=response.body)
989 self._compare_error(id_, expected, given=response.body)
965
990
966 def test_api_fork_repo_fork_exists(self):
991 def test_api_fork_repo_fork_exists(self):
967 fork_name = 'api-repo-fork'
992 fork_name = 'api-repo-fork'
968 create_fork(fork_name, self.REPO_TYPE, self.REPO)
993 create_fork(fork_name, self.REPO_TYPE, self.REPO)
969
994
970 try:
995 try:
971 fork_name = 'api-repo-fork'
996 fork_name = 'api-repo-fork'
972
997
973 id_, params = _build_data(self.apikey, 'fork_repo',
998 id_, params = _build_data(self.apikey, 'fork_repo',
974 repoid=self.REPO,
999 repoid=self.REPO,
975 fork_name=fork_name,
1000 fork_name=fork_name,
976 owner=TEST_USER_ADMIN_LOGIN,
1001 owner=TEST_USER_ADMIN_LOGIN,
977 )
1002 )
978 response = api_call(self, params)
1003 response = api_call(self, params)
979
1004
980 expected = "fork `%s` already exist" % fork_name
1005 expected = "fork `%s` already exist" % fork_name
981 self._compare_error(id_, expected, given=response.body)
1006 self._compare_error(id_, expected, given=response.body)
982 finally:
1007 finally:
983 destroy_repo(fork_name)
1008 destroy_repo(fork_name)
984
1009
985 def test_api_fork_repo_repo_exists(self):
1010 def test_api_fork_repo_repo_exists(self):
986 fork_name = self.REPO
1011 fork_name = self.REPO
987
1012
988 id_, params = _build_data(self.apikey, 'fork_repo',
1013 id_, params = _build_data(self.apikey, 'fork_repo',
989 repoid=self.REPO,
1014 repoid=self.REPO,
990 fork_name=fork_name,
1015 fork_name=fork_name,
991 owner=TEST_USER_ADMIN_LOGIN,
1016 owner=TEST_USER_ADMIN_LOGIN,
992 )
1017 )
993 response = api_call(self, params)
1018 response = api_call(self, params)
994
1019
995 expected = "repo `%s` already exist" % fork_name
1020 expected = "repo `%s` already exist" % fork_name
996 self._compare_error(id_, expected, given=response.body)
1021 self._compare_error(id_, expected, given=response.body)
997
1022
998 @mock.patch.object(RepoModel, 'create_fork', crash)
1023 @mock.patch.object(RepoModel, 'create_fork', crash)
999 def test_api_fork_repo_exception_occurred(self):
1024 def test_api_fork_repo_exception_occurred(self):
1000 fork_name = 'api-repo-fork'
1025 fork_name = 'api-repo-fork'
1001 id_, params = _build_data(self.apikey, 'fork_repo',
1026 id_, params = _build_data(self.apikey, 'fork_repo',
1002 repoid=self.REPO,
1027 repoid=self.REPO,
1003 fork_name=fork_name,
1028 fork_name=fork_name,
1004 owner=TEST_USER_ADMIN_LOGIN,
1029 owner=TEST_USER_ADMIN_LOGIN,
1005 )
1030 )
1006 response = api_call(self, params)
1031 response = api_call(self, params)
1007
1032
1008 expected = 'failed to fork repository `%s` as `%s`' % (self.REPO,
1033 expected = 'failed to fork repository `%s` as `%s`' % (self.REPO,
1009 fork_name)
1034 fork_name)
1010 self._compare_error(id_, expected, given=response.body)
1035 self._compare_error(id_, expected, given=response.body)
1011
1036
1012 def test_api_get_users_group(self):
1037 def test_api_get_users_group(self):
1013 id_, params = _build_data(self.apikey, 'get_users_group',
1038 id_, params = _build_data(self.apikey, 'get_users_group',
1014 usersgroupid=TEST_USER_GROUP)
1039 usersgroupid=TEST_USER_GROUP)
1015 response = api_call(self, params)
1040 response = api_call(self, params)
1016
1041
1017 users_group = UserGroupModel().get_group(TEST_USER_GROUP)
1042 users_group = UserGroupModel().get_group(TEST_USER_GROUP)
1018 members = []
1043 members = []
1019 for user in users_group.members:
1044 for user in users_group.members:
1020 user = user.user
1045 user = user.user
1021 members.append(user.get_api_data())
1046 members.append(user.get_api_data())
1022
1047
1023 ret = users_group.get_api_data()
1048 ret = users_group.get_api_data()
1024 ret['members'] = members
1049 ret['members'] = members
1025 expected = ret
1050 expected = ret
1026 self._compare_ok(id_, expected, given=response.body)
1051 self._compare_ok(id_, expected, given=response.body)
1027
1052
1028 def test_api_get_users_groups(self):
1053 def test_api_get_users_groups(self):
1029
1054
1030 make_users_group('test_users_group2')
1055 make_users_group('test_users_group2')
1031
1056
1032 id_, params = _build_data(self.apikey, 'get_users_groups',)
1057 id_, params = _build_data(self.apikey, 'get_users_groups',)
1033 response = api_call(self, params)
1058 response = api_call(self, params)
1034
1059
1035 expected = []
1060 expected = []
1036 for gr_name in [TEST_USER_GROUP, 'test_users_group2']:
1061 for gr_name in [TEST_USER_GROUP, 'test_users_group2']:
1037 users_group = UserGroupModel().get_group(gr_name)
1062 users_group = UserGroupModel().get_group(gr_name)
1038 ret = users_group.get_api_data()
1063 ret = users_group.get_api_data()
1039 expected.append(ret)
1064 expected.append(ret)
1040 self._compare_ok(id_, expected, given=response.body)
1065 self._compare_ok(id_, expected, given=response.body)
1041
1066
1042 UserGroupModel().delete(users_group='test_users_group2')
1067 UserGroupModel().delete(users_group='test_users_group2')
1043 Session().commit()
1068 Session().commit()
1044
1069
1045 def test_api_create_users_group(self):
1070 def test_api_create_users_group(self):
1046 group_name = 'some_new_group'
1071 group_name = 'some_new_group'
1047 id_, params = _build_data(self.apikey, 'create_users_group',
1072 id_, params = _build_data(self.apikey, 'create_users_group',
1048 group_name=group_name)
1073 group_name=group_name)
1049 response = api_call(self, params)
1074 response = api_call(self, params)
1050
1075
1051 ret = {
1076 ret = {
1052 'msg': 'created new user group `%s`' % group_name,
1077 'msg': 'created new user group `%s`' % group_name,
1053 'users_group': jsonify(UserGroupModel()\
1078 'users_group': jsonify(UserGroupModel()\
1054 .get_by_name(group_name)\
1079 .get_by_name(group_name)\
1055 .get_api_data())
1080 .get_api_data())
1056 }
1081 }
1057 expected = ret
1082 expected = ret
1058 self._compare_ok(id_, expected, given=response.body)
1083 self._compare_ok(id_, expected, given=response.body)
1059
1084
1060 destroy_users_group(group_name)
1085 destroy_users_group(group_name)
1061
1086
1062 def test_api_get_users_group_that_exist(self):
1087 def test_api_get_users_group_that_exist(self):
1063 id_, params = _build_data(self.apikey, 'create_users_group',
1088 id_, params = _build_data(self.apikey, 'create_users_group',
1064 group_name=TEST_USER_GROUP)
1089 group_name=TEST_USER_GROUP)
1065 response = api_call(self, params)
1090 response = api_call(self, params)
1066
1091
1067 expected = "user group `%s` already exist" % TEST_USER_GROUP
1092 expected = "user group `%s` already exist" % TEST_USER_GROUP
1068 self._compare_error(id_, expected, given=response.body)
1093 self._compare_error(id_, expected, given=response.body)
1069
1094
1070 @mock.patch.object(UserGroupModel, 'create', crash)
1095 @mock.patch.object(UserGroupModel, 'create', crash)
1071 def test_api_get_users_group_exception_occurred(self):
1096 def test_api_get_users_group_exception_occurred(self):
1072 group_name = 'exception_happens'
1097 group_name = 'exception_happens'
1073 id_, params = _build_data(self.apikey, 'create_users_group',
1098 id_, params = _build_data(self.apikey, 'create_users_group',
1074 group_name=group_name)
1099 group_name=group_name)
1075 response = api_call(self, params)
1100 response = api_call(self, params)
1076
1101
1077 expected = 'failed to create group `%s`' % group_name
1102 expected = 'failed to create group `%s`' % group_name
1078 self._compare_error(id_, expected, given=response.body)
1103 self._compare_error(id_, expected, given=response.body)
1079
1104
1080 def test_api_add_user_to_users_group(self):
1105 def test_api_add_user_to_users_group(self):
1081 gr_name = 'test_group'
1106 gr_name = 'test_group'
1082 UserGroupModel().create(gr_name)
1107 UserGroupModel().create(gr_name)
1083 Session().commit()
1108 Session().commit()
1084 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1109 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1085 usersgroupid=gr_name,
1110 usersgroupid=gr_name,
1086 userid=TEST_USER_ADMIN_LOGIN)
1111 userid=TEST_USER_ADMIN_LOGIN)
1087 response = api_call(self, params)
1112 response = api_call(self, params)
1088
1113
1089 expected = {
1114 expected = {
1090 'msg': 'added member `%s` to user group `%s`' % (
1115 'msg': 'added member `%s` to user group `%s`' % (
1091 TEST_USER_ADMIN_LOGIN, gr_name
1116 TEST_USER_ADMIN_LOGIN, gr_name
1092 ),
1117 ),
1093 'success': True}
1118 'success': True}
1094 self._compare_ok(id_, expected, given=response.body)
1119 self._compare_ok(id_, expected, given=response.body)
1095
1120
1096 UserGroupModel().delete(users_group=gr_name)
1121 UserGroupModel().delete(users_group=gr_name)
1097 Session().commit()
1122 Session().commit()
1098
1123
1099 def test_api_add_user_to_users_group_that_doesnt_exist(self):
1124 def test_api_add_user_to_users_group_that_doesnt_exist(self):
1100 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1125 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1101 usersgroupid='false-group',
1126 usersgroupid='false-group',
1102 userid=TEST_USER_ADMIN_LOGIN)
1127 userid=TEST_USER_ADMIN_LOGIN)
1103 response = api_call(self, params)
1128 response = api_call(self, params)
1104
1129
1105 expected = 'user group `%s` does not exist' % 'false-group'
1130 expected = 'user group `%s` does not exist' % 'false-group'
1106 self._compare_error(id_, expected, given=response.body)
1131 self._compare_error(id_, expected, given=response.body)
1107
1132
1108 @mock.patch.object(UserGroupModel, 'add_user_to_group', crash)
1133 @mock.patch.object(UserGroupModel, 'add_user_to_group', crash)
1109 def test_api_add_user_to_users_group_exception_occurred(self):
1134 def test_api_add_user_to_users_group_exception_occurred(self):
1110 gr_name = 'test_group'
1135 gr_name = 'test_group'
1111 UserGroupModel().create(gr_name)
1136 UserGroupModel().create(gr_name)
1112 Session().commit()
1137 Session().commit()
1113 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1138 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1114 usersgroupid=gr_name,
1139 usersgroupid=gr_name,
1115 userid=TEST_USER_ADMIN_LOGIN)
1140 userid=TEST_USER_ADMIN_LOGIN)
1116 response = api_call(self, params)
1141 response = api_call(self, params)
1117
1142
1118 expected = 'failed to add member to user group `%s`' % gr_name
1143 expected = 'failed to add member to user group `%s`' % gr_name
1119 self._compare_error(id_, expected, given=response.body)
1144 self._compare_error(id_, expected, given=response.body)
1120
1145
1121 UserGroupModel().delete(users_group=gr_name)
1146 UserGroupModel().delete(users_group=gr_name)
1122 Session().commit()
1147 Session().commit()
1123
1148
1124 def test_api_remove_user_from_users_group(self):
1149 def test_api_remove_user_from_users_group(self):
1125 gr_name = 'test_group_3'
1150 gr_name = 'test_group_3'
1126 gr = UserGroupModel().create(gr_name)
1151 gr = UserGroupModel().create(gr_name)
1127 UserGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
1152 UserGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
1128 Session().commit()
1153 Session().commit()
1129 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
1154 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
1130 usersgroupid=gr_name,
1155 usersgroupid=gr_name,
1131 userid=TEST_USER_ADMIN_LOGIN)
1156 userid=TEST_USER_ADMIN_LOGIN)
1132 response = api_call(self, params)
1157 response = api_call(self, params)
1133
1158
1134 expected = {
1159 expected = {
1135 'msg': 'removed member `%s` from user group `%s`' % (
1160 'msg': 'removed member `%s` from user group `%s`' % (
1136 TEST_USER_ADMIN_LOGIN, gr_name
1161 TEST_USER_ADMIN_LOGIN, gr_name
1137 ),
1162 ),
1138 'success': True}
1163 'success': True}
1139 self._compare_ok(id_, expected, given=response.body)
1164 self._compare_ok(id_, expected, given=response.body)
1140
1165
1141 UserGroupModel().delete(users_group=gr_name)
1166 UserGroupModel().delete(users_group=gr_name)
1142 Session().commit()
1167 Session().commit()
1143
1168
1144 @mock.patch.object(UserGroupModel, 'remove_user_from_group', crash)
1169 @mock.patch.object(UserGroupModel, 'remove_user_from_group', crash)
1145 def test_api_remove_user_from_users_group_exception_occurred(self):
1170 def test_api_remove_user_from_users_group_exception_occurred(self):
1146 gr_name = 'test_group_3'
1171 gr_name = 'test_group_3'
1147 gr = UserGroupModel().create(gr_name)
1172 gr = UserGroupModel().create(gr_name)
1148 UserGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
1173 UserGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
1149 Session().commit()
1174 Session().commit()
1150 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
1175 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
1151 usersgroupid=gr_name,
1176 usersgroupid=gr_name,
1152 userid=TEST_USER_ADMIN_LOGIN)
1177 userid=TEST_USER_ADMIN_LOGIN)
1153 response = api_call(self, params)
1178 response = api_call(self, params)
1154
1179
1155 expected = 'failed to remove member from user group `%s`' % gr_name
1180 expected = 'failed to remove member from user group `%s`' % gr_name
1156 self._compare_error(id_, expected, given=response.body)
1181 self._compare_error(id_, expected, given=response.body)
1157
1182
1158 UserGroupModel().delete(users_group=gr_name)
1183 UserGroupModel().delete(users_group=gr_name)
1159 Session().commit()
1184 Session().commit()
1160
1185
1161 @parameterized.expand([('none', 'repository.none'),
1186 @parameterized.expand([('none', 'repository.none'),
1162 ('read', 'repository.read'),
1187 ('read', 'repository.read'),
1163 ('write', 'repository.write'),
1188 ('write', 'repository.write'),
1164 ('admin', 'repository.admin')])
1189 ('admin', 'repository.admin')])
1165 def test_api_grant_user_permission(self, name, perm):
1190 def test_api_grant_user_permission(self, name, perm):
1166 id_, params = _build_data(self.apikey, 'grant_user_permission',
1191 id_, params = _build_data(self.apikey, 'grant_user_permission',
1167 repoid=self.REPO,
1192 repoid=self.REPO,
1168 userid=TEST_USER_ADMIN_LOGIN,
1193 userid=TEST_USER_ADMIN_LOGIN,
1169 perm=perm)
1194 perm=perm)
1170 response = api_call(self, params)
1195 response = api_call(self, params)
1171
1196
1172 ret = {
1197 ret = {
1173 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1198 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1174 perm, TEST_USER_ADMIN_LOGIN, self.REPO
1199 perm, TEST_USER_ADMIN_LOGIN, self.REPO
1175 ),
1200 ),
1176 'success': True
1201 'success': True
1177 }
1202 }
1178 expected = ret
1203 expected = ret
1179 self._compare_ok(id_, expected, given=response.body)
1204 self._compare_ok(id_, expected, given=response.body)
1180
1205
1181 def test_api_grant_user_permission_wrong_permission(self):
1206 def test_api_grant_user_permission_wrong_permission(self):
1182 perm = 'haha.no.permission'
1207 perm = 'haha.no.permission'
1183 id_, params = _build_data(self.apikey, 'grant_user_permission',
1208 id_, params = _build_data(self.apikey, 'grant_user_permission',
1184 repoid=self.REPO,
1209 repoid=self.REPO,
1185 userid=TEST_USER_ADMIN_LOGIN,
1210 userid=TEST_USER_ADMIN_LOGIN,
1186 perm=perm)
1211 perm=perm)
1187 response = api_call(self, params)
1212 response = api_call(self, params)
1188
1213
1189 expected = 'permission `%s` does not exist' % perm
1214 expected = 'permission `%s` does not exist' % perm
1190 self._compare_error(id_, expected, given=response.body)
1215 self._compare_error(id_, expected, given=response.body)
1191
1216
1192 @mock.patch.object(RepoModel, 'grant_user_permission', crash)
1217 @mock.patch.object(RepoModel, 'grant_user_permission', crash)
1193 def test_api_grant_user_permission_exception_when_adding(self):
1218 def test_api_grant_user_permission_exception_when_adding(self):
1194 perm = 'repository.read'
1219 perm = 'repository.read'
1195 id_, params = _build_data(self.apikey, 'grant_user_permission',
1220 id_, params = _build_data(self.apikey, 'grant_user_permission',
1196 repoid=self.REPO,
1221 repoid=self.REPO,
1197 userid=TEST_USER_ADMIN_LOGIN,
1222 userid=TEST_USER_ADMIN_LOGIN,
1198 perm=perm)
1223 perm=perm)
1199 response = api_call(self, params)
1224 response = api_call(self, params)
1200
1225
1201 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
1226 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
1202 TEST_USER_ADMIN_LOGIN, self.REPO
1227 TEST_USER_ADMIN_LOGIN, self.REPO
1203 )
1228 )
1204 self._compare_error(id_, expected, given=response.body)
1229 self._compare_error(id_, expected, given=response.body)
1205
1230
1206 def test_api_revoke_user_permission(self):
1231 def test_api_revoke_user_permission(self):
1207 id_, params = _build_data(self.apikey, 'revoke_user_permission',
1232 id_, params = _build_data(self.apikey, 'revoke_user_permission',
1208 repoid=self.REPO,
1233 repoid=self.REPO,
1209 userid=TEST_USER_ADMIN_LOGIN,)
1234 userid=TEST_USER_ADMIN_LOGIN,)
1210 response = api_call(self, params)
1235 response = api_call(self, params)
1211
1236
1212 expected = {
1237 expected = {
1213 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1238 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1214 TEST_USER_ADMIN_LOGIN, self.REPO
1239 TEST_USER_ADMIN_LOGIN, self.REPO
1215 ),
1240 ),
1216 'success': True
1241 'success': True
1217 }
1242 }
1218 self._compare_ok(id_, expected, given=response.body)
1243 self._compare_ok(id_, expected, given=response.body)
1219
1244
1220 @mock.patch.object(RepoModel, 'revoke_user_permission', crash)
1245 @mock.patch.object(RepoModel, 'revoke_user_permission', crash)
1221 def test_api_revoke_user_permission_exception_when_adding(self):
1246 def test_api_revoke_user_permission_exception_when_adding(self):
1222 id_, params = _build_data(self.apikey, 'revoke_user_permission',
1247 id_, params = _build_data(self.apikey, 'revoke_user_permission',
1223 repoid=self.REPO,
1248 repoid=self.REPO,
1224 userid=TEST_USER_ADMIN_LOGIN,)
1249 userid=TEST_USER_ADMIN_LOGIN,)
1225 response = api_call(self, params)
1250 response = api_call(self, params)
1226
1251
1227 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
1252 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
1228 TEST_USER_ADMIN_LOGIN, self.REPO
1253 TEST_USER_ADMIN_LOGIN, self.REPO
1229 )
1254 )
1230 self._compare_error(id_, expected, given=response.body)
1255 self._compare_error(id_, expected, given=response.body)
1231
1256
1232 @parameterized.expand([('none', 'repository.none'),
1257 @parameterized.expand([('none', 'repository.none'),
1233 ('read', 'repository.read'),
1258 ('read', 'repository.read'),
1234 ('write', 'repository.write'),
1259 ('write', 'repository.write'),
1235 ('admin', 'repository.admin')])
1260 ('admin', 'repository.admin')])
1236 def test_api_grant_users_group_permission(self, name, perm):
1261 def test_api_grant_users_group_permission(self, name, perm):
1237 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1262 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1238 repoid=self.REPO,
1263 repoid=self.REPO,
1239 usersgroupid=TEST_USER_GROUP,
1264 usersgroupid=TEST_USER_GROUP,
1240 perm=perm)
1265 perm=perm)
1241 response = api_call(self, params)
1266 response = api_call(self, params)
1242
1267
1243 ret = {
1268 ret = {
1244 'msg': 'Granted perm: `%s` for user group: `%s` in repo: `%s`' % (
1269 'msg': 'Granted perm: `%s` for user group: `%s` in repo: `%s`' % (
1245 perm, TEST_USER_GROUP, self.REPO
1270 perm, TEST_USER_GROUP, self.REPO
1246 ),
1271 ),
1247 'success': True
1272 'success': True
1248 }
1273 }
1249 expected = ret
1274 expected = ret
1250 self._compare_ok(id_, expected, given=response.body)
1275 self._compare_ok(id_, expected, given=response.body)
1251
1276
1252 def test_api_grant_users_group_permission_wrong_permission(self):
1277 def test_api_grant_users_group_permission_wrong_permission(self):
1253 perm = 'haha.no.permission'
1278 perm = 'haha.no.permission'
1254 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1279 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1255 repoid=self.REPO,
1280 repoid=self.REPO,
1256 usersgroupid=TEST_USER_GROUP,
1281 usersgroupid=TEST_USER_GROUP,
1257 perm=perm)
1282 perm=perm)
1258 response = api_call(self, params)
1283 response = api_call(self, params)
1259
1284
1260 expected = 'permission `%s` does not exist' % perm
1285 expected = 'permission `%s` does not exist' % perm
1261 self._compare_error(id_, expected, given=response.body)
1286 self._compare_error(id_, expected, given=response.body)
1262
1287
1263 @mock.patch.object(RepoModel, 'grant_users_group_permission', crash)
1288 @mock.patch.object(RepoModel, 'grant_users_group_permission', crash)
1264 def test_api_grant_users_group_permission_exception_when_adding(self):
1289 def test_api_grant_users_group_permission_exception_when_adding(self):
1265 perm = 'repository.read'
1290 perm = 'repository.read'
1266 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1291 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1267 repoid=self.REPO,
1292 repoid=self.REPO,
1268 usersgroupid=TEST_USER_GROUP,
1293 usersgroupid=TEST_USER_GROUP,
1269 perm=perm)
1294 perm=perm)
1270 response = api_call(self, params)
1295 response = api_call(self, params)
1271
1296
1272 expected = 'failed to edit permission for user group: `%s` in repo: `%s`' % (
1297 expected = 'failed to edit permission for user group: `%s` in repo: `%s`' % (
1273 TEST_USER_GROUP, self.REPO
1298 TEST_USER_GROUP, self.REPO
1274 )
1299 )
1275 self._compare_error(id_, expected, given=response.body)
1300 self._compare_error(id_, expected, given=response.body)
1276
1301
1277 def test_api_revoke_users_group_permission(self):
1302 def test_api_revoke_users_group_permission(self):
1278 RepoModel().grant_users_group_permission(repo=self.REPO,
1303 RepoModel().grant_users_group_permission(repo=self.REPO,
1279 group_name=TEST_USER_GROUP,
1304 group_name=TEST_USER_GROUP,
1280 perm='repository.read')
1305 perm='repository.read')
1281 Session().commit()
1306 Session().commit()
1282 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
1307 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
1283 repoid=self.REPO,
1308 repoid=self.REPO,
1284 usersgroupid=TEST_USER_GROUP,)
1309 usersgroupid=TEST_USER_GROUP,)
1285 response = api_call(self, params)
1310 response = api_call(self, params)
1286
1311
1287 expected = {
1312 expected = {
1288 'msg': 'Revoked perm for user group: `%s` in repo: `%s`' % (
1313 'msg': 'Revoked perm for user group: `%s` in repo: `%s`' % (
1289 TEST_USER_GROUP, self.REPO
1314 TEST_USER_GROUP, self.REPO
1290 ),
1315 ),
1291 'success': True
1316 'success': True
1292 }
1317 }
1293 self._compare_ok(id_, expected, given=response.body)
1318 self._compare_ok(id_, expected, given=response.body)
1294
1319
1295 @mock.patch.object(RepoModel, 'revoke_users_group_permission', crash)
1320 @mock.patch.object(RepoModel, 'revoke_users_group_permission', crash)
1296 def test_api_revoke_users_group_permission_exception_when_adding(self):
1321 def test_api_revoke_users_group_permission_exception_when_adding(self):
1297
1322
1298 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
1323 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
1299 repoid=self.REPO,
1324 repoid=self.REPO,
1300 usersgroupid=TEST_USER_GROUP,)
1325 usersgroupid=TEST_USER_GROUP,)
1301 response = api_call(self, params)
1326 response = api_call(self, params)
1302
1327
1303 expected = 'failed to edit permission for user group: `%s` in repo: `%s`' % (
1328 expected = 'failed to edit permission for user group: `%s` in repo: `%s`' % (
1304 TEST_USER_GROUP, self.REPO
1329 TEST_USER_GROUP, self.REPO
1305 )
1330 )
1306 self._compare_error(id_, expected, given=response.body)
1331 self._compare_error(id_, expected, given=response.body)
General Comments 0
You need to be logged in to leave comments. Login now