Revision 3c22bad0
b/snf-astakos-app/astakos/im/functions.py | ||
---|---|---|
44 | 44 |
from django.contrib.auth.models import AnonymousUser |
45 | 45 |
from django.core.exceptions import PermissionDenied |
46 | 46 |
from django.db import IntegrityError |
47 |
from django.db.models import Q |
|
47 | 48 |
from django.http import Http404 |
48 | 49 |
|
49 | 50 |
from synnefo_branding.utils import render_to_string |
... | ... | |
64 | 65 |
from astakos.im.notifications import build_notification, NotificationError |
65 | 66 |
from astakos.im.models import ( |
66 | 67 |
AstakosUser, Invitation, ProjectMembership, ProjectApplication, Project, |
67 |
UserSetting, new_chain) |
|
68 |
from astakos.im.quotas import (qh_sync_user, |
|
69 |
register_pending_apps, qh_sync_project) |
|
68 |
UserSetting, Chain, new_chain) |
|
69 |
from astakos.im.quotas import (qh_sync_user, get_pending_app_quota, |
|
70 |
register_pending_apps, qh_sync_project, |
|
71 |
qh_sync_locked_users, get_users_for_update, |
|
72 |
members_to_sync) |
|
70 | 73 |
from astakos.im.project_notif import ( |
71 | 74 |
membership_change_notify, membership_enroll_notify, |
72 | 75 |
membership_request_notify, membership_leave_request_notify, |
... | ... | |
304 | 307 |
raise IOError(m) |
305 | 308 |
|
306 | 309 |
|
307 |
def get_project_for_update(project_id):
|
|
310 |
def get_chain_for_update(chain_id):
|
|
308 | 311 |
try: |
309 |
return Project.objects.get_for_update(id=project_id)
|
|
310 |
except Project.DoesNotExist:
|
|
311 |
m = _(astakos_messages.UNKNOWN_PROJECT_ID) % project_id
|
|
312 |
return Chain.objects.get_for_update(chain=chain_id)
|
|
313 |
except Chain.DoesNotExist:
|
|
314 |
m = _(astakos_messages.UNKNOWN_PROJECT_ID) % chain_id
|
|
312 | 315 |
raise IOError(m) |
313 | 316 |
|
314 | 317 |
|
315 |
def get_application_for_update(application_id): |
|
318 |
def get_chain_of_application_for_update(app_id): |
|
319 |
app = get_application(app_id) |
|
320 |
return Chain.objects.get_for_update(chain=app.chain_id) |
|
321 |
|
|
322 |
|
|
323 |
def get_application(application_id): |
|
316 | 324 |
try: |
317 |
return ProjectApplication.objects.get_for_update(id=application_id)
|
|
325 |
return ProjectApplication.objects.get(id=application_id) |
|
318 | 326 |
except ProjectApplication.DoesNotExist: |
319 | 327 |
m = _(astakos_messages.UNKNOWN_PROJECT_APPLICATION_ID) % application_id |
320 | 328 |
raise IOError(m) |
... | ... | |
336 | 344 |
raise IOError(m) |
337 | 345 |
|
338 | 346 |
|
339 |
def get_membership_for_update(project_id, user_id):
|
|
347 |
def get_membership(project_id, user_id): |
|
340 | 348 |
try: |
341 |
objs = ProjectMembership.objects |
|
342 |
return objs.get_for_update(project__id=project_id, |
|
343 |
person__id=user_id) |
|
349 |
objs = ProjectMembership.objects.select_related('project', 'person') |
|
350 |
return objs.get(project__id=project_id, person__id=user_id) |
|
344 | 351 |
except ProjectMembership.DoesNotExist: |
345 | 352 |
m = _(astakos_messages.NOT_MEMBERSHIP_REQUEST) |
346 | 353 |
raise IOError(m) |
347 | 354 |
|
348 | 355 |
|
349 |
def get_membership_for_update_by_id(project_id, memb_id):
|
|
356 |
def get_membership_by_id(project_id, memb_id): |
|
350 | 357 |
try: |
351 |
objs = ProjectMembership.objects |
|
352 |
return objs.get_for_update(project__id=project_id, |
|
353 |
id=memb_id) |
|
358 |
objs = ProjectMembership.objects.select_related('project', 'person') |
|
359 |
return objs.get(project__id=project_id, id=memb_id) |
|
354 | 360 |
except ProjectMembership.DoesNotExist: |
355 | 361 |
m = _(astakos_messages.NOT_MEMBERSHIP_REQUEST) |
356 | 362 |
raise IOError(m) |
... | ... | |
396 | 402 |
|
397 | 403 |
|
398 | 404 |
def accept_membership(project_id, memb_id, request_user=None): |
399 |
project = get_project_for_update(project_id) |
|
400 |
accept_membership_checks(project, request_user) |
|
405 |
get_chain_for_update(project_id) |
|
401 | 406 |
|
402 |
membership = get_membership_for_update_by_id(project_id, memb_id)
|
|
407 |
membership = get_membership_by_id(project_id, memb_id) |
|
403 | 408 |
if not membership.can_accept(): |
404 | 409 |
m = _(astakos_messages.NOT_MEMBERSHIP_REQUEST) |
405 | 410 |
raise PermissionDenied(m) |
406 | 411 |
|
412 |
project = membership.project |
|
413 |
accept_membership_checks(project, request_user) |
|
407 | 414 |
user = membership.person |
408 | 415 |
membership.accept() |
409 | 416 |
qh_sync_user(user) |
... | ... | |
420 | 427 |
|
421 | 428 |
|
422 | 429 |
def reject_membership(project_id, memb_id, request_user=None): |
423 |
project = get_project_for_update(project_id)
|
|
424 |
reject_membership_checks(project, request_user) |
|
425 |
membership = get_membership_for_update_by_id(project_id, memb_id)
|
|
430 |
get_chain_for_update(project_id)
|
|
431 |
|
|
432 |
membership = get_membership_by_id(project_id, memb_id) |
|
426 | 433 |
if not membership.can_reject(): |
427 | 434 |
m = _(astakos_messages.NOT_MEMBERSHIP_REQUEST) |
428 | 435 |
raise PermissionDenied(m) |
429 | 436 |
|
437 |
project = membership.project |
|
438 |
reject_membership_checks(project, request_user) |
|
430 | 439 |
user = membership.person |
431 | 440 |
membership.reject() |
432 | 441 |
logger.info("Request of user %s for %s has been rejected." % |
... | ... | |
441 | 450 |
|
442 | 451 |
|
443 | 452 |
def cancel_membership(project_id, request_user): |
444 |
project = get_project_for_update(project_id)
|
|
445 |
cancel_membership_checks(project) |
|
446 |
membership = get_membership_for_update(project_id, request_user.id)
|
|
453 |
get_chain_for_update(project_id)
|
|
454 |
|
|
455 |
membership = get_membership(project_id, request_user.id) |
|
447 | 456 |
if not membership.can_cancel(): |
448 | 457 |
m = _(astakos_messages.NOT_MEMBERSHIP_REQUEST) |
449 | 458 |
raise PermissionDenied(m) |
450 | 459 |
|
460 |
project = membership.project |
|
461 |
cancel_membership_checks(project) |
|
451 | 462 |
membership.cancel() |
452 | 463 |
logger.info("Request of user %s for %s has been cancelled." % |
453 | 464 |
(membership.person.log_display, project)) |
... | ... | |
464 | 475 |
|
465 | 476 |
|
466 | 477 |
def remove_membership(project_id, memb_id, request_user=None): |
467 |
project = get_project_for_update(project_id)
|
|
468 |
remove_membership_checks(project, request_user) |
|
469 |
membership = get_membership_for_update_by_id(project_id, memb_id)
|
|
478 |
get_chain_for_update(project_id)
|
|
479 |
|
|
480 |
membership = get_membership_by_id(project_id, memb_id) |
|
470 | 481 |
if not membership.can_remove(): |
471 | 482 |
m = _(astakos_messages.NOT_ACCEPTED_MEMBERSHIP) |
472 | 483 |
raise PermissionDenied(m) |
473 | 484 |
|
485 |
project = membership.project |
|
486 |
remove_membership_checks(project, request_user) |
|
474 | 487 |
user = membership.person |
475 | 488 |
membership.remove() |
476 | 489 |
qh_sync_user(user) |
... | ... | |
482 | 495 |
|
483 | 496 |
|
484 | 497 |
def enroll_member(project_id, user, request_user=None): |
485 |
project = get_project_for_update(project_id) |
|
498 |
get_chain_for_update(project_id) |
|
499 |
project = get_project_by_id(project_id) |
|
486 | 500 |
accept_membership_checks(project, request_user) |
487 | 501 |
|
488 | 502 |
membership, created = ProjectMembership.objects.get_or_create( |
... | ... | |
524 | 538 |
|
525 | 539 |
|
526 | 540 |
def leave_project(project_id, request_user): |
527 |
project = get_project_for_update(project_id)
|
|
528 |
leave_project_checks(project) |
|
529 |
membership = get_membership_for_update(project_id, request_user.id)
|
|
541 |
get_chain_for_update(project_id)
|
|
542 |
|
|
543 |
membership = get_membership(project_id, request_user.id) |
|
530 | 544 |
if not membership.can_leave(): |
531 | 545 |
m = _(astakos_messages.NOT_ACCEPTED_MEMBERSHIP) |
532 | 546 |
raise PermissionDenied(m) |
533 | 547 |
|
548 |
project = membership.project |
|
549 |
leave_project_checks(project) |
|
550 |
|
|
534 | 551 |
auto_accepted = False |
535 | 552 |
leave_policy = project.application.member_leave_policy |
536 | 553 |
if leave_policy == AUTO_ACCEPT_POLICY: |
537 | 554 |
membership.remove() |
538 | 555 |
qh_sync_user(request_user) |
539 | 556 |
logger.info("User %s has left %s." % |
540 |
(membership.person.log_display, project))
|
|
557 |
(request_user.log_display, project))
|
|
541 | 558 |
auto_accepted = True |
542 | 559 |
else: |
543 | 560 |
membership.leave_request() |
544 | 561 |
logger.info("User %s requested to leave %s." % |
545 |
(membership.person.log_display, project))
|
|
562 |
(request_user.log_display, project))
|
|
546 | 563 |
membership_leave_request_notify(project, membership.person) |
547 | 564 |
return auto_accepted |
548 | 565 |
|
... | ... | |
567 | 584 |
|
568 | 585 |
|
569 | 586 |
def join_project(project_id, request_user): |
570 |
project = get_project_for_update(project_id) |
|
587 |
get_chain_for_update(project_id) |
|
588 |
project = get_project_by_id(project_id) |
|
571 | 589 |
join_project_checks(project) |
572 | 590 |
|
573 | 591 |
membership, created = ProjectMembership.objects.get_or_create( |
... | ... | |
585 | 603 |
membership.accept() |
586 | 604 |
qh_sync_user(request_user) |
587 | 605 |
logger.info("User %s joined %s." % |
588 |
(membership.person.log_display, project))
|
|
606 |
(request_user.log_display, project))
|
|
589 | 607 |
auto_accepted = True |
590 | 608 |
else: |
591 | 609 |
membership_request_notify(project, membership.person) |
592 | 610 |
logger.info("User %s requested to join %s." % |
593 |
(membership.person.log_display, project))
|
|
611 |
(request_user.log_display, project))
|
|
594 | 612 |
return auto_accepted |
595 | 613 |
|
596 | 614 |
|
... | ... | |
610 | 628 |
|
611 | 629 |
precursor = None |
612 | 630 |
if precursor_id is not None: |
613 |
objs = ProjectApplication.objects
|
|
614 |
precursor = objs.get_for_update(id=precursor_id)
|
|
631 |
get_chain_of_application_for_update(precursor_id)
|
|
632 |
precursor = ProjectApplication.objects.get(id=precursor_id)
|
|
615 | 633 |
|
616 | 634 |
if (request_user and |
617 | 635 |
(not precursor.owner == request_user and |
... | ... | |
646 | 664 |
chain = precursor.chain |
647 | 665 |
application.chain = chain |
648 | 666 |
objs = ProjectApplication.objects |
649 |
q = objs.filter(chain=chain, state=ProjectApplication.PENDING) |
|
650 |
pending = q.select_for_update() |
|
667 |
pending = objs.filter(chain=chain, state=ProjectApplication.PENDING) |
|
651 | 668 |
for app in pending: |
652 | 669 |
app.state = ProjectApplication.REPLACED |
653 | 670 |
app.save() |
... | ... | |
662 | 679 |
|
663 | 680 |
|
664 | 681 |
def cancel_application(application_id, request_user=None, reason=""): |
665 |
application = get_application_for_update(application_id) |
|
682 |
get_chain_of_application_for_update(application_id) |
|
683 |
application = get_application(application_id) |
|
666 | 684 |
checkAllowed(application, request_user) |
667 | 685 |
|
668 | 686 |
if not application.can_cancel(): |
... | ... | |
677 | 695 |
|
678 | 696 |
|
679 | 697 |
def dismiss_application(application_id, request_user=None, reason=""): |
680 |
application = get_application_for_update(application_id) |
|
698 |
get_chain_of_application_for_update(application_id) |
|
699 |
application = get_application(application_id) |
|
681 | 700 |
checkAllowed(application, request_user) |
682 | 701 |
|
683 | 702 |
if not application.can_dismiss(): |
... | ... | |
690 | 709 |
|
691 | 710 |
|
692 | 711 |
def deny_application(application_id, request_user=None, reason=""): |
693 |
application = get_application_for_update(application_id) |
|
712 |
get_chain_of_application_for_update(application_id) |
|
713 |
application = get_application(application_id) |
|
694 | 714 |
|
695 | 715 |
checkAllowed(application, request_user, admin_only=True) |
696 | 716 |
|
... | ... | |
707 | 727 |
application_deny_notify(application) |
708 | 728 |
|
709 | 729 |
|
710 |
def approve_application(app_id, request_user=None, reason=""): |
|
730 |
def check_conflicting_projects(application): |
|
731 |
try: |
|
732 |
project = get_project_by_id(application.chain) |
|
733 |
except IOError: |
|
734 |
project = None |
|
711 | 735 |
|
736 |
new_project_name = application.name |
|
712 | 737 |
try: |
713 |
objects = ProjectApplication.objects |
|
714 |
application = objects.get_for_update(id=app_id) |
|
715 |
except ProjectApplication.DoesNotExist: |
|
716 |
m = _(astakos_messages.UNKNOWN_PROJECT_APPLICATION_ID % (app_id,)) |
|
717 |
raise PermissionDenied(m) |
|
738 |
q = Q(name=new_project_name) & ~Q(state=Project.TERMINATED) |
|
739 |
conflicting_project = Project.objects.get(q) |
|
740 |
if (conflicting_project != project): |
|
741 |
m = (_("cannot approve: project with name '%s' " |
|
742 |
"already exists (id: %s)") % ( |
|
743 |
new_project_name, conflicting_project.id)) |
|
744 |
raise PermissionDenied(m) # invalid argument |
|
745 |
except Project.DoesNotExist: |
|
746 |
pass |
|
747 |
|
|
748 |
return project |
|
749 |
|
|
750 |
|
|
751 |
def approve_application(app_id, request_user=None, reason=""): |
|
752 |
get_chain_of_application_for_update(app_id) |
|
753 |
application = get_application(app_id) |
|
718 | 754 |
|
719 | 755 |
checkAllowed(application, request_user, admin_only=True) |
720 | 756 |
|
... | ... | |
723 | 759 |
(application.id, application.state_display())) |
724 | 760 |
raise PermissionDenied(m) |
725 | 761 |
|
726 |
qh_release_pending_app(application.owner) |
|
727 |
project = application.approve(reason) |
|
728 |
qh_sync_project(project) |
|
762 |
project = check_conflicting_projects(application) |
|
763 |
|
|
764 |
# Pre-lock members and owner together in order to impose an ordering |
|
765 |
# on locking users |
|
766 |
members = members_to_sync(project) if project is not None else [] |
|
767 |
uids_to_sync = [member.id for member in members] |
|
768 |
owner = application.owner |
|
769 |
uids_to_sync.append(owner.id) |
|
770 |
get_users_for_update(uids_to_sync) |
|
771 |
|
|
772 |
qh_release_pending_app(owner, locked=True) |
|
773 |
application.approve(reason) |
|
774 |
qh_sync_locked_users(members) |
|
729 | 775 |
logger.info("%s has been approved." % (application.log_display)) |
730 | 776 |
application_approve_notify(application) |
731 | 777 |
|
... | ... | |
741 | 787 |
|
742 | 788 |
|
743 | 789 |
def terminate(project_id, request_user=None): |
744 |
project = get_project_for_update(project_id) |
|
790 |
get_chain_for_update(project_id) |
|
791 |
project = get_project_by_id(project_id) |
|
745 | 792 |
checkAllowed(project, request_user, admin_only=True) |
746 | 793 |
checkAlive(project) |
747 | 794 |
|
... | ... | |
753 | 800 |
|
754 | 801 |
|
755 | 802 |
def suspend(project_id, request_user=None): |
756 |
project = get_project_for_update(project_id) |
|
803 |
get_chain_for_update(project_id) |
|
804 |
project = get_project_by_id(project_id) |
|
757 | 805 |
checkAllowed(project, request_user, admin_only=True) |
758 | 806 |
checkAlive(project) |
759 | 807 |
|
... | ... | |
765 | 813 |
|
766 | 814 |
|
767 | 815 |
def resume(project_id, request_user=None): |
768 |
project = get_project_for_update(project_id) |
|
816 |
get_chain_for_update(project_id) |
|
817 |
project = get_project_by_id(project_id) |
|
769 | 818 |
checkAllowed(project, request_user, admin_only=True) |
770 | 819 |
|
771 | 820 |
if not project.is_suspended: |
... | ... | |
836 | 885 |
return usage |
837 | 886 |
|
838 | 887 |
|
839 |
def qh_add_pending_app(user, precursor=None, force=False, dry_run=False):
|
|
888 |
def get_pending_app_diff(user, precursor):
|
|
840 | 889 |
if precursor is None: |
841 | 890 |
diff = 1 |
842 | 891 |
else: |
... | ... | |
845 | 894 |
q = objs.filter(chain=chain, state=ProjectApplication.PENDING) |
846 | 895 |
count = q.count() |
847 | 896 |
diff = 1 - count |
897 |
return diff |
|
898 |
|
|
899 |
|
|
900 |
def qh_add_pending_app(user, precursor=None, force=False): |
|
901 |
user = AstakosUser.forupdate.get_for_update(id=user.id) |
|
902 |
diff = get_pending_app_diff(user, precursor) |
|
903 |
return register_pending_apps(user, diff, force) |
|
904 |
|
|
848 | 905 |
|
849 |
return register_pending_apps(user, diff, force, dry_run) |
|
906 |
def check_pending_app_quota(user, precursor=None): |
|
907 |
diff = get_pending_app_diff(user, precursor) |
|
908 |
quota = get_pending_app_quota(user) |
|
909 |
limit = quota['limit'] |
|
910 |
usage = quota['usage'] |
|
911 |
if usage + diff > limit: |
|
912 |
return False, limit |
|
913 |
return True, None |
|
850 | 914 |
|
851 | 915 |
|
852 |
def qh_release_pending_app(user): |
|
916 |
def qh_release_pending_app(user, locked=False): |
|
917 |
if not locked: |
|
918 |
user = AstakosUser.forupdate.get_for_update(id=user.id) |
|
853 | 919 |
register_pending_apps(user, -1) |
b/snf-astakos-app/astakos/im/management/commands/quota.py | ||
---|---|---|
36 | 36 |
|
37 | 37 |
from astakos.im.models import AstakosUser |
38 | 38 |
from astakos.im.quotas import ( |
39 |
qh_sync_users, list_user_quotas, add_base_quota) |
|
39 |
qh_sync_users_diffs, list_user_quotas, add_base_quota)
|
|
40 | 40 |
from astakos.im.functions import get_user_by_uuid |
41 | 41 |
from astakos.im.management.commands._common import is_uuid, is_email |
42 | 42 |
from snf_django.lib.db.transaction import commit_on_success_strict |
... | ... | |
117 | 117 |
output_format) |
118 | 118 |
|
119 | 119 |
elif verify or sync: |
120 |
qh_limits, diff_q = qh_sync_users(users, sync=sync, diff_only=True)
|
|
120 |
qh_limits, diff_q = qh_sync_users_diffs(users, sync=sync)
|
|
121 | 121 |
if verify: |
122 | 122 |
self.print_verify(qh_limits, diff_q) |
123 | 123 |
if sync: |
b/snf-astakos-app/astakos/im/models.py | ||
---|---|---|
1473 | 1473 |
return "application %s (%s) for project %s" % ( |
1474 | 1474 |
self.id, self.name, self.chain) |
1475 | 1475 |
|
1476 |
def get_project(self): |
|
1477 |
try: |
|
1478 |
project = Project.objects.get(id=self.chain, state=Project.APPROVED) |
|
1479 |
return Project |
|
1480 |
except Project.DoesNotExist, e: |
|
1481 |
return None |
|
1482 |
|
|
1483 | 1476 |
def state_display(self): |
1484 | 1477 |
return self.APPLICATION_STATE_DISPLAY.get(self.state, _('Unknown')) |
1485 | 1478 |
|
... | ... | |
1579 | 1572 |
def project_exists(self): |
1580 | 1573 |
return self.get_project() is not None |
1581 | 1574 |
|
1582 |
def _get_project_for_update(self): |
|
1583 |
try: |
|
1584 |
objects = Project.objects |
|
1585 |
project = objects.get_for_update(id=self.chain) |
|
1586 |
return project |
|
1587 |
except Project.DoesNotExist: |
|
1588 |
return None |
|
1589 |
|
|
1590 | 1575 |
def can_cancel(self): |
1591 | 1576 |
return self.state == self.PENDING |
1592 | 1577 |
|
... | ... | |
1629 | 1614 |
return self.state == self.PENDING |
1630 | 1615 |
|
1631 | 1616 |
def approve(self, reason): |
1632 |
new_project_name = self.name |
|
1633 | 1617 |
if not self.can_approve(): |
1634 | 1618 |
m = _("cannot approve: project '%s' in state '%s'") % ( |
1635 |
new_project_name, self.state)
|
|
1619 |
self.name, self.state)
|
|
1636 | 1620 |
raise AssertionError(m) # invalid argument |
1637 | 1621 |
|
1638 | 1622 |
now = datetime.now() |
1639 |
project = self._get_project_for_update() |
|
1640 |
|
|
1641 |
try: |
|
1642 |
q = Q(name=new_project_name) & ~Q(state=Project.TERMINATED) |
|
1643 |
conflicting_project = Project.objects.get(q) |
|
1644 |
if (conflicting_project != project): |
|
1645 |
m = (_("cannot approve: project with name '%s' " |
|
1646 |
"already exists (id: %s)") % ( |
|
1647 |
new_project_name, conflicting_project.id)) |
|
1648 |
raise PermissionDenied(m) # invalid argument |
|
1649 |
except Project.DoesNotExist: |
|
1650 |
pass |
|
1623 |
self.state = self.APPROVED |
|
1624 |
self.response_date = now |
|
1625 |
self.response = reason |
|
1626 |
self.save() |
|
1651 | 1627 |
|
1652 |
new_project = False
|
|
1628 |
project = self.get_project()
|
|
1653 | 1629 |
if project is None: |
1654 |
new_project = True |
|
1655 | 1630 |
project = Project(id=self.chain) |
1656 | 1631 |
|
1657 |
project.name = new_project_name
|
|
1632 |
project.name = self.name
|
|
1658 | 1633 |
project.application = self |
1659 | 1634 |
project.last_approval_date = now |
1660 |
|
|
1661 | 1635 |
project.save() |
1662 |
|
|
1663 |
self.state = self.APPROVED |
|
1664 |
self.response_date = now |
|
1665 |
self.response = reason |
|
1666 |
self.save() |
|
1667 | 1636 |
return project |
1668 | 1637 |
|
1669 | 1638 |
@property |
b/snf-astakos-app/astakos/im/quotas.py | ||
---|---|---|
128 | 128 |
|
129 | 129 |
|
130 | 130 |
SYSTEM = 'system' |
131 |
PENDING_APP_RESOURCE = 'astakos.pending_app' |
|
131 | 132 |
|
132 | 133 |
|
133 |
def register_pending_apps(user, quantity, force=False, dry_run=False): |
|
134 |
provision = (user.uuid, SYSTEM, 'astakos.pending_app'), quantity |
|
135 |
name = "DRYRUN" if dry_run else "" |
|
134 |
def register_pending_apps(user, quantity, force=False): |
|
135 |
provision = (user.uuid, SYSTEM, PENDING_APP_RESOURCE), quantity |
|
136 | 136 |
try: |
137 | 137 |
s = qh.issue_commission(clientkey='astakos', |
138 | 138 |
force=force, |
139 |
name=name, |
|
140 | 139 |
provisions=[provision]) |
141 | 140 |
except NoCapacityError as e: |
142 | 141 |
limit = e.data['limit'] |
143 | 142 |
return False, limit |
144 |
accept = not dry_run |
|
145 |
qh.resolve_pending_commission('astakos', s, accept) |
|
143 |
qh.resolve_pending_commission('astakos', s) |
|
146 | 144 |
return True, None |
147 | 145 |
|
148 | 146 |
|
147 |
def get_pending_app_quota(user): |
|
148 |
quota = get_user_quotas(user) |
|
149 |
return quota[SYSTEM][PENDING_APP_RESOURCE] |
|
150 |
|
|
151 |
|
|
149 | 152 |
def add_base_quota(user, resource, capacity): |
150 | 153 |
resource = Resource.objects.get(name=resource) |
154 |
user = get_user_for_update(user.id) |
|
151 | 155 |
obj, created = AstakosUserQuota.objects.get_or_create( |
152 | 156 |
user=user, resource=resource, defaults={ |
153 | 157 |
'capacity': capacity, |
... | ... | |
156 | 160 |
if not created: |
157 | 161 |
obj.capacity = capacity |
158 | 162 |
obj.save() |
159 |
qh_sync_user(user) |
|
163 |
qh_sync_locked_user(user)
|
|
160 | 164 |
|
161 | 165 |
|
162 | 166 |
def remove_base_quota(user, resource): |
167 |
user = get_user_for_update(user.id) |
|
163 | 168 |
AstakosUserQuota.objects.filter( |
164 | 169 |
user=user, resource__name=resource).delete() |
165 |
qh_sync_user(user) |
|
170 |
qh_sync_locked_user(user)
|
|
166 | 171 |
|
167 | 172 |
|
168 | 173 |
def initial_quotas(users): |
... | ... | |
248 | 253 |
|
249 | 254 |
# Syncing to quotaholder |
250 | 255 |
|
251 |
def qh_sync_users(users, sync=True, diff_only=False): |
|
256 |
def get_users_for_update(user_ids): |
|
257 |
uids = sorted(user_ids) |
|
258 |
objs = AstakosUser.forupdate |
|
259 |
return list(objs.filter(id__in=uids).order_by('id').select_for_update()) |
|
260 |
|
|
261 |
|
|
262 |
def get_user_for_update(user_id): |
|
263 |
return get_users_for_update([user_id])[0] |
|
264 |
|
|
265 |
|
|
266 |
def qh_sync_locked_users(users): |
|
267 |
astakos_quotas = astakos_users_quotas(users) |
|
268 |
_set_user_quota(astakos_quotas) |
|
269 |
|
|
270 |
|
|
271 |
def qh_sync_users(users): |
|
272 |
uids = [user.id for user in users] |
|
273 |
users = get_users_for_update(uids) |
|
274 |
qh_sync_locked_users(users) |
|
275 |
|
|
276 |
|
|
277 |
def qh_sync_users_diffs(users, sync=True): |
|
252 | 278 |
uids = [user.id for user in users] |
253 | 279 |
if sync: |
254 |
users = AstakosUser.forupdate.filter(id__in=uids).select_for_update()
|
|
280 |
users = get_users_for_update(uids)
|
|
255 | 281 |
|
256 | 282 |
astakos_quotas = astakos_users_quotas(users) |
283 |
qh_limits = get_users_quota_limits(users) |
|
284 |
diff_quotas = {} |
|
285 |
for holder, local in astakos_quotas.iteritems(): |
|
286 |
registered = qh_limits.get(holder, None) |
|
287 |
if local != registered: |
|
288 |
diff_quotas[holder] = dict(local) |
|
289 |
|
|
290 |
if sync: |
|
291 |
_set_user_quota(diff_quotas) |
|
292 |
return qh_limits, diff_quotas |
|
257 | 293 |
|
258 |
if diff_only: |
|
259 |
qh_limits = get_users_quota_limits(users) |
|
260 |
diff_quotas = {} |
|
261 |
for holder, local in astakos_quotas.iteritems(): |
|
262 |
registered = qh_limits.get(holder, None) |
|
263 |
if local != registered: |
|
264 |
diff_quotas[holder] = dict(local) |
|
265 |
|
|
266 |
if sync: |
|
267 |
_set_user_quota(diff_quotas) |
|
268 |
return qh_limits, diff_quotas |
|
269 |
else: |
|
270 |
if sync: |
|
271 |
_set_user_quota(astakos_quotas) |
|
272 |
return None |
|
294 |
|
|
295 |
def qh_sync_locked_user(user): |
|
296 |
qh_sync_locked_users([user]) |
|
273 | 297 |
|
274 | 298 |
|
275 | 299 |
def qh_sync_user(user): |
276 | 300 |
qh_sync_users([user]) |
277 | 301 |
|
278 | 302 |
|
279 |
def qh_sync_projects(projects): |
|
280 |
projects = list(projects) |
|
281 |
memberships = ProjectMembership.objects.filter( |
|
282 |
project__in=projects, state__in=ProjectMembership.ACTUALLY_ACCEPTED) |
|
283 |
users = set(m.person for m in memberships) |
|
284 |
|
|
285 |
qh_sync_users(users) |
|
303 |
def members_to_sync(project): |
|
304 |
objs = ProjectMembership.objects.select_related('person') |
|
305 |
memberships = objs.filter(project=project, |
|
306 |
state__in=ProjectMembership.ACTUALLY_ACCEPTED) |
|
307 |
return set(m.person for m in memberships) |
|
286 | 308 |
|
287 | 309 |
|
288 | 310 |
def qh_sync_project(project): |
289 |
qh_sync_projects([project]) |
|
311 |
users = members_to_sync(project) |
|
312 |
qh_sync_users(users) |
|
290 | 313 |
|
291 | 314 |
|
292 | 315 |
def qh_add_resource_limit(resource, diff): |
293 | 316 |
objs = AstakosUser.forupdate.filter(Q(email_verified=True) & |
294 | 317 |
~Q(policy=resource)) |
295 |
users = objs.select_for_update() |
|
318 |
users = objs.order_by('id').select_for_update()
|
|
296 | 319 |
uuids = [u.uuid for u in users] |
297 | 320 |
qh.add_resource_limit(holders=uuids, sources=[SYSTEM], |
298 | 321 |
resources=[resource.name], diff=diff) |
... | ... | |
300 | 323 |
|
301 | 324 |
def qh_sync_new_resource(resource, limit): |
302 | 325 |
users = AstakosUser.forupdate.filter( |
303 |
email_verified=True).select_for_update() |
|
326 |
email_verified=True).order_by('id').select_for_update()
|
|
304 | 327 |
|
305 | 328 |
resource_name = resource.name |
306 | 329 |
data = [] |
b/snf-astakos-app/astakos/im/views.py | ||
---|---|---|
94 | 94 |
send_feedback, |
95 | 95 |
logout as auth_logout, |
96 | 96 |
invite as invite_func, |
97 |
qh_add_pending_app,
|
|
97 |
check_pending_app_quota,
|
|
98 | 98 |
accept_membership, reject_membership, remove_membership, cancel_membership, |
99 | 99 |
leave_project, join_project, enroll_member, can_join_request, |
100 | 100 |
can_leave_request, |
... | ... | |
1144 | 1144 |
def project_add(request): |
1145 | 1145 |
user = request.user |
1146 | 1146 |
if not user.is_project_admin(): |
1147 |
ok, limit = qh_add_pending_app(user, dry_run=True)
|
|
1147 |
ok, limit = check_pending_app_quota(user)
|
|
1148 | 1148 |
if not ok: |
1149 | 1149 |
m = _(astakos_messages.PENDING_APPLICATION_LIMIT_ADD) % limit |
1150 | 1150 |
messages.error(request, m) |
... | ... | |
1260 | 1260 |
|
1261 | 1261 |
if not user.is_project_admin(): |
1262 | 1262 |
owner = app.owner |
1263 |
ok, limit = qh_add_pending_app(owner, precursor=app, dry_run=True)
|
|
1263 |
ok, limit = check_pending_app_quota(owner, precursor=app)
|
|
1264 | 1264 |
if not ok: |
1265 | 1265 |
m = _(astakos_messages.PENDING_APPLICATION_LIMIT_MODIFY) % limit |
1266 | 1266 |
messages.error(request, m) |
b/snf-astakos-app/astakos/test/views.py | ||
---|---|---|
37 | 37 |
from astakos.im.models import AstakosUser, ProjectApplication |
38 | 38 |
from astakos.im.functions import (join_project, leave_project, |
39 | 39 |
submit_application, approve_application, |
40 |
get_user_by_id, qh_add_pending_app)
|
|
40 |
get_user_by_id, check_pending_app_quota)
|
|
41 | 41 |
from snf_django.lib.db.transaction import commit_on_success_strict |
42 | 42 |
|
43 | 43 |
|
... | ... | |
62 | 62 |
if prec is not None |
63 | 63 |
else None) |
64 | 64 |
|
65 |
ok, limit = qh_add_pending_app(owner, precursor=precursor, dry_run=True)
|
|
65 |
ok, limit = check_pending_app_quota(owner, precursor=precursor)
|
|
66 | 66 |
if not ok: |
67 | 67 |
raise PermissionDenied('Limit %s reached', limit) |
68 | 68 |
|
Also available in: Unified diff