##// END OF EJS Templates
make the permission update function idempotent
marcink -
r3730:e42e1d4e beta
parent child Browse files
Show More
@@ -1,1039 +1,1039 b''
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 return ('Cache for repository `%s` was invalidated: '
225 225 'invalidated cache keys: %s' % (repoid, invalidated_keys))
226 226 except Exception:
227 227 log.error(traceback.format_exc())
228 228 raise JSONRPCError(
229 229 'Error occurred during cache invalidation action'
230 230 )
231 231
232 232 def lock(self, apiuser, repoid, locked=Optional(None),
233 233 userid=Optional(OAttr('apiuser'))):
234 234 """
235 235 Set locking state on particular repository by given user, if
236 236 this command is runned by non-admin account userid is set to user
237 237 who is calling this method
238 238
239 239 :param apiuser:
240 240 :param repoid:
241 241 :param userid:
242 242 :param locked:
243 243 """
244 244 repo = get_repo_or_error(repoid)
245 245 if HasPermissionAnyApi('hg.admin')(user=apiuser):
246 246 pass
247 247 elif HasRepoPermissionAnyApi('repository.admin',
248 248 'repository.write')(user=apiuser,
249 249 repo_name=repo.repo_name):
250 250 #make sure normal user does not pass someone else userid,
251 251 #he is not allowed to do that
252 252 if not isinstance(userid, Optional) and userid != apiuser.user_id:
253 253 raise JSONRPCError(
254 254 'userid is not the same as your user'
255 255 )
256 256 else:
257 257 raise JSONRPCError('repository `%s` does not exist' % (repoid))
258 258
259 259 if isinstance(userid, Optional):
260 260 userid = apiuser.user_id
261 261
262 262 user = get_user_or_error(userid)
263 263
264 264 if isinstance(locked, Optional):
265 265 lockobj = Repository.getlock(repo)
266 266
267 267 if lockobj[0] is None:
268 268 return ('Repo `%s` not locked. Locked=`False`.'
269 269 % (repo.repo_name))
270 270 else:
271 271 userid, time_ = lockobj
272 272 user = get_user_or_error(userid)
273 273
274 274 return ('Repo `%s` locked by `%s`. Locked=`True`. '
275 275 'Locked since: `%s`'
276 276 % (repo.repo_name, user.username,
277 277 json.dumps(time_to_datetime(time_))))
278 278
279 279 else:
280 280 locked = str2bool(locked)
281 281 try:
282 282 if locked:
283 283 Repository.lock(repo, user.user_id)
284 284 else:
285 285 Repository.unlock(repo)
286 286
287 287 return ('User `%s` set lock state for repo `%s` to `%s`'
288 288 % (user.username, repo.repo_name, locked))
289 289 except Exception:
290 290 log.error(traceback.format_exc())
291 291 raise JSONRPCError(
292 292 'Error occurred locking repository `%s`' % repo.repo_name
293 293 )
294 294
295 295 def get_locks(self, apiuser, userid=Optional(OAttr('apiuser'))):
296 296 """
297 297 Get all locks for given userid, if
298 298 this command is runned by non-admin account userid is set to user
299 299 who is calling this method, thus returning locks for himself
300 300
301 301 :param apiuser:
302 302 :param userid:
303 303 """
304 304 if HasPermissionAnyApi('hg.admin')(user=apiuser):
305 305 pass
306 306 else:
307 307 #make sure normal user does not pass someone else userid,
308 308 #he is not allowed to do that
309 309 if not isinstance(userid, Optional) and userid != apiuser.user_id:
310 310 raise JSONRPCError(
311 311 'userid is not the same as your user'
312 312 )
313 313 ret = []
314 314 if isinstance(userid, Optional):
315 315 user = None
316 316 else:
317 317 user = get_user_or_error(userid)
318 318
319 319 #show all locks
320 320 for r in Repository.getAll():
321 321 userid, time_ = r.locked
322 322 if time_:
323 323 _api_data = r.get_api_data()
324 324 # if we use userfilter just show the locks for this user
325 325 if user:
326 326 if safe_int(userid) == user.user_id:
327 327 ret.append(_api_data)
328 328 else:
329 329 ret.append(_api_data)
330 330
331 331 return ret
332 332
333 333 @HasPermissionAllDecorator('hg.admin')
334 334 def show_ip(self, apiuser, userid):
335 335 """
336 336 Shows IP address as seen from RhodeCode server, together with all
337 337 defined IP addresses for given user
338 338
339 339 :param apiuser:
340 340 :param userid:
341 341 """
342 342 user = get_user_or_error(userid)
343 343 ips = UserIpMap.query().filter(UserIpMap.user == user).all()
344 344 return dict(
345 345 ip_addr_server=self.ip_addr,
346 346 user_ips=ips
347 347 )
348 348
349 349 def get_user(self, apiuser, userid=Optional(OAttr('apiuser'))):
350 350 """"
351 351 Get a user by username, or userid, if userid is given
352 352
353 353 :param apiuser:
354 354 :param userid:
355 355 """
356 356 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
357 357 #make sure normal user does not pass someone else userid,
358 358 #he is not allowed to do that
359 359 if not isinstance(userid, Optional) and userid != apiuser.user_id:
360 360 raise JSONRPCError(
361 361 'userid is not the same as your user'
362 362 )
363 363
364 364 if isinstance(userid, Optional):
365 365 userid = apiuser.user_id
366 366
367 367 user = get_user_or_error(userid)
368 368 data = user.get_api_data()
369 369 data['permissions'] = AuthUser(user_id=user.user_id).permissions
370 370 return data
371 371
372 372 @HasPermissionAllDecorator('hg.admin')
373 373 def get_users(self, apiuser):
374 374 """"
375 375 Get all users
376 376
377 377 :param apiuser:
378 378 """
379 379
380 380 result = []
381 381 for user in UserModel().get_all():
382 382 result.append(user.get_api_data())
383 383 return result
384 384
385 385 @HasPermissionAllDecorator('hg.admin')
386 386 def create_user(self, apiuser, username, email, password,
387 387 firstname=Optional(None), lastname=Optional(None),
388 388 active=Optional(True), admin=Optional(False),
389 389 ldap_dn=Optional(None)):
390 390 """
391 391 Create new user
392 392
393 393 :param apiuser:
394 394 :param username:
395 395 :param email:
396 396 :param password:
397 397 :param firstname:
398 398 :param lastname:
399 399 :param active:
400 400 :param admin:
401 401 :param ldap_dn:
402 402 """
403 403
404 404 if UserModel().get_by_username(username):
405 405 raise JSONRPCError("user `%s` already exist" % username)
406 406
407 407 if UserModel().get_by_email(email, case_insensitive=True):
408 408 raise JSONRPCError("email `%s` already exist" % email)
409 409
410 410 if Optional.extract(ldap_dn):
411 411 # generate temporary password if ldap_dn
412 412 password = PasswordGenerator().gen_password(length=8)
413 413
414 414 try:
415 415 user = UserModel().create_or_update(
416 416 username=Optional.extract(username),
417 417 password=Optional.extract(password),
418 418 email=Optional.extract(email),
419 419 firstname=Optional.extract(firstname),
420 420 lastname=Optional.extract(lastname),
421 421 active=Optional.extract(active),
422 422 admin=Optional.extract(admin),
423 423 ldap_dn=Optional.extract(ldap_dn)
424 424 )
425 425 Session().commit()
426 426 return dict(
427 427 msg='created new user `%s`' % username,
428 428 user=user.get_api_data()
429 429 )
430 430 except Exception:
431 431 log.error(traceback.format_exc())
432 432 raise JSONRPCError('failed to create user `%s`' % username)
433 433
434 434 @HasPermissionAllDecorator('hg.admin')
435 435 def update_user(self, apiuser, userid, username=Optional(None),
436 436 email=Optional(None), firstname=Optional(None),
437 437 lastname=Optional(None), active=Optional(None),
438 438 admin=Optional(None), ldap_dn=Optional(None),
439 439 password=Optional(None)):
440 440 """
441 441 Updates given user
442 442
443 443 :param apiuser:
444 444 :param userid:
445 445 :param username:
446 446 :param email:
447 447 :param firstname:
448 448 :param lastname:
449 449 :param active:
450 450 :param admin:
451 451 :param ldap_dn:
452 452 :param password:
453 453 """
454 454
455 455 user = get_user_or_error(userid)
456 456
457 457 # call function and store only updated arguments
458 458 updates = {}
459 459
460 460 def store_update(attr, name):
461 461 if not isinstance(attr, Optional):
462 462 updates[name] = attr
463 463
464 464 try:
465 465
466 466 store_update(username, 'username')
467 467 store_update(password, 'password')
468 468 store_update(email, 'email')
469 469 store_update(firstname, 'name')
470 470 store_update(lastname, 'lastname')
471 471 store_update(active, 'active')
472 472 store_update(admin, 'admin')
473 473 store_update(ldap_dn, 'ldap_dn')
474 474
475 475 user = UserModel().update_user(user, **updates)
476 476 Session().commit()
477 477 return dict(
478 478 msg='updated user ID:%s %s' % (user.user_id, user.username),
479 479 user=user.get_api_data()
480 480 )
481 481 except Exception:
482 482 log.error(traceback.format_exc())
483 483 raise JSONRPCError('failed to update user `%s`' % userid)
484 484
485 485 @HasPermissionAllDecorator('hg.admin')
486 486 def delete_user(self, apiuser, userid):
487 487 """"
488 488 Deletes an user
489 489
490 490 :param apiuser:
491 491 :param userid:
492 492 """
493 493 user = get_user_or_error(userid)
494 494
495 495 try:
496 496 UserModel().delete(userid)
497 497 Session().commit()
498 498 return dict(
499 499 msg='deleted user ID:%s %s' % (user.user_id, user.username),
500 500 user=None
501 501 )
502 502 except Exception:
503 503 log.error(traceback.format_exc())
504 504 raise JSONRPCError('failed to delete ID:%s %s' % (user.user_id,
505 505 user.username))
506 506
507 507 @HasPermissionAllDecorator('hg.admin')
508 508 def get_users_group(self, apiuser, usersgroupid):
509 509 """"
510 510 Get user group by name or id
511 511
512 512 :param apiuser:
513 513 :param usersgroupid:
514 514 """
515 515 users_group = get_users_group_or_error(usersgroupid)
516 516
517 517 data = users_group.get_api_data()
518 518
519 519 members = []
520 520 for user in users_group.members:
521 521 user = user.user
522 522 members.append(user.get_api_data())
523 523 data['members'] = members
524 524 return data
525 525
526 526 @HasPermissionAllDecorator('hg.admin')
527 527 def get_users_groups(self, apiuser):
528 528 """"
529 529 Get all user groups
530 530
531 531 :param apiuser:
532 532 """
533 533
534 534 result = []
535 535 for users_group in UserGroupModel().get_all():
536 536 result.append(users_group.get_api_data())
537 537 return result
538 538
539 539 @HasPermissionAllDecorator('hg.admin')
540 540 def create_users_group(self, apiuser, group_name,
541 541 owner=Optional(OAttr('apiuser')),
542 542 active=Optional(True)):
543 543 """
544 544 Creates an new usergroup
545 545
546 546 :param apiuser:
547 547 :param group_name:
548 548 :param owner:
549 549 :param active:
550 550 """
551 551
552 552 if UserGroupModel().get_by_name(group_name):
553 553 raise JSONRPCError("user group `%s` already exist" % group_name)
554 554
555 555 try:
556 556 if isinstance(owner, Optional):
557 557 owner = apiuser.user_id
558 558
559 559 owner = get_user_or_error(owner)
560 560 active = Optional.extract(active)
561 561 ug = UserGroupModel().create(name=group_name,
562 562 owner=owner,
563 563 active=active)
564 564 Session().commit()
565 565 return dict(
566 566 msg='created new user group `%s`' % group_name,
567 567 users_group=ug.get_api_data()
568 568 )
569 569 except Exception:
570 570 log.error(traceback.format_exc())
571 571 raise JSONRPCError('failed to create group `%s`' % group_name)
572 572
573 573 @HasPermissionAllDecorator('hg.admin')
574 574 def add_user_to_users_group(self, apiuser, usersgroupid, userid):
575 575 """"
576 576 Add a user to a user group
577 577
578 578 :param apiuser:
579 579 :param usersgroupid:
580 580 :param userid:
581 581 """
582 582 user = get_user_or_error(userid)
583 583 users_group = get_users_group_or_error(usersgroupid)
584 584
585 585 try:
586 586 ugm = UserGroupModel().add_user_to_group(users_group, user)
587 587 success = True if ugm != True else False
588 588 msg = 'added member `%s` to user group `%s`' % (
589 589 user.username, users_group.users_group_name
590 590 )
591 591 msg = msg if success else 'User is already in that group'
592 592 Session().commit()
593 593
594 594 return dict(
595 595 success=success,
596 596 msg=msg
597 597 )
598 598 except Exception:
599 599 log.error(traceback.format_exc())
600 600 raise JSONRPCError(
601 601 'failed to add member to user group `%s`' % (
602 602 users_group.users_group_name
603 603 )
604 604 )
605 605
606 606 @HasPermissionAllDecorator('hg.admin')
607 607 def remove_user_from_users_group(self, apiuser, usersgroupid, userid):
608 608 """
609 609 Remove user from a group
610 610
611 611 :param apiuser:
612 612 :param usersgroupid:
613 613 :param userid:
614 614 """
615 615 user = get_user_or_error(userid)
616 616 users_group = get_users_group_or_error(usersgroupid)
617 617
618 618 try:
619 619 success = UserGroupModel().remove_user_from_group(users_group,
620 620 user)
621 621 msg = 'removed member `%s` from user group `%s`' % (
622 622 user.username, users_group.users_group_name
623 623 )
624 624 msg = msg if success else "User wasn't in group"
625 625 Session().commit()
626 626 return dict(success=success, msg=msg)
627 627 except Exception:
628 628 log.error(traceback.format_exc())
629 629 raise JSONRPCError(
630 630 'failed to remove member from user group `%s`' % (
631 631 users_group.users_group_name
632 632 )
633 633 )
634 634
635 635 def get_repo(self, apiuser, repoid):
636 636 """"
637 637 Get repository by name
638 638
639 639 :param apiuser:
640 640 :param repoid:
641 641 """
642 642 repo = get_repo_or_error(repoid)
643 643
644 644 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
645 645 # check if we have admin permission for this repo !
646 646 if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
647 647 repo_name=repo.repo_name) is False:
648 648 raise JSONRPCError('repository `%s` does not exist' % (repoid))
649 649
650 650 members = []
651 651 followers = []
652 652 for user in repo.repo_to_perm:
653 653 perm = user.permission.permission_name
654 654 user = user.user
655 655 user_data = user.get_api_data()
656 656 user_data['type'] = "user"
657 657 user_data['permission'] = perm
658 658 members.append(user_data)
659 659
660 660 for users_group in repo.users_group_to_perm:
661 661 perm = users_group.permission.permission_name
662 662 users_group = users_group.users_group
663 663 users_group_data = users_group.get_api_data()
664 664 users_group_data['type'] = "users_group"
665 665 users_group_data['permission'] = perm
666 666 members.append(users_group_data)
667 667
668 668 for user in repo.followers:
669 669 followers.append(user.user.get_api_data())
670 670
671 671 data = repo.get_api_data()
672 672 data['members'] = members
673 673 data['followers'] = followers
674 674 return data
675 675
676 676 def get_repos(self, apiuser):
677 677 """"
678 678 Get all repositories
679 679
680 680 :param apiuser:
681 681 """
682 682 result = []
683 683 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
684 684 repos = RepoModel().get_all_user_repos(user=apiuser)
685 685 else:
686 686 repos = RepoModel().get_all()
687 687
688 688 for repo in repos:
689 689 result.append(repo.get_api_data())
690 690 return result
691 691
692 692 @HasPermissionAllDecorator('hg.admin')
693 693 def get_repo_nodes(self, apiuser, repoid, revision, root_path,
694 694 ret_type='all'):
695 695 """
696 696 returns a list of nodes and it's children
697 697 for a given path at given revision. It's possible to specify ret_type
698 698 to show only files or dirs
699 699
700 700 :param apiuser:
701 701 :param repoid: name or id of repository
702 702 :param revision: revision for which listing should be done
703 703 :param root_path: path from which start displaying
704 704 :param ret_type: return type 'all|files|dirs' nodes
705 705 """
706 706 repo = get_repo_or_error(repoid)
707 707 try:
708 708 _d, _f = ScmModel().get_nodes(repo, revision, root_path,
709 709 flat=False)
710 710 _map = {
711 711 'all': _d + _f,
712 712 'files': _f,
713 713 'dirs': _d,
714 714 }
715 715 return _map[ret_type]
716 716 except KeyError:
717 717 raise JSONRPCError('ret_type must be one of %s' % _map.keys())
718 718 except Exception:
719 719 log.error(traceback.format_exc())
720 720 raise JSONRPCError(
721 721 'failed to get repo: `%s` nodes' % repo.repo_name
722 722 )
723 723
724 724 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
725 725 def create_repo(self, apiuser, repo_name, owner=Optional(OAttr('apiuser')),
726 726 repo_type=Optional('hg'),
727 727 description=Optional(''), private=Optional(False),
728 728 clone_uri=Optional(None), landing_rev=Optional('tip'),
729 729 enable_statistics=Optional(False),
730 730 enable_locking=Optional(False),
731 731 enable_downloads=Optional(False)):
732 732 """
733 733 Create repository, if clone_url is given it makes a remote clone
734 734 if repo_name is within a group name the groups will be created
735 735 automatically if they aren't present
736 736
737 737 :param apiuser:
738 738 :param repo_name:
739 739 :param onwer:
740 740 :param repo_type:
741 741 :param description:
742 742 :param private:
743 743 :param clone_uri:
744 744 :param landing_rev:
745 745 """
746 746 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
747 747 if not isinstance(owner, Optional):
748 748 #forbid setting owner for non-admins
749 749 raise JSONRPCError(
750 750 'Only RhodeCode admin can specify `owner` param'
751 751 )
752 752 if isinstance(owner, Optional):
753 753 owner = apiuser.user_id
754 754
755 755 owner = get_user_or_error(owner)
756 756
757 757 if RepoModel().get_by_repo_name(repo_name):
758 758 raise JSONRPCError("repo `%s` already exist" % repo_name)
759 759
760 760 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
761 761 if isinstance(private, Optional):
762 762 private = defs.get('repo_private') or Optional.extract(private)
763 763 if isinstance(repo_type, Optional):
764 764 repo_type = defs.get('repo_type')
765 765 if isinstance(enable_statistics, Optional):
766 766 enable_statistics = defs.get('repo_enable_statistics')
767 767 if isinstance(enable_locking, Optional):
768 768 enable_locking = defs.get('repo_enable_locking')
769 769 if isinstance(enable_downloads, Optional):
770 770 enable_downloads = defs.get('repo_enable_downloads')
771 771
772 772 clone_uri = Optional.extract(clone_uri)
773 773 description = Optional.extract(description)
774 774 landing_rev = Optional.extract(landing_rev)
775 775
776 776 try:
777 777 # create structure of groups and return the last group
778 778 group = map_groups(repo_name)
779 779
780 780 repo = RepoModel().create_repo(
781 781 repo_name=repo_name,
782 782 repo_type=repo_type,
783 783 description=description,
784 784 owner=owner,
785 785 private=private,
786 786 clone_uri=clone_uri,
787 787 repos_group=group,
788 788 landing_rev=landing_rev,
789 789 enable_statistics=enable_statistics,
790 790 enable_downloads=enable_downloads,
791 791 enable_locking=enable_locking
792 792 )
793 793
794 794 Session().commit()
795 795 return dict(
796 796 msg="Created new repository `%s`" % (repo.repo_name),
797 797 repo=repo.get_api_data()
798 798 )
799 799 except Exception:
800 800 log.error(traceback.format_exc())
801 801 raise JSONRPCError('failed to create repository `%s`' % repo_name)
802 802
803 803 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
804 804 def fork_repo(self, apiuser, repoid, fork_name, owner=Optional(OAttr('apiuser')),
805 805 description=Optional(''), copy_permissions=Optional(False),
806 806 private=Optional(False), landing_rev=Optional('tip')):
807 807 repo = get_repo_or_error(repoid)
808 808 repo_name = repo.repo_name
809 809
810 810 _repo = RepoModel().get_by_repo_name(fork_name)
811 811 if _repo:
812 812 type_ = 'fork' if _repo.fork else 'repo'
813 813 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
814 814
815 815 if HasPermissionAnyApi('hg.admin')(user=apiuser):
816 816 pass
817 817 elif HasRepoPermissionAnyApi('repository.admin',
818 818 'repository.write',
819 819 'repository.read')(user=apiuser,
820 820 repo_name=repo.repo_name):
821 821 if not isinstance(owner, Optional):
822 822 #forbid setting owner for non-admins
823 823 raise JSONRPCError(
824 824 'Only RhodeCode admin can specify `owner` param'
825 825 )
826 826 else:
827 827 raise JSONRPCError('repository `%s` does not exist' % (repoid))
828 828
829 829 if isinstance(owner, Optional):
830 830 owner = apiuser.user_id
831 831
832 832 owner = get_user_or_error(owner)
833 833
834 834 try:
835 835 # create structure of groups and return the last group
836 836 group = map_groups(fork_name)
837 837
838 838 form_data = dict(
839 839 repo_name=fork_name,
840 840 repo_name_full=fork_name,
841 841 repo_group=group,
842 842 repo_type=repo.repo_type,
843 843 description=Optional.extract(description),
844 844 private=Optional.extract(private),
845 845 copy_permissions=Optional.extract(copy_permissions),
846 846 landing_rev=Optional.extract(landing_rev),
847 847 update_after_clone=False,
848 848 fork_parent_id=repo.repo_id,
849 849 )
850 850 RepoModel().create_fork(form_data, cur_user=owner)
851 851 return dict(
852 852 msg='Created fork of `%s` as `%s`' % (repo.repo_name,
853 853 fork_name),
854 854 success=True # cannot return the repo data here since fork
855 855 # cann be done async
856 856 )
857 857 except Exception:
858 858 log.error(traceback.format_exc())
859 859 raise JSONRPCError(
860 860 'failed to fork repository `%s` as `%s`' % (repo_name,
861 861 fork_name)
862 862 )
863 863
864 864 def delete_repo(self, apiuser, repoid, forks=Optional(None)):
865 865 """
866 866 Deletes a given repository
867 867
868 868 :param apiuser:
869 869 :param repoid:
870 870 :param forks: detach or delete, what do do with attached forks for repo
871 871 """
872 872 repo = get_repo_or_error(repoid)
873 873
874 874 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
875 875 # check if we have admin permission for this repo !
876 876 if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
877 877 repo_name=repo.repo_name) is False:
878 878 raise JSONRPCError('repository `%s` does not exist' % (repoid))
879 879
880 880 try:
881 881 handle_forks = Optional.extract(forks)
882 882 _forks_msg = ''
883 883 _forks = [f for f in repo.forks]
884 884 if handle_forks == 'detach':
885 885 _forks_msg = ' ' + _('Detached %s forks') % len(_forks)
886 886 elif handle_forks == 'delete':
887 887 _forks_msg = ' ' + _('Deleted %s forks') % len(_forks)
888 888 elif _forks:
889 889 raise JSONRPCError(
890 890 'Cannot delete `%s` it still contains attached forks'
891 891 % repo.repo_name
892 892 )
893 893
894 894 RepoModel().delete(repo, forks=forks)
895 895 Session().commit()
896 896 return dict(
897 897 msg='Deleted repository `%s`%s' % (repo.repo_name, _forks_msg),
898 898 success=True
899 899 )
900 900 except Exception:
901 901 log.error(traceback.format_exc())
902 902 raise JSONRPCError(
903 903 'failed to delete repository `%s`' % repo.repo_name
904 904 )
905 905
906 906 @HasPermissionAllDecorator('hg.admin')
907 907 def grant_user_permission(self, apiuser, repoid, userid, perm):
908 908 """
909 909 Grant permission for user on given repository, or update existing one
910 910 if found
911 911
912 912 :param repoid:
913 913 :param userid:
914 914 :param perm:
915 915 """
916 916 repo = get_repo_or_error(repoid)
917 917 user = get_user_or_error(userid)
918 918 perm = get_perm_or_error(perm)
919 919
920 920 try:
921 921
922 922 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
923 923
924 924 Session().commit()
925 925 return dict(
926 926 msg='Granted perm: `%s` for user: `%s` in repo: `%s`' % (
927 927 perm.permission_name, user.username, repo.repo_name
928 928 ),
929 929 success=True
930 930 )
931 931 except Exception:
932 932 log.error(traceback.format_exc())
933 933 raise JSONRPCError(
934 934 'failed to edit permission for user: `%s` in repo: `%s`' % (
935 935 userid, repoid
936 936 )
937 937 )
938 938
939 939 @HasPermissionAllDecorator('hg.admin')
940 940 def revoke_user_permission(self, apiuser, repoid, userid):
941 941 """
942 942 Revoke permission for user on given repository
943 943
944 944 :param apiuser:
945 945 :param repoid:
946 946 :param userid:
947 947 """
948 948
949 949 repo = get_repo_or_error(repoid)
950 950 user = get_user_or_error(userid)
951 951 try:
952 952
953 953 RepoModel().revoke_user_permission(repo=repo, user=user)
954 954
955 955 Session().commit()
956 956 return dict(
957 957 msg='Revoked perm for user: `%s` in repo: `%s`' % (
958 958 user.username, repo.repo_name
959 959 ),
960 960 success=True
961 961 )
962 962 except Exception:
963 963 log.error(traceback.format_exc())
964 964 raise JSONRPCError(
965 965 'failed to edit permission for user: `%s` in repo: `%s`' % (
966 966 userid, repoid
967 967 )
968 968 )
969 969
970 970 @HasPermissionAllDecorator('hg.admin')
971 971 def grant_users_group_permission(self, apiuser, repoid, usersgroupid,
972 972 perm):
973 973 """
974 974 Grant permission for user group on given repository, or update
975 975 existing one if found
976 976
977 977 :param apiuser:
978 978 :param repoid:
979 979 :param usersgroupid:
980 980 :param perm:
981 981 """
982 982 repo = get_repo_or_error(repoid)
983 983 perm = get_perm_or_error(perm)
984 984 users_group = get_users_group_or_error(usersgroupid)
985 985
986 986 try:
987 987 RepoModel().grant_users_group_permission(repo=repo,
988 988 group_name=users_group,
989 989 perm=perm)
990 990
991 991 Session().commit()
992 992 return dict(
993 993 msg='Granted perm: `%s` for user group: `%s` in '
994 994 'repo: `%s`' % (
995 995 perm.permission_name, users_group.users_group_name,
996 996 repo.repo_name
997 997 ),
998 998 success=True
999 999 )
1000 1000 except Exception:
1001 1001 log.error(traceback.format_exc())
1002 1002 raise JSONRPCError(
1003 1003 'failed to edit permission for user group: `%s` in '
1004 1004 'repo: `%s`' % (
1005 1005 usersgroupid, repo.repo_name
1006 1006 )
1007 1007 )
1008 1008
1009 1009 @HasPermissionAllDecorator('hg.admin')
1010 1010 def revoke_users_group_permission(self, apiuser, repoid, usersgroupid):
1011 1011 """
1012 1012 Revoke permission for user group on given repository
1013 1013
1014 1014 :param apiuser:
1015 1015 :param repoid:
1016 1016 :param usersgroupid:
1017 1017 """
1018 1018 repo = get_repo_or_error(repoid)
1019 1019 users_group = get_users_group_or_error(usersgroupid)
1020 1020
1021 1021 try:
1022 1022 RepoModel().revoke_users_group_permission(repo=repo,
1023 1023 group_name=users_group)
1024 1024
1025 1025 Session().commit()
1026 1026 return dict(
1027 1027 msg='Revoked perm for user group: `%s` in repo: `%s`' % (
1028 1028 users_group.users_group_name, repo.repo_name
1029 1029 ),
1030 1030 success=True
1031 1031 )
1032 1032 except Exception:
1033 1033 log.error(traceback.format_exc())
1034 1034 raise JSONRPCError(
1035 1035 'failed to edit permission for user group: `%s` in '
1036 1036 'repo: `%s`' % (
1037 1037 users_group.users_group_name, repo.repo_name
1038 1038 )
1039 1039 )
@@ -1,144 +1,101 b''
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'])
111 self.sa.add(p)
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])
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