##// END OF EJS Templates
make the permission update function idempotent
marcink -
r3791:e3857cbb default
parent child Browse files
Show More
@@ -1,1031 +1,1031
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.api
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 API controller for RhodeCode
7 7
8 8 :created_on: Aug 20, 2011
9 9 :author: marcink
10 10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software; you can redistribute it and/or
14 14 # modify it under the terms of the GNU General Public License
15 15 # as published by the Free Software Foundation; version 2
16 16 # of the License or (at your opinion) any later version of the license.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program; if not, write to the Free Software
25 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 26 # MA 02110-1301, USA.
27 27
28 28 import traceback
29 29 import logging
30 30
31 31 from rhodecode.controllers.api import JSONRPCController, JSONRPCError
32 32 from rhodecode.lib.auth import PasswordGenerator, AuthUser, \
33 33 HasPermissionAllDecorator, HasPermissionAnyDecorator, \
34 34 HasPermissionAnyApi, HasRepoPermissionAnyApi
35 35 from rhodecode.lib.utils import map_groups, repo2db_mapper
36 36 from rhodecode.lib.utils2 import str2bool, time_to_datetime, safe_int
37 37 from rhodecode.lib import helpers as h
38 38 from rhodecode.model.meta import Session
39 39 from rhodecode.model.scm import ScmModel
40 40 from rhodecode.model.repo import RepoModel
41 41 from rhodecode.model.user import UserModel
42 42 from rhodecode.model.users_group import UserGroupModel
43 from rhodecode.model.permission import PermissionModel
44 from rhodecode.model.db import Repository, RhodeCodeSetting, UserIpMap
43 from rhodecode.model.db import Repository, RhodeCodeSetting, UserIpMap,\
44 Permission
45 45 from rhodecode.lib.compat import json
46 46
47 47 log = logging.getLogger(__name__)
48 48
49 49
50 50 class OptionalAttr(object):
51 51 """
52 52 Special Optional Option that defines other attribute
53 53 """
54 54 def __init__(self, attr_name):
55 55 self.attr_name = attr_name
56 56
57 57 def __repr__(self):
58 58 return '<OptionalAttr:%s>' % self.attr_name
59 59
60 60 def __call__(self):
61 61 return self
62 62 #alias
63 63 OAttr = OptionalAttr
64 64
65 65
66 66 class Optional(object):
67 67 """
68 68 Defines an optional parameter::
69 69
70 70 param = param.getval() if isinstance(param, Optional) else param
71 71 param = param() if isinstance(param, Optional) else param
72 72
73 73 is equivalent of::
74 74
75 75 param = Optional.extract(param)
76 76
77 77 """
78 78 def __init__(self, type_):
79 79 self.type_ = type_
80 80
81 81 def __repr__(self):
82 82 return '<Optional:%s>' % self.type_.__repr__()
83 83
84 84 def __call__(self):
85 85 return self.getval()
86 86
87 87 def getval(self):
88 88 """
89 89 returns value from this Optional instance
90 90 """
91 91 return self.type_
92 92
93 93 @classmethod
94 94 def extract(cls, val):
95 95 if isinstance(val, cls):
96 96 return val.getval()
97 97 return val
98 98
99 99
100 100 def get_user_or_error(userid):
101 101 """
102 102 Get user by id or name or return JsonRPCError if not found
103 103
104 104 :param userid:
105 105 """
106 106 user = UserModel().get_user(userid)
107 107 if user is None:
108 108 raise JSONRPCError("user `%s` does not exist" % userid)
109 109 return user
110 110
111 111
112 112 def get_repo_or_error(repoid):
113 113 """
114 114 Get repo by id or name or return JsonRPCError if not found
115 115
116 116 :param userid:
117 117 """
118 118 repo = RepoModel().get_repo(repoid)
119 119 if repo is None:
120 120 raise JSONRPCError('repository `%s` does not exist' % (repoid))
121 121 return repo
122 122
123 123
124 124 def get_users_group_or_error(usersgroupid):
125 125 """
126 126 Get user group by id or name or return JsonRPCError if not found
127 127
128 128 :param userid:
129 129 """
130 130 users_group = UserGroupModel().get_group(usersgroupid)
131 131 if users_group is None:
132 132 raise JSONRPCError('user group `%s` does not exist' % usersgroupid)
133 133 return users_group
134 134
135 135
136 136 def get_perm_or_error(permid):
137 137 """
138 138 Get permission by id or name or return JsonRPCError if not found
139 139
140 140 :param userid:
141 141 """
142 perm = PermissionModel().get_permission_by_name(permid)
142 perm = Permission.get_by_key(permid)
143 143 if perm is None:
144 144 raise JSONRPCError('permission `%s` does not exist' % (permid))
145 145 return perm
146 146
147 147
148 148 class ApiController(JSONRPCController):
149 149 """
150 150 API Controller
151 151
152 152
153 153 Each method needs to have USER as argument this is then based on given
154 154 API_KEY propagated as instance of user object
155 155
156 156 Preferably this should be first argument also
157 157
158 158
159 159 Each function should also **raise** JSONRPCError for any
160 160 errors that happens
161 161
162 162 """
163 163
164 164 @HasPermissionAllDecorator('hg.admin')
165 165 def pull(self, apiuser, repoid):
166 166 """
167 167 Dispatch pull action on given repo
168 168
169 169 :param apiuser:
170 170 :param repoid:
171 171 """
172 172
173 173 repo = get_repo_or_error(repoid)
174 174
175 175 try:
176 176 ScmModel().pull_changes(repo.repo_name,
177 177 self.rhodecode_user.username)
178 178 return 'Pulled from `%s`' % repo.repo_name
179 179 except Exception:
180 180 log.error(traceback.format_exc())
181 181 raise JSONRPCError(
182 182 'Unable to pull changes from `%s`' % repo.repo_name
183 183 )
184 184
185 185 @HasPermissionAllDecorator('hg.admin')
186 186 def rescan_repos(self, apiuser, remove_obsolete=Optional(False)):
187 187 """
188 188 Dispatch rescan repositories action. If remove_obsolete is set
189 189 than also delete repos that are in database but not in the filesystem.
190 190 aka "clean zombies"
191 191
192 192 :param apiuser:
193 193 :param remove_obsolete:
194 194 """
195 195
196 196 try:
197 197 rm_obsolete = Optional.extract(remove_obsolete)
198 198 added, removed = repo2db_mapper(ScmModel().repo_scan(),
199 199 remove_obsolete=rm_obsolete)
200 200 return {'added': added, 'removed': removed}
201 201 except Exception:
202 202 log.error(traceback.format_exc())
203 203 raise JSONRPCError(
204 204 'Error occurred during rescan repositories action'
205 205 )
206 206
207 207 def invalidate_cache(self, apiuser, repoid):
208 208 """
209 209 Dispatch cache invalidation action on given repo
210 210
211 211 :param apiuser:
212 212 :param repoid:
213 213 """
214 214 repo = get_repo_or_error(repoid)
215 215 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
216 216 # check if we have admin permission for this repo !
217 217 if HasRepoPermissionAnyApi('repository.admin',
218 218 'repository.write')(user=apiuser,
219 219 repo_name=repo.repo_name) is False:
220 220 raise JSONRPCError('repository `%s` does not exist' % (repoid))
221 221
222 222 try:
223 223 invalidated_keys = ScmModel().mark_for_invalidation(repo.repo_name)
224 224 Session().commit()
225 225 return ('Cache for repository `%s` was invalidated: '
226 226 'invalidated cache keys: %s' % (repoid, invalidated_keys))
227 227 except Exception:
228 228 log.error(traceback.format_exc())
229 229 raise JSONRPCError(
230 230 'Error occurred during cache invalidation action'
231 231 )
232 232
233 233 def lock(self, apiuser, repoid, locked=Optional(None),
234 234 userid=Optional(OAttr('apiuser'))):
235 235 """
236 236 Set locking state on particular repository by given user, if
237 237 this command is runned by non-admin account userid is set to user
238 238 who is calling this method
239 239
240 240 :param apiuser:
241 241 :param repoid:
242 242 :param userid:
243 243 :param locked:
244 244 """
245 245 repo = get_repo_or_error(repoid)
246 246 if HasPermissionAnyApi('hg.admin')(user=apiuser):
247 247 pass
248 248 elif HasRepoPermissionAnyApi('repository.admin',
249 249 'repository.write')(user=apiuser,
250 250 repo_name=repo.repo_name):
251 251 #make sure normal user does not pass someone else userid,
252 252 #he is not allowed to do that
253 253 if not isinstance(userid, Optional) and userid != apiuser.user_id:
254 254 raise JSONRPCError(
255 255 'userid is not the same as your user'
256 256 )
257 257 else:
258 258 raise JSONRPCError('repository `%s` does not exist' % (repoid))
259 259
260 260 if isinstance(userid, Optional):
261 261 userid = apiuser.user_id
262 262
263 263 user = get_user_or_error(userid)
264 264
265 265 if isinstance(locked, Optional):
266 266 lockobj = Repository.getlock(repo)
267 267
268 268 if lockobj[0] is None:
269 269 return ('Repo `%s` not locked. Locked=`False`.'
270 270 % (repo.repo_name))
271 271 else:
272 272 userid, time_ = lockobj
273 273 user = get_user_or_error(userid)
274 274
275 275 return ('Repo `%s` locked by `%s`. Locked=`True`. '
276 276 'Locked since: `%s`'
277 277 % (repo.repo_name, user.username,
278 278 json.dumps(time_to_datetime(time_))))
279 279
280 280 else:
281 281 locked = str2bool(locked)
282 282 try:
283 283 if locked:
284 284 Repository.lock(repo, user.user_id)
285 285 else:
286 286 Repository.unlock(repo)
287 287
288 288 return ('User `%s` set lock state for repo `%s` to `%s`'
289 289 % (user.username, repo.repo_name, locked))
290 290 except Exception:
291 291 log.error(traceback.format_exc())
292 292 raise JSONRPCError(
293 293 'Error occurred locking repository `%s`' % repo.repo_name
294 294 )
295 295
296 296 def get_locks(self, apiuser, userid=Optional(OAttr('apiuser'))):
297 297 """
298 298 Get all locks for given userid, if
299 299 this command is runned by non-admin account userid is set to user
300 300 who is calling this method, thus returning locks for himself
301 301
302 302 :param apiuser:
303 303 :param userid:
304 304 """
305 305 if HasPermissionAnyApi('hg.admin')(user=apiuser):
306 306 pass
307 307 else:
308 308 #make sure normal user does not pass someone else userid,
309 309 #he is not allowed to do that
310 310 if not isinstance(userid, Optional) and userid != apiuser.user_id:
311 311 raise JSONRPCError(
312 312 'userid is not the same as your user'
313 313 )
314 314 ret = []
315 315 if isinstance(userid, Optional):
316 316 user = None
317 317 else:
318 318 user = get_user_or_error(userid)
319 319
320 320 #show all locks
321 321 for r in Repository.getAll():
322 322 userid, time_ = r.locked
323 323 if time_:
324 324 _api_data = r.get_api_data()
325 325 # if we use userfilter just show the locks for this user
326 326 if user:
327 327 if safe_int(userid) == user.user_id:
328 328 ret.append(_api_data)
329 329 else:
330 330 ret.append(_api_data)
331 331
332 332 return ret
333 333
334 334 @HasPermissionAllDecorator('hg.admin')
335 335 def show_ip(self, apiuser, userid):
336 336 """
337 337 Shows IP address as seen from RhodeCode server, together with all
338 338 defined IP addresses for given user
339 339
340 340 :param apiuser:
341 341 :param userid:
342 342 """
343 343 user = get_user_or_error(userid)
344 344 ips = UserIpMap.query().filter(UserIpMap.user == user).all()
345 345 return dict(
346 346 ip_addr_server=self.ip_addr,
347 347 user_ips=ips
348 348 )
349 349
350 350 def get_user(self, apiuser, userid=Optional(OAttr('apiuser'))):
351 351 """"
352 352 Get a user by username, or userid, if userid is given
353 353
354 354 :param apiuser:
355 355 :param userid:
356 356 """
357 357 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
358 358 #make sure normal user does not pass someone else userid,
359 359 #he is not allowed to do that
360 360 if not isinstance(userid, Optional) and userid != apiuser.user_id:
361 361 raise JSONRPCError(
362 362 'userid is not the same as your user'
363 363 )
364 364
365 365 if isinstance(userid, Optional):
366 366 userid = apiuser.user_id
367 367
368 368 user = get_user_or_error(userid)
369 369 data = user.get_api_data()
370 370 data['permissions'] = AuthUser(user_id=user.user_id).permissions
371 371 return data
372 372
373 373 @HasPermissionAllDecorator('hg.admin')
374 374 def get_users(self, apiuser):
375 375 """"
376 376 Get all users
377 377
378 378 :param apiuser:
379 379 """
380 380
381 381 result = []
382 382 for user in UserModel().get_all():
383 383 result.append(user.get_api_data())
384 384 return result
385 385
386 386 @HasPermissionAllDecorator('hg.admin')
387 387 def create_user(self, apiuser, username, email, password,
388 388 firstname=Optional(None), lastname=Optional(None),
389 389 active=Optional(True), admin=Optional(False),
390 390 ldap_dn=Optional(None)):
391 391 """
392 392 Create new user
393 393
394 394 :param apiuser:
395 395 :param username:
396 396 :param email:
397 397 :param password:
398 398 :param firstname:
399 399 :param lastname:
400 400 :param active:
401 401 :param admin:
402 402 :param ldap_dn:
403 403 """
404 404
405 405 if UserModel().get_by_username(username):
406 406 raise JSONRPCError("user `%s` already exist" % username)
407 407
408 408 if UserModel().get_by_email(email, case_insensitive=True):
409 409 raise JSONRPCError("email `%s` already exist" % email)
410 410
411 411 if Optional.extract(ldap_dn):
412 412 # generate temporary password if ldap_dn
413 413 password = PasswordGenerator().gen_password(length=8)
414 414
415 415 try:
416 416 user = UserModel().create_or_update(
417 417 username=Optional.extract(username),
418 418 password=Optional.extract(password),
419 419 email=Optional.extract(email),
420 420 firstname=Optional.extract(firstname),
421 421 lastname=Optional.extract(lastname),
422 422 active=Optional.extract(active),
423 423 admin=Optional.extract(admin),
424 424 ldap_dn=Optional.extract(ldap_dn)
425 425 )
426 426 Session().commit()
427 427 return dict(
428 428 msg='created new user `%s`' % username,
429 429 user=user.get_api_data()
430 430 )
431 431 except Exception:
432 432 log.error(traceback.format_exc())
433 433 raise JSONRPCError('failed to create user `%s`' % username)
434 434
435 435 @HasPermissionAllDecorator('hg.admin')
436 436 def update_user(self, apiuser, userid, username=Optional(None),
437 437 email=Optional(None), firstname=Optional(None),
438 438 lastname=Optional(None), active=Optional(None),
439 439 admin=Optional(None), ldap_dn=Optional(None),
440 440 password=Optional(None)):
441 441 """
442 442 Updates given user
443 443
444 444 :param apiuser:
445 445 :param userid:
446 446 :param username:
447 447 :param email:
448 448 :param firstname:
449 449 :param lastname:
450 450 :param active:
451 451 :param admin:
452 452 :param ldap_dn:
453 453 :param password:
454 454 """
455 455
456 456 user = get_user_or_error(userid)
457 457
458 458 # call function and store only updated arguments
459 459 updates = {}
460 460
461 461 def store_update(attr, name):
462 462 if not isinstance(attr, Optional):
463 463 updates[name] = attr
464 464
465 465 try:
466 466
467 467 store_update(username, 'username')
468 468 store_update(password, 'password')
469 469 store_update(email, 'email')
470 470 store_update(firstname, 'name')
471 471 store_update(lastname, 'lastname')
472 472 store_update(active, 'active')
473 473 store_update(admin, 'admin')
474 474 store_update(ldap_dn, 'ldap_dn')
475 475
476 476 user = UserModel().update_user(user, **updates)
477 477 Session().commit()
478 478 return dict(
479 479 msg='updated user ID:%s %s' % (user.user_id, user.username),
480 480 user=user.get_api_data()
481 481 )
482 482 except Exception:
483 483 log.error(traceback.format_exc())
484 484 raise JSONRPCError('failed to update user `%s`' % userid)
485 485
486 486 @HasPermissionAllDecorator('hg.admin')
487 487 def delete_user(self, apiuser, userid):
488 488 """"
489 489 Deletes an user
490 490
491 491 :param apiuser:
492 492 :param userid:
493 493 """
494 494 user = get_user_or_error(userid)
495 495
496 496 try:
497 497 UserModel().delete(userid)
498 498 Session().commit()
499 499 return dict(
500 500 msg='deleted user ID:%s %s' % (user.user_id, user.username),
501 501 user=None
502 502 )
503 503 except Exception:
504 504 log.error(traceback.format_exc())
505 505 raise JSONRPCError('failed to delete ID:%s %s' % (user.user_id,
506 506 user.username))
507 507
508 508 @HasPermissionAllDecorator('hg.admin')
509 509 def get_users_group(self, apiuser, usersgroupid):
510 510 """"
511 511 Get user group by name or id
512 512
513 513 :param apiuser:
514 514 :param usersgroupid:
515 515 """
516 516 users_group = get_users_group_or_error(usersgroupid)
517 517
518 518 data = users_group.get_api_data()
519 519
520 520 members = []
521 521 for user in users_group.members:
522 522 user = user.user
523 523 members.append(user.get_api_data())
524 524 data['members'] = members
525 525 return data
526 526
527 527 @HasPermissionAllDecorator('hg.admin')
528 528 def get_users_groups(self, apiuser):
529 529 """"
530 530 Get all user groups
531 531
532 532 :param apiuser:
533 533 """
534 534
535 535 result = []
536 536 for users_group in UserGroupModel().get_all():
537 537 result.append(users_group.get_api_data())
538 538 return result
539 539
540 540 @HasPermissionAllDecorator('hg.admin')
541 541 def create_users_group(self, apiuser, group_name, active=Optional(True)):
542 542 """
543 543 Creates an new usergroup
544 544
545 545 :param apiuser:
546 546 :param group_name:
547 547 :param active:
548 548 """
549 549
550 550 if UserGroupModel().get_by_name(group_name):
551 551 raise JSONRPCError("user group `%s` already exist" % group_name)
552 552
553 553 try:
554 554 active = Optional.extract(active)
555 555 ug = UserGroupModel().create(name=group_name, active=active)
556 556 Session().commit()
557 557 return dict(
558 558 msg='created new user group `%s`' % group_name,
559 559 users_group=ug.get_api_data()
560 560 )
561 561 except Exception:
562 562 log.error(traceback.format_exc())
563 563 raise JSONRPCError('failed to create group `%s`' % group_name)
564 564
565 565 @HasPermissionAllDecorator('hg.admin')
566 566 def add_user_to_users_group(self, apiuser, usersgroupid, userid):
567 567 """"
568 568 Add a user to a user group
569 569
570 570 :param apiuser:
571 571 :param usersgroupid:
572 572 :param userid:
573 573 """
574 574 user = get_user_or_error(userid)
575 575 users_group = get_users_group_or_error(usersgroupid)
576 576
577 577 try:
578 578 ugm = UserGroupModel().add_user_to_group(users_group, user)
579 579 success = True if ugm != True else False
580 580 msg = 'added member `%s` to user group `%s`' % (
581 581 user.username, users_group.users_group_name
582 582 )
583 583 msg = msg if success else 'User is already in that group'
584 584 Session().commit()
585 585
586 586 return dict(
587 587 success=success,
588 588 msg=msg
589 589 )
590 590 except Exception:
591 591 log.error(traceback.format_exc())
592 592 raise JSONRPCError(
593 593 'failed to add member to user group `%s`' % (
594 594 users_group.users_group_name
595 595 )
596 596 )
597 597
598 598 @HasPermissionAllDecorator('hg.admin')
599 599 def remove_user_from_users_group(self, apiuser, usersgroupid, userid):
600 600 """
601 601 Remove user from a group
602 602
603 603 :param apiuser:
604 604 :param usersgroupid:
605 605 :param userid:
606 606 """
607 607 user = get_user_or_error(userid)
608 608 users_group = get_users_group_or_error(usersgroupid)
609 609
610 610 try:
611 611 success = UserGroupModel().remove_user_from_group(users_group,
612 612 user)
613 613 msg = 'removed member `%s` from user group `%s`' % (
614 614 user.username, users_group.users_group_name
615 615 )
616 616 msg = msg if success else "User wasn't in group"
617 617 Session().commit()
618 618 return dict(success=success, msg=msg)
619 619 except Exception:
620 620 log.error(traceback.format_exc())
621 621 raise JSONRPCError(
622 622 'failed to remove member from user group `%s`' % (
623 623 users_group.users_group_name
624 624 )
625 625 )
626 626
627 627 def get_repo(self, apiuser, repoid):
628 628 """"
629 629 Get repository by name
630 630
631 631 :param apiuser:
632 632 :param repoid:
633 633 """
634 634 repo = get_repo_or_error(repoid)
635 635
636 636 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
637 637 # check if we have admin permission for this repo !
638 638 if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
639 639 repo_name=repo.repo_name) is False:
640 640 raise JSONRPCError('repository `%s` does not exist' % (repoid))
641 641
642 642 members = []
643 643 followers = []
644 644 for user in repo.repo_to_perm:
645 645 perm = user.permission.permission_name
646 646 user = user.user
647 647 user_data = user.get_api_data()
648 648 user_data['type'] = "user"
649 649 user_data['permission'] = perm
650 650 members.append(user_data)
651 651
652 652 for users_group in repo.users_group_to_perm:
653 653 perm = users_group.permission.permission_name
654 654 users_group = users_group.users_group
655 655 users_group_data = users_group.get_api_data()
656 656 users_group_data['type'] = "users_group"
657 657 users_group_data['permission'] = perm
658 658 members.append(users_group_data)
659 659
660 660 for user in repo.followers:
661 661 followers.append(user.user.get_api_data())
662 662
663 663 data = repo.get_api_data()
664 664 data['members'] = members
665 665 data['followers'] = followers
666 666 return data
667 667
668 668 def get_repos(self, apiuser):
669 669 """"
670 670 Get all repositories
671 671
672 672 :param apiuser:
673 673 """
674 674 result = []
675 675 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
676 676 repos = RepoModel().get_all_user_repos(user=apiuser)
677 677 else:
678 678 repos = RepoModel().get_all()
679 679
680 680 for repo in repos:
681 681 result.append(repo.get_api_data())
682 682 return result
683 683
684 684 @HasPermissionAllDecorator('hg.admin')
685 685 def get_repo_nodes(self, apiuser, repoid, revision, root_path,
686 686 ret_type='all'):
687 687 """
688 688 returns a list of nodes and it's children
689 689 for a given path at given revision. It's possible to specify ret_type
690 690 to show only files or dirs
691 691
692 692 :param apiuser:
693 693 :param repoid: name or id of repository
694 694 :param revision: revision for which listing should be done
695 695 :param root_path: path from which start displaying
696 696 :param ret_type: return type 'all|files|dirs' nodes
697 697 """
698 698 repo = get_repo_or_error(repoid)
699 699 try:
700 700 _d, _f = ScmModel().get_nodes(repo, revision, root_path,
701 701 flat=False)
702 702 _map = {
703 703 'all': _d + _f,
704 704 'files': _f,
705 705 'dirs': _d,
706 706 }
707 707 return _map[ret_type]
708 708 except KeyError:
709 709 raise JSONRPCError('ret_type must be one of %s' % _map.keys())
710 710 except Exception:
711 711 log.error(traceback.format_exc())
712 712 raise JSONRPCError(
713 713 'failed to get repo: `%s` nodes' % repo.repo_name
714 714 )
715 715
716 716 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
717 717 def create_repo(self, apiuser, repo_name, owner=Optional(OAttr('apiuser')),
718 718 repo_type=Optional('hg'),
719 719 description=Optional(''), private=Optional(False),
720 720 clone_uri=Optional(None), landing_rev=Optional('tip'),
721 721 enable_statistics=Optional(False),
722 722 enable_locking=Optional(False),
723 723 enable_downloads=Optional(False)):
724 724 """
725 725 Create repository, if clone_url is given it makes a remote clone
726 726 if repo_name is within a group name the groups will be created
727 727 automatically if they aren't present
728 728
729 729 :param apiuser:
730 730 :param repo_name:
731 731 :param onwer:
732 732 :param repo_type:
733 733 :param description:
734 734 :param private:
735 735 :param clone_uri:
736 736 :param landing_rev:
737 737 """
738 738 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
739 739 if not isinstance(owner, Optional):
740 740 #forbid setting owner for non-admins
741 741 raise JSONRPCError(
742 742 'Only RhodeCode admin can specify `owner` param'
743 743 )
744 744 if isinstance(owner, Optional):
745 745 owner = apiuser.user_id
746 746
747 747 owner = get_user_or_error(owner)
748 748
749 749 if RepoModel().get_by_repo_name(repo_name):
750 750 raise JSONRPCError("repo `%s` already exist" % repo_name)
751 751
752 752 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
753 753 if isinstance(private, Optional):
754 754 private = defs.get('repo_private') or Optional.extract(private)
755 755 if isinstance(repo_type, Optional):
756 756 repo_type = defs.get('repo_type')
757 757 if isinstance(enable_statistics, Optional):
758 758 enable_statistics = defs.get('repo_enable_statistics')
759 759 if isinstance(enable_locking, Optional):
760 760 enable_locking = defs.get('repo_enable_locking')
761 761 if isinstance(enable_downloads, Optional):
762 762 enable_downloads = defs.get('repo_enable_downloads')
763 763
764 764 clone_uri = Optional.extract(clone_uri)
765 765 description = Optional.extract(description)
766 766 landing_rev = Optional.extract(landing_rev)
767 767
768 768 try:
769 769 # create structure of groups and return the last group
770 770 group = map_groups(repo_name)
771 771
772 772 repo = RepoModel().create_repo(
773 773 repo_name=repo_name,
774 774 repo_type=repo_type,
775 775 description=description,
776 776 owner=owner,
777 777 private=private,
778 778 clone_uri=clone_uri,
779 779 repos_group=group,
780 780 landing_rev=landing_rev,
781 781 enable_statistics=enable_statistics,
782 782 enable_downloads=enable_downloads,
783 783 enable_locking=enable_locking
784 784 )
785 785
786 786 Session().commit()
787 787 return dict(
788 788 msg="Created new repository `%s`" % (repo.repo_name),
789 789 repo=repo.get_api_data()
790 790 )
791 791 except Exception:
792 792 log.error(traceback.format_exc())
793 793 raise JSONRPCError('failed to create repository `%s`' % repo_name)
794 794
795 795 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
796 796 def fork_repo(self, apiuser, repoid, fork_name, owner=Optional(OAttr('apiuser')),
797 797 description=Optional(''), copy_permissions=Optional(False),
798 798 private=Optional(False), landing_rev=Optional('tip')):
799 799 repo = get_repo_or_error(repoid)
800 800 repo_name = repo.repo_name
801 801
802 802 _repo = RepoModel().get_by_repo_name(fork_name)
803 803 if _repo:
804 804 type_ = 'fork' if _repo.fork else 'repo'
805 805 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
806 806
807 807 if HasPermissionAnyApi('hg.admin')(user=apiuser):
808 808 pass
809 809 elif HasRepoPermissionAnyApi('repository.admin',
810 810 'repository.write',
811 811 'repository.read')(user=apiuser,
812 812 repo_name=repo.repo_name):
813 813 if not isinstance(owner, Optional):
814 814 #forbid setting owner for non-admins
815 815 raise JSONRPCError(
816 816 'Only RhodeCode admin can specify `owner` param'
817 817 )
818 818 else:
819 819 raise JSONRPCError('repository `%s` does not exist' % (repoid))
820 820
821 821 if isinstance(owner, Optional):
822 822 owner = apiuser.user_id
823 823
824 824 owner = get_user_or_error(owner)
825 825
826 826 try:
827 827 # create structure of groups and return the last group
828 828 group = map_groups(fork_name)
829 829
830 830 form_data = dict(
831 831 repo_name=fork_name,
832 832 repo_name_full=fork_name,
833 833 repo_group=group,
834 834 repo_type=repo.repo_type,
835 835 description=Optional.extract(description),
836 836 private=Optional.extract(private),
837 837 copy_permissions=Optional.extract(copy_permissions),
838 838 landing_rev=Optional.extract(landing_rev),
839 839 update_after_clone=False,
840 840 fork_parent_id=repo.repo_id,
841 841 )
842 842 RepoModel().create_fork(form_data, cur_user=owner)
843 843 return dict(
844 844 msg='Created fork of `%s` as `%s`' % (repo.repo_name,
845 845 fork_name),
846 846 success=True # cannot return the repo data here since fork
847 847 # cann be done async
848 848 )
849 849 except Exception:
850 850 log.error(traceback.format_exc())
851 851 raise JSONRPCError(
852 852 'failed to fork repository `%s` as `%s`' % (repo_name,
853 853 fork_name)
854 854 )
855 855
856 856 def delete_repo(self, apiuser, repoid, forks=Optional(None)):
857 857 """
858 858 Deletes a given repository
859 859
860 860 :param apiuser:
861 861 :param repoid:
862 862 :param forks: detach or delete, what do do with attached forks for repo
863 863 """
864 864 repo = get_repo_or_error(repoid)
865 865
866 866 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
867 867 # check if we have admin permission for this repo !
868 868 if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
869 869 repo_name=repo.repo_name) is False:
870 870 raise JSONRPCError('repository `%s` does not exist' % (repoid))
871 871
872 872 try:
873 873 handle_forks = Optional.extract(forks)
874 874 _forks_msg = ''
875 875 _forks = [f for f in repo.forks]
876 876 if handle_forks == 'detach':
877 877 _forks_msg = ' ' + _('Detached %s forks') % len(_forks)
878 878 elif handle_forks == 'delete':
879 879 _forks_msg = ' ' + _('Deleted %s forks') % len(_forks)
880 880 elif _forks:
881 881 raise JSONRPCError(
882 882 'Cannot delete `%s` it still contains attached forks'
883 883 % repo.repo_name
884 884 )
885 885
886 886 RepoModel().delete(repo, forks=forks)
887 887 Session().commit()
888 888 return dict(
889 889 msg='Deleted repository `%s`%s' % (repo.repo_name, _forks_msg),
890 890 success=True
891 891 )
892 892 except Exception:
893 893 log.error(traceback.format_exc())
894 894 raise JSONRPCError(
895 895 'failed to delete repository `%s`' % repo.repo_name
896 896 )
897 897
898 898 @HasPermissionAllDecorator('hg.admin')
899 899 def grant_user_permission(self, apiuser, repoid, userid, perm):
900 900 """
901 901 Grant permission for user on given repository, or update existing one
902 902 if found
903 903
904 904 :param repoid:
905 905 :param userid:
906 906 :param perm:
907 907 """
908 908 repo = get_repo_or_error(repoid)
909 909 user = get_user_or_error(userid)
910 910 perm = get_perm_or_error(perm)
911 911
912 912 try:
913 913
914 914 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
915 915
916 916 Session().commit()
917 917 return dict(
918 918 msg='Granted perm: `%s` for user: `%s` in repo: `%s`' % (
919 919 perm.permission_name, user.username, repo.repo_name
920 920 ),
921 921 success=True
922 922 )
923 923 except Exception:
924 924 log.error(traceback.format_exc())
925 925 raise JSONRPCError(
926 926 'failed to edit permission for user: `%s` in repo: `%s`' % (
927 927 userid, repoid
928 928 )
929 929 )
930 930
931 931 @HasPermissionAllDecorator('hg.admin')
932 932 def revoke_user_permission(self, apiuser, repoid, userid):
933 933 """
934 934 Revoke permission for user on given repository
935 935
936 936 :param apiuser:
937 937 :param repoid:
938 938 :param userid:
939 939 """
940 940
941 941 repo = get_repo_or_error(repoid)
942 942 user = get_user_or_error(userid)
943 943 try:
944 944
945 945 RepoModel().revoke_user_permission(repo=repo, user=user)
946 946
947 947 Session().commit()
948 948 return dict(
949 949 msg='Revoked perm for user: `%s` in repo: `%s`' % (
950 950 user.username, repo.repo_name
951 951 ),
952 952 success=True
953 953 )
954 954 except Exception:
955 955 log.error(traceback.format_exc())
956 956 raise JSONRPCError(
957 957 'failed to edit permission for user: `%s` in repo: `%s`' % (
958 958 userid, repoid
959 959 )
960 960 )
961 961
962 962 @HasPermissionAllDecorator('hg.admin')
963 963 def grant_users_group_permission(self, apiuser, repoid, usersgroupid,
964 964 perm):
965 965 """
966 966 Grant permission for user group on given repository, or update
967 967 existing one if found
968 968
969 969 :param apiuser:
970 970 :param repoid:
971 971 :param usersgroupid:
972 972 :param perm:
973 973 """
974 974 repo = get_repo_or_error(repoid)
975 975 perm = get_perm_or_error(perm)
976 976 users_group = get_users_group_or_error(usersgroupid)
977 977
978 978 try:
979 979 RepoModel().grant_users_group_permission(repo=repo,
980 980 group_name=users_group,
981 981 perm=perm)
982 982
983 983 Session().commit()
984 984 return dict(
985 985 msg='Granted perm: `%s` for user group: `%s` in '
986 986 'repo: `%s`' % (
987 987 perm.permission_name, users_group.users_group_name,
988 988 repo.repo_name
989 989 ),
990 990 success=True
991 991 )
992 992 except Exception:
993 993 log.error(traceback.format_exc())
994 994 raise JSONRPCError(
995 995 'failed to edit permission for user group: `%s` in '
996 996 'repo: `%s`' % (
997 997 usersgroupid, repo.repo_name
998 998 )
999 999 )
1000 1000
1001 1001 @HasPermissionAllDecorator('hg.admin')
1002 1002 def revoke_users_group_permission(self, apiuser, repoid, usersgroupid):
1003 1003 """
1004 1004 Revoke permission for user group on given repository
1005 1005
1006 1006 :param apiuser:
1007 1007 :param repoid:
1008 1008 :param usersgroupid:
1009 1009 """
1010 1010 repo = get_repo_or_error(repoid)
1011 1011 users_group = get_users_group_or_error(usersgroupid)
1012 1012
1013 1013 try:
1014 1014 RepoModel().revoke_users_group_permission(repo=repo,
1015 1015 group_name=users_group)
1016 1016
1017 1017 Session().commit()
1018 1018 return dict(
1019 1019 msg='Revoked perm for user group: `%s` in repo: `%s`' % (
1020 1020 users_group.users_group_name, repo.repo_name
1021 1021 ),
1022 1022 success=True
1023 1023 )
1024 1024 except Exception:
1025 1025 log.error(traceback.format_exc())
1026 1026 raise JSONRPCError(
1027 1027 'failed to edit permission for user group: `%s` in '
1028 1028 'repo: `%s`' % (
1029 1029 users_group.users_group_name, repo.repo_name
1030 1030 )
1031 1031 )
@@ -1,144 +1,101
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.permission
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 permissions model for RhodeCode
7 7
8 8 :created_on: Aug 20, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28
29 29 from sqlalchemy.exc import DatabaseError
30 30
31 from rhodecode.lib.caching_query import FromCache
32
33 31 from rhodecode.model import BaseModel
34 32 from rhodecode.model.db import User, Permission, UserToPerm, UserRepoToPerm,\
35 33 UserRepoGroupToPerm
34 from rhodecode.lib.utils2 import str2bool
36 35
37 36 log = logging.getLogger(__name__)
38 37
39 38
40 39 class PermissionModel(BaseModel):
41 40 """
42 41 Permissions model for RhodeCode
43 42 """
44 43
45 44 cls = Permission
46 45
47 def get_permission(self, permission_id, cache=False):
48 """
49 Get's permissions by id
50
51 :param permission_id: id of permission to get from database
52 :param cache: use Cache for this query
53 """
54 perm = self.sa.query(Permission)
55 if cache:
56 perm = perm.options(FromCache("sql_cache_short",
57 "get_permission_%s" % permission_id))
58 return perm.get(permission_id)
59
60 def get_permission_by_name(self, name, cache=False):
61 """
62 Get's permissions by given name
63
64 :param name: name to fetch
65 :param cache: Use cache for this query
66 """
67 perm = self.sa.query(Permission)\
68 .filter(Permission.permission_name == name)
69 if cache:
70 perm = perm.options(FromCache("sql_cache_short",
71 "get_permission_%s" % name))
72 return perm.scalar()
73
74 46 def update(self, form_result):
75 perm_user = self.sa.query(User)\
76 .filter(User.username ==
77 form_result['perm_user_name']).scalar()
78 u2p = self.sa.query(UserToPerm).filter(UserToPerm.user ==
79 perm_user).all()
80 if len(u2p) != len(User.DEFAULT_PERMISSIONS):
81 raise Exception('Defined: %s should be %s permissions for default'
82 ' user. This should not happen please verify'
83 ' your database' % (len(u2p), len(User.DEFAULT_PERMISSIONS)))
47 perm_user = User.get_by_username(username=form_result['perm_user_name'])
48 u2p = self.sa.query(UserToPerm).filter(UserToPerm.user == perm_user).all()
84 49
85 50 try:
86 # stage 1 change defaults
51 def _make_new(usr, perm_name):
52 new = UserToPerm()
53 new.user = usr
54 new.permission = Permission.get_by_key(perm_name)
55 return new
56 # clear current entries, to make this function idempotent
57 # it will fix even if we define more permissions or permissions
58 # are somehow missing
87 59 for p in u2p:
88 if p.permission.permission_name.startswith('repository.'):
89 p.permission = self.get_permission_by_name(
90 form_result['default_repo_perm'])
91 self.sa.add(p)
92
93 elif p.permission.permission_name.startswith('group.'):
94 p.permission = self.get_permission_by_name(
95 form_result['default_group_perm'])
96 self.sa.add(p)
97
98 elif p.permission.permission_name.startswith('hg.register.'):
99 p.permission = self.get_permission_by_name(
100 form_result['default_register'])
101 self.sa.add(p)
102
103 elif p.permission.permission_name.startswith('hg.create.'):
104 p.permission = self.get_permission_by_name(
105 form_result['default_create'])
106 self.sa.add(p)
107
108 elif p.permission.permission_name.startswith('hg.fork.'):
109 p.permission = self.get_permission_by_name(
110 form_result['default_fork'])
60 self.sa.delete(p)
61 #create fresh set of permissions
62 for def_perm_key in ['default_repo_perm', 'default_group_perm',
63 'default_register', 'default_create',
64 'default_fork']:
65 p = _make_new(perm_user, form_result[def_perm_key])
111 66 self.sa.add(p)
112 67
113 68 #stage 2 update all default permissions for repos if checked
114 69 if form_result['overwrite_default_repo'] == True:
115 70 _def_name = form_result['default_repo_perm'].split('repository.')[-1]
116 _def = self.get_permission_by_name('repository.' + _def_name)
71 _def = Permission.get_by_key('repository.' + _def_name)
117 72 # repos
118 73 for r2p in self.sa.query(UserRepoToPerm)\
119 74 .filter(UserRepoToPerm.user == perm_user)\
120 75 .all():
121 76
122 77 #don't reset PRIVATE repositories
123 78 if not r2p.repository.private:
124 79 r2p.permission = _def
125 80 self.sa.add(r2p)
126 81
127 82 if form_result['overwrite_default_group'] == True:
128 83 _def_name = form_result['default_group_perm'].split('group.')[-1]
129 84 # groups
130 _def = self.get_permission_by_name('group.' + _def_name)
85 _def = Permission.get_by_key('group.' + _def_name)
131 86 for g2p in self.sa.query(UserRepoGroupToPerm)\
132 87 .filter(UserRepoGroupToPerm.user == perm_user)\
133 88 .all():
134 89 g2p.permission = _def
135 90 self.sa.add(g2p)
136 91
137 92 # stage 3 set anonymous access
138 93 if perm_user.username == 'default':
139 perm_user.active = bool(form_result['anonymous'])
94 perm_user.active = str2bool(form_result['anonymous'])
140 95 self.sa.add(perm_user)
141 96
97 self.sa.commit()
142 98 except (DatabaseError,):
143 99 log.error(traceback.format_exc())
100 self.sa.rollback()
144 101 raise
General Comments 0
You need to be logged in to leave comments. Login now