Commit 57292428 authored by Sofia Papagiannaki's avatar Sofia Papagiannaki
Browse files

Merge branch 'latest-quota' of https://code.grnet.gr/git/synnefo into latest-quota

Conflicts:
	snf-astakos-app/astakos/im/forms.py
	snf-astakos-app/astakos/im/functions.py
parents ecdb474b 01f057c7
......@@ -117,6 +117,9 @@ QuotaLimits = namedtuple('QuotaLimits', ('holder',
'import_limit',
'export_limit'))
def qh_register_user(user):
return register_users([user])
def register_users(users):
payload = list(CreateEntityPayload(
entity=u.uuid,
......
......@@ -69,7 +69,8 @@ from astakos.im.widgets import DummyWidget, RecaptchaWidget
from astakos.im.functions import (
send_change_email, submit_application, do_accept_membership_checks)
from astakos.im.util import reserved_email, get_query, model_to_dict
from astakos.im.util import reserved_email, reserved_verified_email, \
get_query, model_to_dict
from astakos.im import auth_providers
import astakos.im.messages as astakos_messages
......@@ -87,8 +88,10 @@ DOMAIN_VALUE_REGEX = re.compile(
class StoreUserMixin(object):
@transaction.commit_on_success
def store_user(self, user, request):
"""
WARNING: this should be wrapped inside a transactional view/method.
"""
user.save()
self.post_store_user(user, request)
return user
......@@ -150,7 +153,7 @@ class LocalUserCreationForm(UserCreationForm, StoreUserMixin):
email = self.cleaned_data['email']
if not email:
raise forms.ValidationError(_(astakos_messages.REQUIRED_FIELD))
if reserved_email(email):
if reserved_verified_email(email):
raise forms.ValidationError(_(astakos_messages.EMAIL_USED))
return email
......@@ -236,6 +239,7 @@ class ThirdPartyUserCreationForm(forms.ModelForm, StoreUserMixin):
widget=forms.HiddenInput(),
label=''
)
class Meta:
model = AstakosUser
fields = ['id', 'email', 'third_party_identifier', 'first_name', 'last_name']
......@@ -269,7 +273,7 @@ class ThirdPartyUserCreationForm(forms.ModelForm, StoreUserMixin):
email = self.cleaned_data['email']
if not email:
raise forms.ValidationError(_(astakos_messages.REQUIRED_FIELD))
if reserved_email(email):
if reserved_verified_email(email):
raise forms.ValidationError(_(astakos_messages.EMAIL_USED))
return email
......@@ -283,10 +287,9 @@ class ThirdPartyUserCreationForm(forms.ModelForm, StoreUserMixin):
pending = PendingThirdPartyUser.objects.get(
token=request.POST.get('third_party_token'),
third_party_identifier= \
self.cleaned_data.get('third_party_identifier'))
self.cleaned_data.get('third_party_identifier'))
return user.add_pending_auth_provider(pending)
def save(self, commit=True):
user = super(ThirdPartyUserCreationForm, self).save(commit=False)
user.set_unusable_password()
......@@ -525,7 +528,7 @@ class EmailChangeForm(forms.ModelForm):
def clean_new_email_address(self):
addr = self.cleaned_data['new_email_address']
if AstakosUser.objects.filter(email__iexact=addr):
if reserved_verified_email(addr):
raise forms.ValidationError(_(astakos_messages.EMAIL_USED))
return addr
......@@ -830,18 +833,12 @@ class ProjectApplicationForm(forms.ModelForm):
return policies
def save(self, commit=True):
application = super(ProjectApplicationForm, self).save(commit=False)
applicant = self.user
comments = self.cleaned_data.pop('comments', None)
return submit_application(
application,
self.resource_policies,
applicant,
comments,
self.precursor_application
)
data = dict(self.cleaned_data)
data['precursor_application'] = self.instance.id
data['owner'] = self.user
data['resource_policies'] = self.resource_policies
submit_application(data, request_user=self.user)
class ProjectSortForm(forms.Form):
sorting = forms.ChoiceField(
......@@ -877,7 +874,7 @@ class AddProjectMembersForm(forms.Form):
self.project = Project.objects.get(application__id=application_id)
self.request_user = kwargs.pop('request_user', None)
super(AddProjectMembersForm, self).__init__(*args, **kwargs)
def clean(self):
try:
do_accept_membership_checks(self.project, self.request_user)
......
......@@ -66,7 +66,13 @@ from astakos.im.settings import (
from astakos.im.notifications import build_notification, NotificationError
from astakos.im.models import (
AstakosUser, ProjectMembership, ProjectApplication, Project,
trigger_sync)
trigger_sync, PendingMembershipError)
from astakos.im.models import submit_application as models_submit_application
from astakos.im.project_notif import (
membership_change_notify,
application_submit_notify, application_approve_notify,
project_termination_notify, project_suspension_notify)
from astakos.im.endpoints.qh import qh_register_user
import astakos.im.messages as astakos_messages
......@@ -292,9 +298,14 @@ def activate(
if not user.activation_sent:
user.activation_sent = datetime.now()
user.save()
qh_register_user(user)
send_helpdesk_notification(user, helpdesk_email_template_name)
send_greeting(user, email_template_name)
def deactivate(user):
user.is_active = False
user.save()
def invite(inviter, email, realname):
inv = Invitation(inviter=inviter, username=email, realname=realname)
inv.save()
......@@ -429,12 +440,25 @@ def get_membership_for_update(project, user):
if isinstance(user, int):
user = get_user_by_id(user)
try:
return ProjectMembership.objects.select_for_update().get(
project=project,
person=user)
sfu = ProjectMembership.objects.select_for_update()
m = sfu.get(project=project, person=user)
if m.is_pending:
raise PendingMembershipError()
return m
except ProjectMembership.DoesNotExist:
raise IOError(_(astakos_messages.NOT_MEMBERSHIP_REQUEST))
def checkAllowed(project, request_user):
if request_user and \
(not project.application.owner == request_user and \
not request_user.is_superuser):
raise PermissionDenied(_(astakos_messages.NOT_ALLOWED))
def checkAlive(project):
if not project.is_alive:
raise PermissionDenied(
_(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
def accept_membership(project_application_id, user, request_user=None):
"""
Raises:
......@@ -445,13 +469,8 @@ def accept_membership(project_application_id, user, request_user=None):
return do_accept_membership(project_id, user, request_user)
def do_accept_membership_checks(project, request_user):
if request_user and \
(not project.application.owner == request_user and \
not request_user.is_superuser):
raise PermissionDenied(_(astakos_messages.NOT_ALLOWED))
if not project.is_alive:
raise PermissionDenied(
_(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
checkAllowed(project, request_user)
checkAlive(project)
join_policy = project.application.member_join_policy
if join_policy == CLOSED_POLICY:
......@@ -460,27 +479,16 @@ def do_accept_membership_checks(project, request_user):
if project.violates_members_limit(adding=1):
raise PermissionDenied(_(astakos_messages.MEMBER_NUMBER_LIMIT_REACHED))
def do_accept_membership(
project_id, user, request_user=None, bypass_checks=False):
def do_accept_membership(project_id, user, request_user=None):
project = get_project_for_update(project_id)
if not bypass_checks:
do_accept_membership_checks(project, request_user)
do_accept_membership_checks(project, request_user)
membership = get_membership_for_update(project, user)
membership.accept()
trigger_sync()
try:
notification = build_notification(
settings.SERVER_EMAIL,
[membership.person.email],
_(PROJECT_MEMBERSHIP_CHANGE_SUBJECT) % project.__dict__,
template='im/projects/project_membership_change_notification.txt',
dictionary={'object':project.application, 'action':'accepted'})
notification.send()
except NotificationError, e:
logger.error(e.message)
membership_change_notify(project, membership.person, 'accepted')
return membership
def reject_membership(project_application_id, user, request_user=None):
......@@ -493,34 +501,18 @@ def reject_membership(project_application_id, user, request_user=None):
return do_reject_membership(project_id, user, request_user)
def do_reject_membership_checks(project, request_user):
if request_user and \
(not project.application.owner == request_user and \
not request_user.is_superuser):
raise PermissionDenied(_(astakos_messages.NOT_ALLOWED))
if not project.is_alive:
raise PermissionDenied(
_(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
checkAllowed(project, request_user)
checkAlive(project)
def do_reject_membership(
project_id, user, request_user=None, bypass_checks=False):
def do_reject_membership(project_id, user, request_user=None):
project = get_project_for_update(project_id)
do_reject_membership_checks(project, request_user)
if not bypass_checks:
do_reject_membership_checks(project, request_user)
membership = get_membership_for_update(project, user)
membership.reject()
try:
notification = build_notification(
settings.SERVER_EMAIL,
[membership.person.email],
_(PROJECT_MEMBERSHIP_CHANGE_SUBJECT) % project.__dict__,
template='im/projects/project_membership_change_notification.txt',
dictionary={'object':project.application, 'action':'rejected'})
notification.send()
except NotificationError, e:
logger.error(e.message)
membership_change_notify(project, membership.person, 'rejected')
return membership
def remove_membership(project_application_id, user, request_user=None):
......@@ -532,40 +524,24 @@ def remove_membership(project_application_id, user, request_user=None):
project_id = get_project_id_of_application_id(project_application_id)
return do_remove_membership(project_id, user, request_user)
def do_remove_membership_checks(project, membership, request_user):
if request_user and \
(not project.application.owner == request_user and \
not request_user.is_superuser):
raise PermissionDenied(_(astakos_messages.NOT_ALLOWED))
if not project.is_alive:
raise PermissionDenied(
_(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
def do_remove_membership(
project_id, user, request_user=None, bypass_checks=False):
project = get_project_for_update(project_id)
if not bypass_checks:
do_remove_membership_checks(project, request_user)
def do_remove_membership_checks(project, membership, request_user=None):
checkAllowed(project, request_user)
checkAlive(project)
leave_policy = project.application.member_leave_policy
if leave_policy == CLOSED_POLICY:
raise PermissionDenied(_(astakos_messages.MEMBER_LEAVE_POLICY_CLOSED))
def do_remove_membership(project_id, user, request_user=None):
project = get_project_for_update(project_id)
do_remove_membership_checks(project, request_user)
membership = get_membership_for_update(project, user)
membership.remove()
trigger_sync()
try:
notification = build_notification(
settings.SERVER_EMAIL,
[membership.person.email],
_(PROJECT_MEMBERSHIP_CHANGE_SUBJECT) % project.__dict__,
template='im/projects/project_membership_change_notification.txt',
dictionary={'object':project.application, 'action':'removed'})
notification.send()
except NotificationError, e:
logger.error(e.message)
membership_change_notify(project, membership.person, 'removed')
return membership
def enroll_member(project_application_id, user, request_user=None):
......@@ -573,9 +549,15 @@ def enroll_member(project_application_id, user, request_user=None):
return do_enroll_member(project_id, user, request_user)
def do_enroll_member(project_id, user, request_user=None):
project = get_project_for_update(project_id)
do_accept_membership_checks(project, request_user)
membership = create_membership(project_id, user)
return do_accept_membership(
project_id, user, request_user, bypass_checks=True)
membership.accept()
trigger_sync()
# TODO send proper notification
return membership
def leave_project(project_application_id, user_id):
"""
......@@ -587,19 +569,15 @@ def leave_project(project_application_id, user_id):
return do_leave_project(project_id, user_id)
def do_leave_project_checks(project):
if not project.is_alive:
m = _(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__
raise PermissionDenied(m)
checkAlive(project)
leave_policy = project.application.member_leave_policy
if leave_policy == CLOSED_POLICY:
raise PermissionDenied(_(astakos_messages.MEMBER_LEAVE_POLICY_CLOSED))
def do_leave_project(project_id, user_id, bypass_checks=False):
def do_leave_project(project_id, user_id):
project = get_project_for_update(project_id)
if not bypass_checks:
do_leave_project_checks(projetc)
do_leave_project_checks(project)
membership = get_membership_for_update(project, user_id)
......@@ -622,19 +600,15 @@ def join_project(project_application_id, user_id):
return do_join_project(project_id, user_id)
def do_join_project_checks(project):
if not project.is_alive:
m = _(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__
raise PermissionDenied(m)
checkAlive(project)
join_policy = project.application.member_join_policy
if join_policy == CLOSED_POLICY:
raise PermissionDenied(_(astakos_messages.MEMBER_JOIN_POLICY_CLOSED))
def do_join_project(project_id, user_id, bypass_checks=False):
def do_join_project(project_id, user_id):
project = get_project_for_update(project_id)
if not bypass_checks:
do_join_project_checks(project)
do_join_project_checks(project)
membership = create_membership(project, user_id)
......@@ -645,23 +619,24 @@ def do_join_project(project_id, user_id, bypass_checks=False):
trigger_sync()
return membership
def submit_application(
application, resource_policies, applicant, comments,
precursor_application=None):
def submit_application(kw, request_user=None):
application.submit(
resource_policies, applicant, comments, precursor_application)
try:
notification = build_notification(
settings.SERVER_EMAIL,
[i[1] for i in settings.ADMINS],
_(PROJECT_CREATION_SUBJECT) % application.__dict__,
template='im/projects/project_creation_notification.txt',
dictionary={'object':application})
notification.send()
except NotificationError, e:
logger.error(e)
kw['applicant'] = request_user
precursor_id = kw.get('precursor_application', None)
if precursor_id is not None:
sfu = ProjectApplication.objects.select_for_update()
precursor = sfu.get(id=precursor_id)
kw['precursor_application'] = precursor
if request_user and \
(not precursor.owner == request_user and \
not request_user.is_superuser):
raise PermissionDenied(_(astakos_messages.NOT_ALLOWED))
application = models_submit_application(**kw)
application_submit_notify(application)
return application
def update_application(app_id, **kw):
......@@ -691,51 +666,21 @@ def approve_application(app):
application.approve()
trigger_sync()
try:
notification = build_notification(
settings.SERVER_EMAIL,
[application.owner.email],
_(PROJECT_APPROVED_SUBJECT) % application.__dict__,
template='im/projects/project_approval_notification.txt',
dictionary={'object':application})
notification.send()
except NotificationError, e:
logger.error(e.message)
application_approve_notify(application)
def terminate(project_id):
project = get_project_for_update(project_id)
if not project.is_alive:
m = _(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__
raise PermissionDenied(m)
checkAlive(project)
project.terminate()
trigger_sync()
try:
notification = build_notification(
settings.SERVER_EMAIL,
[project.application.owner.email],
_(PROJECT_TERMINATION_SUBJECT) % project.__dict__,
template='im/projects/project_termination_notification.txt',
dictionary={'object':project.application}
).send()
except NotificationError, e:
logger.error(e.message)
project_termination_notify(project)
def suspend(project_id):
project = get_project_by_id(project_id)
project.last_approval_date = None
project.save()
trigger_sync()
try:
notification = build_notification(
settings.SERVER_EMAIL,
[project.application.owner.email],
_(PROJECT_SUSPENSION_SUBJECT) % project.__dict__,
template='im/projects/project_suspension_notification.txt',
dictionary={'object':project.application}
).send()
except NotificationError, e:
logger.error(e.message)
project_suspension_notify(project)
......@@ -45,8 +45,16 @@ class Command(NoArgsCommand):
def handle_noargs(self, **options):
try:
register_resources(Resource.objects.all())
register_users(AstakosUser.objects.all())
resources = list(Resource.objects.all())
print("Registering resources")
register_resources(resources)
print("Registering users")
users = list(AstakosUser.objects.verified().all())
if users:
register_users(users)
else:
print(" -> No verified users found.")
except BaseException, e:
logger.exception(e)
raise CommandError("Syncing failed.")
......@@ -53,10 +53,9 @@ class Command(NoArgsCommand):
apps = ProjectApplication.objects.select_related().all().order_by('id')
labels = (
'ApplID', 'PrecApplID', 'ApplState', 'ProjectID', 'name',
'ProjectStatus'
'Application', 'Precursor', 'Status', 'Name', 'Project', 'Status'
)
columns = (10, 10, 10, 10, 20, 10)
columns = (11, 10, 10, 30, 10, 10)
if not options['csv']:
line = ' '.join(l.rjust(w) for l, w in zip(labels, columns))
......@@ -87,8 +86,8 @@ class Command(NoArgsCommand):
str(app.id),
str(prec_id),
app.state,
str(project_id),
app.name,
str(project_id),
status
)
......
......@@ -52,8 +52,8 @@ class Command(NoArgsCommand):
def handle_noargs(self, **options):
services = Service.objects.all().order_by('id')
labels = ('id', 'name', 'url', 'auth_token', 'icon')
columns = (3, 12, 40, 20, 20)
labels = ('id', 'order', 'name', 'url', 'auth_token', 'icon')
columns = (3, 3, 12, 40, 20, 20)
if not options['csv']:
line = ' '.join(l.rjust(w) for l, w in zip(labels, columns))
......@@ -62,7 +62,7 @@ class Command(NoArgsCommand):
self.stdout.write(sep + '\n')
for service in services:
fields = (str(service.id), service.name,
fields = (str(service.id), str(service.order), service.name,
service.url,
service.auth_token or '',
service.icon)
......
# Copyright 2012 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
from optparse import make_option
from datetime import datetime
from django.core.management.base import BaseCommand, CommandError
from django.core.exceptions import ValidationError
from astakos.im.models import Service
from ._common import remove_user_permission, add_user_permission
class Command(BaseCommand):
args = "<service ID>"
help = "Modify service attributes"
option_list = BaseCommand.option_list + (
make_option('--order',
dest='order',
metavar='NUM',
default=None,
help="Set service order"),
make_option('--name',
dest='name',
default=None,
help="Set service name"),
make_option('--url',
dest='url',
default=None,
help="Set service url"),
make_option('--icon',
dest='icon',
default=None,
help="Set service icon (displayed by cloudbar)"),
make_option('--auth-token',
dest='auth_token',
default=None,
help="Set a custom service auth token"),